/******************************************************************************* * Copyright (c) 2015 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Obeo - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.egit.internal.postprocessor; import java.io.File; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.Monitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.DifferenceKind; import org.eclipse.emf.compare.DifferenceSource; import org.eclipse.emf.compare.Match; import org.eclipse.emf.compare.ResourceAttachmentChange; import org.eclipse.emf.compare.diff.DiffBuilder; import org.eclipse.emf.compare.diff.IDiffProcessor; import org.eclipse.emf.compare.ide.internal.utils.StoragePathAdapter; import org.eclipse.emf.compare.postprocessor.IPostProcessor; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; /** * Post-processor to add potential {@link ResourceAttachmentChange} of kind Move. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ @SuppressWarnings("restriction") public class EgitPostProcessor implements IPostProcessor { /** * The diff processor that will be used by this post-processor. Should be passed by the constructor and * accessed by {@link #getDiffProcessor()}. */ private IDiffProcessor diffProcessor; /** * Default Constructor. */ public EgitPostProcessor() { this(new DiffBuilder()); } /** * Constructor. * * @param processor * this instance will be called for each detected difference. */ public EgitPostProcessor(IDiffProcessor processor) { this.diffProcessor = processor; } /** * {@inheritDoc} . * * @see org.eclipse.emf.compare.postprocessor.IPostProcessor#postMatch(org.eclipse.emf.compare.Comparison, * org.eclipse.emf.common.util.Monitor) */ public void postMatch(Comparison comparison, Monitor monitor) { // Nothing to do here for now. } /** * {@inheritDoc} . * * @see org.eclipse.emf.compare.postprocessor.IPostProcessor#postDiff(org.eclipse.emf.compare.Comparison, * org.eclipse.emf.common.util.Monitor) */ public void postDiff(Comparison comparison, Monitor monitor) { for (Match rootMatch : comparison.getMatches()) { checkForDifferences(rootMatch, monitor); } } /** * {@inheritDoc} . * * @see org.eclipse.emf.compare.postprocessor.IPostProcessor#postRequirements(org.eclipse.emf.compare.Comparison * , org.eclipse.emf.common.util.Monitor) */ public void postRequirements(Comparison comparison, Monitor monitor) { // Nothing to do here for now. } /** * {@inheritDoc} . * * @see org.eclipse.emf.compare.postprocessor.IPostProcessor#postEquivalences(org.eclipse.emf.compare.Comparison * , org.eclipse.emf.common.util.Monitor) */ public void postEquivalences(Comparison comparison, Monitor monitor) { // Nothing to do here for now. } /** * {@inheritDoc} . * * @see org.eclipse.emf.compare.postprocessor.IPostProcessor#postConflicts(org.eclipse.emf.compare.Comparison, * org.eclipse.emf.common.util.Monitor) */ public void postConflicts(Comparison comparison, Monitor monitor) { // Nothing to do here for now. } /** * {@inheritDoc} . * * @see org.eclipse.emf.compare.postprocessor.IPostProcessor#postComparison(org.eclipse.emf.compare.Comparison, * org.eclipse.emf.common.util.Monitor) */ public void postComparison(Comparison comparison, Monitor monitor) { // Nothing to do here for now. } /** * This will return the diff processor that has been created through {@link #createDiffProcessor()} for * this differencing process. * * @return The diff processor to notify of difference detections. */ protected IDiffProcessor getDiffProcessor() { return diffProcessor; } /** * Check all matches. * * @param match * The match that is to be checked. * @param monitor * The monitor to report progress or to check for cancellation. */ protected void checkForDifferences(Match match, Monitor monitor) { checkResourceAttachment(match, monitor); for (Match subMatch : match.getSubmatches()) { checkForDifferences(subMatch, monitor); } } /** * Checks whether the given {@link Match}'s sides have changed resources of kind Move. This will only be * called for {@link Match} elements referencing the root(s) of an EMF Resource. We also create resource * attachment of kind Move only for non-local comparison. This is why this check is made in the EMf * compare Egit support plugin. The reason is a local comparison between /MyPath/left.model & * /MyPath/right.model, will always result in {@link ResourceAttachmentChange}s of kind Move, and we don't * want to have {@link ResourceAttachmentChange}s in these cases. * * @param match * The match that is to be checked. * @param monitor * The monitor to report progress or to check for cancellation. */ protected void checkResourceAttachment(Match match, Monitor monitor) { final Comparison comparison = match.getComparison(); if (comparison.getMatchedResources().isEmpty()) { // This is a comparison of EObjects, do not go up to the resources return; } final EObject left = match.getLeft(); final EObject right = match.getRight(); boolean threeWay = comparison.isThreeWay(); if (threeWay) { final EObject origin = match.getOrigin(); if (!isLocalComparison(left, origin) && areNotRootsOfSameResource(left, origin)) { final String uri = left.eResource().getURI().toString(); getDiffProcessor().resourceAttachmentChange(match, uri, DifferenceKind.MOVE, DifferenceSource.LEFT); } if (!isLocalComparison(right, origin) && areNotRootsOfSameResource(right, origin)) { final String uri = right.eResource().getURI().toString(); getDiffProcessor().resourceAttachmentChange(match, uri, DifferenceKind.MOVE, DifferenceSource.RIGHT); } } else { if (!isLocalComparison(left, right) && areNotRootsOfSameResource(left, right)) { final String uri = right.eResource().getURI().toString(); getDiffProcessor().resourceAttachmentChange(match, uri, DifferenceKind.MOVE, DifferenceSource.LEFT); } } } /** * Check if the left and right objects are involved in a local comparison. * * @param left * the first object to check. * @param right * the second object to check. * @return true, if it is a local comparison. false otherwise. */ protected boolean isLocalComparison(EObject left, EObject right) { final Resource leftResource; final Resource rightResource; if (left != null) { leftResource = left.eResource(); } else { leftResource = null; } if (right != null) { rightResource = right.eResource(); } else { rightResource = null; } boolean leftIsLocal = true; boolean rightIsLocal = true; if (leftResource != null) { Adapter leftAdapter = EcoreUtil.getAdapter(leftResource.eAdapters(), StoragePathAdapter.class); if (leftAdapter instanceof StoragePathAdapter) { leftIsLocal = ((StoragePathAdapter)leftAdapter).isLocal(); } } if (rightResource != null) { Adapter rightAdapter = EcoreUtil.getAdapter(rightResource.eAdapters(), StoragePathAdapter.class); if (rightAdapter instanceof StoragePathAdapter) { rightIsLocal = ((StoragePathAdapter)rightAdapter).isLocal(); } } if (!leftIsLocal || !rightIsLocal) { return false; } return true; } /** * Only return false if left and right objects are roots of the "same" resource, i.e. of resources that * match. * * @param left * the first object to check. * @param right * the second object to check. * @return <code>true</code> UNLESS left and right objects are roots of the same resource. */ protected boolean areNotRootsOfSameResource(EObject left, EObject right) { URI leftURI = getDirectResourceURI(left); URI rightURI = getDirectResourceURI(right); if (leftURI != null && rightURI != null) { if (leftURI.equals(rightURI) || isMatchingResourceAndFileURI(leftURI, rightURI) || isMatchingResourceAndFileURI(rightURI, leftURI)) { return false; } return true; } return false; } /** * Indicate whether the 2 provided URIs reprensent the same resource with different schemes. * * @param leftURI * uri that will be checked if it's a platform resource URI * @param rightURI * uri that will be checked if it's a file URI * @return <code>true</code> if rightURI is a file URI that ends with leftURI. */ private boolean isMatchingResourceAndFileURI(URI leftURI, URI rightURI) { return leftURI.isPlatformResource() && rightURI.isFile() && rightURI.toFileString() .replace(File.separatorChar, '/').endsWith(leftURI.toPlatformString(true)); } /** * Provide the URI of the given EObject's direct reource. This will be non-null only if the given EObject * is a root of a resource. * * @param o * The EObject * @return The given EObject's direct resource URI, can be null. */ private URI getDirectResourceURI(EObject o) { if (!(o instanceof InternalEObject)) { return null; // includes cases where o is null } final Resource directResource = ((InternalEObject)o).eDirectResource(); if (directResource != null) { return directResource.getURI(); } return null; } }