org.eclipse.emf.compare.match.DefaultMatchEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.compare.match.DefaultMatchEngine.java

Source

/*******************************************************************************
 * 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 com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

import java.util.Iterator;

import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.compare.CompareFactory;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.MatchResource;
import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
import org.eclipse.emf.compare.match.eobject.IdentifierEObjectMatcher;
import org.eclipse.emf.compare.match.resource.IResourceMatcher;
import org.eclipse.emf.compare.match.resource.ResourceMapping;
import org.eclipse.emf.compare.match.resource.StrategyResourceMatcher;
import org.eclipse.emf.compare.scope.IComparisonScope;
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 {
    /** Root of this comparison, should only be accessed or instantiated through {@link #getComparison()}. */
    protected Comparison comparison;

    /** The comparison scope that will be used by this engine. Should be accessed through {@link #getScope()}. */
    protected IComparisonScope comparisonScope;

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.emf.compare.match.IMatchEngine#match(org.eclipse.emf.compare.scope.IComparisonScope)
     */
    public Comparison match(IComparisonScope scope) {
        this.comparisonScope = scope;

        final Notifier left = getScope().getLeft();
        final Notifier right = getScope().getRight();
        final Notifier origin = getScope().getOrigin();

        // FIXME side-effect coding
        if (left instanceof ResourceSet || right instanceof ResourceSet) {
            match((ResourceSet) left, (ResourceSet) right, (ResourceSet) origin);
        } else if (left instanceof Resource || right instanceof Resource) {
            // Our "roots" are Resources. Consider them matched
            final MatchResource match = createMatchResource((Resource) left, (Resource) right, (Resource) origin);
            getComparison().getMatchedResources().add(match);
            match((Resource) left, (Resource) right, (Resource) origin);
        } else if (left instanceof EObject || right instanceof EObject) {
            match((EObject) left, (EObject) right, (EObject) origin);
        } else {
            // FIXME
        }

        return getComparison();
    }

    /**
     * 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 left
     *            The left {@link ResourceSet}.
     * @param right
     *            The common {@link ResourceSet}.
     * @param origin
     *            The common ancestor of <code>left</code> and <code>right</code>. Can be <code>null</code>.
     */
    protected void match(ResourceSet left, ResourceSet right, ResourceSet origin) {
        final Iterator<? extends Resource> leftChildren = getScope().getCoveredResources(left);
        final Iterator<? extends Resource> rightChildren = getScope().getCoveredResources(right);
        final Iterator<? extends Resource> originChildren;
        if (origin != null) {
            originChildren = getScope().getCoveredResources(origin);
        } else {
            originChildren = Iterators.emptyIterator();
        }

        final IResourceMatcher matcher = getResourceMatcher();
        final Iterable<ResourceMapping> mappings = matcher.createMappings(leftChildren, rightChildren,
                originChildren);

        for (ResourceMapping mapping : mappings) {
            final MatchResource matchResource = createMatchResource(mapping.getLeft(), mapping.getRight(),
                    mapping.getOrigin());
            getComparison().getMatchedResources().add(matchResource);

            match(mapping.getLeft(), mapping.getRight(), mapping.getOrigin());
        }
    }

    /**
     * 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 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>.
     */
    protected void match(Resource left, Resource right, Resource origin) {
        // 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 = getScope().getCoveredEObjects(left);
        } else {
            leftEObjects = Iterators.emptyIterator();
        }
        final Iterator<? extends EObject> rightEObjects;
        if (right != null) {
            rightEObjects = getScope().getCoveredEObjects(right);
        } else {
            rightEObjects = Iterators.emptyIterator();
        }
        final Iterator<? extends EObject> originEObjects;
        if (origin != null) {
            originEObjects = getScope().getCoveredEObjects(origin);
        } else {
            originEObjects = Iterators.emptyIterator();
        }

        final IEObjectMatcher matcher = createEObjectMatcher();
        final Iterable<Match> matches = matcher.createMatches(leftEObjects, rightEObjects, originEObjects);

        Iterables.addAll(getComparison().getMatches(), matches);
    }

    /**
     * This will query the scope for the given {@link EObject}s' children, then delegate to an
     * {@link IEObjectMatcher} to compute the {@link Match}es.
     * <p>
     * We expect at least the <code>left</code> and <code>right</code> EObjects not to be <code>null</code>.
     * </p>
     * 
     * @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>.
     */
    protected void match(EObject left, EObject right, EObject origin) {
        if (left == null || right == null) {
            // FIXME IAE or NPE?
            throw new IllegalArgumentException();
        }

        final Iterator<? extends EObject> leftEObjects = getScope().getChildren(left);
        final Iterator<? extends EObject> rightEObjects = getScope().getChildren(right);
        final Iterator<? extends EObject> originEObjects;
        if (origin != null) {
            originEObjects = getScope().getChildren(origin);
        } else {
            originEObjects = Iterators.emptyIterator();
        }

        final IEObjectMatcher matcher = createEObjectMatcher();
        final Iterable<Match> matches = matcher.createMatches(leftEObjects, rightEObjects, originEObjects);

        Iterables.addAll(getComparison().getMatches(), matches);
    }

    /**
     * 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 getResourceMatcher() {
        return new StrategyResourceMatcher();
    }

    /**
     * This will be used to create the EObject matcher that will be used by this match engine.
     * <p>
     * This default implementation uses an {@link IdentifierEObjectMatcher} to match EObjects through their ID
     * only.
     * </p>
     * 
     * @return An {@link IEObjectMatcher} that can be used to retrieve the {@link Match}es for this
     *         comparison.
     */
    protected IEObjectMatcher createEObjectMatcher() {
        return new IdentifierEObjectMatcher();
    }

    /**
     * Returns the root of this Comparison. This default implementation will instantiate a Comparison model
     * through the factory if needed.
     * 
     * @return The root of this Comparison.
     */
    protected Comparison getComparison() {
        if (comparison == null) {
            comparison = CompareFactory.eINSTANCE.createComparison();
        }
        return comparison;
    }

    /**
     * Returns the comparison scope associated with this engine.
     * 
     * @return The comparison scope associated with this engine.
     */
    protected IComparisonScope getScope() {
        return comparisonScope;
    }

    /**
     * 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.
     */
    protected 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
    }

    /**
     * This can be called to create a {@link MatchResource} for the given three resources. <b>Note </b> that
     * any of the arguments can be <code>null</code>.
     * 
     * @param left
     *            The left resource of this mapping. Can be <code>null</code>.
     * @param right
     *            The right resource of this mapping. Can be <code>null</code>.
     * @param origin
     *            The origin resource of this mapping. Can be <code>null</code>.
     * @return The newly created mapping.
     */
    protected static MatchResource createMatchResource(Resource left, Resource right, Resource origin) {
        final MatchResource matchResource = CompareFactory.eINSTANCE.createMatchResource();
        if (left != null && left.getURI() != null) {
            matchResource.setLeftURI(left.getURI().toString());
        }
        if (right != null && right.getURI() != null) {
            matchResource.setRightURI(right.getURI().toString());
        }
        if (origin != null && origin.getURI() != null) {
            matchResource.setOriginURI(origin.getURI().toString());
        }
        return matchResource;
    }
}