EMF的JMerger使用

在生成java代码的中经常会遇到Java文件的合并问题,EMF的org.eclipse.emf.codegen插件提供了Java文件合并的工具类,用户只需要配置一定的规则就可以解决java文件的合并问题。

 

Jmerge-Name.jpg

EMF提供了简介的API调用:

 

public String mergeContent(String sourceContent,String targetContent)
{
//用户配置的合并规则
String jmergeRuleURI= URI.createPlatformPluginURI(
				"org.eclipse.emf.codegen.ecore/templates/emf-merge.xml", false).toString(); //$NON-NLS-1$

JControlModel model = new JControlModel();
model.initialize(new ASTFacadeHelper(), jmergeRuleURI);
JMerger jMerger = new JMerger(model);
jMerger.setSourceCompilationUnit(jMerger.createCompilationUnitForContents(sourceContent));
jMerger.setTargetCompilationUnit(jMerger.createCompilationUnitForContents(targetContent);
//执行合并规则
jMerger.merge();
return jMerger.getTargetCompilationUnit().getContents();
}				

 从上面代码可以看出合并规则是重点,我们以"org.eclipse.emf.codegen.ecore"插件中本身提供的emf-merge.xml来简要说明规则。

首先需要说明的是,我们对Java中的元素用节点(Node)表示,例如Field、Method都是java可编译单元的节点。对于java文件合并,无非三种情况:

1、如果Java Source中存在该节点,而且Java Target中也同样存在该节点,如何合并

2、如果Java Source中存在该节点,但是Java Target中不存在该节点,如何处理

3、如果Java Source中不存在该节点,但是Java Target中存在该节点,如何处理

在合并规则的xml文件中对这三种情况都有相应的配置。

 

其次在合并规则中有个重要的标记概念(markup),个人理解就是对节点的划分,比如一部分节点是自动生成的,这部分就属于“gen”的标记范围。这样在运用合并规则的时候,就可以对某一个范围进行操作。

 

 

下面首先看一下如何定义标记,

  <merge:dictionaryPattern

    name="generatedUnmodifiableMembers" 

    select="Member/getComment" 

    match="@\s*(gen)erated\s*(This field/method[^(?:\n\r?|\r\n?)]*)*(?:\n\r?|\r\n?)"/>

name属性仅仅在没有捕获的内容的时候作为标记名,例如 match="@\s*model" 可以匹配这个正则表达式,但是正则表达式中没有捕获的内容,那么将会用该名字作为标记,select属性中的内容分为两部分,并由‘/’分割。前面的部分表示节点的具体类型,示例中为Member,则表示节点的实际类型为org.eclipse.emf.codegen.merge.java.facade.JMember(可以查看该包下的JNode及子类,把Java可编译单元解析成具体的语法树模型),后面的getComment表示该实际类型的方法名。match属性为正则表达式。这个标记就表示如果JMember类型的comment可以匹配match所表示的正则表达式,那么这个JMember就属性'gen'标记。其中‘gen’就是正则表达式中捕获的字符串。

如果我的java代码如下:

/**

* Returns the value of the '<em><b>Title</b></em>' attribute.

* @generated

*/

 

String getTitle();

那么该节点会放在‘gen’标记中。

其他定义标签示例:

  <merge:dictionaryPattern

    name="modelMembers" 

    select="Member/getComment" 

 

    match="@\s*(model)"/>

 

 

EMF生成的Java代码的模型标记

 

如何定义合并规则:

1、如果Java Source中存在该节点,而且Java Target中也同样存在该节点,如何合并

使用pull元素来定义规则:

  <merge:pull 

    sourceMarkup="^gen$"

    sourceGet="Member/getComment"

    targetMarkup="^gen$"

    targetPut="Member/setComment"/>

sourceMarkup表示节点所属的标记,sourceGet也是有两部分组成,一部分表示节点的类型,另一部分表示获得的方法。该合并规则表示,如果target中的JMember类型属于'gen'标签,而且对应的source JMember类型也属于'gen'标签,那么会把source的comment设置到target的comment中。

如果想保留target中的部分java注释的话需要使用sourceTransfer属性,例如sourceTransfer="(\s*&lt;!--\s*begin-user-doc.*?end-user-doc\s*-->\s*)(?:\n\r?|\r\n?)",表示将会保留target中“// begin-user-code” 和“ // end-user-code” 包围住的所有的字符。

2、如果Java Source中存在该节点,但是Java Target中不存在该节点,如何处理

使用push元素定义规则:

 <merge:push targetParentMarkup="^gen$" select="Annotation"/>

表示如果Annotation节点的父属于'gen'标记,则会在target中添加该注解。

事实上在默认情况下如果没有声明所属的标记,默认markup为ture的,个人理解这个节点可以实现那些节点可以不用合并到target中,即仅仅在target父类型标记不为'gen'标记,则不会合并到target中。

 

3、如果Java Source中不存在该节点,但是Java Target中存在该节点,如何处理

使用sweep元素来定义规则:

  <merge:sweep markup="^gen$" select="Member"/>

表示如果该节点类型为JMember,而且属于'gen'标记,则会删除该节点。

实际上对于这种情况,如果JMerger提供了三种策略,删除节点,注释节点、重命名节点。默认为删除策略,通过action属性设置

 

如何根据source排序

如果想根据source中的顺序对target进行排序,需要定义排序规则:

  <merge:sort markup="^ordered$" select="Field"/>

如果source中field为f2/f1/f3.而且target中存在相应的Field,那么在合并后的输出Field为source的顺序,即f2/f1/f3。

 

在合并规则的xml根节点上可以定义不执行合并的Pattern

<merge:options 

  indent="  "  

  braceStyle="matching"  

  redirect="Gen"

  block="\s*@\s*generated\s*NOT\s*(?:\n\r?|\r\n?)"

  noImport="\s*//\s*import\s+([\w.*]*)\s*;\s*(?:\n\r?|\r\n?)"

  xmlns:merge="http://www.eclipse.org/org/eclipse/emf/codegen/jmerge/Options">

 

block属性就是不执行合并的模式,当用户的comment能匹配这种模式的时候,pull规则不会应用(即失效)。如果Type的comment匹配这种模式,这这个类型就不会执行合并操作。

 

参考:http://www.blogjava.net/JetGeng/archive/2006/05/01/44261.html

           http://www.blogjava.net/JetGeng/archive/2006/05/02/44342.html

你可能感兴趣的:(emf,JMerger)