EMF DiffMerge/Programmatic Usage
Contents
Define what to compare
The EMF Diff/Merge engine compares and merges model scopes. A model scope is most often a model or a subset of a model. More generally, it can be any set of model elements that is consistently defined, possibly spanning multiple EMF Resources. Keep in mind that elements or references which are outside model scopes are ignored during comparison and merge.
A model scope conforms to interface IEditableModelScope. Have a look at the subtype hierarchy to find predefined implementations of this interface. For example, class FragmentedModelScope allows creating model scopes from EMF Resources.
A comparison can be made between 2 or 3 model scopes which play different roles. Differences will be reported between the target scope and the reference scope (these terms are only used for the sake of distinction but have no deeper meaning). The ancestor scope, if present, is assumed to be the common ancestor of the 2 others before they started becoming different. It allows the engine to detect the origin of each difference.
// Instantiate the scopes to compare IEditableModelScope targetScope = new FragmentedModelScope(aResource, false); // For example IEditableModelScope referenceScope = new FragmentedModelScope(anotherResource, false); // For example
Compute comparison
A comparison conforms to interface IComparison whose default implementation is class EComparisonImpl. Once a comparison has been created from the model scopes, it can be computed.
IComparison comparison = new EComparisonImpl(targetScope, referenceScope); comparison.compute(aMatchPolicy, aDiffPolicy, aMergePolicy, aProgressMonitor);
Computing a comparison means matching the elements of the scopes and finding differences between them. This behavior is driven by a match policy and a diff policy. In addition, a merge policy determines how differences must be merged together so that the model remains consistent: this policy must be passed at this point in order to optimize the construction of the differences. The corresponding interfaces, IMatchPolicy, IDiffPolicy and IMergePolicy, have default implementations which can be customized.
Get and merge differences
The contents of a comparison can be explored in various ways. An easy way is to directly look for all differences, but it is an expensive operation:
Collection<IDifference> differences = comparison.getRemainingDifferences();
Another way is to iterate through the matched and unmatched elements of the target or reference scope:
TreeIterator<IMatch> it = comparison.getAllMatches(Role.TARGET); while (it.hasNext()) { IMatch aMatch = it.next(); Collection<IDifference> someDifferences = aMatch.getAllDifferences(); ... }
An IMatch represents an element in a model scope together with the matching element of the opposite scope, if any. The switch between an IMatch and the corresponding element(s) can be done as follows:
// From IMatch to EObject EObject element = aMatch.get(Role.TARGET); // Or Role.REFERENCE according to the expected scope // From EObject to IMatch IMatch match = comparison.getMapping().getMatchFor(anElement, Role.TARGET);
A difference or set of differences can be merged directly by calling method merge on the comparison. Alternatively, it is possible to use an IMergeSelector that determines if and how each difference must be merged.
comparison.merge(new IMergeSelector() { public Role getMergeDirection(IDifference difference) { Role result = null; ... // If difference is relevant, assign result return result; } }, true, aProgressMonitor);
There are only 3 essential types of difference (subtypes of IDifference):
- Presence of an unmatched element (IElementPresence): an element in a scope has no match in the opposite scope.
- Presence of an unmatched reference value (IReferenceValuePresence): a matched element references another element in only one scope.
- Presence of an unmatched attribute value (IAttributeValuePresence): a matched element owns a certain attribute value in only one scope.
At this stage, ordering differences are also defined via the types above. Multiplicities, containments, opposite references and so on are treated as constraints during merge operations.
Merging a difference means either deleting the presence from its scope (merge destination = presence scope) or reporting the presence to the opposite scope (merge destination = opposite of presence scope). In both cases, it may be necessary to merge other differences at the same time in order to preserve the consistency of the model. This is automatically done by the engine according to the merge policy of the comparison.
Note that these differences are intentionally elementary, technical and "syntactic". This is because a low level of abstraction is simpler to grasp when it comes to reasoning about the syntactic consistency of models being merged. The intent is to provide a sound framework on top of which higher-level "semantic" differences could be defined for specific metamodels.
Or: Use default GUI
Instead of getting and merging differences programmatically, it is possible to let the end-user do the job via a UI.
Let us assume the models to compare belong to a certain EMF editing domain (EditingDomain) and you have already computed the comparison.
// Turn comparison into an input for viewers final EMFDiffNode diffNode = new EMFDiffNode((EComparison)comparison, anEditingDomain); // Show the comparison in a dialog final Display display = Display.getDefault(); display.syncExec(new Runnable() { public void run() { DiffMergeDialog dialog = new DiffMergeDialog( display.getActiveShell(), "Your Title", diffNode); dialog.open(); } });
This will open a dialog that enables the user to visualize and merge differences.
Or: Extend default comparison action
The diff/merge GUI also provides a default comparison action (pop-up menu) whose behavior can be customized.
- User-defined model scopes can be associated to certain kinds of objects to compare (files, resources, EObjects, remote locations, etc.).
- User-defined comparison methods (diff, match, merge policies) can be associated to certain kinds of model scopes.
Plug-in org.eclipse.emf.diffmerge.ui.gmf provides a simple yet complete example of how to proceed.