blob: bee5d6d1b76d73cb82106a873f3dbae7929736ad [file] [log] [blame]
/*******************************************************************************
* 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;
}
}