Jump to: navigation, search

EMF DiffMerge/Programmatic Usage

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); // For example
IEditableModelScope referenceScope = new FragmentedModelScope(anotherResource); // 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.