程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用EMF Query查詢EMF模型

使用EMF Query查詢EMF模型

編輯:關於JAVA

EMF 是 Eclipse 組織推出的建模框架。它能夠幫助我們將模型(UML, XSD等)轉換成為健壯且功能豐富的Java 代碼。通過使用 EMF,我們編寫的程序能免費的獲得一個健壯的模型層,它通常比我們自己手工編寫的模型層更為健壯。事實上,有很多商業產品都使用了 EMF 來作為其模型層。由於 EMF 的廣泛使用,Eclipse 組織為其推出了眾多的周邊模塊。

1 介紹

由於EMF(全稱Eclipse Modeling Framework)在Java陣營中的廣泛使用,用戶迫切的需要更多基於EMF的功能。因而,Eclipse組織為其推出了眾多的周邊模塊。例如目前已經較為成熟的GEF(Graphical Editing Framework)和GMF (Graphical Modeling Framework)就能幫助用戶開發基於EMF的圖形編輯器。事實上,基於EMF的新技術遠不止GEF和GMF。EMFT (Eclipse Modeling Framework Technology) 是Eclipse專門用來發展基於EMF的新技術的專門項目。今天我們將要介紹的EMF Query就是EMFT的一個子組件。我們可以使用它來對EMF模型進行查詢,從而降低了處理復雜模型的難度。

2 建立Library模型

在介紹EMF的文章中,最常用的例子是Library樣例。Library模型的UML圖如下所示: (本文中用到的ecore model和源代碼在附件emfquery.zip中)

圖 1 Library模型

正如我們所看到的,Library例子相當簡單,它僅僅包含三個類:Library, Writer, Book,以及一個BookCategory枚舉類型。在使用EMF時,我們首先需要獲得一個ECore模型,這個Ecore模型將用於定義用戶模型(例如Library模型)的metadata。我們可以從頭開始創建一個ECore模型,也可以通過別的模型導入。如果使用Rational家族中的產品進行UML建模,那麼我們能夠在Rational產品中直接將UML模型導出為ECore模型。另外,我們也可以通過創建XSD文檔或者Annotated Java文件,並且利用EMF自帶的向導轉換為ECore模型。

在獲得了Ecore模型之後,我們在Eclipse中創建一個Java項目。在"New Java Project"向導中,我們將工程的名稱設置為test.emf.query,並且我們應當選擇分離源代碼目錄和輸出目錄。我們在新建好的 test.emf.query項目中建立一個新的model目錄,並將library.ecore文件保存到這個目錄中。

為了生成模型的Java實現,我們首先需要利用EMF提供的向導將.ecore模型轉化為.genmodel模型。這可以通過如圖 2所示的"New EMF Model"向導來進行。

圖 2 使用"New EMF Model"向導建立新項目

我們將Library.genmodel生成到model目錄下,並雙擊其進行編輯。在.genmodel的編輯器中,我們選擇Library包,並修改其"Base Package"屬性為emf.model。這個屬性會影響生成的Java代碼的包名稱。

圖 3 使用"New EMF Model"向導建立新項目

接下來,我們就可以生成Library模型的Java實現了。在Library包上單擊右鍵,並在彈出菜單中選擇Generate Model Code項,在test.emf.query項目中生成Library的Java實現。經過這一步之後,我們的test.emf.query將會變成一個插件項目,這並不意味著EMF只能作為插件被使用。對於插件項目而言,我們可以更為方便的設置對插件的依賴關系。現在我們通過雙擊打開 plugin.xml,然後進入Dependencies標簽頁,並添加對org.eclipse.emf.query插件的依賴,如圖 4所示。

圖 4 添加對EMF Query的依賴

在進行完迄今為止的這些步驟之後,我們可以開始編寫代碼來測試EMF Query的功能了。

3 使用EMF Query查詢

EMF Query是一個非常容易使用的框架,Query框架將遍歷模型中的每個元素,而用戶需要做的工作是編寫一個繼承自Condition的類來判斷模型中的元素是否應該出現在查詢的結果中。在EMF Query框架中,已經提供了大量我們立刻可用的Condition子類。

圖 5 使用Hierachy視圖查看Condition類型

通常情況下,我們既可以使用已有的Condition類也可以創建新的Condition類。對於在EMF模型中進行查詢而言,我們最常用到的是EObjectCondition類及其子類。

表 1 用於EMF的一些Condition類
類名 用途 EObjectCondition 所有用於檢查EMF模型的條件類都應該繼承EObjectCondition類。這個條件類接收一個PruneHandler類的對象。PruneHandler用於判斷是否需要處理當前正在比較的EMF對象的子對象。 EObjectAttributeValueCondition 這個條件類用於判斷當前正在處理的EMF對象的屬性是否滿足特定的條件。這個條件類接收一個PruneHandler對象和另一個Condition對象。傳入的Condition對象將被用於對屬性的值進行判斷。 EObjectReferencerCondition 這個條件類用於判斷當前正在處理的EMF對象是否引用了一個特定的EMF對象。

當我們了解了一些常用的Condition之後,我們就可以開始編寫代碼來對我們的EMF模型進行查詢了。下面的代碼演示了如何利用EObjectAttributeValueCondition和EObjectReferencerCondition來進行模型的查詢。

代碼 1 查詢超過500頁的書籍

public Collection queryLargeBook(EObject root) {
 SELECT select = new SELECT(new FROM(root), new WHERE(
  new EObjectAttributeValueCondition(LibraryPackage.eINSTANCE 
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
   .getBook_Pages(), new NumberCondition.IntegerValue(
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
   new Integer(500), new Integer(Integer.MAX_VALUE)))));
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|

 return select.execute();
 }

在上面的代碼中,我們檢查每個Book對象的Pages屬性。對於Pages屬性的檢查是通過一個NumberCondition對象進行的。

代碼 2 查詢某個作者創作的所有書籍

public Collection queryBookByWriter(EObject root, Writer writer) {
 SELECT select = new SELECT(new FROM(root), new WHERE(
  new EObjectReferencerCondition(writer)));

 return select.execute();
 }

在上面的代碼中,我們查詢了一個作者所寫的所有書籍。除了使用已有的條件類之外,我們也可以創建新的條件類,例如,如果希望查詢所有具有三個作者的書籍,那麼我們可以編寫如下的條件類:

代碼 3 查詢擁有三個作者的所有書籍

private class ThreeWriterCondition extends EObjectCondition {
 public ThreeWriterCondition() {
  super(PruneHandler.NEVER);
 }

 @Override 
 public boolean isSatisfied(EObject eObject) {
  if (eObject instanceof Book) {
  Book book = (Book) eObject;
  List writers = book.getWriter();
  if (writers.size() == 3)
   return true;
  }
  return false;
 }

 }

自己編寫的Condition類並沒有什麼特殊之處,其使用方法與庫中的條件類是完全一樣的:

代碼 4 使用新建的Condition類

public Collection queryBookWithThreeWriters(EObject root) {
 SELECT select = new SELECT(new FROM(root), new WHERE(
  new ThreeWriterCondition()));

 return select.execute();
 }

4 測試結果

最後,我們可以使用JUnit來測試我們的查詢代碼。由於我們的項目基於JDK 5.0開發,因而我們可以使用JUnit 4.0來進行測試。JUnit 4.0充分利用了JDK 5.0中引入的Annotation機制,在代碼編寫方面與之前的版本有著相當大的區別。當然,它還是一樣的簡單易用。下面就是我們用於測試三個 Query函數的測試方法。

代碼 5 使用JUnit 4.0測試查詢

@Test
 public void testQueryLargeBook() {
 Collection books = query.queryLargeBook(library);
 if (books.size() < 1)
  fail("No large book is found");
 for (Iterator iter = books.iterator(); iter.hasNext();) {
  Book element = (Book) iter.next();
  if (element.getPages() < 500)
  fail("Small book is found in the result");
 }
 }

 @Test
 public void testQueryBookByWriter() {
 Collection books = query.queryBookByWriter(library, writer);
 if (books.size() < 1)
  fail("No book is found");
 for (Iterator iter = books.iterator(); iter.hasNext();) {
  Book book = (Book) iter.next();
  if (!book.getWriter().contains(writer))
  fail("Found a book which is not authored by "
   + writer.getName());
 }
 }

 @Test
 public void testQueryBookWithThreeWriters() {
 Collection books = query.queryBookWithThreeWriters(library);
 if (books.size() < 1)
  fail("No book is found");
 for (Iterator iter = books.iterator(); iter.hasNext();) {
  Book book = (Book) iter.next();
  if (book.getWriter().size() != 3)
  fail("Found a book which has less than three authors");
 }
 }

在編寫測試用例時,JUnit 4.0不再強制要求我們遵守方法的命名規則。相反,只要用@Test標注過的方法,就是測試方法。另外,在測試執行之前,我們首先需要生成一個測試用的模型,這可以通過下面的代碼來完成:

代碼 6 生成測試用Library模型

@Before 
 public void setUp() throws Exception {
 lp = LibraryPackage.eINSTANCE;
 lf = LibraryFactory.eINSTANCE;

 library = lf.createLibrary();
 writer = lf.createWriter();
 writer.setName("James Gan");

 Writer writer1 = lf.createWriter();
 writer1.setName("Ping Hao");
 library.getWriters().add(writer1);

 Writer writer2 = lf.createWriter();
 writer2.setName("Qiu Qiu");
 library.getWriters().add(writer2);

 Book book = lf.createBook();
 book.setTitle("How to Query with EMF Query");
 book.setPages(1000);
 book.getWriter().add(writer);
 library.getBooks().add(book);

 Book book1 = lf.createBook();
 book1.setTitle("How to play basketball");
 book1.setPages(1000);
 book1.getWriter().add(writer);
 book1.getWriter().add(writer1);
 book1.getWriter().add(writer2);
 library.getBooks().add(book1);

 library.getWriters().add(writer);

 query = new QueryLibrary();
 }

和@Test一樣,@Before也是JUnit中的Anotation類,它表示被標注的方法將會在測試方法之前執行,用於進行測試場景的設置等工作。最後,我們運行JUnit進行測試,可以獲得如所圖 6示結果,所有方法均通過測試。

圖 6 查看測試結果

5 結論

本文通過一個簡單的例子介紹了使用EMF Query進行模型查詢的基本過程。EMF Query是EMF Technology項目的一個重要的子項目。除了本文介紹的Query組件之外,EMF Technology還包含了其他一些有趣的主題,例如OCL,Teneo,Transaction,Validation和Net4J等。對模型驅動開發感興趣的朋友不妨訪問http://www.eclipse.org/emft 已獲得更進一步的信息。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved