/******************************************************************************* * 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; // } }