Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAxel Richard2013-03-11 13:35:40 +0000
committerGerrit Code Review @ Eclipse.org2013-03-19 09:37:56 +0000
commitdedaabfda0e4ba27b58ef426277225790147d69d (patch)
treedeaf970337fe2b8919cc4c910a04ddc8f71dc01c
parent31ddf8577c1a7b99559ced8d8773610e841d72fd (diff)
downloadorg.eclipse.emf.compare-dedaabfda0e4ba27b58ef426277225790147d69d.tar.gz
org.eclipse.emf.compare-dedaabfda0e4ba27b58ef426277225790147d69d.tar.xz
org.eclipse.emf.compare-dedaabfda0e4ba27b58ef426277225790147d69d.zip
[403055] Provide a match engine extension mechanism.
Make the match engine extensible by providing extension point mechanism. Bug: 403055 Change-Id: I5b824adf66840d76b8046518103d447ad5711c41
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/handler/AbstractCompareHandler.java344
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java9
-rw-r--r--plugins/org.eclipse.emf.compare.rcp/META-INF/MANIFEST.MF1
-rw-r--r--plugins/org.eclipse.emf.compare.rcp/plugin.xml108
-rw-r--r--plugins/org.eclipse.emf.compare.rcp/schema/matchEngine.exsd116
-rw-r--r--plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/EMFCompareRCPPlugin.java31
-rw-r--r--plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/extension/AbstractRegistryEventListener.java530
-rw-r--r--plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/internal/match/MatchEngineFactoryRegistryListener.java126
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/fullcomparison/ProximityComparisonTest.java438
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/match/MatchEngineFactoryRegistryTest.java85
-rw-r--r--plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java172
-rw-r--r--plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF1
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/EMFCompare.java691
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/DefaultMatchEngine.java796
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/IMatchEngine.java208
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryImpl.java105
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryRegistryImpl.java151
17 files changed, 2361 insertions, 1551 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/handler/AbstractCompareHandler.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/handler/AbstractCompareHandler.java
index 0bc0416c7..a327dec19 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/handler/AbstractCompareHandler.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/handler/AbstractCompareHandler.java
@@ -1,140 +1,204 @@
-/*******************************************************************************
- * Copyright (c) 2012, 2013 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.ide.ui.internal.handler;
-
-import com.google.common.collect.Iterators;
-
-import java.util.Iterator;
-
-import org.eclipse.compare.CompareConfiguration;
-import org.eclipse.compare.CompareEditorInput;
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.emf.common.command.CommandStack;
-import org.eclipse.emf.common.notify.AdapterFactory;
-import org.eclipse.emf.common.notify.Notifier;
-import org.eclipse.emf.common.util.Monitor;
-import org.eclipse.emf.compare.CompareFactory;
-import org.eclipse.emf.compare.Comparison;
-import org.eclipse.emf.compare.EMFCompare;
-import org.eclipse.emf.compare.Match;
-import org.eclipse.emf.compare.domain.ICompareEditingDomain;
-import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain;
-import org.eclipse.emf.compare.ide.ui.internal.editor.ComparisonScopeEditorInput;
-import org.eclipse.emf.compare.match.DefaultComparisonFactory;
-import org.eclipse.emf.compare.match.DefaultEqualityHelperFactory;
-import org.eclipse.emf.compare.match.DefaultMatchEngine;
-import org.eclipse.emf.compare.match.IComparisonFactory;
-import org.eclipse.emf.compare.match.IMatchEngine;
-import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
-import org.eclipse.emf.compare.provider.AdapterFactoryUtil;
-import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
-import org.eclipse.emf.compare.scope.IComparisonScope;
-import org.eclipse.emf.compare.utils.UseIdentifiers;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
-import org.eclipse.emf.edit.domain.EditingDomain;
-import org.eclipse.emf.edit.domain.IEditingDomainProvider;
-import org.eclipse.ui.IWorkbenchPart;
-
-/**
- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
- */
-public abstract class AbstractCompareHandler extends AbstractHandler {
-
- protected static CompareEditorInput createCompareEditorInput(IWorkbenchPart part,
- AdapterFactory adapterFactory, Notifier left, Notifier right, Notifier origin) {
- CompareEditorInput input = null;
-
- ICompareEditingDomain editingDomain = createEMFCompareEditingDomain(part, left, right, origin);
-
- final CompareConfiguration configuration = new CompareConfiguration();
- // never use id in EObjects comparison
- IEObjectMatcher eObjectMatcher = DefaultMatchEngine.createDefaultEObjectMatcher(UseIdentifiers.NEVER);
- IMatchEngine matchEngine = new MatchEObjectEngine(eObjectMatcher, new DefaultComparisonFactory(
- new DefaultEqualityHelperFactory()));
- EMFCompare comparator = EMFCompare.builder().setMatchEngine(matchEngine).setPostProcessorRegistry(
- EMFCompareRCPPlugin.getDefault().getPostProcessorRegistry()).build();
- IComparisonScope scope = EMFCompare.createDefaultScope(left, right, origin);
- input = new ComparisonScopeEditorInput(configuration, editingDomain, adapterFactory, comparator,
- scope);
-
- input.setTitle("Compare ('" + AdapterFactoryUtil.getText(adapterFactory, left) + "' - '"
- + AdapterFactoryUtil.getText(adapterFactory, right) + "')");
-
- configuration.setContainer(input);
- return input;
- }
-
- private static ICompareEditingDomain createEMFCompareEditingDomain(final IWorkbenchPart activePart,
- final Notifier left, final Notifier right, Notifier origin) {
-
- EditingDomain delegatingEditingDomain = getDelegatingEditingDomain(activePart, left, right);
- CommandStack delegatingCommandStack = null;
- if (delegatingEditingDomain != null) {
- delegatingCommandStack = delegatingEditingDomain.getCommandStack();
- }
-
- return EMFCompareEditingDomain.create(left, right, origin, delegatingCommandStack);
- }
-
- private static EditingDomain getDelegatingEditingDomain(final IWorkbenchPart activePart, Notifier left,
- Notifier right) {
- EditingDomain delegatingEditingDomain = null;
- if (activePart instanceof IEditingDomainProvider) {
- delegatingEditingDomain = ((IEditingDomainProvider)activePart).getEditingDomain();
- } else if (AdapterFactoryEditingDomain.getEditingDomainFor(left) != null) {
- EditingDomain leftEditingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(left);
- EditingDomain rightEditingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(right);
- // do not use a delegating editing domain if those two are different.
- if (leftEditingDomain == rightEditingDomain) {
- delegatingEditingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(left);
- }
- }
- return delegatingEditingDomain;
- }
-
- private static class MatchEObjectEngine extends DefaultMatchEngine {
- /**
- * @param matcher
- * @param comparisonFactory
- */
- public MatchEObjectEngine(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
- super(matcher, comparisonFactory);
- }
-
- @Override
- protected void match(Comparison comparison, IComparisonScope scope, EObject left, EObject right,
- EObject origin, Monitor monitor) {
- if (left == null || right == null) {
- throw new IllegalArgumentException();
- }
-
- Match rootMatch = CompareFactory.eINSTANCE.createMatch();
- rootMatch.setLeft(left);
- rootMatch.setRight(right);
- rootMatch.setOrigin(origin);
- comparison.getMatches().add(rootMatch);
-
- final Iterator<? extends EObject> leftEObjects = scope.getChildren(left);
- final Iterator<? extends EObject> rightEObjects = scope.getChildren(right);
- final Iterator<? extends EObject> originEObjects;
- if (origin != null) {
- originEObjects = scope.getChildren(origin);
- } else {
- originEObjects = Iterators.emptyIterator();
- }
-
- getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects,
- monitor);
-
- }
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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.ide.ui.internal.handler;
+
+import com.google.common.collect.Iterators;
+
+import java.util.Iterator;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.emf.common.command.CommandStack;
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.CompareFactory;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.EMFCompare;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.domain.ICompareEditingDomain;
+import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain;
+import org.eclipse.emf.compare.ide.ui.internal.editor.ComparisonScopeEditorInput;
+import org.eclipse.emf.compare.match.DefaultComparisonFactory;
+import org.eclipse.emf.compare.match.DefaultEqualityHelperFactory;
+import org.eclipse.emf.compare.match.DefaultMatchEngine;
+import org.eclipse.emf.compare.match.IComparisonFactory;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl;
+import org.eclipse.emf.compare.provider.AdapterFactoryUtil;
+import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.utils.UseIdentifiers;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.edit.domain.IEditingDomainProvider;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+public abstract class AbstractCompareHandler extends AbstractHandler {
+
+ protected static CompareEditorInput createCompareEditorInput(IWorkbenchPart part,
+ AdapterFactory adapterFactory, Notifier left, Notifier right, Notifier origin) {
+ CompareEditorInput input = null;
+
+ ICompareEditingDomain editingDomain = createEMFCompareEditingDomain(part, left, right, origin);
+
+ final CompareConfiguration configuration = new CompareConfiguration();
+ IMatchEngine.Factory eObjectMatchEngineFactory = new MatchEObjectEngineFactory();
+ eObjectMatchEngineFactory.setRanking(9);
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = EMFCompareRCPPlugin.getDefault()
+ .getMatchEngineFactoryRegistry();
+ matchEngineFactoryRegistry.add(eObjectMatchEngineFactory);
+
+ EMFCompare comparator = EMFCompare.builder()
+ .setMatchEngineFactoryRegistry(matchEngineFactoryRegistry).setPostProcessorRegistry(
+ EMFCompareRCPPlugin.getDefault().getPostProcessorRegistry()).build();
+ IComparisonScope scope = EMFCompare.createDefaultScope(left, right, origin);
+ input = new ComparisonScopeEditorInput(configuration, editingDomain, adapterFactory, comparator,
+ scope);
+
+ input.setTitle("Compare ('" + AdapterFactoryUtil.getText(adapterFactory, left) + "' - '"
+ + AdapterFactoryUtil.getText(adapterFactory, right) + "')");
+
+ configuration.setContainer(input);
+ return input;
+ }
+
+ private static ICompareEditingDomain createEMFCompareEditingDomain(final IWorkbenchPart activePart,
+ final Notifier left, final Notifier right, Notifier origin) {
+
+ EditingDomain delegatingEditingDomain = getDelegatingEditingDomain(activePart, left, right);
+ CommandStack delegatingCommandStack = null;
+ if (delegatingEditingDomain != null) {
+ delegatingCommandStack = delegatingEditingDomain.getCommandStack();
+ }
+
+ return EMFCompareEditingDomain.create(left, right, origin, delegatingCommandStack);
+ }
+
+ private static EditingDomain getDelegatingEditingDomain(final IWorkbenchPart activePart, Notifier left,
+ Notifier right) {
+ EditingDomain delegatingEditingDomain = null;
+ if (activePart instanceof IEditingDomainProvider) {
+ delegatingEditingDomain = ((IEditingDomainProvider)activePart).getEditingDomain();
+ } else if (AdapterFactoryEditingDomain.getEditingDomainFor(left) != null) {
+ EditingDomain leftEditingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(left);
+ EditingDomain rightEditingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(right);
+ // do not use a delegating editing domain if those two are different.
+ if (leftEditingDomain == rightEditingDomain) {
+ delegatingEditingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(left);
+ }
+ }
+ return delegatingEditingDomain;
+ }
+
+ /**
+ * A specialized {@link MatchEngineFactoryImpl} that wrap a {@link MatchEObjectEngine}.
+ *
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ */
+ private static class MatchEObjectEngineFactory extends MatchEngineFactoryImpl {
+
+ /**
+ * Default Constructor.
+ */
+ public MatchEObjectEngineFactory() {
+ matchEngine = new MatchEObjectEngine();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
+ * org.eclipse.emf.compare.match.IMatchEngine.Factory.isMatchEngineFactoryFor(org.eclipse.emf.compare
+ * .scope.IComparisonScope)
+ */
+ @Override
+ public boolean isMatchEngineFactoryFor(IComparisonScope scope) {
+ final Notifier left = scope.getLeft();
+ final Notifier right = scope.getRight();
+ if (left instanceof EObject && right instanceof EObject) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * A specialized {@link DefaultMatchEngine} for comparison between EObjects.
+ *
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+ private static class MatchEObjectEngine extends DefaultMatchEngine {
+
+ /**
+ * Default Constructor.
+ */
+ public MatchEObjectEngine() {
+ // never use id in EObjects comparison
+ super(DefaultMatchEngine.createDefaultEObjectMatcher(UseIdentifiers.NEVER),
+ new DefaultComparisonFactory(new DefaultEqualityHelperFactory()));
+ }
+
+ /**
+ * Constructor with matcher and comparison factory parameters.
+ *
+ * @param matcher
+ * The matcher that will be in charge of pairing EObjects together for this comparison
+ * process.
+ * @param comparisonFactory
+ * factory that will be use to instantiate Comparison as return by match() methods.
+ */
+ public MatchEObjectEngine(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
+ super(matcher, comparisonFactory);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.DefaultMatchEngine.match(org.eclipse.emf.compare.Comparison,
+ * org.eclipse.emf.compare.scope.IComparisonScope, org.eclipse.emf.ecore.EObject,
+ * org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject,
+ * org.eclipse.emf.common.util.Monitor)
+ */
+ @Override
+ protected void match(Comparison comparison, IComparisonScope scope, EObject left, EObject right,
+ EObject origin, Monitor monitor) {
+ if (left == null || right == null) {
+ throw new IllegalArgumentException();
+ }
+
+ Match rootMatch = CompareFactory.eINSTANCE.createMatch();
+ rootMatch.setLeft(left);
+ rootMatch.setRight(right);
+ rootMatch.setOrigin(origin);
+ comparison.getMatches().add(rootMatch);
+
+ final Iterator<? extends EObject> leftEObjects = scope.getChildren(left);
+ final Iterator<? extends EObject> rightEObjects = scope.getChildren(right);
+ final Iterator<? extends EObject> originEObjects;
+ if (origin != null) {
+ originEObjects = scope.getChildren(origin);
+ } else {
+ originEObjects = Iterators.emptyIterator();
+ }
+
+ getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects,
+ monitor);
+
+ }
+ }
+}
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 4e65d5cb3..a44e09212 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
@@ -273,9 +273,12 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co
}
final IComparisonScope scope = syncModel.createMinimizedScope();
- final Comparison compareResult = EMFCompare.builder().setPostProcessorRegistry(
- EMFCompareRCPPlugin.getDefault().getPostProcessorRegistry()).build().compare(scope,
- BasicMonitor.toMonitor(monitor));
+ final Comparison compareResult = EMFCompare
+ .builder()
+ .setMatchEngineFactoryRegistry(
+ EMFCompareRCPPlugin.getDefault().getMatchEngineFactoryRegistry())
+ .setPostProcessorRegistry(EMFCompareRCPPlugin.getDefault().getPostProcessorRegistry())
+ .build().compare(scope, BasicMonitor.toMonitor(monitor));
final ResourceSet leftResourceSet = (ResourceSet)scope.getLeft();
final ResourceSet rightResourceSet = (ResourceSet)scope.getRight();
diff --git a/plugins/org.eclipse.emf.compare.rcp/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.rcp/META-INF/MANIFEST.MF
index e893c5b74..6e4c0e677 100644
--- a/plugins/org.eclipse.emf.compare.rcp/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.compare.rcp/META-INF/MANIFEST.MF
@@ -13,6 +13,7 @@ Bundle-Localization: plugin
Export-Package: org.eclipse.emf.compare.rcp,
org.eclipse.emf.compare.rcp.extension,
org.eclipse.emf.compare.rcp.internal;x-internal:=true,
+ org.eclipse.emf.compare.rcp.internal.match;x-internal:=true,
org.eclipse.emf.compare.rcp.internal.merger;x-internal:=true,
org.eclipse.emf.compare.rcp.internal.policy;x-internal:=true,
org.eclipse.emf.compare.rcp.internal.postprocessor;x-internal:=true,
diff --git a/plugins/org.eclipse.emf.compare.rcp/plugin.xml b/plugins/org.eclipse.emf.compare.rcp/plugin.xml
index a5276bd25..48a8ce2c9 100644
--- a/plugins/org.eclipse.emf.compare.rcp/plugin.xml
+++ b/plugins/org.eclipse.emf.compare.rcp/plugin.xml
@@ -1,50 +1,58 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.4"?>
-
-<!--
- Copyright (c) 2013 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
--->
-
-<plugin>
- <extension-point id="postProcessor" name="Post Processor" schema="schema/postProcessor.exsd"/>
- <extension-point id="merger" name="Merger" schema="schema/merger.exsd"/>
- <extension-point id="loadOnDemandPolicy" name="Load on Demand Policy" schema="schema/loadOnDemandPolicy.exsd"/>
-
- <extension point="org.eclipse.emf.ecore.generated_package">
- <package
- uri="http://www.eclipse.org/emf/compare"
- class="org.eclipse.emf.compare.ComparePackage"
- genModel="platform:/plugin/org.eclipse.emf.compare/model/compare.genmodel"/>
- </extension>
-
- <extension
- point="org.eclipse.emf.compare.rcp.merger">
- <merger
- class="org.eclipse.emf.compare.merge.ResourceAttachmentChangeMerger"
- ranking="10">
- </merger>
- <merger
- class="org.eclipse.emf.compare.merge.ReferenceChangeMerger"
- ranking="10">
- </merger>
- <merger
- class="org.eclipse.emf.compare.merge.AttributeChangeMerger"
- ranking="10">
- </merger>
- </extension>
- <extension
- point="org.eclipse.core.contenttype.contentTypes">
- <content-type
- id="org.eclipse.emf.compare.content.type"
- name="EMF Compare"
- priority="normal">
- </content-type>
- </extension>
-</plugin>
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (c) 2013 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
+-->
+
+<plugin>
+ <extension-point id="postProcessor" name="Post Processor" schema="schema/postProcessor.exsd"/>
+ <extension-point id="merger" name="Merger" schema="schema/merger.exsd"/>
+ <extension-point id="loadOnDemandPolicy" name="Load on Demand Policy" schema="schema/loadOnDemandPolicy.exsd"/>
+ <extension-point id="matchEngine" name="Match Engine" schema="schema/matchEngine.exsd"/>
+
+ <extension point="org.eclipse.emf.ecore.generated_package">
+ <package
+ uri="http://www.eclipse.org/emf/compare"
+ class="org.eclipse.emf.compare.ComparePackage"
+ genModel="platform:/plugin/org.eclipse.emf.compare/model/compare.genmodel"/>
+ </extension>
+
+ <extension
+ point="org.eclipse.emf.compare.rcp.merger">
+ <merger
+ class="org.eclipse.emf.compare.merge.ResourceAttachmentChangeMerger"
+ ranking="10">
+ </merger>
+ <merger
+ class="org.eclipse.emf.compare.merge.ReferenceChangeMerger"
+ ranking="10">
+ </merger>
+ <merger
+ class="org.eclipse.emf.compare.merge.AttributeChangeMerger"
+ ranking="10">
+ </merger>
+ </extension>
+ <extension
+ point="org.eclipse.core.contenttype.contentTypes">
+ <content-type
+ id="org.eclipse.emf.compare.content.type"
+ name="EMF Compare"
+ priority="normal">
+ </content-type>
+ </extension>
+ <extension
+ point="org.eclipse.emf.compare.rcp.matchEngine">
+ <engineFactory
+ class="org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl"
+ ranking="10">
+ </engineFactory>
+ </extension>
+</plugin>
diff --git a/plugins/org.eclipse.emf.compare.rcp/schema/matchEngine.exsd b/plugins/org.eclipse.emf.compare.rcp/schema/matchEngine.exsd
new file mode 100644
index 000000000..2572f4e4c
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp/schema/matchEngine.exsd
@@ -0,0 +1,116 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.compare.rcp" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.emf.compare.rcp" id="matchEngine" name="EMF Compare Match Engine"/>
+ </appinfo>
+ <documentation>
+ This extension point can be used in order to customize the match engine used for comparison.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="engineFactory" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="engineFactory">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ Describes a Match engine Factory and its enablement value.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.emf.compare.match.IMatchEngine$Factory"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="ranking" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ 2.1.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ &lt;extension point=&quot;org.eclipse.emf.compare.rcp.matchEngine&quot;&gt;
+ &lt;engineFactory class=&quot;org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl&quot; ranking=&quot;10&quot;/&gt;
+&lt;/extension&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ See org.eclipse.emf.compare.rcp/plugin.xml for existing contributions.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2013 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
+ </documentation>
+ </annotation>
+</schema>
diff --git a/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/EMFCompareRCPPlugin.java b/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/EMFCompareRCPPlugin.java
index b18adbf6f..6590d9d6d 100644
--- a/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/EMFCompareRCPPlugin.java
+++ b/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/EMFCompareRCPPlugin.java
@@ -14,10 +14,13 @@ import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl;
import org.eclipse.emf.compare.merge.IMerger;
import org.eclipse.emf.compare.postprocessor.IPostProcessor;
import org.eclipse.emf.compare.postprocessor.PostProcessorDescriptorRegistryImpl;
import org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener;
+import org.eclipse.emf.compare.rcp.internal.match.MatchEngineFactoryRegistryListener;
import org.eclipse.emf.compare.rcp.internal.merger.MergerExtensionRegistryListener;
import org.eclipse.emf.compare.rcp.internal.policy.LoadOnDemandPolicyRegistryImpl;
import org.eclipse.emf.compare.rcp.internal.policy.LoadOnDemandPolicyRegistryListener;
@@ -41,6 +44,9 @@ public class EMFCompareRCPPlugin extends Plugin {
/** The id of the merger extension point. */
public static final String MERGER_PPID = "merger"; //$NON-NLS-1$
+ /** The id of the match extension point. */
+ public static final String MATCH_ENGINE_PPID = "matchEngine"; //$NON-NLS-1$
+
// This plugin is a singleton, so it's quite ok to keep the plugin in a static field.
private static EMFCompareRCPPlugin plugin;
@@ -62,6 +68,12 @@ public class EMFCompareRCPPlugin extends Plugin {
/** The registry listener that will be used to react to post processor changes. */
private AbstractRegistryEventListener postProcessorFactoryRegistryListener;
+ /** The registry that will hold references to all match engine factories. */
+ private IMatchEngine.Factory.Registry matchEngineFactoryRegistry;
+
+ /** The registry listener that will be used to react to match engine changes. */
+ private MatchEngineFactoryRegistryListener matchEngineRegistryListener;
+
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
@@ -72,6 +84,11 @@ public class EMFCompareRCPPlugin extends Plugin {
final IExtensionRegistry registry = Platform.getExtensionRegistry();
+ matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineRegistryListener = new MatchEngineFactoryRegistryListener(PLUGIN_ID, MATCH_ENGINE_PPID,
+ getLog(), matchEngineFactoryRegistry);
+ matchEngineRegistryListener.readRegistry(registry);
+
mergerRegistry = new IMerger.RegistryImpl();
mergerRegistryListener = new MergerExtensionRegistryListener(PLUGIN_ID, MERGER_PPID, getLog(),
mergerRegistry);
@@ -112,6 +129,10 @@ public class EMFCompareRCPPlugin extends Plugin {
registry.removeListener(mergerRegistryListener);
mergerRegistryListener = null;
mergerRegistry = null;
+
+ registry.removeListener(matchEngineRegistryListener);
+ matchEngineRegistryListener = null;
+ matchEngineFactoryRegistry = null;
}
/**
@@ -143,6 +164,16 @@ public class EMFCompareRCPPlugin extends Plugin {
}
/**
+ * Returns the match engine factory registry to which extension will be registered.
+ *
+ * @return the match engine factory registry to which extension will be registered
+ * @since 3.0
+ */
+ public IMatchEngine.Factory.Registry getMatchEngineFactoryRegistry() {
+ return matchEngineFactoryRegistry;
+ }
+
+ /**
* Log the given message with the given severity to the logger of this plugin.
*
* @param severity
diff --git a/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/extension/AbstractRegistryEventListener.java b/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/extension/AbstractRegistryEventListener.java
index 372b30fec..e85c75a50 100644
--- a/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/extension/AbstractRegistryEventListener.java
+++ b/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/extension/AbstractRegistryEventListener.java
@@ -1,266 +1,264 @@
-/*******************************************************************************
- * Copyright (c) 2012, 2013 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.rcp.extension;
-
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IExtension;
-import org.eclipse.core.runtime.IExtensionPoint;
-import org.eclipse.core.runtime.IExtensionRegistry;
-import org.eclipse.core.runtime.ILog;
-import org.eclipse.core.runtime.IRegistryEventListener;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-
-/**
- * Abstract utility class to listen to the {@link IExtensionRegistry}. It provides base implementation to
- * {@link #added(IExtension[])} and {@link #removed(IExtension[])}. Users must implement
- * {@link #readElement(IConfigurationElement, Action)} according to their extension schema.
- * <p>
- * The helper method {@link #readRegistry()} is browsing already registered extension to the extension
- * registry making it easy to read the registry after having added the listener to it.
- * <p>
- * {@link #readRegistry()} is delegating to {@link #readElement(IConfigurationElement, Action)}.
- *
- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
- */
-public abstract class AbstractRegistryEventListener implements IRegistryEventListener {
-
- /**
- * Enumeration that says if the {@link IConfigurationElement} is to be added or removed from the
- * {@link IExtensionRegistry}.
- *
- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
- */
- protected static enum Action {
- /**
- * The {@link IConfigurationElement} is to be added.
- */
- ADD,
- /**
- * The {@link IConfigurationElement} is to be removed.
- */
- REMOVE
- }
-
- /**
- * The namespace of the extension point to be monitored.
- */
- private final String namespace;
-
- /**
- * The extension point ID to be monitored.
- */
- private final String extensionPointID;
-
- private final ILog log;
-
- /**
- * Creates a new registry event listener.
- *
- * @param namespace
- * The namespace of the extension point to be monitored.
- * @param extensionPointID
- * The extension point ID to be monitored
- * @param log
- * The log object to be used to log error and/or warning.
- */
- public AbstractRegistryEventListener(String namespace, String extensionPointID, ILog log) {
- this.namespace = namespace;
- this.extensionPointID = extensionPointID;
- this.log = log;
- }
-
- /**
- * Reads the given registry and call {@link #readElement(IConfigurationElement, Action)} as the read
- * {@link IConfigurationElement} were just added to it.
- *
- * @param extensionRegistry
- * the registry to read.
- */
- public void readRegistry(IExtensionRegistry extensionRegistry) {
- IExtensionPoint point = extensionRegistry.getExtensionPoint(namespace, extensionPointID);
- if (point != null) {
- IConfigurationElement[] elements = point.getConfigurationElements();
- for (int i = 0; i < elements.length; i++) {
- internalReadElement(elements[i], Action.ADD);
- }
- }
- }
-
- /**
- * Reads the given {@code element}. This method can be call when the element is added or remove. The
- * {@code action} reflect it. It returns true if the element is recognized as valid regarding the
- * monitored extension point. It will be call on sub elements recursively for all recognized ones.
- *
- * @param element
- * the element to be read.
- * @param action
- * is the element added or removed.
- * @return true if the element is recognized as valid regarding the monitored extension point.
- */
- protected boolean readElement(IConfigurationElement element, Action action) {
- final boolean ret;
- if (validateExtensionElement(element)) {
- switch (action) {
- case ADD:
- ret = addedValid(element);
- break;
- case REMOVE:
- ret = removedValid(element);
- break;
- default:
- ret = false;
- break;
- }
- } else {
- ret = false;
- }
- return ret;
- }
-
- /**
- * Validates if the given element is an element for the given extension and is well constructed. Returns
- * true if the element should be further parsed for addition or removal.
- *
- * @param element
- * the element to validate.
- * @return true if the element should be further parsed for addition or removal, else otherwise.
- */
- protected abstract boolean validateExtensionElement(IConfigurationElement element);
-
- /**
- * Process the given element as the addition of a valid element extension.
- *
- * @param element
- * the element to be added.
- * @return true if the given element has been added and if its children should be processed, false
- * otherwise.
- */
- protected abstract boolean addedValid(IConfigurationElement element);
-
- /**
- * Process the given element as the removal of a valid element extension.
- *
- * @param element
- * the element to be removed.
- * @return true if the given element has been removed and if its children should be processed, false
- * otherwise.
- */
- protected abstract boolean removedValid(IConfigurationElement element);
-
- /**
- * Reads the given element and, if recognized, browse recursively the children and try to read it.
- *
- * @param element
- * the element to be read.
- * @param action
- * is the element added or removed.
- */
- private void internalReadElement(IConfigurationElement element, Action action) {
- boolean recognized = readElement(element, action);
- if (recognized) {
- IConfigurationElement[] children = element.getChildren();
- for (int i = 0; i < children.length; ++i) {
- internalReadElement(children[i], action);
- }
- } else {
- log(IStatus.ERROR, element, "Error processing extension: " + element);
- }
- }
-
- /**
- * Delegates the logging of a missing attribute to {@link #log(IConfigurationElement, String)} with a
- * proper message.
- *
- * @param element
- * the element to which an attribute is missing.
- * @param attributeName
- * the name of the missing attribute.
- */
- protected void logMissingAttribute(IConfigurationElement element, String attributeName) {
- log(IStatus.ERROR, element, "The required attribute '" + attributeName + "' not defined");
- }
-
- /**
- * Log the error to the current plugin logger.
- *
- * @param element
- * the element from which comes to the error.
- * @param message
- * the message to be logged.
- */
- protected void log(int severity, IConfigurationElement element, String message) {
- log.log(new Status(severity, element.getDeclaringExtension().getContributor().getName(), message));
- }
-
- /**
- * Log the error to the current plugin logger.
- *
- * @param element
- * the element from which comes to the error.
- * @param message
- * the message to be logged.
- */
- protected void log(IConfigurationElement element, Throwable t) {
- log.log(new Status(IStatus.ERROR, element.getDeclaringExtension().getContributor().getName(), t
- .getMessage(), t));
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtension[])
- */
- public void added(IExtension[] extensions) {
- for (IExtension extension : extensions) {
- if (extension.getExtensionPointUniqueIdentifier().equals(extensionPointID)) {
- IConfigurationElement[] configurationElement = extension.getConfigurationElements();
- for (int j = 0; j < configurationElement.length; ++j) {
- internalReadElement(configurationElement[j], Action.ADD);
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtension[])
- */
- public void removed(IExtension[] extensions) {
- for (IExtension extension : extensions) {
- if (extension.getExtensionPointUniqueIdentifier().equals(extensionPointID)) {
- IConfigurationElement[] configurationElement = extension.getConfigurationElements();
- for (int j = 0; j < configurationElement.length; ++j) {
- internalReadElement(configurationElement[j], Action.REMOVE);
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtensionPoint[])
- */
- public void added(IExtensionPoint[] extensionPoints) {
- // no need to listen to this.
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtensionPoint[])
- */
- public void removed(IExtensionPoint[] extensionPoints) {
- // no need to listen to this.
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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.rcp.extension;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IRegistryEventListener;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Abstract utility class to listen to the {@link IExtensionRegistry}. It provides base implementation to
+ * {@link #added(IExtension[])} and {@link #removed(IExtension[])}. Users must implement
+ * {@link #readElement(IConfigurationElement, Action)} according to their extension schema.
+ * <p>
+ * The helper method {@link #readRegistry()} is browsing already registered extension to the extension
+ * registry making it easy to read the registry after having added the listener to it.
+ * <p>
+ * {@link #readRegistry()} is delegating to {@link #readElement(IConfigurationElement, Action)}.
+ *
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+public abstract class AbstractRegistryEventListener implements IRegistryEventListener {
+
+ /**
+ * Enumeration that says if the {@link IConfigurationElement} is to be added or removed from the
+ * {@link IExtensionRegistry}.
+ *
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+ protected static enum Action {
+ /**
+ * The {@link IConfigurationElement} is to be added.
+ */
+ ADD,
+ /**
+ * The {@link IConfigurationElement} is to be removed.
+ */
+ REMOVE
+ }
+
+ /**
+ * The namespace of the extension point to be monitored.
+ */
+ private final String namespace;
+
+ /**
+ * The extension point ID to be monitored.
+ */
+ private final String extensionPointID;
+
+ private final ILog log;
+
+ /**
+ * Creates a new registry event listener.
+ *
+ * @param namespace
+ * The namespace of the extension point to be monitored.
+ * @param extensionPointID
+ * The extension point ID to be monitored
+ * @param log
+ * The log object to be used to log error and/or warning.
+ */
+ public AbstractRegistryEventListener(String namespace, String extensionPointID, ILog log) {
+ this.namespace = namespace;
+ this.extensionPointID = extensionPointID;
+ this.log = log;
+ }
+
+ /**
+ * Reads the given registry and call {@link #readElement(IConfigurationElement, Action)} as the read
+ * {@link IConfigurationElement} were just added to it.
+ *
+ * @param extensionRegistry
+ * the registry to read.
+ */
+ public void readRegistry(IExtensionRegistry extensionRegistry) {
+ IExtensionPoint point = extensionRegistry.getExtensionPoint(namespace, extensionPointID);
+ if (point != null) {
+ IConfigurationElement[] elements = point.getConfigurationElements();
+ for (int i = 0; i < elements.length; i++) {
+ internalReadElement(elements[i], Action.ADD);
+ }
+ }
+ }
+
+ /**
+ * Reads the given {@code element}. This method can be call when the element is added or remove. The
+ * {@code action} reflect it. It returns true if the element is recognized as valid regarding the
+ * monitored extension point. It will be call on sub elements recursively for all recognized ones.
+ *
+ * @param element
+ * the element to be read.
+ * @param action
+ * is the element added or removed.
+ * @return true if the element is recognized as valid regarding the monitored extension point.
+ */
+ protected boolean readElement(IConfigurationElement element, Action action) {
+ final boolean ret;
+ if (validateExtensionElement(element)) {
+ switch (action) {
+ case ADD:
+ ret = addedValid(element);
+ break;
+ case REMOVE:
+ ret = removedValid(element);
+ break;
+ default:
+ ret = false;
+ break;
+ }
+ } else {
+ ret = false;
+ }
+ return ret;
+ }
+
+ /**
+ * Validates if the given element is an element for the given extension and is well constructed. Returns
+ * true if the element should be further parsed for addition or removal.
+ *
+ * @param element
+ * the element to validate.
+ * @return true if the element should be further parsed for addition or removal, else otherwise.
+ */
+ protected abstract boolean validateExtensionElement(IConfigurationElement element);
+
+ /**
+ * Process the given element as the addition of a valid element extension.
+ *
+ * @param element
+ * the element to be added.
+ * @return true if the given element has been added and if its children should be processed, false
+ * otherwise.
+ */
+ protected abstract boolean addedValid(IConfigurationElement element);
+
+ /**
+ * Process the given element as the removal of a valid element extension.
+ *
+ * @param element
+ * the element to be removed.
+ * @return true if the given element has been removed and if its children should be processed, false
+ * otherwise.
+ */
+ protected abstract boolean removedValid(IConfigurationElement element);
+
+ /**
+ * Reads the given element and, if recognized, browse recursively the children and try to read it.
+ *
+ * @param element
+ * the element to be read.
+ * @param action
+ * is the element added or removed.
+ */
+ private void internalReadElement(IConfigurationElement element, Action action) {
+ boolean recognized = readElement(element, action);
+ if (recognized) {
+ IConfigurationElement[] children = element.getChildren();
+ for (int i = 0; i < children.length; ++i) {
+ internalReadElement(children[i], action);
+ }
+ }
+ }
+
+ /**
+ * Delegates the logging of a missing attribute to {@link #log(IConfigurationElement, String)} with a
+ * proper message.
+ *
+ * @param element
+ * the element to which an attribute is missing.
+ * @param attributeName
+ * the name of the missing attribute.
+ */
+ protected void logMissingAttribute(IConfigurationElement element, String attributeName) {
+ log(IStatus.ERROR, element, "The required attribute '" + attributeName + "' not defined");
+ }
+
+ /**
+ * Log the error to the current plugin logger.
+ *
+ * @param element
+ * the element from which comes to the error.
+ * @param message
+ * the message to be logged.
+ */
+ protected void log(int severity, IConfigurationElement element, String message) {
+ log.log(new Status(severity, element.getDeclaringExtension().getContributor().getName(), message));
+ }
+
+ /**
+ * Log the error to the current plugin logger.
+ *
+ * @param element
+ * the element from which comes to the error.
+ * @param message
+ * the message to be logged.
+ */
+ protected void log(IConfigurationElement element, Throwable t) {
+ log.log(new Status(IStatus.ERROR, element.getDeclaringExtension().getContributor().getName(), t
+ .getMessage(), t));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtension[])
+ */
+ public void added(IExtension[] extensions) {
+ for (IExtension extension : extensions) {
+ if (extension.getExtensionPointUniqueIdentifier().equals(extensionPointID)) {
+ IConfigurationElement[] configurationElement = extension.getConfigurationElements();
+ for (int j = 0; j < configurationElement.length; ++j) {
+ internalReadElement(configurationElement[j], Action.ADD);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtension[])
+ */
+ public void removed(IExtension[] extensions) {
+ for (IExtension extension : extensions) {
+ if (extension.getExtensionPointUniqueIdentifier().equals(extensionPointID)) {
+ IConfigurationElement[] configurationElement = extension.getConfigurationElements();
+ for (int j = 0; j < configurationElement.length; ++j) {
+ internalReadElement(configurationElement[j], Action.REMOVE);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtensionPoint[])
+ */
+ public void added(IExtensionPoint[] extensionPoints) {
+ // no need to listen to this.
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtensionPoint[])
+ */
+ public void removed(IExtensionPoint[] extensionPoints) {
+ // no need to listen to this.
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/internal/match/MatchEngineFactoryRegistryListener.java b/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/internal/match/MatchEngineFactoryRegistryListener.java
new file mode 100644
index 000000000..8169c3329
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp/src/org/eclipse/emf/compare/rcp/internal/match/MatchEngineFactoryRegistryListener.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.rcp.internal.match;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener;
+import org.eclipse.emf.compare.rcp.internal.EMFCompareRCPMessages;
+
+/**
+ * Listener for contributions to the match engine extension.
+ *
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ */
+public class MatchEngineFactoryRegistryListener extends AbstractRegistryEventListener {
+
+ /** TAG_ENGINE. */
+ private static final String TAG_ENGINE_FACTORY = "engineFactory"; //$NON-NLS-1$
+
+ /** ATT_CLASS. */
+ private static final String ATT_CLASS = "class"; //$NON-NLS-1$
+
+ /** ATT_RANKING. */
+ private static final String ATT_RANKING = "ranking"; //$NON-NLS-1$
+
+ /** The match engine factory registry to which extension will be registered. */
+ private final IMatchEngine.Factory.Registry matchEngineFactoryRegistry;
+
+ /**
+ * Creates a new registry listener with the given match engine factory registry to which extension will be
+ * registered.
+ *
+ * @param registry
+ * the match engine factory registry to which extension will be registered.
+ * @param pluginID
+ * The pluginID of the extension point to be monitored.
+ * @param extensionPointID
+ * The extension point ID to be monitored.
+ * @param log
+ * The log object to be used to log error and/or warning.
+ */
+ public MatchEngineFactoryRegistryListener(String pluginID, String extensionPointID, ILog log,
+ IMatchEngine.Factory.Registry registry) {
+ super(pluginID, extensionPointID, log);
+ this.matchEngineFactoryRegistry = registry;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener#validateExtensionElement(org.eclipse.core.runtime.IConfigurationElement)
+ */
+ @Override
+ protected boolean validateExtensionElement(IConfigurationElement element) {
+ final boolean ret;
+ if (TAG_ENGINE_FACTORY.equals(element.getName())) {
+ if (element.getAttribute(ATT_CLASS) == null) {
+ logMissingAttribute(element, ATT_CLASS);
+ ret = false;
+ } else if (element.getAttribute(ATT_RANKING) == null) {
+ logMissingAttribute(element, ATT_RANKING);
+ ret = false;
+ } else if (element.getAttribute(ATT_RANKING) != null) {
+ String rankingStr = element.getAttribute(ATT_RANKING);
+ try {
+ Integer.parseInt(rankingStr);
+ } catch (NumberFormatException nfe) {
+ log(IStatus.ERROR, element, EMFCompareRCPMessages.getString(
+ "malformed.extension.attribute", //$NON-NLS-1$
+ ATT_RANKING));
+ return false;
+ }
+ ret = true;
+ } else {
+ ret = true;
+ }
+ } else {
+ ret = false;
+ }
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener#addedValid(org.eclipse.core.runtime.IConfigurationElement)
+ */
+ @Override
+ protected boolean addedValid(IConfigurationElement element) {
+ try {
+ IMatchEngine.Factory matchEngineFactory = (IMatchEngine.Factory)element
+ .createExecutableExtension(ATT_CLASS);
+ matchEngineFactory.setRanking(Integer.parseInt(element.getAttribute(ATT_RANKING)));
+ IMatchEngine.Factory previous = matchEngineFactoryRegistry.add(matchEngineFactory);
+ if (previous != null) {
+ log(IStatus.WARNING, element, EMFCompareRCPMessages.getString(
+ "duplicate.extension", matchEngineFactoryRegistry.getClass().getName())); //$NON-NLS-1$
+ }
+ } catch (CoreException e) {
+ log(IStatus.ERROR, element, e.getMessage());
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener#removedValid(org.eclipse.core.runtime.IConfigurationElement)
+ */
+ @Override
+ protected boolean removedValid(IConfigurationElement element) {
+ matchEngineFactoryRegistry.remove(element.getAttribute(ATT_CLASS));
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/fullcomparison/ProximityComparisonTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/fullcomparison/ProximityComparisonTest.java
index e354bbec3..2a46ec7e1 100644
--- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/fullcomparison/ProximityComparisonTest.java
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/fullcomparison/ProximityComparisonTest.java
@@ -1,210 +1,228 @@
-/**
- * Copyright (c) 2012 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.fullcomparison;
-
-import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.util.Iterator;
-import java.util.List;
-
-import org.eclipse.emf.compare.AttributeChange;
-import org.eclipse.emf.compare.Comparison;
-import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.DifferenceKind;
-import org.eclipse.emf.compare.DifferenceSource;
-import org.eclipse.emf.compare.EMFCompare;
-import org.eclipse.emf.compare.Match;
-import org.eclipse.emf.compare.ReferenceChange;
-import org.eclipse.emf.compare.ResourceAttachmentChange;
-import org.eclipse.emf.compare.match.DefaultMatchEngine;
-import org.eclipse.emf.compare.scope.IComparisonScope;
-import org.eclipse.emf.compare.tests.framework.EMFCompareAssert;
-import org.eclipse.emf.compare.tests.framework.EMFCompareTestBase;
-import org.eclipse.emf.compare.tests.fullcomparison.data.distance.DistanceMatchInputData;
-import org.eclipse.emf.compare.tests.suite.AllTests;
-import org.eclipse.emf.compare.utils.EMFComparePrettyPrinter;
-import org.eclipse.emf.compare.utils.UseIdentifiers;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EcorePackage;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.junit.Before;
-import org.junit.Test;
-
-@SuppressWarnings("nls")
-public class ProximityComparisonTest extends EMFCompareTestBase {
- @Before
- public void setUp() throws Exception {
- AllTests.fillEMFRegistries();
- }
-
- private DistanceMatchInputData inputData = new DistanceMatchInputData();
-
- @Test
- public void singleEObjectTest() throws Exception {
- EPackage v1 = EcoreUtil.copy(EcorePackage.eINSTANCE);
- EPackage v2 = EcoreUtil.copy(EcorePackage.eINSTANCE);
- v1.getEClassifiers().clear();
- v2.getEClassifiers().clear();
-
- final IComparisonScope scope = EMFCompare.createDefaultScope(v1, v2);
- Comparison result = EMFCompare.builder().build().compare(scope);
- assertAllMatched(Lists.newArrayList(v1), result);
- assertEquals("We are supposed to have zero diffs", 0, result.getDifferences().size());
- }
-
- @Test
- public void matchingSmallRenameChanges() throws Exception {
- Resource left = inputData.getCompareLeft();
- Resource right = inputData.getCompareRight();
- final IComparisonScope scope = EMFCompare.createDefaultScope(left, right);
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- assertEquals("We are supposed to have one rename diff", 1, result.getDifferences().size());
- }
-
- @Test
- public void matchingIndenticInstances() throws Exception {
-
- EPackage v1 = EcoreUtil.copy(EcorePackage.eINSTANCE);
- EPackage v2 = EcoreUtil.copy(EcorePackage.eINSTANCE);
-
- final IComparisonScope scope = EMFCompare.createDefaultScope(v1, v2);
- Comparison result = EMFCompare.builder().build().compare(scope);
-
- assertAllMatched(Lists.newArrayList(v1), result);
- assertEquals("We are supposed to have zero diffs", 0, result.getDifferences().size());
- }
-
- @Test
- public void smallChangeOnEPackage() throws Exception {
-
- EPackage v1 = EcoreUtil.copy(EcorePackage.eINSTANCE);
- EPackage v2 = EcoreUtil.copy(EcorePackage.eINSTANCE);
- v2.setName("renamed");
-
- final IComparisonScope scope = EMFCompare.createDefaultScope(v1, v2);
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- assertAllMatched(Lists.newArrayList(v1), result);
- assertEquals("We are supposed to have a rename", 1, result.getDifferences().size());
- }
-
- @Test
- public void packageAddDelete() throws Exception {
- final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.getPackageAddDeleteLeft(),
- inputData.getPackageAddDeleteRight());
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- EMFComparePrettyPrinter.printComparison(result, System.out);
- final List<Diff> differences = result.getDifferences();
- EMFCompareAssert.assertAddedToReference(differences, "p1.p2", "eSubpackages", "p1.p2.subPackage",
- DifferenceSource.LEFT);
- EMFCompareAssert.assertRemovedFromReference(differences, "p1", "eSubpackages", "p1.another",
- DifferenceSource.LEFT);
- assertEquals("We are supposed to have zero diffs", 2, result.getDifferences().size());
- }
-
- @Test
- public void packageAddRemoveNoRename() throws Exception {
- final IComparisonScope scope = EMFCompare.createDefaultScope(inputData
- .getPackageAddRemoveNoRenameLeft(), inputData.getPackageAddRemoveNoRenameRight());
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- EMFComparePrettyPrinter.printComparison(result, System.out);
- final List<Diff> differences = result.getDifferences();
- // EMFCompareAssert.assertAddedToReference(differences, "p1.p2", "eSubpackages", "p1.p2.subPackage",
- // DifferenceSource.LEFT);
- // EMFCompareAssert.assertRemovedFromReference(differences, "p1", "eSubpackages", "p1.another",
- // DifferenceSource.LEFT);
- assertEquals("We are supposed to have zero diffs", 2, result.getDifferences().size());
- }
-
- @Test
- public void alwaysTakeTheClosestNoMatterTheIterationOrder() throws Exception {
- final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.getVerySmallLeft(), inputData
- .getVerySmallRight());
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- assertEquals(
- "The Match took on element which is close enough (in the limits) preventing the next iteration to take it (it was closest)",
- 1, result.getDifferences().size());
-
- }
-
- @Test
- public void addRemoveAndNotRename() throws Exception {
- final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.get391657Left(), inputData
- .get391657Right());
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- Iterator<AttributeChange> attrChanges = Iterators.filter(result.getDifferences().iterator(),
- AttributeChange.class);
- assertFalse("We are supposed to detect an addition/remove (and not a rename if that's what we get)",
- attrChanges.hasNext());
-
- }
-
- @Test
- public void resourceRootChange() throws Exception {
- final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.get390666Left(), inputData
- .get390666Right(), inputData.get390666Ancestor());
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- Iterator<ResourceAttachmentChange> attrChanges = Iterators.filter(result.getDifferences().iterator(),
- ResourceAttachmentChange.class);
- assertTrue("We are supposed to detect a new attachment to a resource", attrChanges.hasNext());
-
- }
-
- @Test
- public void moveInAReferenceShouldNotAffectMatch() throws Exception {
- /*
- * See bug #391798 : moving elements a lot in a reference (like in changing index) should not affect
- * the matching very much.
- */
- final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.get391798Left(), inputData
- .get391798Right());
- Comparison result = EMFCompare.builder().setMatchEngine(
- DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(scope);
- for (Diff dif : result.getDifferences()) {
- if (dif instanceof ReferenceChange && dif.getKind() == DifferenceKind.MOVE) {
-
- } else {
- fail("We should only detect moves in the reference.");
- }
- }
-
- }
-
- private String print(Comparison result) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- PrintStream ps = new PrintStream(baos);
- EMFComparePrettyPrinter.printDifferences(result, ps);
- return baos.toString();
- }
-
- private void assertAllMatched(Iterable<? extends EObject> eObjects, Comparison comparison) {
- for (EObject eObject : eObjects) {
- final Match match = comparison.getMatch(eObject);
- assertTrue(eObject + " has no match", match != null);
- }
- }
-}
+/**
+ * Copyright (c) 2012 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.fullcomparison;
+
+import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.emf.compare.AttributeChange;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceKind;
+import org.eclipse.emf.compare.DifferenceSource;
+import org.eclipse.emf.compare.EMFCompare;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.ReferenceChange;
+import org.eclipse.emf.compare.ResourceAttachmentChange;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.tests.framework.EMFCompareAssert;
+import org.eclipse.emf.compare.tests.framework.EMFCompareTestBase;
+import org.eclipse.emf.compare.tests.fullcomparison.data.distance.DistanceMatchInputData;
+import org.eclipse.emf.compare.tests.suite.AllTests;
+import org.eclipse.emf.compare.utils.EMFComparePrettyPrinter;
+import org.eclipse.emf.compare.utils.UseIdentifiers;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcorePackage;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("nls")
+public class ProximityComparisonTest extends EMFCompareTestBase {
+ @Before
+ public void setUp() throws Exception {
+ AllTests.fillEMFRegistries();
+ }
+
+ private DistanceMatchInputData inputData = new DistanceMatchInputData();
+
+ @Test
+ public void singleEObjectTest() throws Exception {
+ EPackage v1 = EcoreUtil.copy(EcorePackage.eINSTANCE);
+ EPackage v2 = EcoreUtil.copy(EcorePackage.eINSTANCE);
+ v1.getEClassifiers().clear();
+ v2.getEClassifiers().clear();
+
+ final IComparisonScope scope = EMFCompare.createDefaultScope(v1, v2);
+ Comparison result = EMFCompare.builder().build().compare(scope);
+ assertAllMatched(Lists.newArrayList(v1), result);
+ assertEquals("We are supposed to have zero diffs", 0, result.getDifferences().size());
+ }
+
+ @Test
+ public void matchingSmallRenameChanges() throws Exception {
+ Resource left = inputData.getCompareLeft();
+ Resource right = inputData.getCompareRight();
+ final IComparisonScope scope = EMFCompare.createDefaultScope(left, right);
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ assertEquals("We are supposed to have one rename diff", 1, result.getDifferences().size());
+ }
+
+ @Test
+ public void matchingIndenticInstances() throws Exception {
+
+ EPackage v1 = EcoreUtil.copy(EcorePackage.eINSTANCE);
+ EPackage v2 = EcoreUtil.copy(EcorePackage.eINSTANCE);
+
+ final IComparisonScope scope = EMFCompare.createDefaultScope(v1, v2);
+ Comparison result = EMFCompare.builder().build().compare(scope);
+
+ assertAllMatched(Lists.newArrayList(v1), result);
+ assertEquals("We are supposed to have zero diffs", 0, result.getDifferences().size());
+ }
+
+ @Test
+ public void smallChangeOnEPackage() throws Exception {
+
+ EPackage v1 = EcoreUtil.copy(EcorePackage.eINSTANCE);
+ EPackage v2 = EcoreUtil.copy(EcorePackage.eINSTANCE);
+ v2.setName("renamed");
+
+ final IComparisonScope scope = EMFCompare.createDefaultScope(v1, v2);
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ assertAllMatched(Lists.newArrayList(v1), result);
+ assertEquals("We are supposed to have a rename", 1, result.getDifferences().size());
+ }
+
+ @Test
+ public void packageAddDelete() throws Exception {
+ final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.getPackageAddDeleteLeft(),
+ inputData.getPackageAddDeleteRight());
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ EMFComparePrettyPrinter.printComparison(result, System.out);
+ final List<Diff> differences = result.getDifferences();
+ EMFCompareAssert.assertAddedToReference(differences, "p1.p2", "eSubpackages", "p1.p2.subPackage",
+ DifferenceSource.LEFT);
+ EMFCompareAssert.assertRemovedFromReference(differences, "p1", "eSubpackages", "p1.another",
+ DifferenceSource.LEFT);
+ assertEquals("We are supposed to have zero diffs", 2, result.getDifferences().size());
+ }
+
+ @Test
+ public void packageAddRemoveNoRename() throws Exception {
+ final IComparisonScope scope = EMFCompare.createDefaultScope(inputData
+ .getPackageAddRemoveNoRenameLeft(), inputData.getPackageAddRemoveNoRenameRight());
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ EMFComparePrettyPrinter.printComparison(result, System.out);
+ final List<Diff> differences = result.getDifferences();
+ // EMFCompareAssert.assertAddedToReference(differences, "p1.p2", "eSubpackages", "p1.p2.subPackage",
+ // DifferenceSource.LEFT);
+ // EMFCompareAssert.assertRemovedFromReference(differences, "p1", "eSubpackages", "p1.another",
+ // DifferenceSource.LEFT);
+ assertEquals("We are supposed to have zero diffs", 2, result.getDifferences().size());
+ }
+
+ @Test
+ public void alwaysTakeTheClosestNoMatterTheIterationOrder() throws Exception {
+ final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.getVerySmallLeft(), inputData
+ .getVerySmallRight());
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ assertEquals(
+ "The Match took on element which is close enough (in the limits) preventing the next iteration to take it (it was closest)",
+ 1, result.getDifferences().size());
+
+ }
+
+ @Test
+ public void addRemoveAndNotRename() throws Exception {
+ final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.get391657Left(), inputData
+ .get391657Right());
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ Iterator<AttributeChange> attrChanges = Iterators.filter(result.getDifferences().iterator(),
+ AttributeChange.class);
+ assertFalse("We are supposed to detect an addition/remove (and not a rename if that's what we get)",
+ attrChanges.hasNext());
+
+ }
+
+ @Test
+ public void resourceRootChange() throws Exception {
+ final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.get390666Left(), inputData
+ .get390666Right(), inputData.get390666Ancestor());
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ Iterator<ResourceAttachmentChange> attrChanges = Iterators.filter(result.getDifferences().iterator(),
+ ResourceAttachmentChange.class);
+ assertTrue("We are supposed to detect a new attachment to a resource", attrChanges.hasNext());
+
+ }
+
+ @Test
+ public void moveInAReferenceShouldNotAffectMatch() throws Exception {
+ /*
+ * See bug #391798 : moving elements a lot in a reference (like in changing index) should not affect
+ * the matching very much.
+ */
+ final IComparisonScope scope = EMFCompare.createDefaultScope(inputData.get391798Left(), inputData
+ .get391798Right());
+ IMatchEngine.Factory.Registry matchEngineFactoryRegistry = new MatchEngineFactoryRegistryImpl();
+ matchEngineFactoryRegistry.add(new MatchEngineFactoryImpl(UseIdentifiers.NEVER));
+ Comparison result = EMFCompare.builder().setMatchEngineFactoryRegistry(matchEngineFactoryRegistry)
+ .build().compare(scope);
+ for (Diff dif : result.getDifferences()) {
+ if (dif instanceof ReferenceChange && dif.getKind() == DifferenceKind.MOVE) {
+
+ } else {
+ fail("We should only detect moves in the reference.");
+ }
+ }
+
+ }
+
+ private String print(Comparison result) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ EMFComparePrettyPrinter.printDifferences(result, ps);
+ return baos.toString();
+ }
+
+ private void assertAllMatched(Iterable<? extends EObject> eObjects, Comparison comparison) {
+ for (EObject eObject : eObjects) {
+ final Match match = comparison.getMatch(eObject);
+ assertTrue(eObject + " has no match", match != null);
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/match/MatchEngineFactoryRegistryTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/match/MatchEngineFactoryRegistryTest.java
new file mode 100644
index 000000000..1f829e966
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/match/MatchEngineFactoryRegistryTest.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.match;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.eclipse.emf.compare.EMFCompare;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.tests.fullcomparison.data.identifier.IdentifierMatchInputData;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.junit.Test;
+
+public class MatchEngineFactoryRegistryTest {
+
+ private IdentifierMatchInputData input = new IdentifierMatchInputData();
+
+ @Test
+ public void tesMatchEngineFactoryRegistry() throws IOException {
+ final Resource left = input.getExtlibraryLeft();
+ final Resource right = input.getExtlibraryRight();
+
+ final IMatchEngine.Factory.Registry registry = MatchEngineFactoryRegistryImpl
+ .createStandaloneInstance();
+
+ // Get the appropriate MatchEngineFactory.
+ final IComparisonScope scope = EMFCompare.createDefaultScope(left, right);
+ IMatchEngine.Factory factory = registry.getHighestRankingMatchEngineFactory(scope);
+
+ assertTrue(factory instanceof MatchEngineFactoryImpl);
+ assertEquals(1, registry.getMatchEngineFactories(scope).size());
+
+ // Add new MatchEngineFactory with higher ranking, but not appropriate
+ IMatchEngine.Factory factoryWithHighestRanking = new MatchEngineFactoryTest();
+ factoryWithHighestRanking.setRanking(100);
+ registry.add(factoryWithHighestRanking);
+
+ // Get the appropriate MatchEngineFactory.
+ factory = registry.getHighestRankingMatchEngineFactory(scope);
+
+ assertFalse(factory instanceof MatchEngineFactoryTest);
+ assertEquals(1, registry.getMatchEngineFactories(scope).size());
+
+ // Add new MatchEngineFactory with higher ranking
+ IMatchEngine.Factory factoryWithMiddleRanking = new MatchEngineFactoryOtherTest();
+ factoryWithMiddleRanking.setRanking(50);
+ registry.add(factoryWithMiddleRanking);
+
+ // Get the appropriate MatchEngineFactory.
+ factory = registry.getHighestRankingMatchEngineFactory(scope);
+
+ assertTrue(factory instanceof MatchEngineFactoryOtherTest);
+ assertEquals(2, registry.getMatchEngineFactories(scope).size());
+ }
+
+ private class MatchEngineFactoryTest extends MatchEngineFactoryImpl {
+
+ @Override
+ public boolean isMatchEngineFactoryFor(IComparisonScope scope) {
+ return false;
+ }
+ }
+
+ private class MatchEngineFactoryOtherTest extends MatchEngineFactoryImpl {
+
+ @Override
+ public boolean isMatchEngineFactoryFor(IComparisonScope scope) {
+ return true;
+ }
+ }
+}
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 6722ab275..c9377fa13 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,85 +1,87 @@
-/*******************************************************************************
- * Copyright (c) 2012 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.suite;
-
-import junit.framework.JUnit4TestAdapter;
-import junit.framework.Test;
-import junit.textui.TestRunner;
-
-import org.eclipse.emf.compare.ComparePackage;
-import org.eclipse.emf.compare.tests.command.CommandStackTestSuite;
-import org.eclipse.emf.compare.tests.conflict.ConflictDetectionTest;
-import org.eclipse.emf.compare.tests.diff.DiffUtilTest;
-import org.eclipse.emf.compare.tests.diff.LCSPerformanceTest;
-import org.eclipse.emf.compare.tests.diff.URIDistanceTest;
-import org.eclipse.emf.compare.tests.edit.AllEditTests;
-import org.eclipse.emf.compare.tests.equi.EquiComputingTest;
-import org.eclipse.emf.compare.tests.fragmentation.FragmentationTest;
-import org.eclipse.emf.compare.tests.fullcomparison.DynamicInstanceComparisonTest;
-import org.eclipse.emf.compare.tests.fullcomparison.ExtLibraryTest;
-import org.eclipse.emf.compare.tests.fullcomparison.IdentifierComparisonTest;
-import org.eclipse.emf.compare.tests.fullcomparison.ProximityComparisonTest;
-import org.eclipse.emf.compare.tests.merge.ExtensionMergeTest;
-import org.eclipse.emf.compare.tests.merge.IndividualMergeOutOfScopeValuesTest;
-import org.eclipse.emf.compare.tests.merge.IndividualMergeTest;
-import org.eclipse.emf.compare.tests.merge.MultipleMergeTest;
-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;
-import org.eclipse.emf.compare.tests.req.ReqComputingTest;
-import org.eclipse.emf.compare.tests.scope.DefaultComparisonScopeTest;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
-
-/**
- * This test suite allows us to launch all tests for EMF Compare at once.
- *
- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
- */
-@RunWith(Suite.class)
-@SuiteClasses({CompareTestSuite.class, DefaultComparisonScopeTest.class, IdentifierComparisonTest.class,
- ExtLibraryTest.class, ConflictDetectionTest.class, ReqComputingTest.class, EquiComputingTest.class,
- DiffUtilTest.class, LCSPerformanceTest.class, MultipleMergeTest.class, PostProcessorTest.class,
- IndividualMergeTest.class, ExtensionMergeTest.class, IndividualMergeOutOfScopeValuesTest.class,
- ProximityComparisonTest.class, DynamicInstanceComparisonTest.class, URIDistanceTest.class,
- FragmentationTest.class, AllEditTests.class, CommandStackTestSuite.class, })
-public class AllTests {
- /**
- * Standalone launcher for all of compare's tests.
- *
- * @generated
- */
- public static void main(String[] args) {
- TestRunner.run(suite());
- }
-
- /**
- * This will return a suite populated with all tests available through this class.
- *
- * @generated
- */
- public static Test suite() {
- return new JUnit4TestAdapter(CompareTestSuite.class);
- }
-
- @BeforeClass
- public static void fillEMFRegistries() {
- EPackage.Registry.INSTANCE.put(ComparePackage.eNS_URI, ComparePackage.eINSTANCE);
- EPackage.Registry.INSTANCE.put(NodesPackage.eNS_URI, NodesPackage.eINSTANCE);
-
- Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("nodes", //$NON-NLS-1$
- new NodesResourceFactoryImpl());
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012 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.suite;
+
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
+import org.eclipse.emf.compare.ComparePackage;
+import org.eclipse.emf.compare.tests.command.CommandStackTestSuite;
+import org.eclipse.emf.compare.tests.conflict.ConflictDetectionTest;
+import org.eclipse.emf.compare.tests.diff.DiffUtilTest;
+import org.eclipse.emf.compare.tests.diff.LCSPerformanceTest;
+import org.eclipse.emf.compare.tests.diff.URIDistanceTest;
+import org.eclipse.emf.compare.tests.edit.AllEditTests;
+import org.eclipse.emf.compare.tests.equi.EquiComputingTest;
+import org.eclipse.emf.compare.tests.fragmentation.FragmentationTest;
+import org.eclipse.emf.compare.tests.fullcomparison.DynamicInstanceComparisonTest;
+import org.eclipse.emf.compare.tests.fullcomparison.ExtLibraryTest;
+import org.eclipse.emf.compare.tests.fullcomparison.IdentifierComparisonTest;
+import org.eclipse.emf.compare.tests.fullcomparison.ProximityComparisonTest;
+import org.eclipse.emf.compare.tests.match.MatchEngineFactoryRegistryTest;
+import org.eclipse.emf.compare.tests.merge.ExtensionMergeTest;
+import org.eclipse.emf.compare.tests.merge.IndividualMergeOutOfScopeValuesTest;
+import org.eclipse.emf.compare.tests.merge.IndividualMergeTest;
+import org.eclipse.emf.compare.tests.merge.MultipleMergeTest;
+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;
+import org.eclipse.emf.compare.tests.req.ReqComputingTest;
+import org.eclipse.emf.compare.tests.scope.DefaultComparisonScopeTest;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * This test suite allows us to launch all tests for EMF Compare at once.
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+@RunWith(Suite.class)
+@SuiteClasses({CompareTestSuite.class, DefaultComparisonScopeTest.class, IdentifierComparisonTest.class,
+ ExtLibraryTest.class, ConflictDetectionTest.class, ReqComputingTest.class, EquiComputingTest.class,
+ DiffUtilTest.class, LCSPerformanceTest.class, MultipleMergeTest.class, PostProcessorTest.class,
+ IndividualMergeTest.class, ExtensionMergeTest.class, IndividualMergeOutOfScopeValuesTest.class,
+ ProximityComparisonTest.class, DynamicInstanceComparisonTest.class, URIDistanceTest.class,
+ FragmentationTest.class, AllEditTests.class, CommandStackTestSuite.class,
+ MatchEngineFactoryRegistryTest.class })
+public class AllTests {
+ /**
+ * Standalone launcher for all of compare's tests.
+ *
+ * @generated
+ */
+ public static void main(String[] args) {
+ TestRunner.run(suite());
+ }
+
+ /**
+ * This will return a suite populated with all tests available through this class.
+ *
+ * @generated
+ */
+ public static Test suite() {
+ return new JUnit4TestAdapter(CompareTestSuite.class);
+ }
+
+ @BeforeClass
+ public static void fillEMFRegistries() {
+ EPackage.Registry.INSTANCE.put(ComparePackage.eNS_URI, ComparePackage.eINSTANCE);
+ EPackage.Registry.INSTANCE.put(NodesPackage.eNS_URI, NodesPackage.eINSTANCE);
+
+ Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("nodes", //$NON-NLS-1$
+ new NodesResourceFactoryImpl());
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF
index d9f07247e..4ac6f724b 100644
--- a/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF
@@ -18,6 +18,7 @@ Export-Package: org.eclipse.emf.compare,
org.eclipse.emf.compare.match,
org.eclipse.emf.compare.match.eobject,
org.eclipse.emf.compare.match.eobject.internal;x-internal:=true,
+ org.eclipse.emf.compare.match.impl,
org.eclipse.emf.compare.match.resource,
org.eclipse.emf.compare.merge,
org.eclipse.emf.compare.postprocessor,
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 8c3a910ad..010e68848 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,344 +1,347 @@
-/*******************************************************************************
- * Copyright (c) 2012, 2013 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;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.List;
-
-import org.eclipse.emf.common.notify.Notifier;
-import org.eclipse.emf.common.util.BasicMonitor;
-import org.eclipse.emf.common.util.Monitor;
-import org.eclipse.emf.compare.conflict.DefaultConflictDetector;
-import org.eclipse.emf.compare.conflict.IConflictDetector;
-import org.eclipse.emf.compare.diff.DefaultDiffEngine;
-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.match.DefaultMatchEngine;
-import org.eclipse.emf.compare.match.IMatchEngine;
-import org.eclipse.emf.compare.postprocessor.IPostProcessor;
-import org.eclipse.emf.compare.postprocessor.PostProcessorDescriptorRegistryImpl;
-import org.eclipse.emf.compare.req.DefaultReqEngine;
-import org.eclipse.emf.compare.req.IReqEngine;
-import org.eclipse.emf.compare.scope.DefaultComparisonScope;
-import org.eclipse.emf.compare.scope.IComparisonScope;
-import org.eclipse.emf.compare.utils.UseIdentifiers;
-
-/**
- * This class serves as the main entry point of a comparison. When all that is wanted is a basic comparison of
- * two or three notifiers, a comparison using all of the default configuration can be launched through
- * <code>EMFCompare.builder().build().compare(EMFCompare.createDefaultScope(left, right, origin))</code>.
- * <p>
- * When in need of a more customized comparison, the API can be used through chained calls. For example, if
- * you need to compare two notifiers ({@code left} and {@code right}) while ignoring their identifiers, with a
- * given progress monitor (call it {@code progress}), you can do so through : <code>
- * EMFCompare.builder().setMatchEngine(DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(EMFCompare.createDefaultScope(left, right), new BasicMonitor())
- * </code>.
- * </p>
- *
- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
- */
-public class EMFCompare {
-
- /** The IMatchEngine to use to compute comparison. */
- private final IMatchEngine matchEngine;
-
- /** The IDiffEngine to use to compute comparison. */
- private final IDiffEngine diffEngine;
-
- /** The IReqEngine to use to compute comparison. */
- private final IReqEngine reqEngine;
-
- /** The IEquiEngine to use to compute comparison. */
- private final IEquiEngine equiEngine;
-
- /** The IConflictDetector to use to compute comparison. */
- private final IConflictDetector conflictDetector;
-
- /** The PostProcessorRegistry to use to find an IPostProcessor. */
- private final IPostProcessor.Descriptor.Registry<?> postProcessorDescriptorRegistry;
-
- /**
- * Creates a new EMFCompare object able to compare Notifier with the help of given engines.
- *
- * @param matchEngine
- * IMatchEngine to use to compute comparison
- * @param diffEngine
- * IDiffEngine to use to compute comparison
- * @param reqEngine
- * IReqEngine to use to compute comparison
- * @param equiEngine
- * IEquiEngine to use to compute comparison
- * @param conflictDetector
- * IConflictDetector to use to compute comparison
- * @param postProcessorFactoryRegistry
- * PostProcessorRegistry to use to find an IPostProcessor
- */
- protected EMFCompare(IMatchEngine matchEngine, IDiffEngine diffEngine, IReqEngine reqEngine,
- IEquiEngine equiEngine, IConflictDetector conflictDetector,
- IPostProcessor.Descriptor.Registry<?> postProcessorFactoryRegistry) {
- this.matchEngine = checkNotNull(matchEngine);
- this.diffEngine = checkNotNull(diffEngine);
- this.reqEngine = checkNotNull(reqEngine);
- this.equiEngine = checkNotNull(equiEngine);
- this.conflictDetector = conflictDetector;
- this.postProcessorDescriptorRegistry = checkNotNull(postProcessorFactoryRegistry);
- }
-
- /**
- * Creates a default comparison scope given its left and right notifiers.
- * <p>
- * The default comparison scope covers all proper content of the given notifiers, i.e any element
- * contained directly under that notifier.
- * </p>
- *
- * @param left
- * The left notifier of this scope.
- * @param right
- * The right notifier of this scope.
- * @return The newly created scope, as used as default by EMF Compare.
- * @see DefaultComparisonScope
- */
- public static IComparisonScope createDefaultScope(Notifier left, Notifier right) {
- return new DefaultComparisonScope(left, right, null);
- }
-
- /**
- * Creates the default comparison scope given its left and right notifiers, along with the common ancestor
- * of both.
- * <p>
- * The default comparison scope covers all proper content of the given notifiers, i.e any element
- * contained directly under that notifier.
- * </p>
- *
- * @param left
- * The left notifier of this scope.
- * @param right
- * The right notifier of this scope.
- * @param origin
- * The common ancestor of {@code left} and {@code right}.
- * @return The newly created scope, as used as default by EMF Compare.
- * @see DefaultComparisonScope
- */
- public static IComparisonScope createDefaultScope(Notifier left, Notifier right, Notifier origin) {
- return new DefaultComparisonScope(left, right, origin);
- }
-
- /**
- * Computes and returns a new Comparison object representation the differences between Notifier in the
- * give {@code scope}.
- *
- * @param scope
- * the scope to compare.
- * @return the result of the comparison.
- */
- public Comparison compare(IComparisonScope scope) {
- return compare(scope, new BasicMonitor());
- }
-
- /**
- * Launches the comparison with the given scope and reporting progress to the given {@code monitor}.
- *
- * @param scope
- * the scope to compare.
- * @param monitor
- * the monitor to report progress to.
- * @return the result of the comparison.
- */
- public Comparison compare(IComparisonScope scope, final Monitor monitor) {
- checkNotNull(scope);
- checkNotNull(monitor);
-
- final Comparison comparison = matchEngine.match(scope, monitor);
-
- List<IPostProcessor> postProcessors = postProcessorDescriptorRegistry.getPostProcessors(scope);
-
- for (IPostProcessor iPostProcessor : postProcessors) {
- iPostProcessor.postMatch(comparison, monitor);
- }
-
- diffEngine.diff(comparison, monitor);
-
- for (IPostProcessor iPostProcessor : postProcessors) {
- iPostProcessor.postDiff(comparison, monitor);
- }
-
- reqEngine.computeRequirements(comparison, monitor);
-
- for (IPostProcessor iPostProcessor : postProcessors) {
- iPostProcessor.postRequirements(comparison, monitor);
- }
-
- equiEngine.computeEquivalences(comparison, monitor);
-
- for (IPostProcessor iPostProcessor : postProcessors) {
- iPostProcessor.postEquivalences(comparison, monitor);
- }
-
- if (comparison.isThreeWay() && conflictDetector != null) {
- conflictDetector.detect(comparison, monitor);
-
- for (IPostProcessor iPostProcessor : postProcessors) {
- iPostProcessor.postConflicts(comparison, monitor);
- }
- }
-
- for (IPostProcessor iPostProcessor : postProcessors) {
- iPostProcessor.postComparison(comparison, monitor);
- }
-
- return comparison;
- }
-
- /**
- * Creates a new builder to configure the creation of a new EMFCompare object.
- *
- * @return a new builder.
- */
- public static Builder builder() {
- return new Builder();
- }
-
- /**
- * A Builder pattern to instantiate EMFCompare objects.
- *
- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
- */
- public static class Builder {
-
- /** The IMatchEngine to use to compute comparison. */
- protected IMatchEngine matchEngine;
-
- /** The IReqEngine to use to compute comparison. */
- protected IReqEngine reqEngine;
-
- /** The IDiffEngine to use to compute comparison. */
- protected IDiffEngine diffEngine;
-
- /** The IEquiEngine to use to compute comparison. */
- protected IEquiEngine equiEngine;
-
- /** The IConflictDetector to use to compute conflicts. */
- protected IConflictDetector conflictDetector;
-
- /** The PostProcessorRegistry to use to find an IPostProcessor. */
- protected IPostProcessor.Descriptor.Registry<?> postProcessorRegistry;
-
- /**
- * Creates a new builder object.
- */
- protected Builder() {
- }
-
- /**
- * Sets the IMatchEngine to be used to compute Match.
- *
- * @param me
- * the IMatchEngine to be used to compute comparison.
- * @return this same builder to allow chained call.
- */
- public Builder setMatchEngine(IMatchEngine me) {
- this.matchEngine = checkNotNull(me);
- return this;
- }
-
- /**
- * Sets the IDiffEngine to be used to compute Diff.
- *
- * @param de
- * the IDiffEngine to be used to compute Diff.
- * @return this same builder to allow chained call.
- */
- public Builder setDiffEngine(IDiffEngine de) {
- this.diffEngine = checkNotNull(de);
- return this;
- }
-
- /**
- * Sets the IReqEngine to be used to compute dependencies between Diff.
- *
- * @param re
- * the IReqEngine to be used to compute dependencies between Diff.
- * @return this same builder to allow chained call.
- */
- public Builder setRequirementEngine(IReqEngine re) {
- this.reqEngine = checkNotNull(re);
- return this;
- }
-
- /**
- * Sets the IEquiEngine to be used to compute equivalences between Diff.
- *
- * @param ee
- * the IEquiEngine to be used to compute equivalences between Diff
- * @return this same builder to allow chained call.
- */
- public Builder setEquivalenceEngine(IEquiEngine ee) {
- this.equiEngine = checkNotNull(ee);
- return this;
- }
-
- /**
- * Sets the IEquiEngine to be used to compute conflicts between Diff.
- *
- * @param cd
- * the IEquiEngine to be used to compute conflicts between Diff.
- * @return this same builder to allow chained call.
- */
- public Builder setConflictDetector(IConflictDetector cd) {
- this.conflictDetector = checkNotNull(cd);
- return this;
- }
-
- /**
- * Sets the PostProcessor to be used to find the post processor of each comparison steps.
- *
- * @param r
- * the PostProcessor to be used to find the post processor of each comparison steps.
- * @return this same builder to allow chained call.
- */
- public Builder setPostProcessorRegistry(IPostProcessor.Descriptor.Registry<?> r) {
- this.postProcessorRegistry = checkNotNull(r);
- return this;
- }
-
- /**
- * Instantiates and return an EMFCompare object configured with the previously given engines.
- *
- * @return an EMFCompare object configured with the previously given engines
- */
- public EMFCompare build() {
- if (matchEngine == null) {
- matchEngine = DefaultMatchEngine.create(UseIdentifiers.WHEN_AVAILABLE);
- }
- if (diffEngine == null) {
- diffEngine = new DefaultDiffEngine(new DiffBuilder());
- }
- if (reqEngine == null) {
- reqEngine = new DefaultReqEngine();
- }
- if (equiEngine == null) {
- equiEngine = new DefaultEquiEngine();
- }
- if (postProcessorRegistry == null) {
- postProcessorRegistry = new PostProcessorDescriptorRegistryImpl<Object>();
- }
- if (conflictDetector == null) {
- conflictDetector = new DefaultConflictDetector();
- }
- return new EMFCompare(this.matchEngine, this.diffEngine, this.reqEngine, this.equiEngine,
- this.conflictDetector, this.postProcessorRegistry);
- }
- }
-
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.common.util.BasicMonitor;
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.conflict.DefaultConflictDetector;
+import org.eclipse.emf.compare.conflict.IConflictDetector;
+import org.eclipse.emf.compare.diff.DefaultDiffEngine;
+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.match.IMatchEngine;
+import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl;
+import org.eclipse.emf.compare.postprocessor.IPostProcessor;
+import org.eclipse.emf.compare.postprocessor.PostProcessorDescriptorRegistryImpl;
+import org.eclipse.emf.compare.req.DefaultReqEngine;
+import org.eclipse.emf.compare.req.IReqEngine;
+import org.eclipse.emf.compare.scope.DefaultComparisonScope;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+
+/**
+ * This class serves as the main entry point of a comparison. When all that is wanted is a basic comparison of
+ * two or three notifiers, a comparison using all of the default configuration can be launched through
+ * <code>EMFCompare.builder().build().compare(EMFCompare.createDefaultScope(left, right, origin))</code>.
+ * <p>
+ * When in need of a more customized comparison, the API can be used through chained calls. For example, if
+ * you need to compare two notifiers ({@code left} and {@code right}) while ignoring their identifiers, with a
+ * given progress monitor (call it {@code progress}), you can do so through : <code>
+ * EMFCompare.builder().setMatchEngine(DefaultMatchEngine.create(UseIdentifiers.NEVER)).build().compare(EMFCompare.createDefaultScope(left, right), new BasicMonitor())
+ * </code>.
+ * </p>
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+public class EMFCompare {
+
+ /** The registry we'll use to create a match engine for this comparison. */
+ private final IMatchEngine.Factory.Registry matchEngineFactoryRegistry;
+
+ /** The IDiffEngine to use to compute comparison. */
+ private final IDiffEngine diffEngine;
+
+ /** The IReqEngine to use to compute comparison. */
+ private final IReqEngine reqEngine;
+
+ /** The IEquiEngine to use to compute comparison. */
+ private final IEquiEngine equiEngine;
+
+ /** The IConflictDetector to use to compute comparison. */
+ private final IConflictDetector conflictDetector;
+
+ /** The PostProcessorRegistry to use to find an IPostProcessor. */
+ private final IPostProcessor.Descriptor.Registry<?> postProcessorDescriptorRegistry;
+
+ /**
+ * Creates a new EMFCompare object able to compare Notifier with the help of given engines.
+ *
+ * @param matchEngineFactoryRegistry
+ * {@link IMatchEngine.Factory.Registry} to use to find a match engine factory to compute
+ * comparison
+ * @param diffEngine
+ * IDiffEngine to use to compute comparison
+ * @param reqEngine
+ * IReqEngine to use to compute comparison
+ * @param equiEngine
+ * IEquiEngine to use to compute comparison
+ * @param conflictDetector
+ * IConflictDetector to use to compute comparison
+ * @param postProcessorFactoryRegistry
+ * PostProcessorRegistry to use to find an IPostProcessor
+ */
+ protected EMFCompare(IMatchEngine.Factory.Registry matchEngineFactoryRegistry, IDiffEngine diffEngine,
+ IReqEngine reqEngine, IEquiEngine equiEngine, IConflictDetector conflictDetector,
+ IPostProcessor.Descriptor.Registry<?> postProcessorFactoryRegistry) {
+ this.matchEngineFactoryRegistry = checkNotNull(matchEngineFactoryRegistry);
+ this.diffEngine = checkNotNull(diffEngine);
+ this.reqEngine = checkNotNull(reqEngine);
+ this.equiEngine = checkNotNull(equiEngine);
+ this.conflictDetector = conflictDetector;
+ this.postProcessorDescriptorRegistry = checkNotNull(postProcessorFactoryRegistry);
+ }
+
+ /**
+ * Creates a default comparison scope given its left and right notifiers.
+ * <p>
+ * The default comparison scope covers all proper content of the given notifiers, i.e any element
+ * contained directly under that notifier.
+ * </p>
+ *
+ * @param left
+ * The left notifier of this scope.
+ * @param right
+ * The right notifier of this scope.
+ * @return The newly created scope, as used as default by EMF Compare.
+ * @see DefaultComparisonScope
+ */
+ public static IComparisonScope createDefaultScope(Notifier left, Notifier right) {
+ return new DefaultComparisonScope(left, right, null);
+ }
+
+ /**
+ * Creates the default comparison scope given its left and right notifiers, along with the common ancestor
+ * of both.
+ * <p>
+ * The default comparison scope covers all proper content of the given notifiers, i.e any element
+ * contained directly under that notifier.
+ * </p>
+ *
+ * @param left
+ * The left notifier of this scope.
+ * @param right
+ * The right notifier of this scope.
+ * @param origin
+ * The common ancestor of {@code left} and {@code right}.
+ * @return The newly created scope, as used as default by EMF Compare.
+ * @see DefaultComparisonScope
+ */
+ public static IComparisonScope createDefaultScope(Notifier left, Notifier right, Notifier origin) {
+ return new DefaultComparisonScope(left, right, origin);
+ }
+
+ /**
+ * Computes and returns a new Comparison object representation the differences between Notifier in the
+ * give {@code scope}.
+ *
+ * @param scope
+ * the scope to compare.
+ * @return the result of the comparison.
+ */
+ public Comparison compare(IComparisonScope scope) {
+ return compare(scope, new BasicMonitor());
+ }
+
+ /**
+ * Launches the comparison with the given scope and reporting progress to the given {@code monitor}.
+ *
+ * @param scope
+ * the scope to compare.
+ * @param monitor
+ * the monitor to report progress to.
+ * @return the result of the comparison.
+ */
+ public Comparison compare(IComparisonScope scope, final Monitor monitor) {
+ checkNotNull(scope);
+ checkNotNull(monitor);
+
+ final Comparison comparison = matchEngineFactoryRegistry.getHighestRankingMatchEngineFactory(scope)
+ .getMatchEngine().match(scope, monitor);
+
+ List<IPostProcessor> postProcessors = postProcessorDescriptorRegistry.getPostProcessors(scope);
+
+ for (IPostProcessor iPostProcessor : postProcessors) {
+ iPostProcessor.postMatch(comparison, monitor);
+ }
+
+ diffEngine.diff(comparison, monitor);
+
+ for (IPostProcessor iPostProcessor : postProcessors) {
+ iPostProcessor.postDiff(comparison, monitor);
+ }
+
+ reqEngine.computeRequirements(comparison, monitor);
+
+ for (IPostProcessor iPostProcessor : postProcessors) {
+ iPostProcessor.postRequirements(comparison, monitor);
+ }
+
+ equiEngine.computeEquivalences(comparison, monitor);
+
+ for (IPostProcessor iPostProcessor : postProcessors) {
+ iPostProcessor.postEquivalences(comparison, monitor);
+ }
+
+ if (comparison.isThreeWay() && conflictDetector != null) {
+ conflictDetector.detect(comparison, monitor);
+
+ for (IPostProcessor iPostProcessor : postProcessors) {
+ iPostProcessor.postConflicts(comparison, monitor);
+ }
+ }
+
+ for (IPostProcessor iPostProcessor : postProcessors) {
+ iPostProcessor.postComparison(comparison, monitor);
+ }
+
+ return comparison;
+ }
+
+ /**
+ * Creates a new builder to configure the creation of a new EMFCompare object.
+ *
+ * @return a new builder.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A Builder pattern to instantiate EMFCompare objects.
+ *
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+ public static class Builder {
+
+ /** The registry we'll use to create a match engine for this comparison. */
+ protected IMatchEngine.Factory.Registry matchEngineFactoryRegistry;
+
+ /** The IReqEngine to use to compute comparison. */
+ protected IReqEngine reqEngine;
+
+ /** The IDiffEngine to use to compute comparison. */
+ protected IDiffEngine diffEngine;
+
+ /** The IEquiEngine to use to compute comparison. */
+ protected IEquiEngine equiEngine;
+
+ /** The IConflictDetector to use to compute conflicts. */
+ protected IConflictDetector conflictDetector;
+
+ /** The PostProcessorRegistry to use to find an IPostProcessor. */
+ protected IPostProcessor.Descriptor.Registry<?> registry;
+
+ /**
+ * Creates a new builder object.
+ */
+ protected Builder() {
+ }
+
+ /**
+ * Sets the IMatchEngine.Factory.Registry to be used to find a match engine factory to compute
+ * comparison.
+ *
+ * @param mefr
+ * the IMatchEngine.Factory.Registry to be used to find a match engine factory to compute
+ * comparison.
+ * @return this same builder to allow chained call.
+ */
+ public Builder setMatchEngineFactoryRegistry(IMatchEngine.Factory.Registry mefr) {
+ this.matchEngineFactoryRegistry = checkNotNull(mefr);
+ return this;
+ }
+
+ /**
+ * Sets the IDiffEngine to be used to compute Diff.
+ *
+ * @param de
+ * the IDiffEngine to be used to compute Diff.
+ * @return this same builder to allow chained call.
+ */
+ public Builder setDiffEngine(IDiffEngine de) {
+ this.diffEngine = checkNotNull(de);
+ return this;
+ }
+
+ /**
+ * Sets the IReqEngine to be used to compute dependencies between Diff.
+ *
+ * @param re
+ * the IReqEngine to be used to compute dependencies between Diff.
+ * @return this same builder to allow chained call.
+ */
+ public Builder setRequirementEngine(IReqEngine re) {
+ this.reqEngine = checkNotNull(re);
+ return this;
+ }
+
+ /**
+ * Sets the IEquiEngine to be used to compute equivalences between Diff.
+ *
+ * @param ee
+ * the IEquiEngine to be used to compute equivalences between Diff
+ * @return this same builder to allow chained call.
+ */
+ public Builder setEquivalenceEngine(IEquiEngine ee) {
+ this.equiEngine = checkNotNull(ee);
+ return this;
+ }
+
+ /**
+ * Sets the IEquiEngine to be used to compute conflicts between Diff.
+ *
+ * @param cd
+ * the IEquiEngine to be used to compute conflicts between Diff.
+ * @return this same builder to allow chained call.
+ */
+ public Builder setConflictDetector(IConflictDetector cd) {
+ this.conflictDetector = checkNotNull(cd);
+ return this;
+ }
+
+ /**
+ * Sets the PostProcessor to be used to find the post processor of each comparison steps.
+ *
+ * @param r
+ * the PostProcessor to be used to find the post processor of each comparison steps.
+ * @return this same builder to allow chained call.
+ */
+ public Builder setPostProcessorRegistry(IPostProcessor.Descriptor.Registry<?> r) {
+ this.registry = checkNotNull(r);
+ return this;
+ }
+
+ /**
+ * Instantiates and return an EMFCompare object configured with the previously given engines.
+ *
+ * @return an EMFCompare object configured with the previously given engines
+ */
+ public EMFCompare build() {
+ if (matchEngineFactoryRegistry == null) {
+ matchEngineFactoryRegistry = MatchEngineFactoryRegistryImpl.createStandaloneInstance();
+ }
+ if (diffEngine == null) {
+ diffEngine = new DefaultDiffEngine(new DiffBuilder());
+ }
+ if (reqEngine == null) {
+ reqEngine = new DefaultReqEngine();
+ }
+ if (equiEngine == null) {
+ equiEngine = new DefaultEquiEngine();
+ }
+ if (registry == null) {
+ registry = new PostProcessorDescriptorRegistryImpl();
+ }
+ if (conflictDetector == null) {
+ conflictDetector = new DefaultConflictDetector();
+ }
+ return new EMFCompare(this.matchEngineFactoryRegistry, this.diffEngine, this.reqEngine,
+ this.equiEngine, this.conflictDetector, this.registry);
+ }
+ }
+
+}
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 5e23d5e2a..68eaca20b 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,397 +1,399 @@
-/*******************************************************************************
- * Copyright (c) 2012 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
- * Eike Stepper - (390846) Make the DefaultMatchEngine more extensible
- *******************************************************************************/
-package org.eclipse.emf.compare.match;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-
-import java.util.Iterator;
-import java.util.List;
-
-import org.eclipse.emf.common.notify.Notifier;
-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.MatchResource;
-import org.eclipse.emf.compare.match.eobject.EditionDistance;
-import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
-import org.eclipse.emf.compare.match.eobject.IdentifierEObjectMatcher;
-import org.eclipse.emf.compare.match.eobject.ProximityEObjectMatcher;
-import org.eclipse.emf.compare.match.resource.IResourceMatcher;
-import org.eclipse.emf.compare.match.resource.StrategyResourceMatcher;
-import org.eclipse.emf.compare.scope.IComparisonScope;
-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;
-
-/**
- * The Match engine orchestrates the matching process : it takes an {@link IComparisonScope scope} as input,
- * iterates over its {@link IComparisonScope#getLeft() left}, {@link IComparisonScope#getRight() right} and
- * {@link IComparisonScope#getOrigin() origin} roots and delegates to {@link IResourceMatcher}s and
- * {@link IEObjectMatcher}s in order to create the result {@link Comparison} model for this scope.
- *
- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
- */
-public class DefaultMatchEngine implements IMatchEngine {
-
- /**
- * Default max size of the EObject's URI loading cache.
- */
- public static final int DEFAULT_EOBJECT_URI_CACHE_MAX_SIZE = 1024;
-
- /** The delegate {@link IEObjectMatcher matcher} that will actually pair EObjects together. */
- private final IEObjectMatcher eObjectMatcher;
-
- /** The factory that will be use to instantiate Comparison as return by match() methods. */
- private final IComparisonFactory comparisonFactory;
-
- /**
- * This default engine delegates the pairing of EObjects to an {@link IEObjectMatcher}.
- *
- * @param matcher
- * The matcher that will be in charge of pairing EObjects together for this comparison process.
- * @param comparisonFactory
- * factory that will be use to instantiate Comparison as return by match() methods.
- */
- public DefaultMatchEngine(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
- this.eObjectMatcher = checkNotNull(matcher);
- this.comparisonFactory = checkNotNull(comparisonFactory);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.match.IMatchEngine#match(org.eclipse.emf.compare.scope.IComparisonScope,
- * org.eclipse.emf.common.util.Monitor)
- */
- public Comparison match(IComparisonScope scope, Monitor monitor) {
- Comparison comparison = comparisonFactory.createComparison();
-
- final Notifier left = scope.getLeft();
- final Notifier right = scope.getRight();
- final Notifier origin = scope.getOrigin();
-
- comparison.setThreeWay(origin != null);
-
- match(comparison, scope, left, right, origin, monitor);
-
- return comparison;
- }
-
- /**
- * This methods will delegate to the proper "match(T, T, T)" implementation according to the types of
- * {@code left}, {@code right} and {@code origin}.
- *
- * @param comparison
- * The comparison to which will be added detected matches.
- * @param scope
- * The comparison scope that should be used by this engine to determine the objects to match.
- * @param left
- * The left {@link Notifier}.
- * @param right
- * The right {@link Notifier}.
- * @param origin
- * The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
- * @param monitor
- * The monitor to report progress or to check for cancellation
- */
- protected void match(Comparison comparison, IComparisonScope scope, final Notifier left,
- final Notifier right, final Notifier origin, Monitor monitor) {
- // FIXME side-effect coding
- if (left instanceof ResourceSet || right instanceof ResourceSet) {
- match(comparison, scope, (ResourceSet)left, (ResourceSet)right, (ResourceSet)origin, monitor);
- } else if (left instanceof Resource || right instanceof Resource) {
- match(comparison, scope, (Resource)left, (Resource)right, (Resource)origin, monitor);
- } else if (left instanceof EObject || right instanceof EObject) {
- match(comparison, scope, (EObject)left, (EObject)right, (EObject)origin, monitor);
- } else {
- // TODO Cannot happen ... for now. Should we log an exception?
- }
- }
-
- /**
- * This will be used to match the given {@link ResourceSet}s. This default implementation will query the
- * comparison scope for these resource sets children, then delegate to an {@link IResourceMatcher} to
- * determine the resource mappings.
- *
- * @param comparison
- * The comparison to which will be added detected matches.
- * @param scope
- * The comparison scope that should be used by this engine to determine the objects to match.
- * @param left
- * The left {@link ResourceSet}.
- * @param right
- * The right {@link ResourceSet}.
- * @param origin
- * The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
- * @param monitor
- * The monitor to report progress or to check for cancellation
- */
- protected void match(Comparison comparison, IComparisonScope scope, ResourceSet left, ResourceSet right,
- ResourceSet origin, Monitor monitor) {
- final Iterator<? extends Resource> leftChildren = scope.getCoveredResources(left);
- final Iterator<? extends Resource> rightChildren = scope.getCoveredResources(right);
- final Iterator<? extends Resource> originChildren;
- if (origin != null) {
- originChildren = scope.getCoveredResources(origin);
- } else {
- originChildren = Iterators.emptyIterator();
- }
-
- final IResourceMatcher resourceMatcher = createResourceMatcher();
- final Iterable<MatchResource> mappings = resourceMatcher.createMappings(leftChildren, rightChildren,
- originChildren);
-
- final List<Iterator<? extends EObject>> leftIterators = Lists.newLinkedList();
- final List<Iterator<? extends EObject>> rightIterators = Lists.newLinkedList();
- final List<Iterator<? extends EObject>> originIterators = Lists.newLinkedList();
-
- for (MatchResource mapping : mappings) {
- comparison.getMatchedResources().add(mapping);
-
- final Resource leftRes = mapping.getLeft();
- final Resource rightRes = mapping.getRight();
- final Resource originRes = mapping.getOrigin();
-
- if (leftRes != null) {
- leftIterators.add(scope.getCoveredEObjects(leftRes));
- }
-
- if (rightRes != null) {
- rightIterators.add(scope.getCoveredEObjects(rightRes));
- }
-
- if (originRes != null) {
- originIterators.add(scope.getCoveredEObjects(originRes));
- }
- }
-
- final Iterator<? extends EObject> leftEObjects = Iterators.concat(leftIterators.iterator());
- final Iterator<? extends EObject> rightEObjects = Iterators.concat(rightIterators.iterator());
- final Iterator<? extends EObject> originEObjects = Iterators.concat(originIterators.iterator());
-
- getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects, monitor);
- }
-
- /**
- * This will only query the scope for the given Resources' children, then delegate to an
- * {@link IEObjectMatcher} to determine the EObject matches.
- * <p>
- * We expect at least two of the given resources not to be <code>null</code>.
- * </p>
- *
- * @param comparison
- * The comparison to which will be added detected matches.
- * @param scope
- * The comparison scope that should be used by this engine to determine the objects to match.
- * @param left
- * The left {@link Resource}. Can be <code>null</code>.
- * @param right
- * The right {@link Resource}. Can be <code>null</code>.
- * @param origin
- * The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
- * @param monitor
- * The monitor to report progress or to check for cancellation
- */
- protected void match(Comparison comparison, IComparisonScope scope, Resource left, Resource right,
- Resource origin, Monitor monitor) {
- // Our "roots" are Resources. Consider them matched
- final MatchResource match = CompareFactory.eINSTANCE.createMatchResource();
-
- match.setLeft(left);
- match.setRight(right);
- match.setOrigin(origin);
-
- if (left != null) {
- URI uri = left.getURI();
- if (uri != null) {
- match.setLeftURI(uri.toString());
- }
- }
-
- if (right != null) {
- URI uri = right.getURI();
- if (uri != null) {
- match.setRightURI(uri.toString());
- }
- }
-
- if (origin != null) {
- URI uri = origin.getURI();
- if (uri != null) {
- match.setOriginURI(uri.toString());
- }
- }
-
- comparison.getMatchedResources().add(match);
-
- // We need at least two resources to match them
- if (atLeastTwo(left == null, right == null, origin == null)) {
- /*
- * TODO But if we have only one resource, which is then unmatched, should we not still do
- * something with it?
- */
- return;
- }
-
- final Iterator<? extends EObject> leftEObjects;
- if (left != null) {
- leftEObjects = scope.getCoveredEObjects(left);
- } else {
- leftEObjects = Iterators.emptyIterator();
- }
- final Iterator<? extends EObject> rightEObjects;
- if (right != null) {
- rightEObjects = scope.getCoveredEObjects(right);
- } else {
- rightEObjects = Iterators.emptyIterator();
- }
- final Iterator<? extends EObject> originEObjects;
- if (origin != null) {
- originEObjects = scope.getCoveredEObjects(origin);
- } else {
- originEObjects = Iterators.emptyIterator();
- }
-
- getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects, monitor);
-
- }
-
- /**
- * This will query the scope for the given {@link EObject}s' children, then delegate to an
- * {@link IEObjectMatcher} to compute the Matches.
- * <p>
- * We expect at least the <code>left</code> and <code>right</code> EObjects not to be <code>null</code>.
- * </p>
- *
- * @param comparison
- * The comparison to which will be added detected matches.
- * @param scope
- * The comparison scope that should be used by this engine to determine the objects to match.
- * @param left
- * The left {@link EObject}.
- * @param right
- * The right {@link EObject}.
- * @param origin
- * The common ancestor of <code>left</code> and <code>right</code>.
- * @param monitor
- * The monitor to report progress or to check for cancellation.
- */
- protected void match(Comparison comparison, IComparisonScope scope, EObject left, EObject right,
- EObject origin, Monitor monitor) {
- if (left == null || right == null) {
- throw new IllegalArgumentException();
- }
-
- final Iterator<? extends EObject> leftEObjects = Iterators.concat(Iterators.singletonIterator(left),
- scope.getChildren(left));
- final Iterator<? extends EObject> rightEObjects = Iterators.concat(
- Iterators.singletonIterator(right), scope.getChildren(right));
- final Iterator<? extends EObject> originEObjects;
- if (origin != null) {
- originEObjects = Iterators.concat(Iterators.singletonIterator(origin), scope.getChildren(origin));
- } else {
- originEObjects = Iterators.emptyIterator();
- }
-
- getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects, monitor);
- }
-
- /**
- * This will be used to create the resource matcher that will be used by this match engine.
- *
- * @return An {@link IResourceMatcher} that can be used to retrieve the {@link MatchResource}s for this
- * comparison.
- */
- protected IResourceMatcher createResourceMatcher() {
- return new StrategyResourceMatcher();
- }
-
- /**
- * Returns the EObject matcher associated with this match engine.
- *
- * @return The EObject matcher associated with this match engine.
- */
- protected final IEObjectMatcher getEObjectMatcher() {
- return eObjectMatcher;
- }
-
- /**
- * This will check that at least two of the three given booleans are <code>true</code>.
- *
- * @param condition1
- * First of the three booleans.
- * @param condition2
- * Second of the three booleans.
- * @param condition3
- * Third of the three booleans.
- * @return <code>true</code> if at least two of the three given booleans are <code>true</code>,
- * <code>false</code> otherwise.
- */
- private static boolean atLeastTwo(boolean condition1, boolean condition2, boolean condition3) {
- // CHECKSTYLE:OFF This expression is alone in its method, and documented.
- return condition1 && (condition2 || condition3) || (condition2 && condition3);
- // CHECKSTYLE:ON
- }
-
- /**
- * Helper creator method that instantiate a {@link DefaultMatchEngine} that will use identifiers as
- * specified by the given {@code useIDs} enumeration.
- *
- * @param useIDs
- * the kinds of matcher to use.
- * @return a new {@link DefaultMatchEngine} instance.
- */
- public static IMatchEngine create(UseIdentifiers useIDs) {
- final IComparisonFactory comparisonFactory = new DefaultComparisonFactory(
- new DefaultEqualityHelperFactory());
- final IEObjectMatcher matcher = createDefaultEObjectMatcher(useIDs);
-
- final IMatchEngine matchEngine = new DefaultMatchEngine(matcher, comparisonFactory);
- return matchEngine;
- }
-
- /**
- * Creates and configures an {@link IEObjectMatcher} with the strategy given by {@code useIDs}. The
- * {@code cache} will be used to cache some expensive computation (should better a LoadingCache).
- *
- * @param useIDs
- * which strategy the return IEObjectMatcher must follow.
- * @return a new IEObjectMatcher.
- */
- public static IEObjectMatcher createDefaultEObjectMatcher(UseIdentifiers useIDs) {
- final IEObjectMatcher matcher;
- final EditionDistance editionDistance = new EditionDistance();
- switch (useIDs) {
- case NEVER:
- matcher = new ProximityEObjectMatcher(editionDistance);
- break;
- case ONLY:
- matcher = new IdentifierEObjectMatcher();
- break;
- case WHEN_AVAILABLE:
- // fall through to default
- default:
- // Use an ID matcher, delegating to proximity when no ID is available
- final IEObjectMatcher contentMatcher = new ProximityEObjectMatcher(editionDistance);
- matcher = new IdentifierEObjectMatcher(contentMatcher);
- break;
-
- }
-
- return matcher;
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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
+ * Eike Stepper - (390846) Make the DefaultMatchEngine more extensible
+ *******************************************************************************/
+package org.eclipse.emf.compare.match;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.emf.common.notify.Notifier;
+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.MatchResource;
+import org.eclipse.emf.compare.match.eobject.EditionDistance;
+import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
+import org.eclipse.emf.compare.match.eobject.IdentifierEObjectMatcher;
+import org.eclipse.emf.compare.match.eobject.ProximityEObjectMatcher;
+import org.eclipse.emf.compare.match.resource.IResourceMatcher;
+import org.eclipse.emf.compare.match.resource.StrategyResourceMatcher;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+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;
+
+/**
+ * The Match engine orchestrates the matching process : it takes an {@link IComparisonScope scope} as input,
+ * iterates over its {@link IComparisonScope#getLeft() left}, {@link IComparisonScope#getRight() right} and
+ * {@link IComparisonScope#getOrigin() origin} roots and delegates to {@link IResourceMatcher}s and
+ * {@link IEObjectMatcher}s in order to create the result {@link Comparison} model for this scope.
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+public class DefaultMatchEngine implements IMatchEngine {
+
+ /**
+ * Default max size of the EObject's URI loading cache.
+ */
+ public static final int DEFAULT_EOBJECT_URI_CACHE_MAX_SIZE = 1024;
+
+ /** The delegate {@link IEObjectMatcher matcher} that will actually pair EObjects together. */
+ private final IEObjectMatcher eObjectMatcher;
+
+ /** The factory that will be use to instantiate Comparison as return by match() methods. */
+ private final IComparisonFactory comparisonFactory;
+
+ /**
+ * This default engine delegates the pairing of EObjects to an {@link IEObjectMatcher}.
+ *
+ * @param matcher
+ * The matcher that will be in charge of pairing EObjects together for this comparison process.
+ * @param comparisonFactory
+ * factory that will be use to instantiate Comparison as return by match() methods.
+ * @since 3.0
+ */
+ public DefaultMatchEngine(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
+ this.eObjectMatcher = checkNotNull(matcher);
+ this.comparisonFactory = checkNotNull(comparisonFactory);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine#match(org.eclipse.emf.compare.scope.IComparisonScope,
+ * org.eclipse.emf.common.util.Monitor)
+ */
+ public Comparison match(IComparisonScope scope, Monitor monitor) {
+ Comparison comparison = comparisonFactory.createComparison();
+
+ final Notifier left = scope.getLeft();
+ final Notifier right = scope.getRight();
+ final Notifier origin = scope.getOrigin();
+
+ comparison.setThreeWay(origin != null);
+
+ match(comparison, scope, left, right, origin, monitor);
+
+ return comparison;
+ }
+
+ /**
+ * This methods will delegate to the proper "match(T, T, T)" implementation according to the types of
+ * {@code left}, {@code right} and {@code origin}.
+ *
+ * @param comparison
+ * The comparison to which will be added detected matches.
+ * @param scope
+ * The comparison scope that should be used by this engine to determine the objects to match.
+ * @param left
+ * The left {@link Notifier}.
+ * @param right
+ * The right {@link Notifier}.
+ * @param origin
+ * The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
+ * @param monitor
+ * The monitor to report progress or to check for cancellation
+ */
+ protected void match(Comparison comparison, IComparisonScope scope, final Notifier left,
+ final Notifier right, final Notifier origin, Monitor monitor) {
+ // FIXME side-effect coding
+ if (left instanceof ResourceSet || right instanceof ResourceSet) {
+ match(comparison, scope, (ResourceSet)left, (ResourceSet)right, (ResourceSet)origin, monitor);
+ } else if (left instanceof Resource || right instanceof Resource) {
+ match(comparison, scope, (Resource)left, (Resource)right, (Resource)origin, monitor);
+ } else if (left instanceof EObject || right instanceof EObject) {
+ match(comparison, scope, (EObject)left, (EObject)right, (EObject)origin, monitor);
+ } else {
+ // TODO Cannot happen ... for now. Should we log an exception?
+ }
+ }
+
+ /**
+ * This will be used to match the given {@link ResourceSet}s. This default implementation will query the
+ * comparison scope for these resource sets children, then delegate to an {@link IResourceMatcher} to
+ * determine the resource mappings.
+ *
+ * @param comparison
+ * The comparison to which will be added detected matches.
+ * @param scope
+ * The comparison scope that should be used by this engine to determine the objects to match.
+ * @param left
+ * The left {@link ResourceSet}.
+ * @param right
+ * The right {@link ResourceSet}.
+ * @param origin
+ * The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
+ * @param monitor
+ * The monitor to report progress or to check for cancellation
+ */
+ protected void match(Comparison comparison, IComparisonScope scope, ResourceSet left, ResourceSet right,
+ ResourceSet origin, Monitor monitor) {
+ final Iterator<? extends Resource> leftChildren = scope.getCoveredResources(left);
+ final Iterator<? extends Resource> rightChildren = scope.getCoveredResources(right);
+ final Iterator<? extends Resource> originChildren;
+ if (origin != null) {
+ originChildren = scope.getCoveredResources(origin);
+ } else {
+ originChildren = Iterators.emptyIterator();
+ }
+
+ final IResourceMatcher resourceMatcher = createResourceMatcher();
+ final Iterable<MatchResource> mappings = resourceMatcher.createMappings(leftChildren, rightChildren,
+ originChildren);
+
+ final List<Iterator<? extends EObject>> leftIterators = Lists.newLinkedList();
+ final List<Iterator<? extends EObject>> rightIterators = Lists.newLinkedList();
+ final List<Iterator<? extends EObject>> originIterators = Lists.newLinkedList();
+
+ for (MatchResource mapping : mappings) {
+ comparison.getMatchedResources().add(mapping);
+
+ final Resource leftRes = mapping.getLeft();
+ final Resource rightRes = mapping.getRight();
+ final Resource originRes = mapping.getOrigin();
+
+ if (leftRes != null) {
+ leftIterators.add(scope.getCoveredEObjects(leftRes));
+ }
+
+ if (rightRes != null) {
+ rightIterators.add(scope.getCoveredEObjects(rightRes));
+ }
+
+ if (originRes != null) {
+ originIterators.add(scope.getCoveredEObjects(originRes));
+ }
+ }
+
+ final Iterator<? extends EObject> leftEObjects = Iterators.concat(leftIterators.iterator());
+ final Iterator<? extends EObject> rightEObjects = Iterators.concat(rightIterators.iterator());
+ final Iterator<? extends EObject> originEObjects = Iterators.concat(originIterators.iterator());
+
+ getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects, monitor);
+ }
+
+ /**
+ * This will only query the scope for the given Resources' children, then delegate to an
+ * {@link IEObjectMatcher} to determine the EObject matches.
+ * <p>
+ * We expect at least two of the given resources not to be <code>null</code>.
+ * </p>
+ *
+ * @param comparison
+ * The comparison to which will be added detected matches.
+ * @param scope
+ * The comparison scope that should be used by this engine to determine the objects to match.
+ * @param left
+ * The left {@link Resource}. Can be <code>null</code>.
+ * @param right
+ * The right {@link Resource}. Can be <code>null</code>.
+ * @param origin
+ * The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
+ * @param monitor
+ * The monitor to report progress or to check for cancellation
+ */
+ protected void match(Comparison comparison, IComparisonScope scope, Resource left, Resource right,
+ Resource origin, Monitor monitor) {
+ // Our "roots" are Resources. Consider them matched
+ final MatchResource match = CompareFactory.eINSTANCE.createMatchResource();
+
+ match.setLeft(left);
+ match.setRight(right);
+ match.setOrigin(origin);
+
+ if (left != null) {
+ URI uri = left.getURI();
+ if (uri != null) {
+ match.setLeftURI(uri.toString());
+ }
+ }
+
+ if (right != null) {
+ URI uri = right.getURI();
+ if (uri != null) {
+ match.setRightURI(uri.toString());
+ }
+ }
+
+ if (origin != null) {
+ URI uri = origin.getURI();
+ if (uri != null) {
+ match.setOriginURI(uri.toString());
+ }
+ }
+
+ comparison.getMatchedResources().add(match);
+
+ // We need at least two resources to match them
+ if (atLeastTwo(left == null, right == null, origin == null)) {
+ /*
+ * TODO But if we have only one resource, which is then unmatched, should we not still do
+ * something with it?
+ */
+ return;
+ }
+
+ final Iterator<? extends EObject> leftEObjects;
+ if (left != null) {
+ leftEObjects = scope.getCoveredEObjects(left);
+ } else {
+ leftEObjects = Iterators.emptyIterator();
+ }
+ final Iterator<? extends EObject> rightEObjects;
+ if (right != null) {
+ rightEObjects = scope.getCoveredEObjects(right);
+ } else {
+ rightEObjects = Iterators.emptyIterator();
+ }
+ final Iterator<? extends EObject> originEObjects;
+ if (origin != null) {
+ originEObjects = scope.getCoveredEObjects(origin);
+ } else {
+ originEObjects = Iterators.emptyIterator();
+ }
+
+ getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects, monitor);
+
+ }
+
+ /**
+ * This will query the scope for the given {@link EObject}s' children, then delegate to an
+ * {@link IEObjectMatcher} to compute the Matches.
+ * <p>
+ * We expect at least the <code>left</code> and <code>right</code> EObjects not to be <code>null</code>.
+ * </p>
+ *
+ * @param comparison
+ * The comparison to which will be added detected matches.
+ * @param scope
+ * The comparison scope that should be used by this engine to determine the objects to match.
+ * @param left
+ * The left {@link EObject}.
+ * @param right
+ * The right {@link EObject}.
+ * @param origin
+ * The common ancestor of <code>left</code> and <code>right</code>.
+ * @param monitor
+ * The monitor to report progress or to check for cancellation.
+ */
+ protected void match(Comparison comparison, IComparisonScope scope, EObject left, EObject right,
+ EObject origin, Monitor monitor) {
+ if (left == null || right == null) {
+ throw new IllegalArgumentException();
+ }
+
+ final Iterator<? extends EObject> leftEObjects = Iterators.concat(Iterators.singletonIterator(left),
+ scope.getChildren(left));
+ final Iterator<? extends EObject> rightEObjects = Iterators.concat(
+ Iterators.singletonIterator(right), scope.getChildren(right));
+ final Iterator<? extends EObject> originEObjects;
+ if (origin != null) {
+ originEObjects = Iterators.concat(Iterators.singletonIterator(origin), scope.getChildren(origin));
+ } else {
+ originEObjects = Iterators.emptyIterator();
+ }
+
+ getEObjectMatcher().createMatches(comparison, leftEObjects, rightEObjects, originEObjects, monitor);
+ }
+
+ /**
+ * This will be used to create the resource matcher that will be used by this match engine.
+ *
+ * @return An {@link IResourceMatcher} that can be used to retrieve the {@link MatchResource}s for this
+ * comparison.
+ */
+ protected IResourceMatcher createResourceMatcher() {
+ return new StrategyResourceMatcher();
+ }
+
+ /**
+ * Returns the EObject matcher associated with this match engine.
+ *
+ * @return The EObject matcher associated with this match engine.
+ */
+ protected final IEObjectMatcher getEObjectMatcher() {
+ return eObjectMatcher;
+ }
+
+ /**
+ * This will check that at least two of the three given booleans are <code>true</code>.
+ *
+ * @param condition1
+ * First of the three booleans.
+ * @param condition2
+ * Second of the three booleans.
+ * @param condition3
+ * Third of the three booleans.
+ * @return <code>true</code> if at least two of the three given booleans are <code>true</code>,
+ * <code>false</code> otherwise.
+ */
+ private static boolean atLeastTwo(boolean condition1, boolean condition2, boolean condition3) {
+ // CHECKSTYLE:OFF This expression is alone in its method, and documented.
+ return condition1 && (condition2 || condition3) || (condition2 && condition3);
+ // CHECKSTYLE:ON
+ }
+
+ /**
+ * Helper creator method that instantiate a {@link DefaultMatchEngine} that will use identifiers as
+ * specified by the given {@code useIDs} enumeration.
+ *
+ * @param useIDs
+ * the kinds of matcher to use.
+ * @return a new {@link DefaultMatchEngine} instance.
+ */
+ public static IMatchEngine create(UseIdentifiers useIDs) {
+ final IComparisonFactory comparisonFactory = new DefaultComparisonFactory(
+ new DefaultEqualityHelperFactory());
+ final IEObjectMatcher matcher = createDefaultEObjectMatcher(useIDs);
+
+ final IMatchEngine matchEngine = new DefaultMatchEngine(matcher, comparisonFactory);
+ return matchEngine;
+ }
+
+ /**
+ * Creates and configures an {@link IEObjectMatcher} with the strategy given by {@code useIDs}. The
+ * {@code cache} will be used to cache some expensive computation (should better a LoadingCache).
+ *
+ * @param useIDs
+ * which strategy the return IEObjectMatcher must follow.
+ * @return a new IEObjectMatcher.
+ */
+ public static IEObjectMatcher createDefaultEObjectMatcher(UseIdentifiers useIDs) {
+ final IEObjectMatcher matcher;
+ final EditionDistance editionDistance = new EditionDistance();
+ switch (useIDs) {
+ case NEVER:
+ matcher = new ProximityEObjectMatcher(editionDistance);
+ break;
+ case ONLY:
+ matcher = new IdentifierEObjectMatcher();
+ break;
+ case WHEN_AVAILABLE:
+ // fall through to default
+ default:
+ // Use an ID matcher, delegating to proximity when no ID is available
+ final IEObjectMatcher contentMatcher = new ProximityEObjectMatcher(editionDistance);
+ matcher = new IdentifierEObjectMatcher(contentMatcher);
+ break;
+
+ }
+
+ return matcher;
+ }
+
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/IMatchEngine.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/IMatchEngine.java
index 8617c1839..6c4ff55c1 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/IMatchEngine.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/IMatchEngine.java
@@ -1,56 +1,152 @@
-/*******************************************************************************
- * Copyright (c) 2012 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;
-
-import org.eclipse.emf.common.util.Monitor;
-import org.eclipse.emf.compare.Comparison;
-import org.eclipse.emf.compare.scope.IComparisonScope;
-
-/**
- * This class defines the general contract of a Matching engine. We expect subclasses to have a public,
- * no-argument default constructor for instantiation.
- * <p>
- * We generally expect that a call to {@link #match(IComparisonScope)} will return us every single
- * {@link org.eclipse.emf.compare.Match matches} that can be determined from the given
- * {@link IComparisonScope context}. This includes all three of :
- * <ul>
- * <li>Elements that are present on all three sides of the comparison scope,</li>
- * <li>Elements that are present on only two sides,</li>
- * <li>Elements that are only present on a single side.</li>
- * </ul>
- * </p>
- * <p>
- * Clients can subclass the {@link DefaultMatchEngine default implementation} when all that is needed is to
- * change the matching strategy.
- * </p>
- *
- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
- * @see DefaultMatchEngine
- */
-public interface IMatchEngine {
-
- /**
- * This is the entry point of a Comparison process. It is expected to use the provided scope in order to
- * determine all objects that need to be matched.
- * <p>
- * The returned Comparison should include both matched an unmatched objects. It is not the match engine's
- * responsibility to determine differences between objects, only to match them together.
- * </p>
- *
- * @param scope
- * The comparison scope that should be used by this engine to determine the objects to match.
- * @param monitor
- * The monitor to report progress or to check for cancellation
- * @return An initialized {@link Comparison} model with all matches determined.
- */
- Comparison match(IComparisonScope scope, Monitor monitor);
-
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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;
+
+import java.util.List;
+
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+
+/**
+ * This class defines the general contract of a Matching engine. We expect subclasses to have a public,
+ * no-argument default constructor for instantiation.
+ * <p>
+ * We generally expect that a call to {@link #match(IComparisonScope)} will return us every single
+ * {@link org.eclipse.emf.compare.Match matches} that can be determined from the given
+ * {@link IComparisonScope context}. This includes all three of :
+ * <ul>
+ * <li>Elements that are present on all three sides of the comparison scope,</li>
+ * <li>Elements that are present on only two sides,</li>
+ * <li>Elements that are only present on a single side.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Clients can subclass the {@link DefaultMatchEngine default implementation} when all that is needed is to
+ * change the matching strategy.
+ * </p>
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ * @see DefaultMatchEngine
+ */
+public interface IMatchEngine {
+
+ /**
+ * This is the entry point of a Comparison process. It is expected to use the provided scope in order to
+ * determine all objects that need to be matched.
+ * <p>
+ * The returned Comparison should include both matched an unmatched objects. It is not the match engine's
+ * responsibility to determine differences between objects, only to match them together.
+ * </p>
+ *
+ * @param scope
+ * The comparison scope that should be used by this engine to determine the objects to match.
+ * @param monitor
+ * The monitor to report progress or to check for cancellation
+ * @return An initialized {@link Comparison} model with all matches determined.
+ */
+ Comparison match(IComparisonScope scope, Monitor monitor);
+
+ /**
+ * Wrapper describing the given match engine.
+ *
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ * @since 3.0
+ */
+ interface Factory {
+
+ /**
+ * Returns the wrapped match engine.
+ *
+ * @return the wrapped match engine.
+ */
+ IMatchEngine getMatchEngine();
+
+ /**
+ * Returns the ranking of this match engine factory.
+ *
+ * @return The ranking.
+ * @since 3.0
+ */
+ int getRanking();
+
+ /**
+ * Set the ranking of this match engine factory.
+ *
+ * @param parseInt
+ * The ranking.
+ * @since 3.0
+ */
+ void setRanking(int parseInt);
+
+ /**
+ * Check if the match engine factory is a good candidate for comparison.
+ *
+ * @param scope
+ * The scope on which the match engine factory will be applied.
+ * @return True if it is the good candidate, false otherwise.
+ * @since 3.0
+ */
+ boolean isMatchEngineFactoryFor(IComparisonScope scope);
+
+ /**
+ * A registry of {@link IMatchEngine.Factory}.
+ *
+ * @since 3.0
+ */
+ interface Registry {
+
+ /**
+ * Returns the match engine factory, for the given scope, owning the highest ranking.
+ *
+ * @param scope
+ * The given scope.
+ * @return The found match engine factory.
+ */
+ IMatchEngine.Factory getHighestRankingMatchEngineFactory(IComparisonScope scope);
+
+ /**
+ * Returns the list of {@link IMatchEngine.Factory} contained in the registry.
+ *
+ * @param scope
+ * The scope on which the match engine factories will be applied.
+ * @return The list of {@link IMatchEngine.Factory} contained in the registry.
+ */
+ List<IMatchEngine.Factory> getMatchEngineFactories(IComparisonScope scope);
+
+ /**
+ * Add to the registry the given {@link IMatchEngine.Factory}.
+ *
+ * @param matchEngineFactory
+ * The given {@link IMatchEngine.Factory}.
+ * @return The previous value associated with the class name of the given
+ * {@link IMatchEngine.Factory}, or null if there was no entry in the registry for the
+ * class name.
+ */
+ IMatchEngine.Factory add(IMatchEngine.Factory matchEngineFactory);
+
+ /**
+ * Remove from the registry the {@link IMatchEngine.Factory} designated by the given
+ * {@link String} .
+ *
+ * @param className
+ * The given {@link String} representing a {@link IMatchEngine.Factory}.
+ * @return The {@link IMatchEngine.Factory} designated by the given {@link String}.
+ */
+ IMatchEngine.Factory remove(String className);
+
+ /**
+ * Clear the registry.
+ */
+ void clear();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryImpl.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryImpl.java
new file mode 100644
index 000000000..76c61c36c
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryImpl.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.impl;
+
+import org.eclipse.emf.compare.match.DefaultComparisonFactory;
+import org.eclipse.emf.compare.match.DefaultEqualityHelperFactory;
+import org.eclipse.emf.compare.match.DefaultMatchEngine;
+import org.eclipse.emf.compare.match.IComparisonFactory;
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.utils.UseIdentifiers;
+
+/**
+ * The default implementation of the {@link IMatchEngine.Factory.Registry}.
+ *
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ * @since 3.0
+ */
+public class MatchEngineFactoryImpl implements IMatchEngine.Factory {
+
+ /** The match engine created by this factory. */
+ protected IMatchEngine matchEngine;
+
+ /** Ranking of this match engine. */
+ private int ranking;
+
+ /**
+ * Constructor that instantiate a {@link DefaultMatchEngine}.
+ */
+ public MatchEngineFactoryImpl() {
+ this(UseIdentifiers.WHEN_AVAILABLE);
+ }
+
+ /**
+ * Constructor that instantiate a {@link DefaultMatchEngine} that will use identifiers as specified by the
+ * given {@code useIDs} enumeration.
+ *
+ * @param useIDs
+ * the kinds of matcher to use.
+ */
+ public MatchEngineFactoryImpl(UseIdentifiers useIDs) {
+ final IComparisonFactory comparisonFactory = new DefaultComparisonFactory(
+ new DefaultEqualityHelperFactory());
+ final IEObjectMatcher matcher = DefaultMatchEngine.createDefaultEObjectMatcher(useIDs);
+ matchEngine = new DefaultMatchEngine(matcher, comparisonFactory);
+ }
+
+ /**
+ * Constructor that instantiate a {@link DefaultMatchEngine} with the given parameters.
+ *
+ * @param matcher
+ * The matcher that will be in charge of pairing EObjects together for this comparison process.
+ * @param comparisonFactory
+ * factory that will be use to instantiate Comparison as return by match() methods.
+ */
+ public MatchEngineFactoryImpl(IEObjectMatcher matcher, IComparisonFactory comparisonFactory) {
+ matchEngine = new DefaultMatchEngine(matcher, comparisonFactory);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory#getMatchEngine()
+ */
+ public IMatchEngine getMatchEngine() {
+ return matchEngine;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory#getRanking()
+ */
+ public int getRanking() {
+ return ranking;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory#setRanking(int)
+ */
+ public void setRanking(int r) {
+ ranking = r;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory#isMatchEngineFactoryFor(org.eclipse.emf.compare.scope.IComparisonScope)
+ */
+ public boolean isMatchEngineFactoryFor(IComparisonScope scope) {
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryRegistryImpl.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryRegistryImpl.java
new file mode 100644
index 000000000..63d35a1ed
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/match/impl/MatchEngineFactoryRegistryImpl.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.impl;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Lists.newArrayList;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.emf.compare.match.IMatchEngine;
+import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.utils.UseIdentifiers;
+
+/**
+ * The default implementation of the {@link IMatchEngine.Factory.Registry}.
+ *
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ * @since 3.0
+ */
+public class MatchEngineFactoryRegistryImpl implements IMatchEngine.Factory.Registry {
+
+ /** A map that associates the class name to theirs {@link IMatchEngine.Factory}. */
+ private final Map<String, IMatchEngine.Factory> map;
+
+ /**
+ * Constructs the registry.
+ */
+ public MatchEngineFactoryRegistryImpl() {
+ map = new ConcurrentHashMap<String, IMatchEngine.Factory>();
+ }
+
+ /**
+ * Returns a registry filled with the default match engine factory provided by EMF Compare
+ * {@link MatchEngineFactoryImpl}.
+ *
+ * @return A registry filled with the default match engine factory provided by EMF Compare.
+ */
+ public static IMatchEngine.Factory.Registry createStandaloneInstance() {
+ final IMatchEngine.Factory.Registry registry = new MatchEngineFactoryRegistryImpl();
+
+ final IMatchEngine.Factory matchEngineFactory = new MatchEngineFactoryImpl(
+ UseIdentifiers.WHEN_AVAILABLE);
+ matchEngineFactory.setRanking(10);
+
+ registry.add(matchEngineFactory);
+
+ return registry;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory.Registry#getHighestRankingMatchEngine(java.lang.Object)
+ */
+ public IMatchEngine.Factory getHighestRankingMatchEngineFactory(IComparisonScope scope) {
+ Iterator<IMatchEngine.Factory> matchEngineFactories = getMatchEngineFactories(scope).iterator();
+
+ IMatchEngine.Factory ret = null;
+
+ if (matchEngineFactories.hasNext()) {
+ IMatchEngine.Factory highestRanking = matchEngineFactories.next();
+ while (matchEngineFactories.hasNext()) {
+ IMatchEngine.Factory engineFactory = matchEngineFactories.next();
+ if (engineFactory.getRanking() > highestRanking.getRanking()) {
+ highestRanking = engineFactory;
+ }
+ }
+ ret = highestRanking;
+ }
+
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory.Registry#getMatchEngines(org.eclipse.emf.compare.scope.IComparisonScope)
+ */
+ public List<IMatchEngine.Factory> getMatchEngineFactories(IComparisonScope scope) {
+ Iterable<IMatchEngine.Factory> matchEngineFactories = filter(map.values(),
+ isMatchEngineFactoryActivable(scope));
+ List<IMatchEngine.Factory> ret = newArrayList();
+ for (IMatchEngine.Factory matchEngineFactory : matchEngineFactories) {
+ ret.add(matchEngineFactory);
+ }
+ return ret;
+ }
+
+ /**
+ * Returns a predicate that represents the activation condition based on the scope.
+ *
+ * @param scope
+ * The scope on which the group provider will be applied.
+ * @return A predicate that represents the activation condition based on the scope.
+ */
+ private static Predicate<IMatchEngine.Factory> isMatchEngineFactoryActivable(final IComparisonScope scope) {
+ return new Predicate<IMatchEngine.Factory>() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see com.google.common.base.Predicate#apply(java.lang.Object)
+ */
+ public boolean apply(IMatchEngine.Factory factory) {
+ return factory.isMatchEngineFactoryFor(scope);
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory.Registry#add(org.eclipse.emf.compare.match.IMatchEngine)
+ */
+ public IMatchEngine.Factory add(IMatchEngine.Factory filter) {
+ Preconditions.checkNotNull(filter);
+ return map.put(filter.getClass().getName(), filter);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory.Registry#remove(java.lang.String)
+ */
+ public IMatchEngine.Factory remove(String className) {
+ return map.remove(className);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.match.IMatchEngine.Factory.Registry#clear()
+ */
+ public void clear() {
+ map.clear();
+ }
+
+}

Back to the top