Summary
This document outlines the changes to the behaviour of EMF code merge due to the new implementation of the source code manipulation API.November 15, 2006
EMF relies on source code manipulation API to update the code with the new changes to the model. The previous implementation of the API does not support Java 5.0. The new implementation that supports Java 5.0 has been created and became the default implementation used by EMF.
The new implementation brings a few changes to the merge behaviour. These changes are outlined in this document.
If you are using EMF to generate and merge your code, see an overview of changes.
If your code is not Java 5.0 and you would like to use the previous behaviour of EMF code merge, see Switching to Previous Implementation section.
If you are using custom merge rules or JMerger tool outside EMF, see detailed information about the changes to the behaviour of merge rules.
The only comments that are merged are the Javadoc. Previously, if a comment is merged, all preceding comments were overwritten or not merged at all. See Example 1.
If a field that is moved is followed by a declaration that is removed, hanging comments between the field and the declaration will be kept and moved with the field. Previously, all comments preceding the removed declaration were removed. See Example 2.
Comments that immediately follow declarations may cause no empty lines inserted between moved or new declarations. See Example 2.
Empty lines are not added for new imports generated by changes in the model.
Code originally generated by EMF:
public abstract class BookImpl extends EObjectImpl implements Book
{
/**
* The default value of the '{@link #getPublicationDate() <em>Publication Date</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null;
/**
* The cached value of the '{@link #getPublicationDate() <em>Publication Date</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
}
User has modified Javadoc and added extra comments between the nodes:
public abstract class BookImpl extends EObjectImpl implements Book
{
// comment before PUBLICATION_DATE_EDEFAULT
/**
* <!-- begin-user-doc -->
* <p>User comment for PUBLICATION_DATE_EDEFAULT</p>
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null; // line comment at the end of PUBLICATION_DATE_EDEFAULT
// comments between fields
/*
* comments between fields
*/
// comment before publicationDate
/**
* <!-- begin-user-doc -->
* <p>User comment for publicationDate</p>
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
}
Now code has been regenerated by EMF. Note that all the comments are kept, and the changes are made to Javadoc only.
public abstract class BookImpl extends EObjectImpl implements Book
{
// comment before PUBLICATION_DATE_EDEFAULT
/**
* The default value of the '{@link #getPublicationDate() <em>Publication Date</em>}' attribute.
* <!-- begin-user-doc -->
* <p>User comment for PUBLICATION_DATE_EDEFAULT</p>
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null; // line comment at the end of PUBLICATION_DATE_EDEFAULT
// comments between fields
/*
* comments between fields
*/
// comment before publicationDate
/**
* The cached value of the '{@link #getPublicationDate() <em>Publication Date</em>}' attribute.
* <!-- begin-user-doc -->
* <p>User comment for publicationDate</p>
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
}
Below is the output produced by previous implementation. Note that all the comments between the nodes have been removed.
public abstract class BookImpl extends EObjectImpl implements Book
{
/**
* The default value of the '{@link #getPublicationDate() <em>Publication Date</em>}' attribute.
* <!-- begin-user-doc -->
* <p>User comment for PUBLICATION_DATE_EDEFAULT</p>
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null; // line comment at the end of PUBLICATION_DATE_EDEFAULT
/**
* The cached value of the '{@link #getPublicationDate() <em>Publication Date</em>}' attribute.
* <!-- begin-user-doc -->
* <p>User comment for publicationDate</p>
* <!-- end-user-doc -->
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
}
Code originally generated by EMF:
public abstract class BookImpl extends EObjectImpl implements Book
{
/**
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
/**
* @generated
* @ordered
*/
protected static final String AUTHOR_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected String author = AUTHOR_EDEFAULT;
/**
* @generated
* @ordered
*/
protected static final String TITLE_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected String title = TITLE_EDEFAULT;
}
User has moved title
field and added extra
comments.
Here is the new code:
public abstract class BookImpl extends EObjectImpl implements Book
{
/**
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null;
// comment immediately following the PUBLICATION_DATE_EDEFAULT will make the 'title' field
// inserted without empty lines after the move
/**
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
// preceding comments will be moved with 'TITLE_EDEFAULT'
/**
* @generated
* @ordered
*/
protected static final String TITLE_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected String title = TITLE_EDEFAULT;
// hanging comments after 'title' before removed 'isbn' one will be moved with 'title'
// preceding comment will be removed with 'ISBN_DEFAULT'
/**
* @generated
* @ordered
*/
protected static final String ISBN_EDEFAULT = null;
// comments between removed fields are removed
/**
* @generated
* @ordered
*/
protected String isbn = ISBN_EDEFAULT; // comment at the last line will be removed
// comments after removed 'isbn' are kept
/**
* @generated
* @ordered
*/
protected static final String AUTHOR_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected String author = AUTHOR_EDEFAULT;
}
User has removed isbn
field in the model, and
regenerated the code.
Here is the output code:
public abstract class BookImpl extends EObjectImpl implements Book
{
/**
* @generated
* @ordered
*/
protected static final Date PUBLICATION_DATE_EDEFAULT = null;
// comment immediately following the PUBLICATION_DATE_EDEFAULT will make the 'title' field
// inserted without empty lines after the move
/**
* @generated
* @ordered
*/
protected Date publicationDate = PUBLICATION_DATE_EDEFAULT;
// comments after removed 'isbn' are kept
/**
* @generated
* @ordered
*/
protected static final String AUTHOR_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected String author = AUTHOR_EDEFAULT;
// preceding comments will be moved with 'TITLE_EDEFAULT'
/**
* @generated
* @ordered
*/
protected static final String TITLE_EDEFAULT = null;
/**
* @generated
* @ordered
*/
protected String title = TITLE_EDEFAULT;
// hanging comments after 'title' before removed 'isbn' one will be moved with 'title'
}
Note that hanging comments before removed isbn
field have been kept and moved with title
field. Previous
implementation removes all the comments preceding removed nodes.
There are no empty lines before title
field due the
line comments immediately following PUBLICATION_DATE_EDEFAULT
field.
If you do not like the new behaviour of code merge, you can switch to the previous behaviour. Note that the previous behaviour does not support Java 5.0.
To switch to the previous behaviour of EMF code merge, follow these steps:
Open Properties View for your .genmodel
file.
To open EMF Generator, double-click on your .genmodel
file.
Right-click on your model and select Show Properties View.
In Properties window, set Templates & Merge > Facade Helper Class to org.eclipse.emf.codegen.merge.java.facade.jdom.JDOMFacadeHelper. This property sets the implementation class of the source code manipulation API.
If you would like to switch back to the new behaviour at a later time, set the above property to org.eclipse.emf.codegen.merge.java.facade.ast.ASTFacadeHelper.
JMerger now uses a new implementation of source code manipulation API for merging Java code. The new implementation uses JDT AST package, and adds support for Java 5.0. Previously, JMerger used implementation of the API based on JDOM package.
There are some changes in behaviour of JMerger rules due to the new implementation.
Methods getComment and setComment of a Member node operate only on Javadoc immediately preceding the Member.
When any Node is copied from source to target, the Javadoc immediately preceding the Node and a line comment at the last line of the Node are the only surrounding comments that are copied with the Node. No other leading or trailing comments are copied.
If Fields are moved (sort rule), Javadoc, all comments preceding the Field, and a line comment at the last line of the Field are moved with the Field. However, when the first Field of the Type is moved, preceding hanging comments are not moved with the Field.
If Member is removed, Javadoc, all comments preceding the Member, and a line comment at the last line of the Member are removed. The hanging comments between Member nodes are kept if at most one of the two Member nodes surrounding the comments is removed. If the removed Member is preceded by a Field that is being moved, comments between these nodes will be moved with the Field.
JDOM implementation uses all preceding comments in the getComment/setComment methods. In addition, all preceding comments and the line comment at the last line of the node are moved, copied, and removed with the node.
AST uses formatter options from Javacore to format the code and to calculate the indentation of the inserted and moved nodes. Therefore, it is important that Javacore options are set correctly.
The indent
parameter in merge.xml
(or
another XML file used for initialization of JControlModel
)
allows overwriting of the options from Javacore. If indent
option is set, AST Facade implementation determines the indentation
options as follows. Tab character is set to tab or space based on the
first character of indent
string. Tab size and indentation
size are both set to be the length of indent
string.
Here is an example of the fragment of merge.xml
file
that sets indentation to be two spaces.
<?xml version="1.0" encoding="UTF-8"?>
<merge:options
indent=" "
xmlns:merge="http://www.eclipse.org/org/eclipse/emf/codegen/jmerge/Options">
To set the indent to be one tab character, use indent="	"
instead.
Note that AST implementation same as JDOM replaces all leading tab characters on each line by indent string when nodes are copied from the source to the target.
braceStyle
in
merge.xml
has to be used to correct this behaviour.
When Member nodes are inserted, empty lines surrounding new Member nodes are inserted based on existing nodes. If there are comments surrounding existing nodes in the target file, new lines might not be inserted as desired.
If you are developing a custom code manipulation tool based on
the AST Facade implementation (org.eclipse.emf.codegen.merge.java.facade.ast
package), you should be aware of some limitations of this
implementation.
Changes made to the tree are not reflected by get()
methods. Therefore, all information must be gathered before making
changes to the nodes.
Nodes copied (i.e. from the source to the target tree) by
calling cloneNode(...)
do not have an internal structure
or content for attributes and child nodes.
There are a few reasons for such limitations. AST does not keep
contents of each node in the node itself. Since JMerger is string based,
set()
methods replace the nodes with string placeholder
nodes that do not have an internal structure. Nodes are cloned and moved
by using similar string placeholders. In situations where a node is
cloned, moved, inserted, or replaced by string content, it would be an
extra overhead to keep the original content for each child and attribute
of the node.
For more information about the AST Facade implementation, see
Javadoc for classes in org.eclipse.emf.codegen.merge.java.facade.ast
package.