/******************************************************************************* * Copyright (c) 2012, 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.rcp.ui.internal.util; import static com.google.common.collect.Iterables.any; import static org.eclipse.emf.compare.merge.AbstractMerger.isInTerminalState; import com.google.common.base.Predicate; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.AttributeChange; import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.FeatureMapChange; import org.eclipse.emf.compare.Match; import org.eclipse.emf.compare.MatchResource; import org.eclipse.emf.compare.ReferenceChange; import org.eclipse.emf.compare.ResourceAttachmentChange; import org.eclipse.emf.compare.internal.merge.IMergeData; import org.eclipse.emf.compare.internal.merge.MergeMode; import org.eclipse.emf.compare.internal.merge.MergeOperation; import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide; import org.eclipse.emf.compare.rcp.ui.mergeviewer.item.IMergeViewerItem; import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroupProvider; import org.eclipse.emf.compare.utils.IEqualityHelper; import org.eclipse.emf.compare.utils.ReferenceUtil; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.impl.MinimalEObjectImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.emf.edit.tree.TreeNode; /** * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a> */ public final class MergeViewerUtil { private MergeViewerUtil() { } public static List<?> getValues(Diff diff, MergeViewerSide side) { Match ownerMatch = diff.getMatch(); EObject eObject = getEObject(ownerMatch, side); EStructuralFeature affectedFeature = getAffectedFeature(diff); return ReferenceUtil.getAsList(eObject, affectedFeature); } public static List<?> getFeatureValues(Match match, EStructuralFeature feature, MergeViewerSide side) { final EObject eObject = getEObject(match, side); return ReferenceUtil.getAsList(eObject, feature); } public static EObject getEObject(Match match, MergeViewerSide side) { final EObject eObject; switch (side) { case ANCESTOR: eObject = match.getOrigin(); break; case LEFT: eObject = match.getLeft(); break; case RIGHT: eObject = match.getRight(); break; default: throw new IllegalStateException(); } return eObject; } public static EObject getBestSideEObject(Match match, MergeViewerSide side) { final MergeViewerSide[] sideLookupOrder; if (side == MergeViewerSide.ANCESTOR) { sideLookupOrder = new MergeViewerSide[] {MergeViewerSide.ANCESTOR, MergeViewerSide.LEFT, MergeViewerSide.RIGHT, }; } else { sideLookupOrder = new MergeViewerSide[] {side, side.opposite(), MergeViewerSide.ANCESTOR, }; } EObject objectOnSide = null; for (int i = 0; i < sideLookupOrder.length && objectOnSide == null; i++) { objectOnSide = MergeViewerUtil.getEObject(match, sideLookupOrder[i]); } return objectOnSide; } public static EStructuralFeature getAffectedFeature(Diff diff) { final EStructuralFeature feature; if (diff instanceof ReferenceChange) { feature = ((ReferenceChange)diff).getReference(); } else if (diff instanceof AttributeChange) { feature = ((AttributeChange)diff).getAttribute(); } else if (diff instanceof FeatureMapChange) { feature = ((FeatureMapChange)diff).getAttribute(); } else { feature = null; } return feature; } /** * Returns either {@link ReferenceChange#getValue()}, {@link AttributeChange#getValue()} or a * {@link FeatureMapChange#getValue()} depending on the runtime type of the give {@code diff} or null * otherwise. * * @param diff * @return */ public static Object getDiffValue(Diff diff) { final Object ret; if (diff instanceof ReferenceChange) { ret = ((ReferenceChange)diff).getValue(); } else if (diff instanceof AttributeChange) { ret = ((AttributeChange)diff).getValue(); } else if (diff instanceof FeatureMapChange) { Object entry = ((FeatureMapChange)diff).getValue(); if (entry instanceof FeatureMap.Entry) { ret = ((FeatureMap.Entry)entry).getValue(); } else { ret = null; } } else { ret = null; } return ret; } public static Object getValueFromDiff(final Diff diff, MergeViewerSide side) { Object diffValue = getDiffValue(diff); EStructuralFeature feature = getAffectedFeature(diff); Match ownerMatch = diff.getMatch(); Object ret = matchingValue(diffValue, ownerMatch, feature, side, ownerMatch.getComparison()); return ret; } private static Object matchingValue(Object object, Match ownerMatch, EStructuralFeature feature, MergeViewerSide side, Comparison comparison) { final Object ret; if (object instanceof EObject) { final Match matchOfValue = comparison.getMatch((EObject)object); if (matchOfValue != null) { ret = getEObject(matchOfValue, side); } else { ret = matchingValue(object, getFeatureValues(ownerMatch, feature, side), comparison); } } else { ret = matchingValue(object, getFeatureValues(ownerMatch, feature, side), comparison); } return ret; } private static Object matchingValue(Object value, List<?> in, Comparison comparison) { Object ret = null; IEqualityHelper equalityHelper = comparison.getEqualityHelper(); Iterator<?> valuesIterator = in.iterator(); while (valuesIterator.hasNext() && ret == null) { Object object = valuesIterator.next(); if (equalityHelper.matchingValues(object, value)) { ret = object; } } return ret; } /** * Returns the current resource on the given side of the given comparison. * * @param comparison * The given comparison. * @param side * The given side. * @param diff * The given diff (a {@link ResourceAttachmentChange}. * @return The current resource on the given side of the given comparison. */ public static Resource getResource(Comparison comparison, MergeViewerSide side, Diff diff) { if (!(diff instanceof ResourceAttachmentChange)) { return null; } Resource resource = getResourceViaMatch(side, (ResourceAttachmentChange)diff); if (resource == null) { resource = getResourceViaMatchResource(comparison, side, (ResourceAttachmentChange)diff); } return resource; } /** * Returns the current resource on the given side of the given {@link ResourceAttachmentChange}. The * resource is obtained via the match of the given {@link ResourceAttachmentChange}. * * @param side * The given side. * @param diff * The given {@link ResourceAttachmentChange}. * @return The current resource on the given side of the given comparison. */ private static Resource getResourceViaMatch(MergeViewerSide side, ResourceAttachmentChange diff) { Resource resource = null; final Match match = diff.getMatch(); switch (side) { case ANCESTOR: EObject origin = match.getOrigin(); if (origin instanceof MinimalEObjectImpl) { resource = ((MinimalEObjectImpl)origin).eDirectResource(); } break; case LEFT: EObject left = match.getLeft(); if (left instanceof MinimalEObjectImpl) { resource = ((MinimalEObjectImpl)left).eDirectResource(); } break; case RIGHT: EObject right = match.getRight(); if (right instanceof MinimalEObjectImpl) { resource = ((MinimalEObjectImpl)right).eDirectResource(); } break; default: throw new IllegalStateException(); } return resource; } /** * Returns the current resource on the given side of the given {@link ResourceAttachmentChange}. The * resource is obtained via the {@link MatchResource} that corresponds to the URI of the given * {@link ResourceAttachmentChange}. * * @param comparison * The given comparison. * @param side * The given side. * @param diff * The given {@link ResourceAttachmentChange}. * @return The current resource on the given side of the given comparison. */ private static Resource getResourceViaMatchResource(Comparison comparison, MergeViewerSide side, ResourceAttachmentChange diff) { Resource resource = null; Collection<MatchResource> matchResources = comparison.getMatchedResources(); String diffResourceURI = diff.getResourceURI(); for (MatchResource matchResource : matchResources) { switch (side) { case ANCESTOR: resource = matchResource.getOrigin(); break; case LEFT: resource = matchResource.getLeft(); break; case RIGHT: resource = matchResource.getRight(); break; default: throw new IllegalStateException(); } if (resource != null && diffResourceURI != null) { URI resourceURI = resource.getURI(); if (diffResourceURI.equals(resourceURI.toString())) { return resource; } else if (side == MergeViewerSide.RIGHT && (diffResourceURI.equals(matchResource.getLeftURI()) || diffResourceURI.equals(matchResource.getOriginURI()))) { return resource; } else if (side == MergeViewerSide.LEFT && (diffResourceURI.equals(matchResource.getRightURI()) || diffResourceURI.equals(matchResource.getOriginURI()))) { return resource; } else if (side == MergeViewerSide.ANCESTOR && (diffResourceURI.equals(matchResource.getLeftURI()) || diffResourceURI.equals(matchResource.getRightURI()))) { return resource; } } } return resource; } /** * Returns the contents of the current resource on the given side of the given comparison. * * @param comparison * The given comparison. * @param side * The given side. * @return The contents of the current resource on the given side of the given comparison. */ public static List<EObject> getResourceContents(Comparison comparison, MergeViewerSide side, Diff diff) { Resource resource = getResource(comparison, side, diff); if (resource != null) { return resource.getContents(); } return Collections.emptyList(); } /** * @param diff * @param side * @return */ public static Object getResourceAttachmentChangeValue(ResourceAttachmentChange diff, MergeViewerSide side) { final Object ret; Match match = diff.getMatch(); switch (side) { case ANCESTOR: ret = match.getOrigin(); break; case LEFT: ret = match.getLeft(); break; case RIGHT: ret = match.getRight(); break; default: throw new IllegalStateException(); } return ret; } public static Object getValueFromResourceAttachmentChange(final ResourceAttachmentChange diff, Comparison comparison, MergeViewerSide side) { Object diffValue = getResourceAttachmentChangeValue(diff, side); Object ret = matchingValue(diffValue, comparison, side); return ret; } public static Object matchingValue(Object object, Comparison comparison, MergeViewerSide side) { final Object ret; if (object instanceof EObject) { final Match matchOfValue = comparison.getMatch((EObject)object); if (matchOfValue != null) { switch (side) { case ANCESTOR: ret = matchOfValue.getOrigin(); break; case LEFT: ret = matchOfValue.getLeft(); break; case RIGHT: ret = matchOfValue.getRight(); break; default: throw new IllegalStateException(); } } else { ret = matchingValue(object, comparison, getResourceContents(comparison, side, (ResourceAttachmentChange)object)); } } else { ret = matchingValue(object, comparison, getResourceContents(comparison, side, null)); } return ret; } public static Object matchingValue(Object value, Comparison comparison, List<?> in) { Object ret = null; IEqualityHelper equalityHelper = comparison.getEqualityHelper(); Iterator<?> valuesIterator = in.iterator(); while (valuesIterator.hasNext() && ret == null) { Object object = valuesIterator.next(); if (equalityHelper.matchingValues(object, value)) { ret = object; } } return ret; } /** * Returns true if the given diff is displayed in an group as provided by the {@code groupProvider} and * not filtered by the given filter {@code predicate}. * * @param diff * the {@link Diff} to check. * @param groupProvider * the {@link IDifferenceGroupProvider}. * @param predicate * the filter {@link Predicate}. * @return {@code true} if the given {@code diff} is visible in the given {@code groupProvider}, * {@code false} otherwise. */ public static boolean isVisibleInMergeViewer(Diff diff, IDifferenceGroupProvider groupProvider, Predicate<? super EObject> predicate) { Iterable<TreeNode> nodes = groupProvider.getTreeNodes(diff); return any(nodes, predicate); } /** * Checks if the given diff is considered as a mark as merged diff. * * @see MergeOperation * @param diff * the given Diff. * @param item * the given IMergeViewerItem associated with the diff. * @param compareConfiguration * the compare configuration object to use with this viewer. * @return true, if the given diff is considered as a mark as merged diff, false otherwise. */ public static boolean isMarkAsMerged(Diff diff, IMergeViewerItem item, IEMFCompareConfiguration compareConfiguration) { final boolean markAsMerged; if (isInTerminalState(diff)) { IMergeData mergeData = (IMergeData)EcoreUtil.getExistingAdapter(diff, IMergeData.class); if (mergeData != null) { boolean leftEditable = compareConfiguration.isLeftEditable(); boolean rightEditable = compareConfiguration.isRightEditable(); MergeMode mergeMode = MergeMode.getMergeMode(diff, leftEditable, rightEditable); MergeOperation mergeAction = mergeMode.getMergeAction(diff, leftEditable, rightEditable); if (mergeAction == MergeOperation.MARK_AS_MERGE) { markAsMerged = true; } else { markAsMerged = false; } } else { markAsMerged = false; } } else { markAsMerged = false; } return markAsMerged; } }