| /******************************************************************************* |
| * Copyright (c) 2016 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: |
| * Alexandra Buzila - initial API and implementation |
| * Philip Langer - bug 501864 |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.uml2.internal.postprocessor; |
| |
| import static com.google.common.base.Predicates.instanceOf; |
| import static com.google.common.collect.Iterables.all; |
| import static com.google.common.collect.Iterables.filter; |
| import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefined; |
| |
| import com.google.common.base.Optional; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableList.Builder; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Iterators; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.Monitor; |
| 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.Match; |
| import org.eclipse.emf.compare.ReferenceChange; |
| import org.eclipse.emf.compare.postprocessor.IPostProcessor; |
| import org.eclipse.emf.compare.uml2.internal.MultiplicityElementChange; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| |
| /** |
| * Post processor handling conflicts of {@link MultiplicityElementChange MultiplicityElementChanges}. |
| * |
| * @author Alexandra Buzila <abuzila@eclipsesource.com> |
| */ |
| public class MultiplicityElementChangePostProcessor implements IPostProcessor { |
| |
| /** {@inheritDoc} */ |
| public void postMatch(Comparison comparison, Monitor monitor) { |
| // do nothing |
| } |
| |
| /** {@inheritDoc} */ |
| public void postDiff(Comparison comparison, Monitor monitor) { |
| // do nothing |
| } |
| |
| /** {@inheritDoc} */ |
| public void postRequirements(Comparison comparison, Monitor monitor) { |
| // do nothing |
| } |
| |
| /** {@inheritDoc} */ |
| public void postEquivalences(Comparison comparison, Monitor monitor) { |
| // do nothing |
| } |
| |
| /** {@inheritDoc} */ |
| public void postConflicts(Comparison comparison, Monitor monitor) { |
| // do nothing |
| } |
| |
| /** {@inheritDoc} */ |
| public void postComparison(Comparison comparison, Monitor monitor) { |
| updateRequiresAndRefines(comparison); |
| verifyConflicts(comparison); |
| } |
| |
| /** |
| * Update the "refinedBy" and "requiredBy" relationships for the MultiplicityElementChanges. |
| * <p> |
| * If a MultiplicityElementChange is refined by a difference, <b>refiningDiff</b>, which in turn refines |
| * another diff, <b>refinedDiff</b>, then the refinement relationships will be updated such that the |
| * MultiplicityElementChange will refine the <b>refinedDiff</b>, instead of the <b>refiningDiff</b>. |
| * <p> |
| * This is done to avoid having diffs that refine multiple elements. Moreover, since any refined diffs of |
| * a refining diff are marked as required by the {@link UMLPostProcessor}, the requirement relationship |
| * needs to be updated accordingly. |
| * |
| * @param comparison |
| * the current comparison |
| */ |
| private void updateRequiresAndRefines(Comparison comparison) { |
| Iterator<Diff> multiplicityChanges = Iterators.filter(comparison.getDifferences().iterator(), |
| instanceOf(MultiplicityElementChange.class)); |
| while (multiplicityChanges.hasNext()) { |
| MultiplicityElementChange refChange = (MultiplicityElementChange)multiplicityChanges.next(); |
| for (Diff refiningDiff : refChange.getRefinedBy()) { |
| ArrayList<Diff> refinedChangesToUpdate = new ArrayList<Diff>(); |
| for (Diff refinedDiff : refiningDiff.getRefines()) { |
| if (refinedDiff != refChange) { |
| refinedChangesToUpdate.add(refinedDiff); |
| } |
| } |
| for (Diff refined : refinedChangesToUpdate) { |
| refined.getRefinedBy().remove(refiningDiff); |
| refined.getRefinedBy().add(refChange); |
| refined.getRequires().remove(refChange); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Verifies whether the {@link ConflictKind} of conflicts between MultiplicityChanges is correct. |
| * |
| * @param comparison |
| * the comparison containing the conflicts |
| */ |
| private void verifyConflicts(Comparison comparison) { |
| for (Conflict conflict : comparison.getConflicts()) { |
| if (all(conflict.getDifferences(), anyRefined(instanceOf(MultiplicityElementChange.class)))) { |
| final Iterable<Diff> leftDiffs = collectRefinedDiffs(conflict.getLeftDifferences(), |
| instanceOf(MultiplicityElementChange.class)); |
| for (Diff leftDiff : leftDiffs) { |
| final MultiplicityElementChange leftMultiplicityChange = (MultiplicityElementChange)leftDiff; |
| final Match match = leftMultiplicityChange.getMatch(); |
| final Iterable<Diff> rightDiffs = collectRefinedDiffs(conflict.getRightDifferences(), |
| instanceOf(MultiplicityElementChange.class)); |
| for (Diff rightDiff : rightDiffs) { |
| verifyConflict(match, leftMultiplicityChange, (MultiplicityElementChange)rightDiff); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Collects the refined differences that fulfill the given predicate from the given diffs. |
| * |
| * @param diffs |
| * The diffs to collect its refined differences from. |
| * @param predicate |
| * The predicate to be fulfilled. |
| * @return The list of refined differences fulfilling the predicate. |
| */ |
| private Iterable<Diff> collectRefinedDiffs(List<Diff> diffs, Predicate<Object> predicate) { |
| Builder<Diff> builder = ImmutableList.builder(); |
| for (Diff diff : diffs) { |
| builder.addAll(filter(diff.getRefines(), predicate)); |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Verifies whether the {@link ConflictKind} of conflicts between MultiplicityChanges is correct, for a |
| * given match. Specifically, it checks whether the type of all the diffs refining the multiplicity |
| * reference changes is correct and makes sure that if a change contains both PSEUDO and REAL conflicts, |
| * the conflict of the multiplicity change is marked as REAL. |
| * |
| * @param match |
| * the {@link Match} for the object the multiplicity changes refer to |
| * @param leftChange |
| * the left side change |
| * @param rightChange |
| * the right side change |
| */ |
| private void verifyConflict(Match match, MultiplicityElementChange leftChange, |
| MultiplicityElementChange rightChange) { |
| final Optional<ReferenceChange> leftReferenceChange = tryGetReferenceChange(leftChange); |
| final Optional<ReferenceChange> rightReferenceChange = tryGetReferenceChange(rightChange); |
| if (!leftReferenceChange.isPresent() || !rightReferenceChange.isPresent()) { |
| return; |
| } |
| |
| final EReference leftReference = leftReferenceChange.get().getReference(); |
| final EReference rightReference = rightReferenceChange.get().getReference(); |
| if (!leftReference.equals(rightReference)) { |
| return; |
| } |
| |
| final boolean sameValue = sameValue(leftReference, match.getLeft(), match.getRight()); |
| updateConflict(leftReferenceChange.get(), rightReferenceChange.get(), sameValue); |
| } |
| |
| /** |
| * Updates the conflict kind of the given references that refine MultiplicityElementChanges. |
| * |
| * @param diff |
| * the change from the left side |
| * @param diff2 |
| * the change from the right side |
| * @param sameValue |
| * specifies whether the conflicting references have the same value (pseudo conflict) or not |
| * (real conflict) |
| */ |
| private void updateConflict(Diff diff, Diff diff2, boolean sameValue) { |
| if (sameValue && diff.getConflict().getKind() != ConflictKind.PSEUDO) { |
| diff.getConflict().setKind(ConflictKind.PSEUDO); |
| } else if (!sameValue && (diff.getConflict().getKind() != ConflictKind.REAL |
| || diff2.getConflict().getKind() != ConflictKind.REAL)) { |
| Conflict conflict = diff.getConflict(); |
| conflict.setKind(ConflictKind.REAL); |
| // make sure the multiplicity changes' conflict is the real one |
| diff.setConflict(conflict); |
| diff2.setConflict(conflict); |
| } |
| } |
| |
| /** |
| * Returns the reference change that refines the given multiplicity element change. A multiplicity change |
| * should only have one refining diff, which is a reference change. |
| * |
| * @param change |
| * The {@link MultiplicityElementChange} to get its refining reference change. |
| * @return The refining reference change. |
| */ |
| private Optional<ReferenceChange> tryGetReferenceChange(MultiplicityElementChange change) { |
| final Iterable<ReferenceChange> refChanges = filter(change.getRefinedBy(), ReferenceChange.class); |
| return Optional.fromNullable(Iterables.getFirst(refChanges, null)); |
| } |
| |
| /** |
| * Checks whether the given EObjects have the same value for the specified {@link EStructuralFeature}. |
| * |
| * @param feature |
| * the {@link EStructuralFeature} to check |
| * @param object1 |
| * the first object |
| * @param object2 |
| * the second object |
| * @return true if the two given {@link EObject}s have the same value for the given |
| * {@link EStructuralFeature}. |
| */ |
| private boolean sameValue(EStructuralFeature feature, EObject object1, EObject object2) { |
| if (object1 == null || object2 == null) { |
| return object1 == object2; |
| } |
| Object value1 = object1.eGet(feature); |
| Object value2 = object2.eGet(feature); |
| if (value1 == null || value2 == null) { |
| return value1 == value2; |
| } |
| if (value1 instanceof EObject && value2 instanceof EObject) { |
| return EcoreUtil.equals((EObject)value1, (EObject)value2); |
| } |
| return value1.equals(value2); |
| } |
| |
| } |