Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EMF Compare/Developer Guide"

Line 1: Line 1:
== Architecture ==
+
== Architecture ==
  
 +
=== Comparison Process  ===
  
 +
 The comparison process is divided in 2 phases : matching and differencing. The matching phase browses both models trying to figure out which element of model
  
 +
1 corresponds to which element of model 2. The differencing process then browses the result of the matching and creates the corresponding delta. The result of
  
 +
both phases can be serialized as models.
  
== Using the Compare Services ==
+
<br>
  
 +
[[Image:Compare Process.png]]
  
 +
<br>
  
 +
=== Plugins Architecture  ===
  
 +
[[Image:Compare Plugins.png]]
  
 +
=== Extensibility<br>  ===
  
 +
The red boxes in the following picture represent the component which were designed for extensibility. On all of these components can be plugged your own engines
  
== Tutorials ==
+
or behavior.
 +
<div>[[Image:Compare general extensibility.png]]<br></div>
 +
<br>
 +
 
 +
== Using the Compare Services  ==
 +
 
 +
<br>
 +
 
 +
==== Introduction  ====
 +
 
 +
<br>
 +
 
 +
<br>
 +
 
 +
All the following examples will use a simple (some would say "stupid") domain model used to keep personal information, here&nbsp;
 +
 
 +
contact lists.
 +
 
 +
<br>
 +
 
 +
Here is the diagram:
 +
 
 +
[[Image:AddressBookMM.png]]<br>
 +
 
 +
<br>
 +
 
 +
The whole comparison process is divided in two phases&nbsp;:
 +
 
 +
&nbsp;
 +
 
 +
#&nbsp;matching both versions of the elements and providing a match model.
 +
#computing the diff (delta) model from this match.
 +
 
 +
<br>
 +
 
 +
When comparing (using the user interface) two models conformed to the AddressBook metamodel you'll get:
 +
 
 +
<br>
 +
 
 +
[[Image:AddressBookComparaison1.png]]
 +
 
 +
<br>
 +
 
 +
This is an emfdiff model displayed using the emf compare editor. Both match and delta information are kept in this model, let's have&nbsp;
 +
 
 +
a further look on the match part:
 +
 
 +
<br> [[Image:AddressBookMatch.png]]
 +
 
 +
The match model weave elements from both versions of the model.
 +
 
 +
<br> [[Image:AddressBookMatchDetail.png]]
 +
 
 +
<br> From this match model EMF compare is able to compute the delta (diff):
 +
 
 +
<br>
 +
 
 +
[[Image:AddressBookDiffDetail.png]]
 +
 
 +
<br> <br>
 +
 
 +
==== Getting the differences of two models  ====
 +
 
 +
<br>
 +
 
 +
Getting these differences using some code is easy, here is the snippet::
 +
 
 +
<br>
 +
<pre>private DiffModel getDiff(AddressBook v1, AddressBook v2) throws Exception {
 +
        Map options = new HashMap();
 +
        MatchModel match = MatchService.doContentMatch(v1, v2, options);
 +
        // ...
 +
</pre>
 +
And you'll get the Match model which you can browse like any other EMF model.
 +
 
 +
Let's now produce the diff from this match information::
 +
 
 +
<br>
 +
<pre>  DiffModel diff =  DiffService.doDiff(match);
 +
        return diff;
 +
    }</pre>
 +
&nbsp;&nbsp;
 +
 
 +
Both Diff and Match services leverage the Eclipse architecture to find the best match/diff engine from the file extension.
 +
 
 +
But EMF Compare can be used to get the differences of two models within Eclipse or out of Eclipse (standalone mode) and using it in standalone mode you'll loose the ability of "auto-picking" the right match/diff engine considering the file extension&nbsp;: you'll have to call the engines yourself. Here the generic one::
 +
 
 +
<br>
 +
<pre>private DiffModel getDiff(AddressBook v1, AddressBook v2) throws Exception {
 +
        Map options = new HashMap();
 +
        MatchModel match = new GenericMatchEngine().contentMatch(v1,v2,options);       
 +
        DiffModel diff =  new GenericDiffEngine().doDiff(match);
 +
        return diff;
 +
    }
 +
</pre>
 +
<br>
 +
 
 +
The generic match engine can be parameterized thanks to some options available in the MatchOptions interface:
 +
 
 +
<br>
 +
 
 +
{| width="800" border="1" align="center" cellpadding="1" cellspacing="1"
 +
|-
 +
| Name
 +
| Description
 +
| Type
 +
|-
 +
| OPTION_DISTINCT_METAMODELS
 +
| If set to true, the engine will be able to compare two models from different metamodel and find similarities.
 +
| Boolean
 +
|-
 +
| OPTION_IGNORE_ID
 +
| If set to true, the engine will ignore the Ecore ID's and will consider the elements data to match them.
 +
| Boolean
 +
|-
 +
| OPTION_IGNORE_XMI_ID
 +
| If set to true, the engine will ignore the XMI ID's &nbsp;and will consider the elements data to match them.&nbsp;
 +
| Boolean
 +
|-
 +
| OPTION_SEARCH_WINDOW
 +
| The search window is the number of elements the engine will consider at the same time, the bigger it is, more precise is the result, but slower is the process.
 +
| Integer
 +
|-
 +
| OPTION_PROGRESS_MONITOR
 +
| If set with an IProgressMonitor instance, this one will&#124; IProgressMonitor be used to monitor the match process.
 +
| IProgressMonitor
 +
|}
 +
 
 +
&nbsp;
 +
<blockquote>&nbsp;&nbsp;please note that all these calls can be done with 3 models, left, right and the ancestor one. Then you'll be able to detect conflicts. <br></blockquote>
 +
<br>
 +
 
 +
==== Merging the differences  ====
 +
 
 +
<br>
 +
 
 +
<br>
 +
 
 +
Once you get the differences you can merge them. You can merge every detected delta from the diff model using the merge service::
 +
 
 +
<br>
 +
<pre>AddModelElement add = DiffHelper.isAdded(alice, changes);
 +
MergeService.merge(add, false);
 +
</pre>
 +
Here is a quick view of all the diff elements:
 +
 
 +
<br>
 +
 
 +
!images/DiffMM.jpg!
 +
 
 +
<br>
 +
 
 +
==== Using EMF Compare in standalone mode  ====
 +
 
 +
<br>
 +
 
 +
<br>
 +
 
 +
You can setup your environment to use EMF Compare in standalone mode. The will looks like this&nbsp;::
 +
 
 +
<br>
 +
<pre>/**
 +
* This application will try and launch an headless model comparison.
 +
*
 +
* @author Cedric Brun &lt;a href="mailto:cedric.brun@obeo.fr"&gt;cedric.brun@obeo.fr&lt;/a&gt;
 +
*/
 +
public final class ExampleLauncher {
 +
/**
 +
* This class doesn't need to be instantiated.
 +
*/
 +
private ExampleLauncher() {
 +
// prevents instantiation
 +
}
 +
 +
/**
 +
* Launcher of this application.
 +
*
 +
* @param args
 +
*            Arguments of the launch.
 +
*/
 +
public static void main(String[] args) {
 +
if (args.length == 2 &amp;&amp; new File(args[0]).canRead() &amp;&amp; new File(args[1]).canRead()) {
 +
// Creates the resourceSet where we'll load the models
 +
final ResourceSet resourceSet = new ResourceSetImpl();
 +
// Register additionnal packages here. For UML2 for instance&nbsp;:
 +
// Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION,
 +
// UMLResource.Factory.INSTANCE);
 +
// resourceSet.getPackageRegistry().put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE);
 +
 +
try {
 +
System.out.println("Loading resources.\n"); //$NON-NLS-1$
 +
// Loads the two models passed as arguments
 +
final EObject model1 = ModelUtils.load(new File(args[0]), resourceSet);
 +
final EObject model2 = ModelUtils.load(new File(args[1]), resourceSet);
 +
 +
// Creates the match then the diff model for those two models
 +
System.out.println("Matching models.\n"); //$NON-NLS-1$
 +
final MatchModel match = MatchService.doMatch(model1, model2, Collections
 +
.&lt;String, Object&gt; emptyMap());
 +
System.out.println("Differencing models.\n"); //$NON-NLS-1$
 +
final DiffModel diff = DiffService.doDiff(match, false);
 +
 +
System.out.println("Merging difference to args[1].\n"); //$NON-NLS-1$
 +
final List&lt;DiffElement&gt; differences = new ArrayList&lt;DiffElement&gt;(diff.getOwnedElements());
 +
// This will merge all references to the right model (second argument).
 +
MergeService.merge(differences, true);
 +
 +
// Prints the results
 +
try {
 +
System.out.println("MatchModel&nbsp;:\n"); //$NON-NLS-1$
 +
System.out.println(ModelUtils.serialize(match));
 +
System.out.println("DiffModel&nbsp;:\n"); //$NON-NLS-1$
 +
System.out.println(ModelUtils.serialize(diff));
 +
} catch (IOException e) {
 +
e.printStackTrace();
 +
}
 +
 +
// Serializes the result as "result.emfdiff" in the directory this class has been called from.
 +
System.out.println("saving emfdiff as \"result.emfdiff\""); //$NON-NLS-1$
 +
final ModelInputSnapshot snapshot = DiffFactory.eINSTANCE.createModelInputSnapshot();
 +
snapshot.setDate(Calendar.getInstance().getTime());
 +
snapshot.setMatch(match);
 +
snapshot.setDiff(diff);
 +
ModelUtils.save(snapshot, "result.emfdiff"); //$NON-NLS-1$
 +
} catch (IOException e) {
 +
// shouldn't be thrown
 +
e.printStackTrace();
 +
} catch (InterruptedException e) {
 +
e.printStackTrace();
 +
}
 +
} else {
 +
System.out.println("usage&nbsp;: ExampleLauncher &lt;Model1&gt; &lt;Model2&gt;"); //$NON-NLS-1$
 +
}
 +
}
 +
}</pre><div><br></div>
 +
<br>
 +
 
 +
== Tutorials ==
  
 
*[[Setting up a development environment for EMF Compare|Setting up a development environment for EMF Compare]]  
 
*[[Setting up a development environment for EMF Compare|Setting up a development environment for EMF Compare]]  

Revision as of 09:12, 3 March 2012

Architecture

Comparison Process

 The comparison process is divided in 2 phases : matching and differencing. The matching phase browses both models trying to figure out which element of model

1 corresponds to which element of model 2. The differencing process then browses the result of the matching and creates the corresponding delta. The result of

both phases can be serialized as models.


Compare Process.png


Plugins Architecture

Compare Plugins.png

Extensibility

The red boxes in the following picture represent the component which were designed for extensibility. On all of these components can be plugged your own engines

or behavior.

Compare general extensibility.png


Using the Compare Services


Introduction



All the following examples will use a simple (some would say "stupid") domain model used to keep personal information, here 

contact lists.


Here is the diagram:

AddressBookMM.png


The whole comparison process is divided in two phases :

 

  1.  matching both versions of the elements and providing a match model.
  2. computing the diff (delta) model from this match.


When comparing (using the user interface) two models conformed to the AddressBook metamodel you'll get:


AddressBookComparaison1.png


This is an emfdiff model displayed using the emf compare editor. Both match and delta information are kept in this model, let's have 

a further look on the match part:


AddressBookMatch.png

The match model weave elements from both versions of the model.


AddressBookMatchDetail.png


From this match model EMF compare is able to compute the delta (diff):


AddressBookDiffDetail.png



Getting the differences of two models


Getting these differences using some code is easy, here is the snippet::


private DiffModel getDiff(AddressBook v1, AddressBook v2) throws Exception {
        Map options = new HashMap();
        MatchModel match = MatchService.doContentMatch(v1, v2, options);
        // ...

And you'll get the Match model which you can browse like any other EMF model.

Let's now produce the diff from this match information::


  DiffModel diff =  DiffService.doDiff(match);
        return diff;
    }

  

Both Diff and Match services leverage the Eclipse architecture to find the best match/diff engine from the file extension.

But EMF Compare can be used to get the differences of two models within Eclipse or out of Eclipse (standalone mode) and using it in standalone mode you'll loose the ability of "auto-picking" the right match/diff engine considering the file extension : you'll have to call the engines yourself. Here the generic one::


private DiffModel getDiff(AddressBook v1, AddressBook v2) throws Exception {
        Map options = new HashMap();
        MatchModel match = new GenericMatchEngine().contentMatch(v1,v2,options);        
        DiffModel diff =  new GenericDiffEngine().doDiff(match);
        return diff;
    }


The generic match engine can be parameterized thanks to some options available in the MatchOptions interface:


Name Description Type
OPTION_DISTINCT_METAMODELS If set to true, the engine will be able to compare two models from different metamodel and find similarities. Boolean
OPTION_IGNORE_ID If set to true, the engine will ignore the Ecore ID's and will consider the elements data to match them. Boolean
OPTION_IGNORE_XMI_ID If set to true, the engine will ignore the XMI ID's  and will consider the elements data to match them.  Boolean
OPTION_SEARCH_WINDOW The search window is the number of elements the engine will consider at the same time, the bigger it is, more precise is the result, but slower is the process. Integer
OPTION_PROGRESS_MONITOR If set with an IProgressMonitor instance, this one will| IProgressMonitor be used to monitor the match process. IProgressMonitor

 

  please note that all these calls can be done with 3 models, left, right and the ancestor one. Then you'll be able to detect conflicts.


Merging the differences



Once you get the differences you can merge them. You can merge every detected delta from the diff model using the merge service::


AddModelElement add = DiffHelper.isAdded(alice, changes);
MergeService.merge(add, false);

Here is a quick view of all the diff elements:


!images/DiffMM.jpg!


Using EMF Compare in standalone mode



You can setup your environment to use EMF Compare in standalone mode. The will looks like this ::


/**
	 * This application will try and launch an headless model comparison.
	 * 
	 * @author Cedric Brun <a href="mailto:cedric.brun@obeo.fr">cedric.brun@obeo.fr</a>
	 */
	public final class ExampleLauncher {
		/**
		 * This class doesn't need to be instantiated.
		 */
		private ExampleLauncher() {
			// prevents instantiation
		}
	
		/**
		 * Launcher of this application.
		 * 
		 * @param args
		 *            Arguments of the launch.
		 */
		public static void main(String[] args) {
			if (args.length == 2 && new File(args[0]).canRead() && new File(args[1]).canRead()) {
				// Creates the resourceSet where we'll load the models
				final ResourceSet resourceSet = new ResourceSetImpl();
				// Register additionnal packages here. For UML2 for instance :
	//			Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION,
	//					UMLResource.Factory.INSTANCE);
	//			resourceSet.getPackageRegistry().put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE);
	
				try {
					System.out.println("Loading resources.\n"); //$NON-NLS-1$
					// Loads the two models passed as arguments
					final EObject model1 = ModelUtils.load(new File(args[0]), resourceSet);
					final EObject model2 = ModelUtils.load(new File(args[1]), resourceSet);
	
					// Creates the match then the diff model for those two models
					System.out.println("Matching models.\n"); //$NON-NLS-1$
					final MatchModel match = MatchService.doMatch(model1, model2, Collections
							.<String, Object> emptyMap());
					System.out.println("Differencing models.\n"); //$NON-NLS-1$
					final DiffModel diff = DiffService.doDiff(match, false);
					
					System.out.println("Merging difference to args[1].\n"); //$NON-NLS-1$
					final List<DiffElement> differences = new ArrayList<DiffElement>(diff.getOwnedElements());
					// This will merge all references to the right model (second argument).
					MergeService.merge(differences, true);
	
					// Prints the results
					try {
						System.out.println("MatchModel :\n"); //$NON-NLS-1$
						System.out.println(ModelUtils.serialize(match));
						System.out.println("DiffModel :\n"); //$NON-NLS-1$
						System.out.println(ModelUtils.serialize(diff));
					} catch (IOException e) {
						e.printStackTrace();
					}
	
					// Serializes the result as "result.emfdiff" in the directory this class has been called from.
					System.out.println("saving emfdiff as \"result.emfdiff\""); //$NON-NLS-1$
					final ModelInputSnapshot snapshot = DiffFactory.eINSTANCE.createModelInputSnapshot();
					snapshot.setDate(Calendar.getInstance().getTime());
					snapshot.setMatch(match);
					snapshot.setDiff(diff);
					ModelUtils.save(snapshot, "result.emfdiff"); //$NON-NLS-1$
				} catch (IOException e) {
					// shouldn't be thrown
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println("usage : ExampleLauncher <Model1> <Model2>"); //$NON-NLS-1$
			}
		}
	}


Tutorials

You'll find a few more tutorials on the Eclipse Online Help once you installed EMF Compare

  • Architecture
  • Using the Compare Services
  • Adapting the Comparison Process to your Ecore Model
  • Adding new actions to the export menu

Additional UI APIs:

Feel free to add any tutorial or documentation on the wiki, we'll integrate them back in the online help.

Back to the top