代码自动生成,生成的代码许多时候需要修改,以满足我们的需求,而修改的代码我们希望重新生成代码的时候不被覆盖,那就需要一种Merge技术。JMerger和JET模板是在EMF代码生成中使用的关键技术。下面我们看一下一个使用JMerger的例子:
两个java文件:
HelloSrc.java
package hello;
public class Hello{
public void say(){
System.out.println("hello");
}
/**
* @generated
*/
public void sayHelloTo(String name){
System.out.println("Hello," + name);
}
public void test(){
}
}
HelloTarget.java
package hello;
public class Hello
{
public void say(){
System.out.println("hello world");
}
/**
* @generated
*/
public void sayHelloTo(String name){
System.out.println(name);
}
}
我们使用JMerger来合并覆盖原先生成的的方法//@generated标记的方法,而我们
自己定义的方法和去掉@generated的的方法则不被覆盖。
我们需要增加某种机制来告诉 JMerge 有些方法已经被修改过了,因此这些方法不应该被重写。要实现这种功能,可以使用 <merge:dictionaryPattern> 元素。 merge:dictionaryPattern 允许您使用正则表达式来区分 Java 元素:
<?xml version="1.0" encoding="UTF-8"?>
<merge:options xmlns:merge="http://www.eclipse.org/org/eclipse/emf/codegen/jmerge/Options">
<merge:dictionaryPattern
name="generatedMember"
select="Member/getComment"
match=
"s*@s*(gen)erateds*"/>
<merge:pull
targetMarkup="^gen$"
sourceGet="Method/getBody"
targetPut="Method/setBody"/>
</merge:options>
dictionaryPattern 定义了一个正则表达式,它可以匹配注释中包含 " @generated " 的成员。 select 属性列出了要对这个成员的哪些部分与在 match 属性中给出的正则表达式进行比较。 dictionaryPattern 是由字符串 gen 定义的,它就是 match 属性值中圆括号中的内容。merge:pull 元素多了一个附加属性 targetMarkup 。这个属性可以匹配 dictionaryPattern ,它必须在应用合并规则之前对目标代码进行匹配。此处,我们正在检查的是目标代码,而不是源代码,因此用户可以定制这些代码。当用户删除注释中的 " @generated " 标签时, dictionaryPattern 就不会与目标代码匹配,因此就不会合并这个方法体
package hello;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.emf.codegen.merge.java.JControlModel;
import org.eclipse.emf.codegen.merge.java.JMerger;
import org.eclipse.emf.codegen.merge.java.facade.ast.ASTFacadeHelper;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
public class JMergerTest
{
public static void merge( File src, File target )
{
JControlModel model = new JControlModel();
ASTFacadeHelper astFacadeHelper = new ASTFacadeHelper()
{
Map<String, String> options;
@SuppressWarnings("unchecked")
@Override
public Map getJavaCoreOptions()
{
if( options == null )
{
options = new HashMap<String, String>();
options.put( JavaCore.COMPILER_COMPLIANCE,
JavaCore.VERSION_1_6 );
options.put( JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6 );
options.put( JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
JavaCore.VERSION_1_6 );
options.put( JavaCore.COMPILER_PB_ASSERT_IDENTIFIER,
JavaCore.ERROR );
options.put( JavaCore.COMPILER_PB_ENUM_IDENTIFIER,
JavaCore.ERROR );
options.put( JavaCore.COMPILER_CODEGEN_INLINE_JSR_BYTECODE,
JavaCore.ENABLED );
options.put( JavaCore.COMPILER_DOC_COMMENT_SUPPORT,
JavaCore.ENABLED );
Map cfo = DefaultCodeFormatterConstants.getEclipseDefaultSettings();
options.putAll( cfo );
options.put( DefaultCodeFormatterConstants.FORMATTER_BLANK_LINES_AFTER_PACKAGE,
"1" );
}
return options;
}
};
String mergexml = JMergerTest.class.getResource( "merge.xml" ).getFile();
model.initialize( astFacadeHelper, mergexml );
JMerger jMerger = new JMerger(model);
try
{
jMerger.setSourceCompilationUnit( jMerger.createCompilationUnitForInputStream(
new FileInputStream( src )));
jMerger.setTargetCompilationUnit( jMerger.createCompilationUnitForInputStream(
new FileInputStream(target)));
}
catch( Exception e )
{
e.printStackTrace();
}
jMerger.merge();
String contents = jMerger.getTargetCompilationUnit().getContents();
System.out.println(contents);
}
public static void main( String[] args )
{
File src = new File("HelloSrc.java" );
File target = new File("HelloTarget.java");
JMergerTest.merge( src, target );
}
}
结果:
package hello;
public class Hello
{
public void say(){
System.out.println("hello world");
}
/**
* @generated
*/
public void sayHelloTo(String name){
System.out.println("Hello," + name);
}
public void test(){
}
}
参考http://www.ibm.com/developerworks/library/os-ecemf3/
上面有更详细的介绍,但代码使用的版本有点老。