Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Delaigue2015-01-22 14:27:45 +0000
committerLaurent Delaigue2015-02-06 09:27:13 +0000
commite9a3d2fe29984dba7eb5f7ef4844551c828044ad (patch)
tree8bf0890a9a8ad7961be89c9c84c1159498c45b92
parent29a9ac12a847a69937bf5b3523c83cfcc93c590d (diff)
downloadorg.eclipse.emf.compare-e9a3d2fe29984dba7eb5f7ef4844551c828044ad.tar.gz
org.eclipse.emf.compare-e9a3d2fe29984dba7eb5f7ef4844551c828044ad.tar.xz
org.eclipse.emf.compare-e9a3d2fe29984dba7eb5f7ef4844551c828044ad.zip
[457034] ProximityEObjectMatcher should support cancellation
Improved monitoring by making use of SubMonitor and supporting cancel where it makes sense. Also added dedicated unit tests. Didn't want to break the API so added TODOs instead... Bug: 457034 Change-Id: I92dc82e2217c0cce818fb3d95a545f38f97bc86b Signed-off-by: Laurent Delaigue <laurent.delaigue@obeo.fr>
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java146
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java8
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/MonitorCancelTest.java247
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/MonitorCancelInputData.java36
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryLeft.ecore109
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryOrigin.ecore111
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryRight.ecore112
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java7
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/ComparisonCanceledException.java32
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/EMFCompare.java89
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/diff/DefaultDiffEngine.java8
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/emfcomparemessages.properties12
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/equi/DefaultEquiEngine.java8
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/SafeSubMonitor.java46
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/DefaultMatchEngine.java12
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/IdentifierEObjectMatcher.java16
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/ProximityEObjectMatcher.java834
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java8
18 files changed, 1334 insertions, 507 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java
index 0400d4c6d..61afc5f48 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 Obeo.
+ * Copyright (c) 2014, 2015 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -27,6 +27,7 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.emf.common.notify.Notifier;
@@ -62,9 +63,9 @@ import org.eclipse.team.core.mapping.IResourceMappingMerger;
import org.eclipse.team.core.mapping.provider.MergeStatus;
/**
- * A customized merger for the EMFResourceMappings. This will use EMF Compare to recompute the logical model
- * of the mappings it needs to merge, then merge everything to the left model if there are no conflicts,
- * stopping dead if there are any conflicts.
+ * A customized merger for the {@link EMFResourceMapping}s. This will use EMF Compare to recompute the logical
+ * model of the mappings it needs to merge, then merge everything to the left model if there are no conflicts,
+ * stopping dead if there is any conflict.
* <p>
* Mapping mergers are usually retrieved through an adapter registered on the ModelProvider. In this case,
* {@code org.eclipse.core.runtime.Platform.getAdapterManager().getAdapter(emfModelProvider, IResourceMappingMerger.class)}
@@ -91,68 +92,97 @@ public class EMFResourceMappingMerger implements IResourceMappingMerger {
return hasInvalidMappings;
}
- final Set<ResourceMapping> failingMappings = new HashSet<ResourceMapping>();
- for (ResourceMapping mapping : emfMappings) {
- // validateMappings() has made sure we only have EMFResourceMappings
- final SynchronizationModel syncModel = ((EMFResourceMapping)mapping).getLatestModel();
-
- final IModelMinimizer minimizer = new IdenticalResourceMinimizer();
- minimizer.minimize(syncModel, monitor);
- final IComparisonScope scope = ComparisonScopeBuilder.create(syncModel, monitor);
-
- final Comparison comparison = EMFCompare.builder().build().compare(scope,
- BasicMonitor.toMonitor(monitor));
- final IMerger.Registry mergerRegistry = EMFCompareRCPPlugin.getDefault().getMergerRegistry();
- if (hasRealConflict(comparison)) {
- // pre-merge what can be
- final Graph<Diff> differencesGraph = MergeDependenciesUtil.mapDifferences(comparison,
- mergerRegistry, true);
- final PruningIterator<Diff> iterator = differencesGraph.breadthFirstIterator();
- final Monitor emfMonitor = BasicMonitor.toMonitor(monitor);
-
- while (iterator.hasNext()) {
- final Diff next = iterator.next();
- if (hasConflict(ConflictKind.REAL).apply(next)) {
- iterator.prune();
- } else {
- if (next.getState() != DifferenceState.MERGED) {
- final IMerger merger = mergerRegistry.getHighestRankingMerger(next);
- merger.copyRightToLeft(next, emfMonitor);
- }
+ // Use a sub-monitor with 10 ticks per child
+ // For the time being, Cancel is not supported here because reverting changes is problematic
+ SubMonitor subMonitor = SubMonitor.convert(monitor, emfMappings.length);
+ try {
+ final Set<ResourceMapping> failingMappings = new HashSet<ResourceMapping>();
+ for (ResourceMapping mapping : emfMappings) {
+ mergeMapping(mapping, mergeContext, failingMappings, subMonitor.newChild(1));
+ }
+
+ if (!failingMappings.isEmpty()) {
+ final ResourceMapping[] failingArray = failingMappings
+ .toArray(new ResourceMapping[failingMappings.size()]);
+ return new MergeStatus(EMFCompareIDEUIPlugin.PLUGIN_ID, EMFCompareIDEUIMessages
+ .getString("EMFResourceMappingMerger.mergeFailedConflicts"), failingArray); //$NON-NLS-1$
+ }
+ return Status.OK_STATUS;
+ } finally {
+ if (monitor != null) {
+ monitor.done();
+ }
+ }
+ }
+
+ /**
+ * Merges one mapping.
+ *
+ * @param mapping
+ * The mapping to merge
+ * @param mergeContext
+ * The merge context
+ * @param failingMappings
+ * The set of failing mappings
+ * @param subMonitor
+ * The progress monitor to use, 10 ticks will be consumed
+ */
+ private void mergeMapping(ResourceMapping mapping, IMergeContext mergeContext,
+ final Set<ResourceMapping> failingMappings, IProgressMonitor monitor) throws CoreException {
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
+ // validateMappings() has made sure we only have EMFResourceMappings
+ final SynchronizationModel syncModel = ((EMFResourceMapping)mapping).getLatestModel();
+
+ final IModelMinimizer minimizer = new IdenticalResourceMinimizer();
+ minimizer.minimize(syncModel, subMonitor.newChild(1)); // 10%
+ final IComparisonScope scope = ComparisonScopeBuilder.create(syncModel, subMonitor.newChild(3)); // 40%
+
+ final Comparison comparison = EMFCompare.builder().build().compare(scope,
+ BasicMonitor.toMonitor(SubMonitor.convert(subMonitor.newChild(1), 10))); // 50%
+ final IMerger.Registry mergerRegistry = EMFCompareRCPPlugin.getDefault().getMergerRegistry();
+ if (hasRealConflict(comparison)) {
+ // pre-merge what can be
+ final Graph<Diff> differencesGraph = MergeDependenciesUtil.mapDifferences(comparison,
+ mergerRegistry, true);
+ final PruningIterator<Diff> iterator = differencesGraph.breadthFirstIterator();
+ final Monitor emfMonitor = BasicMonitor.toMonitor(subMonitor.newChild(5)); // 100%
+
+ while (iterator.hasNext()) {
+ final Diff next = iterator.next();
+ if (hasConflict(ConflictKind.REAL).apply(next)) {
+ iterator.prune();
+ } else {
+ if (next.getState() != DifferenceState.MERGED) {
+ final IMerger merger = mergerRegistry.getHighestRankingMerger(next);
+ merger.copyRightToLeft(next, emfMonitor);
}
}
+ }
- save(scope.getLeft());
+ save(scope.getLeft());
- failingMappings.add(mapping);
- } else {
- final IBatchMerger merger = new BatchMerger(mergerRegistry, fromSide(DifferenceSource.RIGHT));
- merger.copyAllRightToLeft(comparison.getDifferences(), BasicMonitor.toMonitor(monitor));
- save(scope.getLeft());
-
- for (IStorage storage : syncModel.getLeftTraversal().getStorages()) {
- if (storage.getFullPath() == null) {
- EMFCompareIDEUIPlugin.getDefault().getLog().log(
- new Status(IStatus.WARNING, EMFCompareIDEUIPlugin.PLUGIN_ID,
- EMFCompareIDEUIMessages
- .getString("EMFResourceMappingMerger.mergeIncomplete"))); //$NON-NLS-1$
- } else {
- final IDiff diff = mergeContext.getDiffTree().getDiff(storage.getFullPath());
- if (diff != null) {
- mergeContext.markAsMerged(diff, true, monitor);
- }
+ failingMappings.add(mapping);
+ } else {
+ final IBatchMerger merger = new BatchMerger(mergerRegistry, fromSide(DifferenceSource.RIGHT));
+ merger.copyAllRightToLeft(comparison.getDifferences(), BasicMonitor.toMonitor(subMonitor
+ .newChild(4))); // 60%
+ save(scope.getLeft());
+
+ for (IStorage storage : syncModel.getLeftTraversal().getStorages()) {
+ if (storage.getFullPath() == null) {
+ EMFCompareIDEUIPlugin.getDefault().getLog().log(
+ new Status(IStatus.WARNING, EMFCompareIDEUIPlugin.PLUGIN_ID,
+ EMFCompareIDEUIMessages
+ .getString("EMFResourceMappingMerger.mergeIncomplete"))); //$NON-NLS-1$
+ } else {
+ final IDiff diff = mergeContext.getDiffTree().getDiff(storage.getFullPath());
+ if (diff != null) {
+ mergeContext.markAsMerged(diff, true, subMonitor.newChild(1)); // 100%
}
}
}
}
-
- if (!failingMappings.isEmpty()) {
- final ResourceMapping[] failingArray = failingMappings
- .toArray(new ResourceMapping[failingMappings.size()]);
- return new MergeStatus(EMFCompareIDEUIPlugin.PLUGIN_ID, EMFCompareIDEUIMessages
- .getString("EMFResourceMappingMerger.mergeFailedConflicts"), failingArray); //$NON-NLS-1$
- }
- return Status.OK_STATUS;
+ subMonitor.setWorkRemaining(0);
}
/**
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
index 19712fbb4..0ada13e04 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
@@ -790,6 +790,7 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap
}
void compareInputChanged(CompareInputAdapter input, IProgressMonitor monitor) {
+ // TODO See why monitor is not used
compareInputChanged(null, (Comparison)input.getComparisonObject());
}
@@ -797,7 +798,9 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap
EMFCompare comparator = getCompareConfiguration().getEMFComparator();
IComparisonScope comparisonScope = input.getComparisonScope();
- final Comparison comparison = comparator.compare(comparisonScope, BasicMonitor.toMonitor(monitor));
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
+ final Comparison comparison = comparator.compare(comparisonScope, BasicMonitor.toMonitor(subMonitor
+ .newChild(10)));
compareInputChanged(input.getComparisonScope(), comparison);
}
@@ -951,8 +954,9 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap
EMFCompareBuilderConfigurator.createDefault().configure(comparisonBuilder);
+ SubMonitor subMonitorChild = SubMonitor.convert(subMonitor.newChild(15), 10);
final Comparison compareResult = comparisonBuilder.build().compare(scope,
- BasicMonitor.toMonitor(subMonitor.newChild(15)));
+ BasicMonitor.toMonitor(subMonitorChild));
compareResult.eAdapters().add(new ForwardingCompareInputAdapter(input));
ICompareInputLabelProvider labelProvider = getCompareConfiguration().getLabelProvider();
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/MonitorCancelTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/MonitorCancelTest.java
new file mode 100644
index 000000000..063433443
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/MonitorCancelTest.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.tests.monitor;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.Iterators;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.emf.common.util.BasicMonitor;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
+import org.eclipse.emf.compare.EMFCompare;
+import org.eclipse.emf.compare.diff.DefaultDiffEngine;
+import org.eclipse.emf.compare.diff.IDiffEngine;
+import org.eclipse.emf.compare.equi.DefaultEquiEngine;
+import org.eclipse.emf.compare.equi.IEquiEngine;
+import org.eclipse.emf.compare.match.DefaultMatchEngine;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.match.IMatchEngine.Factory;
+import org.eclipse.emf.compare.req.DefaultReqEngine;
+import org.eclipse.emf.compare.req.IReqEngine;
+import org.eclipse.emf.compare.scope.AbstractComparisonScope;
+import org.eclipse.emf.compare.scope.DefaultComparisonScope;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.tests.monitor.data.MonitorCancelInputData;
+import org.eclipse.emf.compare.utils.UseIdentifiers;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MonitorCancelTest {
+
+ private Comparison fComparison;
+
+ private Comparison fComparisonWithDiff;
+
+ /**
+ * Checks that the sending of a ComparisonCanceledException by the match engine causes a cancelled
+ * comparison to be returned.
+ */
+ @Test
+ public void testCancelDuringMatch() {
+ EMFCompare compare = EMFCompare.builder().setMatchEngineFactoryRegistry(TestRegistry.INSTANCE)
+ .build();
+ Comparison comparison = compare.compare(getEmptyScope(), new BasicMonitor());
+ assertEquals(Diagnostic.CANCEL, comparison.getDiagnostic().getSeverity());
+ }
+
+ /**
+ * Checks that the sending of a ComparisonCanceledException by the diff engine causes a cancelled
+ * comparison to be returned.
+ */
+ @Test
+ public void testCancelDuringDiff() {
+ EMFCompare compare = EMFCompare.builder().setDiffEngine(new IDiffEngine() {
+ public void diff(Comparison comparison, Monitor monitor) {
+ throw new ComparisonCanceledException();
+ }
+ }).build();
+ Comparison comparison = compare.compare(getEmptyScope(), new BasicMonitor());
+ assertEquals(Diagnostic.CANCEL, comparison.getDiagnostic().getSeverity());
+ }
+
+ /**
+ * Checks that the sending of a ComparisonCanceledException by the req engine causes a cancelled
+ * comparison to be returned.
+ */
+ @Test
+ public void testCancelDuringReq() {
+ EMFCompare compare = EMFCompare.builder().setRequirementEngine(new IReqEngine() {
+ public void computeRequirements(Comparison comparison, Monitor monitor) {
+ throw new ComparisonCanceledException();
+ }
+ }).build();
+ Comparison comparison = compare.compare(getEmptyScope(), new BasicMonitor());
+ assertEquals(Diagnostic.CANCEL, comparison.getDiagnostic().getSeverity());
+ }
+
+ /**
+ * Checks that the sending of a ComparisonCanceledException by the equivalence engine causes a cancelled
+ * comparison to be returned.
+ */
+ @Test
+ public void testCancelDuringEqui() {
+ EMFCompare compare = EMFCompare.builder().setEquivalenceEngine(new IEquiEngine() {
+ public void computeEquivalences(Comparison comparison, Monitor monitor) {
+ throw new ComparisonCanceledException();
+ }
+ }).build();
+ Comparison comparison = compare.compare(getEmptyScope(), new BasicMonitor());
+ assertEquals(Diagnostic.CANCEL, comparison.getDiagnostic().getSeverity());
+ }
+
+ @Test(expected = ComparisonCanceledException.class)
+ public void testMonitorCancelInMatch() throws IOException {
+ IMatchEngine engine = DefaultMatchEngine.create(UseIdentifiers.WHEN_AVAILABLE);
+ engine.match(getNonEmptyTestScope(), getCanceledMonitor());
+ }
+
+ @Test(expected = ComparisonCanceledException.class)
+ public void testMonitorCancelInDiff() throws IOException {
+ IDiffEngine diffEngine = new DefaultDiffEngine();
+ diffEngine.diff(fComparison, getCanceledMonitor());
+ }
+
+ @Test(expected = ComparisonCanceledException.class)
+ public void testMonitorCancelInReq() throws IOException {
+ IReqEngine reqEngine = new DefaultReqEngine();
+ reqEngine.computeRequirements(fComparisonWithDiff, getCanceledMonitor());
+ }
+
+ @Test(expected = ComparisonCanceledException.class)
+ public void testMonitorCancelInEqui() throws IOException {
+ IEquiEngine equiEngine = new DefaultEquiEngine();
+ equiEngine.computeEquivalences(fComparisonWithDiff, getCanceledMonitor());
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ IMatchEngine matchEngine = DefaultMatchEngine.create(UseIdentifiers.WHEN_AVAILABLE);
+ fComparison = matchEngine.match(getNonEmptyTestScope(), new BasicMonitor());
+ fComparisonWithDiff = matchEngine.match(getNonEmptyTestScope(), new BasicMonitor());
+ IDiffEngine diffEngine = new DefaultDiffEngine();
+ diffEngine.diff(fComparisonWithDiff, new BasicMonitor());
+ }
+
+ private BasicMonitor getCanceledMonitor() {
+ return new BasicMonitor() {
+ @Override
+ public boolean isCanceled() {
+ return true;
+ }
+ };
+ }
+
+ private IComparisonScope getNonEmptyTestScope() throws IOException {
+ final MonitorCancelInputData testData = new MonitorCancelInputData();
+ return new DefaultComparisonScope(testData.getLeft(), testData.getRight(), testData.getOrigin());
+ }
+
+ private IComparisonScope getEmptyScope() {
+ return new AbstractComparisonScope(null, null, null) {
+
+ public Iterator<? extends Resource> getCoveredResources(ResourceSet resourceSet) {
+ return Iterators.emptyIterator();
+ }
+
+ public Iterator<? extends EObject> getCoveredEObjects(Resource resource) {
+ return Iterators.emptyIterator();
+ }
+
+ public Iterator<? extends EObject> getChildren(EObject eObject) {
+ return Iterators.emptyIterator();
+ }
+ };
+ }
+
+ /**
+ * A match engine to use for tests, which systematically throws a ComparisonCanceledException.
+ *
+ * @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
+ */
+ private static final class CanceledMatchEngine implements IMatchEngine {
+
+ private static final CanceledMatchEngine INSTANCE = new CanceledMatchEngine();
+
+ public Comparison match(IComparisonScope scope, Monitor monitor) {
+ throw new ComparisonCanceledException();
+ }
+
+ }
+
+ /**
+ * A factory for tests that provides a CanceledMatchEngine.
+ *
+ * @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a
+ */
+ private static final class TestFactory implements IMatchEngine.Factory {
+
+ private static final TestFactory INSTANCE = new TestFactory();
+
+ public IMatchEngine getMatchEngine() {
+ return CanceledMatchEngine.INSTANCE;
+ }
+
+ public int getRanking() {
+ return 0;
+ }
+
+ public void setRanking(int parseInt) {
+ // Intentionally blank
+ }
+
+ public boolean isMatchEngineFactoryFor(IComparisonScope scope) {
+ return true;
+ }
+
+ }
+
+ /**
+ * A registry for tests that provides a TestFactory.
+ *
+ * @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
+ */
+ private static final class TestRegistry implements IMatchEngine.Factory.Registry {
+
+ private static final TestRegistry INSTANCE = new TestRegistry();
+
+ public Factory getHighestRankingMatchEngineFactory(IComparisonScope scope) {
+ return TestFactory.INSTANCE;
+ }
+
+ public List<Factory> getMatchEngineFactories(IComparisonScope scope) {
+ return Arrays.<Factory> asList(TestFactory.INSTANCE);
+ }
+
+ public Factory add(Factory matchEngineFactory) {
+ throw new RuntimeException();
+ }
+
+ public Factory remove(String className) {
+ return null;
+ }
+
+ public void clear() {
+ // Intentionally blank
+ }
+
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/MonitorCancelInputData.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/MonitorCancelInputData.java
new file mode 100644
index 000000000..f989c0510
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/MonitorCancelInputData.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.tests.monitor.data;
+
+import java.io.IOException;
+
+import org.eclipse.emf.compare.tests.framework.AbstractInputData;
+import org.eclipse.emf.ecore.resource.Resource;
+
+/**
+ * Provides input models to the unit tests of the matching by id.
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+@SuppressWarnings("nls")
+public class MonitorCancelInputData extends AbstractInputData {
+ public Resource getLeft() throws IOException {
+ return loadFromClassLoader("extlibraryLeft.ecore");
+ }
+
+ public Resource getRight() throws IOException {
+ return loadFromClassLoader("extlibraryRight.ecore");
+ }
+
+ public Resource getOrigin() throws IOException {
+ return loadFromClassLoader("extlibraryOrigin.ecore");
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryLeft.ecore b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryLeft.ecore
new file mode 100644
index 000000000..b26cef279
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryLeft.ecore
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmi:id="_14sTEG60EeGkd4g88tZXfA" name="extlibrary" nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0" nsPrefix="extlib">
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_146VgG60EeGkd4g88tZXfA" name="Book" eSuperTypes="_15LbQG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_146VgW60EeGkd4g88tZXfA" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_146Vg260EeGkd4g88tZXfA" name="pages" defaultValueLiteral="100">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_1468kW60EeGkd4g88tZXfA" name="category" eType="_15F7sG60EeGkd4g88tZXfA" unsettable="true"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_148KsG60EeGkd4g88tZXfA" name="author" lowerBound="1" eType="_15CRUW60EeGkd4g88tZXfA" eOpposite="_15EGgG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_148KsW60EeGkd4g88tZXfA" name="Library" eSuperTypes="_15OelG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_148Ksm60EeGkd4g88tZXfA" name="name">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_148xwW60EeGkd4g88tZXfA" name="writers" upperBound="-1" eType="_15CRUW60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_148xwm60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_148xw260EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_149Y0G60EeGkd4g88tZXfA" name="employees" upperBound="-1" eType="_15OekG60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_149Y0W60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_149Y0m60EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15AcIG60EeGkd4g88tZXfA" name="borrowers" upperBound="-1" eType="_15NQcW60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_15AcIW60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_15BDMG60EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BDMW60EeGkd4g88tZXfA" name="stock" ordered="false" upperBound="-1" eType="_15Hw4m60EeGkd4g88tZXfA" containment="true" resolveProxies="false"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BDMm60EeGkd4g88tZXfA" name="books" ordered="false" upperBound="-1" eType="_146VgG60EeGkd4g88tZXfA" transient="true" derived="true"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BqQG60EeGkd4g88tZXfA" name="branches" upperBound="-1" eType="_148KsW60EeGkd4g88tZXfA" containment="true" eOpposite="_15BqQm60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BqQm60EeGkd4g88tZXfA" name="parentBranch" eType="_148KsW60EeGkd4g88tZXfA" eOpposite="_15BqQG60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15BqRG60EeGkd4g88tZXfA" name="people" upperBound="-1">
+ <eAnnotations xmi:id="_15BqRW60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_15BqRm60EeGkd4g88tZXfA" key="kind" value="group"/>
+ </eAnnotations>
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EFeatureMapEntry"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15CRUW60EeGkd4g88tZXfA" name="Writer" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15CRUm60EeGkd4g88tZXfA" name="name" volatile="true" transient="true">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15EGgG60EeGkd4g88tZXfA" name="books" upperBound="-1" eType="_146VgG60EeGkd4g88tZXfA" eOpposite="_148KsG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EEnum" xmi:id="_15F7sG60EeGkd4g88tZXfA" name="BookCategory">
+ <eLiterals xmi:id="_15HJ0G60EeGkd4g88tZXfA" name="Mystery"/>
+ <eLiterals xmi:id="_15Hw4G60EeGkd4g88tZXfA" name="ScienceFiction" value="1"/>
+ <eLiterals xmi:id="_15Hw4W60EeGkd4g88tZXfA" name="Biography" value="2"/>
+ <eLiterals xmi:id="_XID4MG9IEeG7V_vNzpYwOw" name="Encyclopedia" value="3" literal="Encyclopedia"/>
+ <eLiterals xmi:id="_XIEfQG9IEeG7V_vNzpYwOw" name="Dictionary" value="4"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15Hw4m60EeGkd4g88tZXfA" name="Item" abstract="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15Hw4260EeGkd4g88tZXfA" name="publicationDate">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15IX8G60EeGkd4g88tZXfA" name="Borrowable" abstract="true" interface="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15JmEG60EeGkd4g88tZXfA" name="copies" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15JmEm60EeGkd4g88tZXfA" name="borrowers" ordered="false" upperBound="-1" eType="_15NQcW60EeGkd4g88tZXfA" eOpposite="_15N3gG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15LbQG60EeGkd4g88tZXfA" name="CirculatingItem" abstract="true" eSuperTypes="_15Hw4m60EeGkd4g88tZXfA _15IX8G60EeGkd4g88tZXfA"/>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MCUG60EeGkd4g88tZXfA" name="AudioVisualItem" abstract="true" eSuperTypes="_15LbQG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCUm60EeGkd4g88tZXfA" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCVG60EeGkd4g88tZXfA" name="length" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCVm60EeGkd4g88tZXfA" name="damaged">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MpYW60EeGkd4g88tZXfA" name="BookOnTape" eSuperTypes="_15MCUG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15MpZG60EeGkd4g88tZXfA" name="author" eType="_15CRUW60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MpZm60EeGkd4g88tZXfA" name="VideoCassette" eSuperTypes="_15MCUG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15NQcG60EeGkd4g88tZXfA" name="cast" upperBound="-1" eType="_15N3gm60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15NQcW60EeGkd4g88tZXfA" name="Borrower" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15N3gG60EeGkd4g88tZXfA" name="borrowed" upperBound="-1" eType="_15IX8G60EeGkd4g88tZXfA" eOpposite="_15JmEm60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15N3gm60EeGkd4g88tZXfA" name="Person" eSuperTypes="_15OelG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_XIJ-0G9IEeG7V_vNzpYwOw" name="fullName">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15OekG60EeGkd4g88tZXfA" name="Employee" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15Oekm60EeGkd4g88tZXfA" name="manager" eType="_15OekG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15OelG60EeGkd4g88tZXfA" name="Addressable" abstract="true" interface="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15OelW60EeGkd4g88tZXfA" name="address">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_XIKl4W9IEeG7V_vNzpYwOw" name="Magazine" eSuperTypes="_15LbQG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_XIKl429IEeG7V_vNzpYwOw" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_XIKl5W9IEeG7V_vNzpYwOw" name="pages">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryOrigin.ecore b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryOrigin.ecore
new file mode 100644
index 000000000..c891aa81b
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryOrigin.ecore
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmi:id="_14sTEG60EeGkd4g88tZXfA" name="extlibrary" nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0" nsPrefix="extlib">
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_146VgG60EeGkd4g88tZXfA" name="Book" eSuperTypes="_15LbQG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_146VgW60EeGkd4g88tZXfA" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_146Vg260EeGkd4g88tZXfA" name="pages" defaultValueLiteral="100">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_1468kW60EeGkd4g88tZXfA" name="category" eType="_15F7sG60EeGkd4g88tZXfA" unsettable="true"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_148KsG60EeGkd4g88tZXfA" name="author" lowerBound="1" eType="_15CRUW60EeGkd4g88tZXfA" eOpposite="_15EGgG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_148KsW60EeGkd4g88tZXfA" name="Library" eSuperTypes="_15OelG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_148Ksm60EeGkd4g88tZXfA" name="name">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_148xwW60EeGkd4g88tZXfA" name="writers" upperBound="-1" eType="_15CRUW60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_148xwm60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_148xw260EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_149Y0G60EeGkd4g88tZXfA" name="employees" upperBound="-1" eType="_15OekG60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_149Y0W60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_149Y0m60EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15AcIG60EeGkd4g88tZXfA" name="borrowers" upperBound="-1" eType="_15NQcW60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_15AcIW60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_15BDMG60EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BDMW60EeGkd4g88tZXfA" name="stock" ordered="false" upperBound="-1" eType="_15Hw4m60EeGkd4g88tZXfA" containment="true" resolveProxies="false"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BDMm60EeGkd4g88tZXfA" name="books" ordered="false" upperBound="-1" eType="_146VgG60EeGkd4g88tZXfA" transient="true" derived="true"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BqQG60EeGkd4g88tZXfA" name="branches" upperBound="-1" eType="_148KsW60EeGkd4g88tZXfA" containment="true" eOpposite="_15BqQm60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BqQm60EeGkd4g88tZXfA" name="parentBranch" eType="_148KsW60EeGkd4g88tZXfA" eOpposite="_15BqQG60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15BqRG60EeGkd4g88tZXfA" name="people" upperBound="-1">
+ <eAnnotations xmi:id="_15BqRW60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_15BqRm60EeGkd4g88tZXfA" key="kind" value="group"/>
+ </eAnnotations>
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EFeatureMapEntry"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15CRUW60EeGkd4g88tZXfA" name="Writer" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15CRUm60EeGkd4g88tZXfA" name="name" volatile="true" transient="true">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15EGgG60EeGkd4g88tZXfA" name="books" upperBound="-1" eType="_146VgG60EeGkd4g88tZXfA" eOpposite="_148KsG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EEnum" xmi:id="_15F7sG60EeGkd4g88tZXfA" name="BookCategory">
+ <eLiterals xmi:id="_15HJ0G60EeGkd4g88tZXfA" name="Mystery"/>
+ <eLiterals xmi:id="_15Hw4G60EeGkd4g88tZXfA" name="ScienceFiction" value="1"/>
+ <eLiterals xmi:id="_15Hw4W60EeGkd4g88tZXfA" name="Biography" value="2"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15Hw4m60EeGkd4g88tZXfA" name="Item" abstract="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15Hw4260EeGkd4g88tZXfA" name="publicationDate">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15IX8G60EeGkd4g88tZXfA" name="Lendable" abstract="true" interface="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15JmEG60EeGkd4g88tZXfA" name="copies" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15JmEm60EeGkd4g88tZXfA" name="borrowers" ordered="false" upperBound="-1" eType="_15NQcW60EeGkd4g88tZXfA" eOpposite="_15N3gG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15LbQG60EeGkd4g88tZXfA" name="CirculatingItem" abstract="true" eSuperTypes="_15Hw4m60EeGkd4g88tZXfA _15IX8G60EeGkd4g88tZXfA"/>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15LbQ260EeGkd4g88tZXfA" name="Periodical" abstract="true" eSuperTypes="_15Hw4m60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15LbRW60EeGkd4g88tZXfA" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15LbR260EeGkd4g88tZXfA" name="issuesPerYear" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MCUG60EeGkd4g88tZXfA" name="AudioVisualItem" abstract="true" eSuperTypes="_15LbQG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCUm60EeGkd4g88tZXfA" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCVG60EeGkd4g88tZXfA" name="minutesLength" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCVm60EeGkd4g88tZXfA" name="damaged">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MpYW60EeGkd4g88tZXfA" name="BookOnTape" eSuperTypes="_15MCUG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15MpY260EeGkd4g88tZXfA" name="reader" eType="_15N3gm60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15MpZG60EeGkd4g88tZXfA" name="author" eType="_15CRUW60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MpZm60EeGkd4g88tZXfA" name="VideoCassette" eSuperTypes="_15MCUG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15NQcG60EeGkd4g88tZXfA" name="cast" upperBound="-1" eType="_15N3gm60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15NQcW60EeGkd4g88tZXfA" name="Borrower" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15N3gG60EeGkd4g88tZXfA" name="borrowed" upperBound="-1" eType="_15IX8G60EeGkd4g88tZXfA" eOpposite="_15JmEm60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15N3gm60EeGkd4g88tZXfA" name="Person" eSuperTypes="_15OelG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15N3g260EeGkd4g88tZXfA" name="firstName" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15N3hW60EeGkd4g88tZXfA" name="lastName" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15OekG60EeGkd4g88tZXfA" name="Employee" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15Oekm60EeGkd4g88tZXfA" name="manager" eType="_15OekG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15OelG60EeGkd4g88tZXfA" name="Addressable" abstract="true" interface="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15OelW60EeGkd4g88tZXfA" name="address">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryRight.ecore b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryRight.ecore
new file mode 100644
index 000000000..09d5b0720
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/monitor/data/extlibraryRight.ecore
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmi:id="_14sTEG60EeGkd4g88tZXfA" name="extlibrary" nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0" nsPrefix="extlib">
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_146VgG60EeGkd4g88tZXfA" name="Book" eSuperTypes="_15LbQG60EeGkd4g88tZXfA _9M9ys29IEeGekPcBm25hwQ">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_146Vg260EeGkd4g88tZXfA" name="pages" defaultValueLiteral="100">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_1468kW60EeGkd4g88tZXfA" name="category" eType="_15F7sG60EeGkd4g88tZXfA" unsettable="true"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_148KsG60EeGkd4g88tZXfA" name="author" lowerBound="1" eType="_15CRUW60EeGkd4g88tZXfA" eOpposite="_15EGgG60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_9MlYMG9IEeGekPcBm25hwQ" name="subtitle">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_148KsW60EeGkd4g88tZXfA" name="Library" eSuperTypes="_15OelG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_148Ksm60EeGkd4g88tZXfA" name="name">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_148xwW60EeGkd4g88tZXfA" name="writers" upperBound="-1" eType="_15CRUW60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_148xwm60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_148xw260EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_149Y0G60EeGkd4g88tZXfA" name="employees" upperBound="-1" eType="_15OekG60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_149Y0W60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_149Y0m60EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15AcIG60EeGkd4g88tZXfA" name="borrowers" upperBound="-1" eType="_15NQcW60EeGkd4g88tZXfA" volatile="true" transient="true" derived="true" containment="true" resolveProxies="false">
+ <eAnnotations xmi:id="_15AcIW60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_15BDMG60EeGkd4g88tZXfA" key="group" value="#people"/>
+ </eAnnotations>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BDMW60EeGkd4g88tZXfA" name="stock" ordered="false" upperBound="-1" eType="_15Hw4m60EeGkd4g88tZXfA" containment="true" resolveProxies="false"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BDMm60EeGkd4g88tZXfA" name="books" ordered="false" upperBound="-1" eType="_146VgG60EeGkd4g88tZXfA" transient="true" derived="true"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BqQG60EeGkd4g88tZXfA" name="branches" upperBound="-1" eType="_148KsW60EeGkd4g88tZXfA" containment="true" eOpposite="_15BqQm60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15BqQm60EeGkd4g88tZXfA" name="parentBranch" eType="_148KsW60EeGkd4g88tZXfA" eOpposite="_15BqQG60EeGkd4g88tZXfA"/>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15BqRG60EeGkd4g88tZXfA" name="people" upperBound="-1">
+ <eAnnotations xmi:id="_15BqRW60EeGkd4g88tZXfA" source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+ <details xmi:id="_15BqRm60EeGkd4g88tZXfA" key="kind" value="group"/>
+ </eAnnotations>
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EFeatureMapEntry"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15CRUW60EeGkd4g88tZXfA" name="Writer" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15CRUm60EeGkd4g88tZXfA" name="name" volatile="true" transient="true">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15EGgG60EeGkd4g88tZXfA" name="books" upperBound="-1" eType="_146VgG60EeGkd4g88tZXfA" eOpposite="_148KsG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EEnum" xmi:id="_15F7sG60EeGkd4g88tZXfA" name="BookCategory">
+ <eLiterals xmi:id="_15HJ0G60EeGkd4g88tZXfA" name="Mystery"/>
+ <eLiterals xmi:id="_15Hw4G60EeGkd4g88tZXfA" name="ScienceFiction" value="1"/>
+ <eLiterals xmi:id="_15Hw4W60EeGkd4g88tZXfA" name="Biography" value="2"/>
+ <eLiterals xmi:id="_9Mw-YG9IEeGekPcBm25hwQ" name="Manga" value="3" literal="Manga"/>
+ <eLiterals xmi:id="_9Mw-YW9IEeGekPcBm25hwQ" name="Manhwa" value="5"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15Hw4m60EeGkd4g88tZXfA" name="Item" abstract="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15Hw4260EeGkd4g88tZXfA" name="publicationDate">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15IX8G60EeGkd4g88tZXfA" name="Lendable" abstract="true" interface="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15JmEG60EeGkd4g88tZXfA" name="copies" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15JmEm60EeGkd4g88tZXfA" name="borrowers" ordered="false" upperBound="-1" eType="_15NQcW60EeGkd4g88tZXfA" eOpposite="_15N3gG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15LbQG60EeGkd4g88tZXfA" name="CirculatingItem" abstract="true" eSuperTypes="_15Hw4m60EeGkd4g88tZXfA _15IX8G60EeGkd4g88tZXfA"/>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15LbQ260EeGkd4g88tZXfA" name="Periodical" abstract="true" eSuperTypes="_15Hw4m60EeGkd4g88tZXfA _9M9ys29IEeGekPcBm25hwQ">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15LbR260EeGkd4g88tZXfA" name="issuesPerYear" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MCUG60EeGkd4g88tZXfA" name="AudioVisualItem" abstract="true" eSuperTypes="_15LbQG60EeGkd4g88tZXfA _9M9ys29IEeGekPcBm25hwQ">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCVG60EeGkd4g88tZXfA" name="minutes" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15MCVm60EeGkd4g88tZXfA" name="damaged">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MpYW60EeGkd4g88tZXfA" name="BookOnTape" eSuperTypes="_15MCUG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15MpZG60EeGkd4g88tZXfA" name="author" eType="_15CRUW60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15MpZm60EeGkd4g88tZXfA" name="VideoCassette" eSuperTypes="_15MCUG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15NQcG60EeGkd4g88tZXfA" name="cast" upperBound="-1" eType="_15N3gm60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15NQcW60EeGkd4g88tZXfA" name="Borrower" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15N3gG60EeGkd4g88tZXfA" name="borrowed" upperBound="-1" eType="_15IX8G60EeGkd4g88tZXfA" eOpposite="_15JmEm60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15N3gm60EeGkd4g88tZXfA" name="Person" eSuperTypes="_15OelG60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15N3g260EeGkd4g88tZXfA" name="firstName" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15N3hW60EeGkd4g88tZXfA" name="familyName" lowerBound="1">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15OekG60EeGkd4g88tZXfA" name="Employee" eSuperTypes="_15N3gm60EeGkd4g88tZXfA">
+ <eStructuralFeatures xsi:type="ecore:EReference" xmi:id="_15Oekm60EeGkd4g88tZXfA" name="manager" eType="_15OekG60EeGkd4g88tZXfA"/>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_15OelG60EeGkd4g88tZXfA" name="Addressable" abstract="true" interface="true">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_15OelW60EeGkd4g88tZXfA" name="address">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_9M9ysW9IEeGekPcBm25hwQ" name="Magazine" eSuperTypes="_15LbQ260EeGkd4g88tZXfA"/>
+ <eClassifiers xsi:type="ecore:EClass" xmi:id="_9M9ys29IEeGekPcBm25hwQ" name="TitledItem">
+ <eStructuralFeatures xsi:type="ecore:EAttribute" xmi:id="_9M9ytG9IEeGekPcBm25hwQ" name="title">
+ <eType xsi:type="ecore:EDataType" href="http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java
index 3ffa48368..75261b04d 100644
--- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo.
+ * Copyright (c) 2012, 2015 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -20,9 +20,9 @@ import org.eclipse.emf.compare.tests.command.CommandStackTestSuite;
import org.eclipse.emf.compare.tests.conflict.ConflictDetectionTest;
import org.eclipse.emf.compare.tests.conflict.MultiLineAttributeConflictDetectionTest;
import org.eclipse.emf.compare.tests.diff.ComparisonUtilTest;
-import org.eclipse.emf.compare.tests.diff.FeatureMapMoveDiffTest;
import org.eclipse.emf.compare.tests.diff.DiffUtilTest;
import org.eclipse.emf.compare.tests.diff.FeatureFilterTest;
+import org.eclipse.emf.compare.tests.diff.FeatureMapMoveDiffTest;
import org.eclipse.emf.compare.tests.diff.LCSPerformanceTest;
import org.eclipse.emf.compare.tests.diff.ThreeWayTextDiffTest;
import org.eclipse.emf.compare.tests.diff.URIDistanceTest;
@@ -48,6 +48,7 @@ import org.eclipse.emf.compare.tests.merge.MultipleMergeTest;
import org.eclipse.emf.compare.tests.merge.PseudoConflictMergeTest;
import org.eclipse.emf.compare.tests.merge.ThreeWayBatchMergingTest;
import org.eclipse.emf.compare.tests.merge.TwoWayBatchMergingTest;
+import org.eclipse.emf.compare.tests.monitor.MonitorCancelTest;
import org.eclipse.emf.compare.tests.nodes.NodesPackage;
import org.eclipse.emf.compare.tests.nodes.util.NodesResourceFactoryImpl;
import org.eclipse.emf.compare.tests.postprocess.PostProcessorTest;
@@ -80,7 +81,7 @@ import org.junit.runners.Suite.SuiteClasses;
FeatureMapsPseudoConflictsMergeTest.class, TwoWayBatchMergingTest.class, EqualityHelperTest.class,
FeatureFilterTest.class, ThreeWayBatchMergingTest.class,
MultiLineAttributeConflictDetectionTest.class, ThreeWayTextDiffTest.class,
- MultiLineAttributeMergeTest.class })
+ MultiLineAttributeMergeTest.class, MonitorCancelTest.class })
public class AllTests {
/**
* Standalone launcher for all of compare's tests.
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/ComparisonCanceledException.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/ComparisonCanceledException.java
new file mode 100644
index 000000000..169b9a7f2
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/ComparisonCanceledException.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare;
+
+/**
+ * Exception used to manage cancellation of comparison operations.
+ *
+ * @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
+ * @since 3.2
+ */
+public class ComparisonCanceledException extends RuntimeException {
+
+ /**
+ * The serial version UID.
+ */
+ private static final long serialVersionUID = -3584412451390015285L;
+
+ /**
+ * Default constructor.
+ */
+ public ComparisonCanceledException() {
+ super();
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/EMFCompare.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/EMFCompare.java
index f66eea734..f0a17b83c 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/EMFCompare.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/EMFCompare.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2013 Obeo.
+ * Copyright (c) 2012, 2015 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -16,8 +16,10 @@ import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.conflict.DefaultConflictDetector;
import org.eclipse.emf.compare.conflict.IConflictDetector;
@@ -26,6 +28,8 @@ import org.eclipse.emf.compare.diff.DiffBuilder;
import org.eclipse.emf.compare.diff.IDiffEngine;
import org.eclipse.emf.compare.equi.DefaultEquiEngine;
import org.eclipse.emf.compare.equi.IEquiEngine;
+import org.eclipse.emf.compare.internal.spec.ComparisonSpec;
+import org.eclipse.emf.compare.internal.utils.SafeSubMonitor;
import org.eclipse.emf.compare.match.IMatchEngine;
import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl;
import org.eclipse.emf.compare.postprocessor.IPostProcessor;
@@ -51,6 +55,13 @@ import org.eclipse.emf.compare.scope.IComparisonScope;
*/
public class EMFCompare {
+ /**
+ * The value for diagnostics coming from EMF compare.
+ *
+ * @since 3.2
+ */
+ public static final String DIAGNOSTIC_SOURCE = "org.eclipse.emf.compare"; //$NON-NLS-1$
+
/** The registry we'll use to create a match engine for this comparison. */
private final IMatchEngine.Factory.Registry matchEngineFactoryRegistry;
@@ -158,43 +169,77 @@ public class EMFCompare {
* Launches the comparison with the given scope and reporting progress to the given {@code monitor}.
*
* @param scope
- * the scope to compare.
+ * the scope to compare, must not be {@code null}.
* @param monitor
- * the monitor to report progress to.
- * @return the result of the comparison.
+ * the monitor to report progress to, must not be {@code null}. {@code done()} will be called
+ * on it. If the monitor is cancelled, the result may be {@code null} (in rare cases) or
+ * contain a Diagnostic that indicates cancellation. <b>Note:</b> The given monitor is expected
+ * to use 10 ticks for 100%.
+ * @return The result of the comparison, which is never null but may be empty if the monitor has been
+ * canceled immediately after entering this method. The returned comparison will contain a
+ * relevant diagnostic indicating if the comparison has been canceled or if problems have occurred
+ * during its computation. Consequently, it is necessary to check the diagnostic of the returned
+ * comparison before using it.
+ * @throws ComparisonCanceledException
+ * If the comparison is cancelled at any time.
*/
public Comparison compare(IComparisonScope scope, final Monitor monitor) {
checkNotNull(scope);
checkNotNull(monitor);
- final Comparison comparison = matchEngineFactoryRegistry.getHighestRankingMatchEngineFactory(scope)
- .getMatchEngine().match(scope, monitor);
+ Comparison comparison = null;
+ try {
+ Monitor subMonitor = new SafeSubMonitor(monitor);
+ comparison = matchEngineFactoryRegistry.getHighestRankingMatchEngineFactory(scope)
+ .getMatchEngine().match(scope, subMonitor);
- List<IPostProcessor> postProcessors = postProcessorDescriptorRegistry.getPostProcessors(scope);
+ monitor.worked(1);
+ List<IPostProcessor> postProcessors = postProcessorDescriptorRegistry.getPostProcessors(scope);
- postMatch(comparison, postProcessors, monitor);
-
- if (!hasToStop(comparison, monitor)) {
- diffEngine.diff(comparison, monitor);
- postDiff(comparison, postProcessors, monitor);
+ postMatch(comparison, postProcessors, subMonitor);
+ monitor.worked(1);
if (!hasToStop(comparison, monitor)) {
- reqEngine.computeRequirements(comparison, monitor);
- postRequirements(comparison, postProcessors, monitor);
+ diffEngine.diff(comparison, subMonitor);
+ monitor.worked(1);
+ postDiff(comparison, postProcessors, subMonitor);
+ monitor.worked(1);
if (!hasToStop(comparison, monitor)) {
- equiEngine.computeEquivalences(comparison, monitor);
- postEquivalences(comparison, postProcessors, monitor);
-
- detectConflicts(comparison, postProcessors, monitor);
-
- postComparison(comparison, postProcessors, monitor);
+ reqEngine.computeRequirements(comparison, subMonitor);
+ monitor.worked(1);
+ postRequirements(comparison, postProcessors, subMonitor);
+ monitor.worked(1);
+
+ if (!hasToStop(comparison, monitor)) {
+ equiEngine.computeEquivalences(comparison, subMonitor);
+ monitor.worked(1);
+ postEquivalences(comparison, postProcessors, subMonitor);
+ monitor.worked(1);
+
+ detectConflicts(comparison, postProcessors, subMonitor);
+ monitor.worked(1);
+
+ postComparison(comparison, postProcessors, subMonitor);
+ }
}
}
+ } catch (ComparisonCanceledException e) {
+ if (comparison == null) {
+ comparison = new ComparisonSpec();
+ }
+ BasicDiagnostic cancelledDiag = new BasicDiagnostic(Diagnostic.CANCEL, DIAGNOSTIC_SOURCE, 0,
+ EMFCompareMessages.getString("EMFCompare.ComparisonCancelled"), null); //$NON-NLS-1$
+ Diagnostic diag = comparison.getDiagnostic();
+ if (diag != null && diag instanceof DiagnosticChain) {
+ ((DiagnosticChain)diag).merge(cancelledDiag);
+ } else {
+ comparison.setDiagnostic(cancelledDiag);
+ }
+ } finally {
+ monitor.done();
}
- monitor.done();
-
return comparison;
}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/diff/DefaultDiffEngine.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/diff/DefaultDiffEngine.java
index 41c1d4573..fce5da029 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/diff/DefaultDiffEngine.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/diff/DefaultDiffEngine.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo and others.
+ * Copyright (c) 2012, 2015 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -21,8 +21,10 @@ import java.util.List;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.DifferenceSource;
+import org.eclipse.emf.compare.EMFCompareMessages;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.internal.utils.ComparisonUtil;
import org.eclipse.emf.compare.internal.utils.DiffUtil;
@@ -108,6 +110,7 @@ public class DefaultDiffEngine implements IDiffEngine {
* org.eclipse.emf.common.util.Monitor)
*/
public void diff(Comparison comparison, Monitor monitor) {
+ monitor.subTask(EMFCompareMessages.getString("DefaultDiffEngine.monitor.diff")); //$NON-NLS-1$
for (Match rootMatch : comparison.getMatches()) {
checkForDifferences(rootMatch, monitor);
}
@@ -123,6 +126,9 @@ public class DefaultDiffEngine implements IDiffEngine {
* The monitor to report progress or to check for cancellation.
*/
protected void checkForDifferences(Match match, Monitor monitor) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
checkResourceAttachment(match, monitor);
final FeatureFilter featureFilter = createFeatureFilter();
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/emfcomparemessages.properties b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/emfcomparemessages.properties
index d12ec275a..5c696e52e 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/emfcomparemessages.properties
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/emfcomparemessages.properties
@@ -1,5 +1,5 @@
################################################################################
-# Copyright (c) 2006, 2012 Obeo.
+# Copyright (c) 2006, 2015 Obeo.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
@@ -26,4 +26,12 @@ ResourceAttachmentChangeSpec.MissingRS = Could not locate resource set to create
IMerger.MissingMerger = No merger found for diff {0}
ProximityEObjectMatcher.monitor.indexing = indexing objects
-ProximityEObjectMatcher.monitor.matching = matching objects \ No newline at end of file
+ProximityEObjectMatcher.monitor.matching = matching objects
+
+EMFCompare.ComparisonCancelled = Comparison has been cancelled.
+DefaultReqEngine.monitor.req = Computing requirements...
+DefaultDiffEngine.monitor.diff = Looking for differences...
+DefaultEquiEngine.monitor.eq = Computing equivalences...
+DefaultMatchEngine.monitor.match.resourceSet = Matching given resource sets...
+DefaultMatchEngine.monitor.match.resource = Matching given resources...
+DefaultMatchEngine.monitor.match.eobject = Matching given EObjects...
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/equi/DefaultEquiEngine.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/equi/DefaultEquiEngine.java
index b98db2ed1..1e9de6e06 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/equi/DefaultEquiEngine.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/equi/DefaultEquiEngine.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo and others.
+ * Copyright (c) 2012, 2015 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -22,8 +22,10 @@ import java.util.Set;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.CompareFactory;
import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
+import org.eclipse.emf.compare.EMFCompareMessages;
import org.eclipse.emf.compare.Equivalence;
import org.eclipse.emf.compare.FeatureMapChange;
import org.eclipse.emf.compare.Match;
@@ -54,7 +56,11 @@ public class DefaultEquiEngine implements IEquiEngine {
* org.eclipse.emf.common.util.Monitor)
*/
public void computeEquivalences(Comparison comparison, Monitor monitor) {
+ monitor.subTask(EMFCompareMessages.getString("DefaultEquiEngine.monitor.eq")); //$NON-NLS-1$
for (Diff difference : comparison.getDifferences()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
checkForEquivalences(comparison, difference);
}
}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/SafeSubMonitor.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/SafeSubMonitor.java
new file mode 100644
index 000000000..7f99402f3
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/SafeSubMonitor.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.internal.utils;
+
+import org.eclipse.emf.common.util.BasicMonitor;
+import org.eclipse.emf.common.util.Monitor;
+
+/**
+ * An EMF {@link Monitor} that can be safely passed to clients because it will ignore calls to
+ * {@code worked(int)} and {@code done()} to allow the caller to master the number of ticks consumed, whatever
+ * the clients do with the monitor. Such a monitor allows clients to cancel an operation while preventing them
+ * to negatively (or positively alas) impact the progress report.
+ *
+ * @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
+ */
+@SuppressWarnings("unused")
+// implements Monitor required for Juno and older
+public class SafeSubMonitor extends BasicMonitor.Delegating implements Monitor {
+ /**
+ * Constructor.
+ *
+ * @param monitor
+ * The wrapped monitor.
+ */
+ public SafeSubMonitor(Monitor monitor) {
+ super(monitor);
+ }
+
+ @Override
+ public void worked(int work) {
+ // Does nothing on purpose
+ }
+
+ @Override
+ public void done() {
+ // Does nothing on purpose
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/DefaultMatchEngine.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/DefaultMatchEngine.java
index d22304eb2..2acbf5bc9 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/DefaultMatchEngine.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/DefaultMatchEngine.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo and others.
+ * Copyright (c) 2012, 2015 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -24,6 +24,8 @@ import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.compare.CompareFactory;
import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
+import org.eclipse.emf.compare.EMFCompareMessages;
import org.eclipse.emf.compare.MatchResource;
import org.eclipse.emf.compare.match.eobject.CachingDistance;
import org.eclipse.emf.compare.match.eobject.EditionDistance;
@@ -146,6 +148,7 @@ public class DefaultMatchEngine implements IMatchEngine {
*/
protected void match(Comparison comparison, IComparisonScope scope, ResourceSet left, ResourceSet right,
ResourceSet origin, Monitor monitor) {
+ monitor.subTask(EMFCompareMessages.getString("DefaultMatchEngine.monitor.match.resourceSet")); //$NON-NLS-1$
final Iterator<? extends Resource> leftChildren = scope.getCoveredResources(left);
final Iterator<? extends Resource> rightChildren = scope.getCoveredResources(right);
final Iterator<? extends Resource> originChildren;
@@ -156,6 +159,8 @@ public class DefaultMatchEngine implements IMatchEngine {
}
final IResourceMatcher resourceMatcher = createResourceMatcher();
+
+ // TODO Change API to pass the monitor to createMappings()
final Iterable<MatchResource> mappings = resourceMatcher.createMappings(leftChildren, rightChildren,
originChildren);
@@ -164,6 +169,9 @@ public class DefaultMatchEngine implements IMatchEngine {
final List<Iterator<? extends EObject>> originIterators = Lists.newLinkedList();
for (MatchResource mapping : mappings) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
comparison.getMatchedResources().add(mapping);
final Resource leftRes = mapping.getLeft();
@@ -212,6 +220,7 @@ public class DefaultMatchEngine implements IMatchEngine {
*/
protected void match(Comparison comparison, IComparisonScope scope, Resource left, Resource right,
Resource origin, Monitor monitor) {
+ monitor.subTask(EMFCompareMessages.getString("DefaultMatchEngine.monitor.match.resource")); //$NON-NLS-1$
// Our "roots" are Resources. Consider them matched
final MatchResource match = CompareFactory.eINSTANCE.createMatchResource();
@@ -296,6 +305,7 @@ public class DefaultMatchEngine implements IMatchEngine {
*/
protected void match(Comparison comparison, IComparisonScope scope, EObject left, EObject right,
EObject origin, Monitor monitor) {
+ monitor.subTask(EMFCompareMessages.getString("DefaultMatchEngine.monitor.match.eobject")); //$NON-NLS-1$
if (left == null || right == null) {
throw new IllegalArgumentException();
}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/IdentifierEObjectMatcher.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/IdentifierEObjectMatcher.java
index a11f29af2..3b4d08f7c 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/IdentifierEObjectMatcher.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/IdentifierEObjectMatcher.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012-2014 Obeo and others.
+ * Copyright (c) 2012, 2015 Obeo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -28,6 +28,7 @@ import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.CompareFactory;
import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.match.eobject.EObjectIndex.Side;
import org.eclipse.emf.ecore.EObject;
@@ -111,6 +112,9 @@ public class IdentifierEObjectMatcher implements IEObjectMatcher {
public void createMatches(Comparison comparison, Iterator<? extends EObject> leftEObjects,
Iterator<? extends EObject> rightEObjects, Iterator<? extends EObject> originEObjects,
Monitor monitor) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
final List<EObject> leftEObjectsNoID = Lists.newArrayList();
final List<EObject> rightEObjectsNoID = Lists.newArrayList();
final List<EObject> originEObjectsNoID = Lists.newArrayList();
@@ -118,6 +122,7 @@ public class IdentifierEObjectMatcher implements IEObjectMatcher {
diagnostic = new BasicDiagnostic(Diagnostic.OK, "org.eclipse.emf.common", 0, //$NON-NLS-1$
org.eclipse.emf.common.CommonPlugin.INSTANCE.getString("_UI_OK_diagnostic_0"), null); //$NON-NLS-1$
+ // TODO Change API to pass the monitor to matchPerId()
final Set<Match> matches = matchPerId(leftEObjects, rightEObjects, originEObjects, leftEObjectsNoID,
rightEObjectsNoID, originEObjectsNoID);
@@ -130,16 +135,25 @@ public class IdentifierEObjectMatcher implements IEObjectMatcher {
doDelegation(comparison, leftEObjectsNoID, rightEObjectsNoID, originEObjectsNoID, monitor);
} else {
for (EObject eObject : leftEObjectsNoID) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
Match match = CompareFactory.eINSTANCE.createMatch();
match.setLeft(eObject);
matches.add(match);
}
for (EObject eObject : rightEObjectsNoID) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
Match match = CompareFactory.eINSTANCE.createMatch();
match.setRight(eObject);
matches.add(match);
}
for (EObject eObject : originEObjectsNoID) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
Match match = CompareFactory.eINSTANCE.createMatch();
match.setOrigin(eObject);
matches.add(match);
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/ProximityEObjectMatcher.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/ProximityEObjectMatcher.java
index 1b1488dc2..d0ea1d575 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/ProximityEObjectMatcher.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/eobject/ProximityEObjectMatcher.java
@@ -1,415 +1,419 @@
-/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Obeo - initial API and implementation
- *******************************************************************************/
-package org.eclipse.emf.compare.match.eobject;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.emf.common.util.BasicEList;
-import org.eclipse.emf.common.util.BasicMonitor;
-import org.eclipse.emf.common.util.Monitor;
-import org.eclipse.emf.compare.CompareFactory;
-import org.eclipse.emf.compare.Comparison;
-import org.eclipse.emf.compare.EMFCompareMessages;
-import org.eclipse.emf.compare.Match;
-import org.eclipse.emf.compare.match.eobject.EObjectIndex.Side;
-import org.eclipse.emf.compare.match.eobject.internal.ByTypeIndex;
-import org.eclipse.emf.compare.match.eobject.internal.MatchAheadOfTime;
-import org.eclipse.emf.ecore.EObject;
-
-/**
- * This matcher is using a distance function to match EObject. It guarantees that elements are matched with
- * the other EObject having the lowest distance. If two elements have the same distance regarding the other
- * EObject it will arbitrary pick one. (You should probably not rely on this and make sure your distance only
- * return 0 if both EObject have the very same content). The matcher will try to use the fact that it is a
- * distance to achieve a suitable scalability. It is also build on the following assumptions :
- * <ul>
- * <li>Most EObjects have no difference and have their corresponding EObject on the other sides of the model
- * (right and origins)</li>
- * <li>Two consecutive calls on the distance function with the same parameters will give the same distance.</li>
- * </ul>
- * The scalability you'll get will highly depend on the complexity of the distance function. The
- * implementation is not caching any distance result from two EObjects.
- *
- * @author <a href="mailto:cedric.brun@obeo.fr">Cedric Brun</a>
- */
-public class ProximityEObjectMatcher implements IEObjectMatcher, ScopeQuery {
- /**
- * Number of elements to index before a starting a match ahead step.
- */
- private static final int NB_ELEMENTS_BETWEEN_MATCH_AHEAD = 10000;
-
- /**
- * The index which keep the EObjects.
- */
- private EObjectIndex index;
-
- /**
- * Keeps track of which side was the EObject from.
- */
- private Map<EObject, Side> eObjectsToSide = Maps.newHashMap();
-
- /**
- * Create the matcher using the given distance function.
- *
- * @param meter
- * a function to measure the distance between two {@link EObject}s.
- */
- public ProximityEObjectMatcher(DistanceFunction meter) {
- this.index = new ByTypeIndex(meter, this);
- }
-
- /**
- * {@inheritDoc}
- */
-
- public void createMatches(Comparison comparison, Iterator<? extends EObject> leftEObjects,
- Iterator<? extends EObject> rightEObjects, Iterator<? extends EObject> originEObjects,
- Monitor monitor) {
- if (!leftEObjects.hasNext() && !rightEObjects.hasNext() && !originEObjects.hasNext()) {
- return;
- }
-
- // FIXME: how to create an EMF submonitor
- Monitor subMonitor = new BasicMonitor();
- subMonitor.beginTask(EMFCompareMessages.getString("ProximityEObjectMatcher.monitor.indexing"), 1); //$NON-NLS-1$
- int nbElements = 0;
- int lastSegment = 0;
- /*
- * We are iterating through the three sides of the scope at the same time so that index might apply
- * pre-matching strategies elements if they wish.
- */
- while (leftEObjects.hasNext() || rightEObjects.hasNext() || originEObjects.hasNext()) {
-
- if (leftEObjects.hasNext()) {
- EObject next = leftEObjects.next();
- nbElements++;
- index.index(next, Side.LEFT);
- eObjectsToSide.put(next, Side.LEFT);
- }
-
- if (rightEObjects.hasNext()) {
- EObject next = rightEObjects.next();
- index.index(next, Side.RIGHT);
- eObjectsToSide.put(next, Side.RIGHT);
- }
-
- if (originEObjects.hasNext()) {
- EObject next = originEObjects.next();
- index.index(next, Side.ORIGIN);
- eObjectsToSide.put(next, Side.ORIGIN);
- }
- if (nbElements / NB_ELEMENTS_BETWEEN_MATCH_AHEAD > lastSegment) {
- matchAheadOfTime(comparison, subMonitor);
- lastSegment++;
- }
-
- }
-
- subMonitor.worked(1);
- subMonitor.done();
-
- // FIXME: how to create an EMF submonitor
- subMonitor = new BasicMonitor();
- subMonitor.beginTask(EMFCompareMessages.getString("ProximityEObjectMatcher.monitor.matching"), //$NON-NLS-1$
- nbElements);
-
- matchIndexedObjects(comparison, subMonitor);
-
- createUnmatchesForRemainingObjects(comparison);
- subMonitor.done();
- restructureMatchModel(comparison);
-
- }
-
- /**
- * If the index supports it, match element ahead of time, in case of failure the elements are kept and
- * will be processed again later on.
- *
- * @param comparison
- * the current comoparison.
- * @param monitor
- * monitor to track progress.
- */
- private void matchAheadOfTime(Comparison comparison, Monitor monitor) {
- if (index instanceof MatchAheadOfTime) {
- matchList(comparison, ((MatchAheadOfTime)index).getValuesToMatchAhead(Side.LEFT), false, monitor);
- matchList(comparison, ((MatchAheadOfTime)index).getValuesToMatchAhead(Side.RIGHT), false, monitor);
- }
- }
-
- /**
- * Match elements for real, if no match is found for an element, an object will be created to represent
- * this unmatch and the element will not be processed again.
- *
- * @param comparison
- * the current comparison.
- * @param subMonitor
- * monitor to track progress.
- */
- private void matchIndexedObjects(Comparison comparison, Monitor subMonitor) {
- Iterable<EObject> todo = index.getValuesStillThere(Side.LEFT);
- while (todo.iterator().hasNext()) {
- todo = matchList(comparison, todo, true, subMonitor);
- }
- todo = index.getValuesStillThere(Side.RIGHT);
- while (todo.iterator().hasNext()) {
- todo = matchList(comparison, todo, true, subMonitor);
- }
-
- }
-
- /**
- * Create all the Match objects for the remaining EObjects.
- *
- * @param comparison
- * the current comparison.
- */
- private void createUnmatchesForRemainingObjects(Comparison comparison) {
- for (EObject notFound : index.getValuesStillThere(Side.RIGHT)) {
- areMatching(comparison, null, notFound, null);
- }
- for (EObject notFound : index.getValuesStillThere(Side.LEFT)) {
- areMatching(comparison, notFound, null, null);
- }
- for (EObject notFound : index.getValuesStillThere(Side.ORIGIN)) {
- areMatching(comparison, null, null, notFound);
- }
- }
-
- /**
- * Process the list of objects matching them. This method might not be able to process all the EObjects if
- * - for instance, their container has not been matched already. Every object which could not be matched
- * is returned in the list.
- *
- * @param comparison
- * the comparison being built.
- * @param todoList
- * the list of objects to process.
- * @param createUnmatches
- * whether elements which have no match should trigger the creation of a Match object (meaning
- * we won't try to match them afterwards) or not.
- * @param monitor
- * a monitor to track progress.
- * @return the list of EObjects which could not be processed for some reason.
- */
- private Iterable<EObject> matchList(Comparison comparison, Iterable<EObject> todoList,
- boolean createUnmatches, Monitor monitor) {
- Set<EObject> remainingResult = Sets.newLinkedHashSet();
- List<EObject> requiredContainers = Lists.newArrayList();
- Iterator<EObject> todo = todoList.iterator();
- while (todo.hasNext()) {
- EObject next = todo.next();
- /*
- * Let's first add every container which is in scope
- */
- EObject container = next.eContainer();
- while (container != null && isInScope(container)) {
- if (comparison.getMatch(container) == null) {
- requiredContainers.add(0, container);
- }
- container = container.eContainer();
- }
- }
- Iterator<EObject> containersAndTodo = Iterators.concat(requiredContainers.iterator(), todoList
- .iterator());
- while (containersAndTodo.hasNext()) {
- EObject next = containersAndTodo.next();
- /*
- * At this point you need to be sure the element has not been matched in any other way before.
- */
- if (comparison.getMatch(next) == null) {
- if (!tryToMatch(comparison, next, createUnmatches)) {
- remainingResult.add(next);
- monitor.worked(1);
- }
- }
- }
- return remainingResult;
- }
-
- /**
- * Try to create a Match. If the match got created, register it (having actual left/right/origin matches
- * or not), if not, then return false. Cases where it might not create the match : if some required data
- * has not been computed yet (for instance if the container of an object has not been matched and if the
- * distance need to know if it's match to find the children matches).
- *
- * @param comparison
- * the comparison under construction, it will be updated with the new match.
- * @param a
- * object to match.
- * @param createUnmatches
- * whether elements which have no match should trigger the creation of a Match object (meaning
- * we won't try to match them afterwards) or not.
- * @return false if the conditions are not fulfilled to create the match, true otherwhise.
- */
- private boolean tryToMatch(Comparison comparison, EObject a, boolean createUnmatches) {
- boolean okToMatch = false;
- Side aSide = eObjectsToSide.get(a);
- assert aSide != null;
- Side bSide = Side.LEFT;
- Side cSide = Side.RIGHT;
- if (aSide == Side.RIGHT) {
- bSide = Side.LEFT;
- cSide = Side.ORIGIN;
- } else if (aSide == Side.LEFT) {
- bSide = Side.RIGHT;
- cSide = Side.ORIGIN;
- } else if (aSide == Side.ORIGIN) {
- bSide = Side.LEFT;
- cSide = Side.RIGHT;
- }
- assert aSide != bSide;
- assert bSide != cSide;
- assert cSide != aSide;
- Map<Side, EObject> closests = index.findClosests(comparison, a, aSide);
- if (closests != null) {
- EObject lObj = closests.get(bSide);
- EObject aObj = closests.get(cSide);
- if (lObj != null || aObj != null) {
- // we have at least one other match
- areMatching(comparison, closests.get(Side.LEFT), closests.get(Side.RIGHT), closests
- .get(Side.ORIGIN));
- okToMatch = true;
- } else if (createUnmatches) {
- areMatching(comparison, closests.get(Side.LEFT), closests.get(Side.RIGHT), closests
- .get(Side.ORIGIN));
- okToMatch = true;
- }
- }
- return okToMatch;
- }
-
- /**
- * Process all the matches of the given comparison and re-attach them to their parent if one is found.
- *
- * @param comparison
- * the comparison to restructure.
- */
- private void restructureMatchModel(Comparison comparison) {
- Iterator<Match> it = ImmutableList.copyOf(Iterators.filter(comparison.eAllContents(), Match.class))
- .iterator();
-
- while (it.hasNext()) {
- Match cur = it.next();
- EObject possibleContainer = null;
- if (cur.getLeft() != null) {
- possibleContainer = cur.getLeft().eContainer();
- }
- if (possibleContainer == null && cur.getRight() != null) {
- possibleContainer = cur.getRight().eContainer();
- }
- if (possibleContainer == null && cur.getOrigin() != null) {
- possibleContainer = cur.getOrigin().eContainer();
- }
- Match possibleContainerMatch = comparison.getMatch(possibleContainer);
- if (possibleContainerMatch != null) {
- ((BasicEList<Match>)possibleContainerMatch.getSubmatches()).addUnique(cur);
- }
- }
- }
-
- /**
- * Register the given object as a match and add it in the comparison.
- *
- * @param comparison
- * container for the Match.
- * @param left
- * left element.
- * @param right
- * right element
- * @param origin
- * origin element.
- * @return the created match.
- */
- private Match areMatching(Comparison comparison, EObject left, EObject right, EObject origin) {
- Match result = CompareFactory.eINSTANCE.createMatch();
- result.setLeft(left);
- result.setRight(right);
- result.setOrigin(origin);
- ((BasicEList<Match>)comparison.getMatches()).addUnique(result);
- if (left != null) {
- index.remove(left, Side.LEFT);
- }
- if (right != null) {
- index.remove(right, Side.RIGHT);
- }
- if (origin != null) {
- index.remove(origin, Side.ORIGIN);
- }
- return result;
- }
-
- /**
- * This represent a distance function used by the {@link ProximityEObjectMatcher} to compare EObjects and
- * retrieve the closest EObject from one side to another. Axioms of the distance are supposed to be
- * respected more especially :
- * <ul>
- * <li>symetry : dist(a,b) == dist(b,a)</li>
- * <li>separation :dist(a,a) == 0</li>
- * </ul>
- * Triangular inequality is not leveraged with the current implementation but might be at some point to
- * speed up the indexing. <br/>
- * computing the distance between two EObjects should be a <b> fast operation</b> or the scalability of
- * the whole matching phase will be poor.
- *
- * @author cedric brun <cedric.brun@obeo.fr>
- */
- public interface DistanceFunction {
- /**
- * Return the distance between two EObjects. When the two objects should considered as completely
- * different the implementation is expected to return Double.MAX_VALUE.
- *
- * @param inProgress
- * the comparison being processed right now. This might be used for the distance to
- * retrieve other matches for instance.
- * @param a
- * first object.
- * @param b
- * second object.
- * @return the distance between the two EObjects or Double.MAX_VALUE when the objects are considered
- * too different to be the same.
- */
- double distance(Comparison inProgress, EObject a, EObject b);
-
- /**
- * Check that two objects are equals from the distance function point of view (distance should be 0)
- * You should prefer this method when you just want to check objects are not equals enabling the
- * distance to stop sooner.
- *
- * @param inProgress
- * the comparison being processed right now. This might be used for the distance to
- * retrieve other matches for instance.
- * @param a
- * first object.
- * @param b
- * second object.
- * @return true of the two objects are equals, false otherwise.
- */
- boolean areIdentic(Comparison inProgress, EObject a, EObject b);
-
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isInScope(EObject eContainer) {
- return eObjectsToSide.get(eContainer) != null;
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2015 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.match.eobject;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.CompareFactory;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
+import org.eclipse.emf.compare.EMFCompareMessages;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.match.eobject.EObjectIndex.Side;
+import org.eclipse.emf.compare.match.eobject.internal.ByTypeIndex;
+import org.eclipse.emf.compare.match.eobject.internal.MatchAheadOfTime;
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * This matcher is using a distance function to match EObject. It guarantees that elements are matched with
+ * the other EObject having the lowest distance. If two elements have the same distance regarding the other
+ * EObject it will arbitrary pick one. (You should probably not rely on this and make sure your distance only
+ * return 0 if both EObject have the very same content). The matcher will try to use the fact that it is a
+ * distance to achieve a suitable scalability. It is also build on the following assumptions :
+ * <ul>
+ * <li>Most EObjects have no difference and have their corresponding EObject on the other sides of the model
+ * (right and origins)</li>
+ * <li>Two consecutive calls on the distance function with the same parameters will give the same distance.</li>
+ * </ul>
+ * The scalability you'll get will highly depend on the complexity of the distance function. The
+ * implementation is not caching any distance result from two EObjects.
+ *
+ * @author <a href="mailto:cedric.brun@obeo.fr">Cedric Brun</a>
+ */
+public class ProximityEObjectMatcher implements IEObjectMatcher, ScopeQuery {
+ /**
+ * Number of elements to index before a starting a match ahead step.
+ */
+ private static final int NB_ELEMENTS_BETWEEN_MATCH_AHEAD = 10000;
+
+ /**
+ * The index which keep the EObjects.
+ */
+ private EObjectIndex index;
+
+ /**
+ * Keeps track of which side was the EObject from.
+ */
+ private Map<EObject, Side> eObjectsToSide = Maps.newHashMap();
+
+ /**
+ * Create the matcher using the given distance function.
+ *
+ * @param meter
+ * a function to measure the distance between two {@link EObject}s.
+ */
+ public ProximityEObjectMatcher(DistanceFunction meter) {
+ this.index = new ByTypeIndex(meter, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+
+ public void createMatches(Comparison comparison, Iterator<? extends EObject> leftEObjects,
+ Iterator<? extends EObject> rightEObjects, Iterator<? extends EObject> originEObjects,
+ Monitor monitor) {
+ if (!leftEObjects.hasNext() && !rightEObjects.hasNext() && !originEObjects.hasNext()) {
+ return;
+ }
+
+ monitor.subTask(EMFCompareMessages.getString("ProximityEObjectMatcher.monitor.indexing")); //$NON-NLS-1$
+ int nbElements = 0;
+ int lastSegment = 0;
+ /*
+ * We are iterating through the three sides of the scope at the same time so that index might apply
+ * pre-matching strategies elements if they wish.
+ */
+ while (leftEObjects.hasNext() || rightEObjects.hasNext() || originEObjects.hasNext()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
+
+ if (leftEObjects.hasNext()) {
+ EObject next = leftEObjects.next();
+ nbElements++;
+ index.index(next, Side.LEFT);
+ eObjectsToSide.put(next, Side.LEFT);
+ }
+
+ if (rightEObjects.hasNext()) {
+ EObject next = rightEObjects.next();
+ index.index(next, Side.RIGHT);
+ eObjectsToSide.put(next, Side.RIGHT);
+ }
+
+ if (originEObjects.hasNext()) {
+ EObject next = originEObjects.next();
+ index.index(next, Side.ORIGIN);
+ eObjectsToSide.put(next, Side.ORIGIN);
+ }
+ if (nbElements / NB_ELEMENTS_BETWEEN_MATCH_AHEAD > lastSegment) {
+ matchAheadOfTime(comparison, monitor);
+ lastSegment++;
+ }
+
+ }
+
+ monitor.subTask(EMFCompareMessages.getString("ProximityEObjectMatcher.monitor.matching")); //$NON-NLS-1$
+ matchIndexedObjects(comparison, monitor);
+
+ createUnmatchesForRemainingObjects(comparison);
+ restructureMatchModel(comparison);
+
+ }
+
+ /**
+ * If the index supports it, match element ahead of time, in case of failure the elements are kept and
+ * will be processed again later on.
+ *
+ * @param comparison
+ * the current comoparison.
+ * @param monitor
+ * monitor to track progress.
+ */
+ private void matchAheadOfTime(Comparison comparison, Monitor monitor) {
+ if (index instanceof MatchAheadOfTime) {
+ matchList(comparison, ((MatchAheadOfTime)index).getValuesToMatchAhead(Side.LEFT), false, monitor);
+ matchList(comparison, ((MatchAheadOfTime)index).getValuesToMatchAhead(Side.RIGHT), false, monitor);
+ }
+ }
+
+ /**
+ * Match elements for real, if no match is found for an element, an object will be created to represent
+ * this unmatch and the element will not be processed again.
+ *
+ * @param comparison
+ * the current comparison.
+ * @param monitor
+ * monitor to track progress.
+ */
+ private void matchIndexedObjects(Comparison comparison, Monitor monitor) {
+ Iterable<EObject> todo = index.getValuesStillThere(Side.LEFT);
+ while (todo.iterator().hasNext()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
+ todo = matchList(comparison, todo, true, monitor);
+ }
+ todo = index.getValuesStillThere(Side.RIGHT);
+ while (todo.iterator().hasNext()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
+ todo = matchList(comparison, todo, true, monitor);
+ }
+
+ }
+
+ /**
+ * Create all the Match objects for the remaining EObjects.
+ *
+ * @param comparison
+ * the current comparison.
+ */
+ private void createUnmatchesForRemainingObjects(Comparison comparison) {
+ for (EObject notFound : index.getValuesStillThere(Side.RIGHT)) {
+ areMatching(comparison, null, notFound, null);
+ }
+ for (EObject notFound : index.getValuesStillThere(Side.LEFT)) {
+ areMatching(comparison, notFound, null, null);
+ }
+ for (EObject notFound : index.getValuesStillThere(Side.ORIGIN)) {
+ areMatching(comparison, null, null, notFound);
+ }
+ }
+
+ /**
+ * Process the list of objects matching them. This method might not be able to process all the EObjects if
+ * - for instance, their container has not been matched already. Every object which could not be matched
+ * is returned in the list.
+ *
+ * @param comparison
+ * the comparison being built.
+ * @param todoList
+ * the list of objects to process.
+ * @param createUnmatches
+ * whether elements which have no match should trigger the creation of a Match object (meaning
+ * we won't try to match them afterwards) or not.
+ * @param monitor
+ * a monitor to track progress.
+ * @return the list of EObjects which could not be processed for some reason.
+ */
+ private Iterable<EObject> matchList(Comparison comparison, Iterable<EObject> todoList,
+ boolean createUnmatches, Monitor monitor) {
+ Set<EObject> remainingResult = Sets.newLinkedHashSet();
+ List<EObject> requiredContainers = Lists.newArrayList();
+ Iterator<EObject> todo = todoList.iterator();
+ while (todo.hasNext()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
+ EObject next = todo.next();
+ /*
+ * Let's first add every container which is in scope
+ */
+ EObject container = next.eContainer();
+ while (container != null && isInScope(container)) {
+ if (comparison.getMatch(container) == null) {
+ requiredContainers.add(0, container);
+ }
+ container = container.eContainer();
+ }
+ }
+ Iterator<EObject> containersAndTodo = Iterators.concat(requiredContainers.iterator(), todoList
+ .iterator());
+ while (containersAndTodo.hasNext()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
+ EObject next = containersAndTodo.next();
+ /*
+ * At this point you need to be sure the element has not been matched in any other way before.
+ */
+ if (comparison.getMatch(next) == null) {
+ if (!tryToMatch(comparison, next, createUnmatches)) {
+ remainingResult.add(next);
+ }
+ }
+ }
+ return remainingResult;
+ }
+
+ /**
+ * Try to create a Match. If the match got created, register it (having actual left/right/origin matches
+ * or not), if not, then return false. Cases where it might not create the match : if some required data
+ * has not been computed yet (for instance if the container of an object has not been matched and if the
+ * distance need to know if it's match to find the children matches).
+ *
+ * @param comparison
+ * the comparison under construction, it will be updated with the new match.
+ * @param a
+ * object to match.
+ * @param createUnmatches
+ * whether elements which have no match should trigger the creation of a Match object (meaning
+ * we won't try to match them afterwards) or not.
+ * @return false if the conditions are not fulfilled to create the match, true otherwhise.
+ */
+ private boolean tryToMatch(Comparison comparison, EObject a, boolean createUnmatches) {
+ boolean okToMatch = false;
+ Side aSide = eObjectsToSide.get(a);
+ assert aSide != null;
+ Side bSide = Side.LEFT;
+ Side cSide = Side.RIGHT;
+ if (aSide == Side.RIGHT) {
+ bSide = Side.LEFT;
+ cSide = Side.ORIGIN;
+ } else if (aSide == Side.LEFT) {
+ bSide = Side.RIGHT;
+ cSide = Side.ORIGIN;
+ } else if (aSide == Side.ORIGIN) {
+ bSide = Side.LEFT;
+ cSide = Side.RIGHT;
+ }
+ assert aSide != bSide;
+ assert bSide != cSide;
+ assert cSide != aSide;
+ Map<Side, EObject> closests = index.findClosests(comparison, a, aSide);
+ if (closests != null) {
+ EObject lObj = closests.get(bSide);
+ EObject aObj = closests.get(cSide);
+ if (lObj != null || aObj != null) {
+ // we have at least one other match
+ areMatching(comparison, closests.get(Side.LEFT), closests.get(Side.RIGHT), closests
+ .get(Side.ORIGIN));
+ okToMatch = true;
+ } else if (createUnmatches) {
+ areMatching(comparison, closests.get(Side.LEFT), closests.get(Side.RIGHT), closests
+ .get(Side.ORIGIN));
+ okToMatch = true;
+ }
+ }
+ return okToMatch;
+ }
+
+ /**
+ * Process all the matches of the given comparison and re-attach them to their parent if one is found.
+ *
+ * @param comparison
+ * the comparison to restructure.
+ */
+ private void restructureMatchModel(Comparison comparison) {
+ Iterator<Match> it = ImmutableList.copyOf(Iterators.filter(comparison.eAllContents(), Match.class))
+ .iterator();
+
+ while (it.hasNext()) {
+ Match cur = it.next();
+ EObject possibleContainer = null;
+ if (cur.getLeft() != null) {
+ possibleContainer = cur.getLeft().eContainer();
+ }
+ if (possibleContainer == null && cur.getRight() != null) {
+ possibleContainer = cur.getRight().eContainer();
+ }
+ if (possibleContainer == null && cur.getOrigin() != null) {
+ possibleContainer = cur.getOrigin().eContainer();
+ }
+ Match possibleContainerMatch = comparison.getMatch(possibleContainer);
+ if (possibleContainerMatch != null) {
+ ((BasicEList<Match>)possibleContainerMatch.getSubmatches()).addUnique(cur);
+ }
+ }
+ }
+
+ /**
+ * Register the given object as a match and add it in the comparison.
+ *
+ * @param comparison
+ * container for the Match.
+ * @param left
+ * left element.
+ * @param right
+ * right element
+ * @param origin
+ * origin element.
+ * @return the created match.
+ */
+ private Match areMatching(Comparison comparison, EObject left, EObject right, EObject origin) {
+ Match result = CompareFactory.eINSTANCE.createMatch();
+ result.setLeft(left);
+ result.setRight(right);
+ result.setOrigin(origin);
+ ((BasicEList<Match>)comparison.getMatches()).addUnique(result);
+ if (left != null) {
+ index.remove(left, Side.LEFT);
+ }
+ if (right != null) {
+ index.remove(right, Side.RIGHT);
+ }
+ if (origin != null) {
+ index.remove(origin, Side.ORIGIN);
+ }
+ return result;
+ }
+
+ /**
+ * This represent a distance function used by the {@link ProximityEObjectMatcher} to compare EObjects and
+ * retrieve the closest EObject from one side to another. Axioms of the distance are supposed to be
+ * respected more especially :
+ * <ul>
+ * <li>symetry : dist(a,b) == dist(b,a)</li>
+ * <li>separation :dist(a,a) == 0</li>
+ * </ul>
+ * Triangular inequality is not leveraged with the current implementation but might be at some point to
+ * speed up the indexing. <br/>
+ * computing the distance between two EObjects should be a <b> fast operation</b> or the scalability of
+ * the whole matching phase will be poor.
+ *
+ * @author cedric brun <cedric.brun@obeo.fr>
+ */
+ public interface DistanceFunction {
+ /**
+ * Return the distance between two EObjects. When the two objects should considered as completely
+ * different the implementation is expected to return Double.MAX_VALUE.
+ *
+ * @param inProgress
+ * the comparison being processed right now. This might be used for the distance to
+ * retrieve other matches for instance.
+ * @param a
+ * first object.
+ * @param b
+ * second object.
+ * @return the distance between the two EObjects or Double.MAX_VALUE when the objects are considered
+ * too different to be the same.
+ */
+ double distance(Comparison inProgress, EObject a, EObject b);
+
+ /**
+ * Check that two objects are equals from the distance function point of view (distance should be 0)
+ * You should prefer this method when you just want to check objects are not equals enabling the
+ * distance to stop sooner.
+ *
+ * @param inProgress
+ * the comparison being processed right now. This might be used for the distance to
+ * retrieve other matches for instance.
+ * @param a
+ * first object.
+ * @param b
+ * second object.
+ * @return true of the two objects are equals, false otherwise.
+ */
+ boolean areIdentic(Comparison inProgress, EObject a, EObject b);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isInScope(EObject eContainer) {
+ return eObjectsToSide.get(eContainer) != null;
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java
index 08c724eeb..692713fc4 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2014 Obeo.
+ * Copyright (c) 2012, 2015 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -28,9 +28,11 @@ import java.util.Set;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.ComparisonCanceledException;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.DifferenceSource;
+import org.eclipse.emf.compare.EMFCompareMessages;
import org.eclipse.emf.compare.FeatureMapChange;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.ReferenceChange;
@@ -59,7 +61,11 @@ public class DefaultReqEngine implements IReqEngine {
* @see org.eclipse.emf.compare.req.IReqEngine#computeRequirements(Comparison, Monitor)
*/
public void computeRequirements(Comparison comparison, Monitor monitor) {
+ monitor.subTask(EMFCompareMessages.getString("DefaultReqEngine.monitor.req")); //$NON-NLS-1$
for (Diff difference : comparison.getDifferences()) {
+ if (monitor.isCanceled()) {
+ throw new ComparisonCanceledException();
+ }
checkForRequiredDifferences(comparison, difference);
}
}

Back to the top