| /******************************************************************************* |
| * Copyright (c) 2014 EclipseSource Muenchen GmbH and others. |
| * 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: |
| * Philip Langer - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.uml2.internal.postprocessor; |
| |
| import static com.google.common.collect.Iterables.filter; |
| |
| import com.google.common.base.Predicate; |
| |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.Monitor; |
| import org.eclipse.emf.compare.CompareFactory; |
| import org.eclipse.emf.compare.Comparison; |
| import org.eclipse.emf.compare.Conflict; |
| import org.eclipse.emf.compare.ConflictKind; |
| import org.eclipse.emf.compare.Diff; |
| import org.eclipse.emf.compare.DifferenceKind; |
| import org.eclipse.emf.compare.DifferenceSource; |
| import org.eclipse.emf.compare.Match; |
| import org.eclipse.emf.compare.conflict.DefaultConflictDetector; |
| import org.eclipse.emf.compare.postprocessor.IPostProcessor; |
| import org.eclipse.emf.compare.uml2.internal.OpaqueElementBodyChange; |
| import org.eclipse.emf.compare.uml2.internal.postprocessor.util.UMLCompareUtil; |
| import org.eclipse.emf.ecore.EObject; |
| |
| /** |
| * This post-processor or IConflictDetector adds conflicts specific to opaque element body changes. |
| * |
| * @author Philip Langer <planger@eclipsesource.com> |
| */ |
| public class OpaqueElementBodyChangePostProcessor extends DefaultConflictDetector implements IPostProcessor { |
| |
| /** |
| * A predicate for {@link Diff} that can be used to check whether the {@link Diff} is a |
| * {@link OpaqueElementBodyChange}. |
| */ |
| private static final Predicate<Diff> IS_OPAQUE_ELEMENT_CHANGE = new Predicate<Diff>() { |
| public boolean apply(Diff diff) { |
| return diff instanceof OpaqueElementBodyChange; |
| } |
| }; |
| |
| /** |
| * A predicate for {@link Diff} that can be used to check whether the {@link Diff} is a |
| * {@link OpaqueElementBodyChange} and its {@link DifferenceSource} is LEFT. |
| */ |
| private static final Predicate<Diff> IS_LEFT_OPAQUE_ELEMENT_CHANGE = new Predicate<Diff>() { |
| public boolean apply(Diff diff) { |
| return DifferenceSource.LEFT.equals(diff.getSource()) && IS_OPAQUE_ELEMENT_CHANGE.apply(diff); |
| } |
| }; |
| |
| /** |
| * A predicate for {@link Diff} that can be used to check whether the {@link Diff} is a |
| * {@link OpaqueElementBodyChange} and its {@link DifferenceSource} is RIGHT. |
| */ |
| private static final Predicate<Diff> IS_RIGHT_OPAQUE_ELEMENT_CHANGE = new Predicate<Diff>() { |
| public boolean apply(Diff diff) { |
| return DifferenceSource.RIGHT.equals(diff.getSource()) && IS_OPAQUE_ELEMENT_CHANGE.apply(diff); |
| } |
| }; |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPostProcessor#postMatch(Comparison, Monitor) |
| */ |
| public void postMatch(Comparison comparison, Monitor monitor) { |
| // nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPostProcessor#postDiff(Comparison, Monitor) |
| */ |
| public void postDiff(Comparison comparison, Monitor monitor) { |
| // nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPostProcessor#postRequirements(Comparison, Monitor) |
| */ |
| public void postRequirements(Comparison comparison, Monitor monitor) { |
| // nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPostProcessor#postEquivalences(Comparison, Monitor) |
| */ |
| public void postEquivalences(Comparison comparison, Monitor monitor) { |
| // nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPostProcessor#postConflicts(Comparison, Monitor) |
| */ |
| public void postConflicts(Comparison comparison, Monitor monitor) { |
| // nothing to do |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see IPostProcessor#postComparison(Comparison, Monitor) |
| */ |
| public void postComparison(Comparison comparison, Monitor monitor) { |
| detect(comparison, monitor); |
| } |
| |
| /** |
| * Detects and adds conflicts related to changes of the language and bodies of opaque actions, behaviors, |
| * and expressions. |
| * |
| * @param comparison |
| * The comparison to check for conflicts. |
| * @param monitor |
| * The monitor to use for progress reporting. |
| */ |
| @Override |
| public void detect(Comparison comparison, Monitor monitor) { |
| final EList<Diff> diffs = comparison.getDifferences(); |
| final Iterable<Diff> leftBodyChanges = filter(diffs, IS_LEFT_OPAQUE_ELEMENT_CHANGE); |
| for (Diff leftDiff : leftBodyChanges) { |
| final OpaqueElementBodyChange leftBodyChange = (OpaqueElementBodyChange)leftDiff; |
| final EObject leftOpaqueElement = leftBodyChange.getDiscriminant(); |
| final Match opaqueElementMatch = comparison.getMatch(leftOpaqueElement); |
| final EList<Diff> rightDiffs = comparison.getDifferences(opaqueElementMatch.getRight()); |
| final Iterable<Diff> rightBodyChanges = filter(rightDiffs, IS_RIGHT_OPAQUE_ELEMENT_CHANGE); |
| for (Diff rightDiff : rightBodyChanges) { |
| OpaqueElementBodyChange rightBodyChange = (OpaqueElementBodyChange)rightDiff; |
| if (isConflicting(leftBodyChange, rightBodyChange)) { |
| final Conflict conflict = createRealConflict(leftBodyChange, rightBodyChange); |
| comparison.getConflicts().add(conflict); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange1} is conflicting with the given {@code bodyChange2}. Note |
| * that the conflict relation among differences is symmetric, that means {@code bodyChange1} and |
| * {@code bodyChange2} can be switched and this method returns the same result. |
| * |
| * @param bodyChange1 |
| * The one {@code OpaqueElementBodyChange} to check. |
| * @param bodyChange2 |
| * The other {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if {@code bodyChange1} and {@code bodyChange2} are conflicting, |
| * <code>false</code> otherwise. |
| */ |
| private boolean isConflicting(OpaqueElementBodyChange bodyChange1, OpaqueElementBodyChange bodyChange2) { |
| boolean areConflicting = false; |
| if (concernSameLanguage(bodyChange1, bodyChange2)) { |
| if (areDifferenceKindChange(bodyChange1, bodyChange2)) { |
| areConflicting = isThreeWayTextConflict(bodyChange1); |
| } else if (areDifferenceKindChangeAndDelete(bodyChange1, bodyChange2)) { |
| areConflicting = true; |
| } else if (areDifferenceKindAdd(bodyChange1, bodyChange2)) { |
| areConflicting = true; |
| } else if (areDifferenceKindMove(bodyChange1, bodyChange2)) { |
| areConflicting = true; |
| } |
| } |
| return areConflicting; |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange1} and {@code bodyChange2} concern the same language |
| * value. |
| * |
| * @param bodyChange1 |
| * The one {@code OpaqueElementBodyChange} to check. |
| * @param bodyChange2 |
| * The other {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if {@code bodyChange1} and {@code bodyChange2} concern the same language |
| * value, <code>false</code> otherwise. |
| */ |
| private boolean concernSameLanguage(OpaqueElementBodyChange bodyChange1, |
| OpaqueElementBodyChange bodyChange2) { |
| return bodyChange1.getLanguage().equals(bodyChange2.getLanguage()); |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange1} and {@code bodyChange2} both are of |
| * {@link DifferenceKind} CHANGE. |
| * |
| * @param bodyChange1 |
| * The one {@code OpaqueElementBodyChange} to check. |
| * @param bodyChange2 |
| * The other {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if {@code bodyChange1} and {@code bodyChange2} both are of |
| * {@link DifferenceKind} CHANGE, <code>false</code> otherwise. |
| */ |
| private boolean areDifferenceKindChange(OpaqueElementBodyChange bodyChange1, |
| OpaqueElementBodyChange bodyChange2) { |
| return DifferenceKind.CHANGE.equals(bodyChange1.getKind()) |
| && DifferenceKind.CHANGE.equals(bodyChange2.getKind()); |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange1} and {@code bodyChange2} both are of |
| * {@link DifferenceKind} ADD. |
| * |
| * @param bodyChange1 |
| * The one {@code OpaqueElementBodyChange} to check. |
| * @param bodyChange2 |
| * The other {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if {@code bodyChange1} and {@code bodyChange2} both are of |
| * {@link DifferenceKind} ADD, <code>false</code> otherwise. |
| */ |
| private boolean areDifferenceKindAdd(OpaqueElementBodyChange bodyChange1, |
| OpaqueElementBodyChange bodyChange2) { |
| return DifferenceKind.ADD.equals(bodyChange1.getKind()) |
| && DifferenceKind.ADD.equals(bodyChange2.getKind()); |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange1} is of {@link DifferenceKind} CHANGE and |
| * {@code bodyChange2} of {@link DifferenceKind} DELETE or vice versa. |
| * |
| * @param bodyChange1 |
| * The one {@code OpaqueElementBodyChange} to check. |
| * @param bodyChange2 |
| * The other {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if {@code bodyChange1} and {@code bodyChange2} are of {@link DifferenceKind} |
| * CHANGE and {@link DifferenceKind} DELETE or vice versa. |
| */ |
| private boolean areDifferenceKindChangeAndDelete(OpaqueElementBodyChange bodyChange1, |
| OpaqueElementBodyChange bodyChange2) { |
| return (DifferenceKind.CHANGE.equals(bodyChange1.getKind()) |
| && DifferenceKind.DELETE.equals(bodyChange2.getKind())) |
| || (DifferenceKind.DELETE.equals(bodyChange1.getKind()) |
| && DifferenceKind.CHANGE.equals(bodyChange2.getKind())); |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange1} and {@code bodyChange2} both are of |
| * {@link DifferenceKind} MOVE. |
| * |
| * @param bodyChange1 |
| * The one {@code OpaqueElementBodyChange} to check. |
| * @param bodyChange2 |
| * The other {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if {@code bodyChange1} and {@code bodyChange2} both are of |
| * {@link DifferenceKind} MOVE, <code>false</code> otherwise. |
| */ |
| private boolean areDifferenceKindMove(OpaqueElementBodyChange bodyChange1, |
| OpaqueElementBodyChange bodyChange2) { |
| return DifferenceKind.MOVE.equals(bodyChange1.getKind()) |
| && DifferenceKind.MOVE.equals(bodyChange2.getKind()); |
| } |
| |
| /** |
| * Specifies whether the given {@code bodyChange} is a non-mergeable text change. |
| * <p> |
| * Changes are non-mergeable if they cannot be merged with opposite changes using a line-based three-way |
| * merge algorithm. |
| * </p> |
| * |
| * @param bodyChange |
| * The {@code OpaqueElementBodyChange} to check. |
| * @return <code>true</code> if the change is conflicting (i.e., non-mergeable), <code>false</code> |
| * otherwise. |
| */ |
| private boolean isThreeWayTextConflict(OpaqueElementBodyChange bodyChange) { |
| if (bodyChange.getMatch().getOrigin() == null) { |
| return false; |
| } |
| |
| final Match match = bodyChange.getMatch(); |
| final EObject originContainer = match.getOrigin(); |
| final EObject leftContainer = match.getLeft(); |
| final EObject rightContainer = match.getRight(); |
| |
| final String language = bodyChange.getLanguage(); |
| final String originBody = UMLCompareUtil.getOpaqueElementBody(originContainer, language); |
| final String leftBody = UMLCompareUtil.getOpaqueElementBody(leftContainer, language); |
| final String rightBody = UMLCompareUtil.getOpaqueElementBody(rightContainer, language); |
| |
| return !isMergeableText(leftBody, rightBody, originBody); |
| } |
| |
| /** |
| * Creates and returns a {@link Conflict} for the two given {@link OpaqueElementBodyChange changes}, |
| * {@code bodyChange1} and {@code bodyChange2}. |
| * |
| * @param bodyChange1 |
| * The one {@link OpaqueElementBodyChange} to create a conflict for. |
| * @param bodyChange2 |
| * The other {@link OpaqueElementBodyChange} to create a conflict for. |
| * @return The created conflict. |
| */ |
| private Conflict createRealConflict(final OpaqueElementBodyChange bodyChange1, |
| OpaqueElementBodyChange bodyChange2) { |
| Conflict conflict = CompareFactory.eINSTANCE.createConflict(); |
| conflict.setKind(ConflictKind.REAL); |
| conflict.getDifferences().add(bodyChange1); |
| conflict.getDifferences().add(bodyChange2); |
| return conflict; |
| } |
| |
| } |