diff options
author | Axel Richard | 2013-03-11 13:35:40 +0000 |
---|---|---|
committer | Gerrit Code Review @ Eclipse.org | 2013-03-19 09:37:56 +0000 |
commit | dedaabfda0e4ba27b58ef426277225790147d69d (patch) | |
tree | deaf970337fe2b8919cc4c910a04ddc8f71dc01c | |
parent | 31ddf8577c1a7b99559ced8d8773610e841d72fd (diff) | |
download | org.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
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> + <extension point="org.eclipse.emf.compare.rcp.matchEngine"> + <engineFactory class="org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl" ranking="10"/> +</extension> + </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(); + } + +} |