WEB开发网
开发学院软件开发Java 使用 EMF Query 查询 EMF 模型 阅读

使用 EMF Query 查询 EMF 模型

 2009-12-17 00:00:00 来源:WEB开发网   
核心提示:EMF 是 Eclipse 组织推出的建模框架,它能够帮助我们将模型(UML, XSD等)转换成为健壮且功能丰富的Java 代码,使用 EMF Query 查询 EMF 模型,通过使用 EMF,我们编写的程序能免费的获得一个健壮的模型层,Transaction,Validation和Net4J等,它通常比我们自己手工编

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模型
使用 EMF Query 查询 EMF 模型

正如我们所看到的,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"向导建立新项目
使用 EMF Query 查询 EMF 模型

我们将Library.genmodel生成到model目录下,并双击其进行编辑。在.genmodel的编辑器中,我们选择Library包,并修改其"Base Package"属性为emf.model。这个属性会影响生成的Java代码的包名称。


图 3 使用"New EMF Model"向导建立新项目
使用 EMF Query 查询 EMF 模型

接下来,我们就可以生成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 查询 EMF 模型

在进行完迄今为止的这些步骤之后,我们可以开始编写代码来测试EMF Query的功能了。

3 使用EMF Query查询

EMF Query是一个非常容易使用的框架,Query框架将遍历模型中的每个元素,而用户需要做的工作是编写一个继承自Condition的类来判断模型中的元素是否应该出现在查询的结果中。在EMF Query框架中,已经提供了大量我们立刻可用的Condition子类。


图 5 使用Hierachy视图查看Condition类型
使用 EMF Query 查询 EMF 模型

通常情况下,我们既可以使用已有的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 查看测试结果
使用 EMF Query 查询 EMF 模型

5 结论

本文通过一个简单的例子介绍了使用EMF Query进行模型查询的基本过程。EMF Query是EMF Technology项目的一个重要的子项目。除了本文介绍的Query组件之外,EMF Technology还包含了其他一些有趣的主题,例如OCL,Teneo,Transaction,Validation和Net4J等。对模型驱动开发感兴趣的朋友不妨访问http://www.eclipse.org/emft 已获得更进一步的信息。

本文示例源代码或素材下载

Tags:使用 EMF Query

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接