blob: cfbab6e3115ec24931c3ee5bb53a8bf6e1d965c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 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.diagram.internal.factories.extensions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
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.Match;
import org.eclipse.emf.compare.ReferenceChange;
import org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff;
import org.eclipse.emf.compare.diagram.internal.extensions.EdgeChange;
import org.eclipse.emf.compare.diagram.internal.extensions.ExtensionsFactory;
import org.eclipse.emf.compare.utils.EMFComparePredicates;
import org.eclipse.emf.compare.utils.MatchUtil;
import org.eclipse.emf.compare.utils.ReferenceUtil;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.gmf.runtime.notation.IdentityAnchor;
import org.eclipse.gmf.runtime.notation.NotationPackage;
/**
* Factory of edge changes.
*
* @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a>
*/
public class EdgeChangeFactory extends NodeChangeFactory {
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.internal.postprocessor.factories.AbstractChangeFactory#getExtensionKind()
*/
@Override
public Class<? extends Diff> getExtensionKind() {
return EdgeChange.class;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.diagram.internal.factories.extensions.NodeChangeFactory#createExtension()
*/
@Override
public DiagramDiff createExtension() {
return ExtensionsFactory.eINSTANCE.createEdgeChange();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.internal.postprocessor.factories.AbstractChangeFactory#setView(org.eclipse.emf.compare.diagram.internal.extensions.DiagramDiff,
* org.eclipse.emf.compare.Diff)
*/
@Override
public EObject setView(DiagramDiff extension, Diff refiningDiff) {
EObject view = super.setView(extension, refiningDiff);
while (view != null && !(view instanceof Edge)) {
view = view.eContainer();
}
extension.setView(view);
return view;
}
@Override
public void setRefiningChanges(Diff extension, DifferenceKind extensionKind, Diff refiningDiff) {
super.setRefiningChanges(extension, extensionKind, refiningDiff);
if (extensionKind == DifferenceKind.CHANGE) {
extension.getRefinedBy().addAll(Collections2.filter(getAllDifferencesForChange(refiningDiff),
EMFComparePredicates.fromSide(extension.getSource())));
}
}
/**
* Predicate to check that the given difference is the main unit difference for this macroscopic add or
* delete of edge.
*
* @return The predicate.
*/
public static Predicate<? super Diff> isMainDiffForAddOrDeleteEdge() {
return new Predicate<Diff>() {
public boolean apply(Diff difference) {
return difference instanceof ReferenceChange
&& (isRelatedToAnAddEdge((ReferenceChange)difference)
|| isRelatedToADeleteEdge((ReferenceChange)difference));
}
};
}
/**
* Get all differences which are part of a change extension, from the given difference (being part of the
* result).
*
* @param input
* The given difference.
* @return The found differences.
*/
@Override
protected Collection<Diff> getAllDifferencesForChange(Diff input) {
final Match match = input.getMatch();
final Comparison comparison = match.getComparison();
EObject objectContainingDiff = MatchUtil.getContainer(comparison, input);
Match edgeMatch = getEdgeMatch(comparison, objectContainingDiff);
Iterable<Diff> diffs = Iterables.filter(edgeMatch.getAllDifferences(), new Predicate<Diff>() {
public boolean apply(Diff diff) {
return getRelatedExtensionKind(diff) == DifferenceKind.CHANGE;
}
});
Collection<Diff> result = new ArrayList<Diff>();
Iterator<Diff> it = diffs.iterator();
while (it.hasNext()) {
Diff diff = it.next();
result.add(diff);
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.internal.postprocessor.factories.AbstractChangeFactory#isRelatedToAnExtensionAdd(org.eclipse.emf.compare.ReferenceChange)
*/
@Override
protected boolean isRelatedToAnExtensionAdd(ReferenceChange input) {
return isRelatedToAnAddEdge(input);
}
/**
* It checks that the given reference change concerns the add of an edge.
*
* @param input
* The reference change.
* @return True if it concerns the add of an edge, False otherwise.
*/
protected static boolean isRelatedToAnAddEdge(ReferenceChange input) {
return isContainmentOnSemanticEdge(input) && input.getKind() == DifferenceKind.ADD;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.internal.postprocessor.factories.AbstractChangeFactory#isRelatedToAnExtensionDelete(org.eclipse.emf.compare.ReferenceChange)
*/
@Override
protected boolean isRelatedToAnExtensionDelete(ReferenceChange input) {
return isRelatedToADeleteEdge(input);
}
/**
* It checks that the given reference change concerns the delete of an edge.
*
* @param input
* The reference change.
* @return True if it concerns the delete of an edge, False otherwise.
*/
protected static boolean isRelatedToADeleteEdge(ReferenceChange input) {
return isContainmentOnSemanticEdge(input) && input.getKind() == DifferenceKind.DELETE;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.internal.postprocessor.factories.AbstractChangeFactory#isRelatedToAnExtensionMove(org.eclipse.emf.compare.AttributeChange)
*/
@Override
protected boolean isRelatedToAnExtensionChange(AttributeChange input) {
return (input.getAttribute().eContainer().equals(NotationPackage.eINSTANCE.getRelativeBendpoints())
|| input.getAttribute().equals(NotationPackage.eINSTANCE.getIdentityAnchor_Id())) &&
/* isContainerBasedOnSemanticEdge(input) && */input.getRefines().isEmpty();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.internal.postprocessor.factories.AbstractChangeFactory#isRelatedToAnExtensionMove(org.eclipse.emf.compare.ReferenceChange)
*/
@Override
protected boolean isRelatedToAnExtensionChange(ReferenceChange input) {
return input.getValue() instanceof IdentityAnchor && input.getReference().isContainment() &&
/* isContainerBasedOnSemanticEdge(input) && */input.getRefines().isEmpty()
&& !isLeadedByAddOrDeleteEdge(input);
}
/**
* Get the match of the edge containing the given object (<code>subObject</code>).
*
* @param comparison
* The comparison
* @param subObject
* The object contained in the current edge.
* @return The match of the edge.
*/
private Match getEdgeMatch(Comparison comparison, EObject subObject) {
EObject object = subObject;
while (!(object instanceof Edge) && object != null) {
object = object.eContainer();
}
return comparison.getMatch(object);
}
/**
* It checks that the given reference change is leaded by an ADD or DELETE of the same object.
*
* @param input
* The reference change.
* @return True if it is leaded by an add or delete, False otherwise.
*/
private boolean isLeadedByAddOrDeleteEdge(ReferenceChange input) {
boolean result = false;
EObject addedOrDeletedObject = input.getValue();
EObject container = addedOrDeletedObject.eContainer();
while (!(container instanceof Edge) && container != null) {
container = container.eContainer();
}
if (container instanceof Edge) {
result = Iterators.any(input.getMatch().getComparison().getDifferences(container).iterator(),
isMainDiffForAddOrDeleteEdge());
}
return result;
}
/**
* It checks that the given difference is on a containment link to an Edge attached to a semantic object.
*
* @param input
* The difference.
* @return True if the difference matches with the predicate.
*/
private static boolean isContainmentOnSemanticEdge(ReferenceChange input) {
return input.getReference().isContainment() && input.getValue() instanceof Edge
&& ReferenceUtil.safeEGet(input.getValue(), NotationPackage.Literals.VIEW__ELEMENT) != null;
}
// FIXME: It could be a solution for:
// /emf.compare.q7.tests/tests/diagram/class/conflicts/changeEdge_deleteEdgeTarget/result/bugs.txt
// /**
// * It checks that the parent of the value of the given difference is a view attached to a semantic
// object.
// *
// * @param input
// * The difference.
// * @return True if the difference matches with the predicate.
// */
// private static boolean isContainerBasedOnSemanticEdge(ReferenceChange input) {
// EObject container = input.getValue().eContainer();
// return isBasedOnASemanticObject(container);
// }
//
// /**
// * It checks that the parent of the object containing the value of the given difference is a view
// attached
// * to a semantic object.
// *
// * @param input
// * The difference.
// * @return True if the difference matches with the predicate.
// */
// private static boolean isContainerBasedOnSemanticEdge(AttributeChange input) {
// EObject container = MatchUtil.getContainer(input.getMatch().getComparison(), input).eContainer();
// return isBasedOnASemanticObject(container);
// }
//
// private static boolean isBasedOnASemanticObject(EObject container) {
// return container instanceof View
// && ReferenceUtil.safeEGet(container, NotationPackage.Literals.VIEW__ELEMENT) != null;
// }
}