diff options
10 files changed, 184 insertions, 241 deletions
diff --git a/plugins/org.eclipse.emf.compare.diagram.papyrus.tests.git/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/AdditiveMergeDiagramTests.java b/plugins/org.eclipse.emf.compare.diagram.papyrus.tests.git/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/AdditiveMergeDiagramTests.java index c56a38df6..9e649ea2b 100644 --- a/plugins/org.eclipse.emf.compare.diagram.papyrus.tests.git/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/AdditiveMergeDiagramTests.java +++ b/plugins/org.eclipse.emf.compare.diagram.papyrus.tests.git/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/AdditiveMergeDiagramTests.java @@ -10,11 +10,14 @@ *******************************************************************************/ package org.eclipse.emf.compare.diagram.papyrus.tests.merge; +import static com.google.common.base.Predicates.not; +import static com.google.common.base.Predicates.or; import static com.google.common.collect.Iterables.all; import static org.eclipse.emf.compare.ConflictKind.PSEUDO; import static org.eclipse.emf.compare.ConflictKind.REAL; import static org.eclipse.emf.compare.DifferenceKind.MOVE; import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasDirectOrIndirectConflict; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.isInRealAddAddConflict; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -37,7 +40,6 @@ import org.eclipse.jgit.api.Status; import org.eclipse.jgit.lib.Repository; import org.junit.runner.RunWith; -import com.google.common.base.Predicates; import com.google.common.collect.Collections2; @RunWith(GitTestRunner.class) @@ -56,7 +58,8 @@ public class AdditiveMergeDiagramTests { Comparison comparison = support.compare("wave", "expected", "model.notation"); - assertTrue(all(comparison.getDifferences(), hasDirectOrIndirectConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } @GitMerge(local = "wired", remote = "wave") @@ -68,7 +71,8 @@ public class AdditiveMergeDiagramTests { Comparison comparison = support.compare("wired", "expected", "model.notation"); - assertTrue(all(comparison.getDifferences(), hasDirectOrIndirectConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } /** @@ -90,7 +94,7 @@ public class AdditiveMergeDiagramTests { // Let's just check that all diffs are in conflict Collection<Diff> diffs = Collections2.filter(comparison.getDifferences(), - Predicates.not(hasDirectOrIndirectConflict(PSEUDO, REAL))); + not(hasDirectOrIndirectConflict(PSEUDO, REAL))); assertEquals(2, diffs.size()); // Since we cannot be sure of the order of the merged element, this is possible that a side and the @@ -116,6 +120,7 @@ public class AdditiveMergeDiagramTests { // package on both sides and it's (currently) impossible to guarantee // the order in which they will be placed in their parent during a merge // Let's just check that all diffs are in conflict - assertTrue(all(comparison.getDifferences(), hasDirectOrIndirectConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } } diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests.git/src/org/eclipse/emf/compare/ide/ui/tests/merge/AdditiveMergeTests.java b/plugins/org.eclipse.emf.compare.ide.ui.tests.git/src/org/eclipse/emf/compare/ide/ui/tests/merge/AdditiveMergeTests.java index a5114bdef..28d1147b9 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests.git/src/org/eclipse/emf/compare/ide/ui/tests/merge/AdditiveMergeTests.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests.git/src/org/eclipse/emf/compare/ide/ui/tests/merge/AdditiveMergeTests.java @@ -8,8 +8,11 @@ *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.tests.merge; +import static com.google.common.base.Predicates.or; import static com.google.common.collect.Iterables.all; import static org.eclipse.emf.compare.ConflictKind.PSEUDO; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasDirectOrIndirectConflict; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.isInRealAddAddConflict; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -24,7 +27,6 @@ import org.eclipse.emf.compare.ide.ui.tests.git.framework.GitTestSupport; import org.eclipse.emf.compare.ide.ui.tests.git.framework.annotations.GitInput; import org.eclipse.emf.compare.ide.ui.tests.git.framework.annotations.GitMerge; import org.eclipse.emf.compare.ide.ui.tests.git.framework.annotations.GitMergeStrategy; -import org.eclipse.emf.compare.utils.EMFComparePredicates; import org.eclipse.jgit.api.Status; import org.eclipse.jgit.lib.Repository; import org.junit.runner.RunWith; @@ -43,7 +45,8 @@ public class AdditiveMergeTests { Comparison comparison = support.compare("branch1", "expected", "network.ecore"); - assertTrue(all(comparison.getDifferences(), EMFComparePredicates.hasConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } @GitMerge(local = "branch2", remote = "branch1") @@ -55,7 +58,8 @@ public class AdditiveMergeTests { Comparison comparison = support.compare("branch2", "expected", "network.ecore"); - assertTrue(all(comparison.getDifferences(), EMFComparePredicates.hasConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } @GitMerge(local = "branch1", remote = "branch2") @@ -67,7 +71,8 @@ public class AdditiveMergeTests { Comparison comparison = support.compare("branch1", "expected", "network.uml"); - assertTrue(all(comparison.getDifferences(), EMFComparePredicates.hasConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } @GitMerge(local = "branch2", remote = "branch1") @@ -79,6 +84,7 @@ public class AdditiveMergeTests { Comparison comparison = support.compare("branch2", "expected", "network.uml"); - assertTrue(all(comparison.getDifferences(), EMFComparePredicates.hasConflict(PSEUDO))); + assertTrue(all(comparison.getDifferences(), + or(hasDirectOrIndirectConflict(PSEUDO), isInRealAddAddConflict()))); } } diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/conflict/DefaultConflictDetector.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/conflict/DefaultConflictDetector.java index 98ad60f4a..38c2b6293 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/conflict/DefaultConflictDetector.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/conflict/DefaultConflictDetector.java @@ -12,14 +12,11 @@ *******************************************************************************/ package org.eclipse.emf.compare.conflict; -import static com.google.common.base.Predicates.and; import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isAddOrSetDiff; import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isDeleteOrUnsetDiff; import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isFeatureMapContainment; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; import static org.eclipse.emf.compare.utils.EMFComparePredicates.possiblyConflictingWith; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueIs; +import static org.eclipse.emf.compare.utils.MatchUtil.matchingIndices; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; @@ -280,7 +277,7 @@ public class DefaultConflictDetector implements IConflictDetector { } else if (diff.getMatch() == candidate.getMatch() && diff.getReference() == candidate.getReference()) { // Same value added in the same container/reference couple - if (!diffIsDelete && !candidateIsDelete && matchingIndices(comparison, diff.getMatch(), + if (!diffIsDelete && !candidateIsDelete && matchingIndices(diff.getMatch(), diff.getReference(), diff.getValue(), candidate.getValue())) { kind = ConflictKind.PSEUDO; } @@ -381,9 +378,8 @@ public class DefaultConflictDetector implements IConflictDetector { } else if (diff.getMatch() == candidate.getMatch() && diff.getAttribute() == candidate.getAttribute()) { // Same value added in the same container/reference couple with the same key - if (!diffIsDelete - && !candidateIsDelete && matchingIndices(comparison, diff.getMatch(), - diff.getAttribute(), diff.getValue(), candidate.getValue()) + if (!diffIsDelete && !candidateIsDelete && matchingIndices(diff.getMatch(), + diff.getAttribute(), diff.getValue(), candidate.getValue()) && haveSameKey(diff, candidate)) { kind = ConflictKind.PSEUDO; } @@ -673,7 +669,7 @@ public class DefaultConflictDetector implements IConflictDetector { if (diff.getMatch() == candidate.getMatch() && comparison.getEqualityHelper().matchingValues(changedValue, candidateValue)) { // Same value moved in both side of the same container - if (matchingIndices(comparison, diff.getMatch(), feature, changedValue, candidateValue)) { + if (matchingIndices(diff.getMatch(), feature, changedValue, candidateValue)) { conflictOn(comparison, diff, candidate, ConflictKind.PSEUDO); } else { conflictOn(comparison, diff, candidate, ConflictKind.REAL); @@ -839,8 +835,7 @@ public class DefaultConflictDetector implements IConflictDetector { // same value can appear twice. conflictOn(comparison, diff, candidate, ConflictKind.REAL); } - } else if (matchingIndices(comparison, diff.getMatch(), feature, addedValue, - candidateValue)) { + } else if (matchingIndices(diff.getMatch(), feature, addedValue, candidateValue)) { conflictOn(comparison, diff, candidate, ConflictKind.PSEUDO); } else { conflictOn(comparison, diff, candidate, ConflictKind.REAL); @@ -1044,114 +1039,6 @@ public class DefaultConflictDetector implements IConflictDetector { } /** - * This will be used whenever we check for conflictual MOVEs in order to determine whether we have a - * pseudo conflict or a real conflict. - * <p> - * Namely, this will retrieve the value of the given {@code feature} on the right and left sides of the - * given {@code match}, then check whether the two given values are on the same index. - * </p> - * <p> - * Note that no sanity checks will be made on either the match's sides or the feature. - * </p> - * - * @param comparison - * Provides us with the necessary information to match EObjects. - * @param match - * Match for which we need to check a feature. - * @param feature - * The feature which values we need to check. - * @param value1 - * First of the two values which index we are to compare. - * @param value2 - * Second of the two values which index we are to compare. - * @return {@code true} if the two given values are located at the same index in the given feature's - * values list, {@code false} otherwise. - */ - @SuppressWarnings("unchecked") - private boolean matchingIndices(Comparison comparison, Match match, EStructuralFeature feature, - Object value1, Object value2) { - boolean matching = false; - if (feature.isMany()) { - final List<Object> leftValues = (List<Object>)ReferenceUtil.safeEGet(match.getLeft(), feature); - final List<Object> rightValues = (List<Object>)ReferenceUtil.safeEGet(match.getRight(), feature); - - // FIXME the detection _will_ fail for non-unique lists with multiple identical values... - int leftIndex = -1; - int rightIndex = -1; - for (int i = 0; i < leftValues.size(); i++) { - final Object left = leftValues.get(i); - if (comparison.getEqualityHelper().matchingValues(left, value1)) { - break; - } else if (hasDiff(match, feature, left) || hasDeleteDiff(match, feature, left)) { - // Do not increment. - } else { - leftIndex++; - } - } - for (int i = 0; i < rightValues.size(); i++) { - final Object right = rightValues.get(i); - if (comparison.getEqualityHelper().matchingValues(right, value2)) { - break; - } else if (hasDiff(match, feature, right) || hasDeleteDiff(match, feature, right)) { - // Do not increment. - } else { - rightIndex++; - } - } - matching = leftIndex == rightIndex; - } else { - matching = true; - } - return matching; - } - - /** - * Checks whether the given {@code match} presents a difference of any kind on the given {@code feature}'s - * {@code value}. - * - * @param match - * The match which differences we'll check. - * @param feature - * The feature on which we expect a difference. - * @param value - * The value we expect to have changed inside {@code feature}. - * @return <code>true</code> if there is such a Diff on {@code match}, <code>false</code> otherwise. - */ - private boolean hasDiff(Match match, EStructuralFeature feature, Object value) { - return Iterables.any(match.getDifferences(), and(onFeature(feature.getName()), valueIs(value))); - } - - /** - * Checks whether the given {@code value} has been deleted from the given {@code feature} of {@code match} - * . - * - * @param match - * The match which differences we'll check. - * @param feature - * The feature on which we expect a difference. - * @param value - * The value we expect to have been removed from {@code feature}. - * @return <code>true</code> if there is such a Diff on {@code match}, <code>false</code> otherwise. - */ - @SuppressWarnings("unchecked") - private boolean hasDeleteDiff(Match match, EStructuralFeature feature, Object value) { - final Comparison comparison = match.getComparison(); - final Object expectedValue; - if (value instanceof EObject && comparison.isThreeWay()) { - final Match valueMatch = comparison.getMatch((EObject)value); - if (valueMatch != null) { - expectedValue = valueMatch.getOrigin(); - } else { - expectedValue = value; - } - } else { - expectedValue = value; - } - return Iterables.any(match.getDifferences(), - and(onFeature(feature.getName()), valueIs(expectedValue), ofKind(DifferenceKind.DELETE))); - } - - /** * This will be called whenever we detect a new conflict in order to create (or update) the actual * association. * diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AbstractConflictSearch.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AbstractConflictSearch.java index 9eda013a4..4adecb1b2 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AbstractConflictSearch.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AbstractConflictSearch.java @@ -13,16 +13,10 @@ package org.eclipse.emf.compare.internal.conflict; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Predicates.and; import static org.eclipse.emf.compare.ConflictKind.PSEUDO; import static org.eclipse.emf.compare.ConflictKind.REAL; -import static org.eclipse.emf.compare.DifferenceKind.DELETE; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueIs; import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.util.List; @@ -47,7 +41,6 @@ import org.eclipse.emf.compare.internal.ThreeWayTextDiff; import org.eclipse.emf.compare.utils.ReferenceUtil; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; @@ -233,111 +226,6 @@ public abstract class AbstractConflictSearch<T extends Diff> { } /** - * This will be used whenever we check for conflictual MOVEs in order to determine whether we have a - * pseudo conflict or a real conflict. - * <p> - * Namely, this will retrieve the value of the given {@code feature} on the right and left sides of the - * given {@code match}, then check whether the two given values are on the same index. - * </p> - * <p> - * Note that no sanity checks will be made on either the match's sides or the feature. - * </p> - * - * @param match - * Match for which we need to check a feature. - * @param feature - * The feature which values we need to check. - * @param value1 - * First of the two values which index we are to compare. - * @param value2 - * Second of the two values which index we are to compare. - * @return {@code true} if the two given values are located at the same index in the given feature's - * values list, {@code false} otherwise. - */ - protected boolean matchingIndices(Match match, EStructuralFeature feature, Object value1, Object value2) { - boolean matching = false; - if (feature.isMany()) { - @SuppressWarnings("unchecked") - final List<Object> leftValues = (List<Object>)ReferenceUtil.safeEGet(match.getLeft(), feature); - @SuppressWarnings("unchecked") - final List<Object> rightValues = (List<Object>)ReferenceUtil.safeEGet(match.getRight(), feature); - - // FIXME the detection _will_ fail for non-unique lists with multiple identical values... - int leftIndex = -1; - int rightIndex = -1; - for (int i = 0; i < leftValues.size(); i++) { - final Object left = leftValues.get(i); - if (comparison.getEqualityHelper().matchingValues(left, value1)) { - break; - } else if (hasDiff(match, feature, left) || hasDeleteDiff(match, feature, left)) { - // Do not increment. - } else { - leftIndex++; - } - } - for (int i = 0; i < rightValues.size(); i++) { - final Object right = rightValues.get(i); - if (comparison.getEqualityHelper().matchingValues(right, value2)) { - break; - } else if (hasDiff(match, feature, right) || hasDeleteDiff(match, feature, right)) { - // Do not increment. - } else { - rightIndex++; - } - } - matching = leftIndex == rightIndex; - } else { - matching = true; - } - return matching; - } - - /** - * Checks whether the given {@code match} presents a difference of any kind on the given {@code feature}'s - * {@code value}. - * - * @param match - * The match which differences we'll check. - * @param feature - * The feature on which we expect a difference. - * @param value - * The value we expect to have changed inside {@code feature}. - * @return <code>true</code> if there is such a Diff on {@code match}, <code>false</code> otherwise. - */ - protected boolean hasDiff(Match match, EStructuralFeature feature, Object value) { - return Iterables.any(match.getDifferences(), and(onFeature(feature.getName()), valueIs(value))); - } - - /** - * Checks whether the given {@code value} has been deleted from the given {@code feature} of {@code match} - * . - * - * @param match - * The match which differences we'll check. - * @param feature - * The feature on which we expect a difference. - * @param value - * The value we expect to have been removed from {@code feature}. - * @return <code>true</code> if there is such a Diff on {@code match}, <code>false</code> otherwise. - */ - protected boolean hasDeleteDiff(Match match, EStructuralFeature feature, Object value) { - checkArgument(match.getComparison() == comparison); - final Object expectedValue; - if (value instanceof EObject && comparison.isThreeWay()) { - final Match valueMatch = comparison.getMatch((EObject)value); - if (valueMatch != null) { - expectedValue = valueMatch.getOrigin(); - } else { - expectedValue = value; - } - } else { - expectedValue = value; - } - return Iterables.any(match.getDifferences(), - and(onFeature(feature.getName()), valueIs(expectedValue), ofKind(DELETE))); - } - - /** * This will be called whenever we detect a new conflict in order to create (or update) the actual * association. * diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AttributeChangeConflictSearch.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AttributeChangeConflictSearch.java index 30270e033..ba39f3142 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AttributeChangeConflictSearch.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AttributeChangeConflictSearch.java @@ -21,6 +21,7 @@ import static org.eclipse.emf.compare.DifferenceKind.MOVE; import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; import static org.eclipse.emf.compare.utils.EMFComparePredicates.possiblyConflictingWith; +import static org.eclipse.emf.compare.utils.MatchUtil.matchingIndices; import com.google.common.collect.Iterables; diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/ContainmentRefChangeConflictSearch.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/ContainmentRefChangeConflictSearch.java index ddaa96e2b..30d8c7523 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/ContainmentRefChangeConflictSearch.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/ContainmentRefChangeConflictSearch.java @@ -23,6 +23,7 @@ import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; import static org.eclipse.emf.compare.utils.EMFComparePredicates.possiblyConflictingWith; import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueMatches; +import static org.eclipse.emf.compare.utils.MatchUtil.matchingIndices; import com.google.common.collect.Iterables; diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/FeatureMapChangeConflictSearch.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/FeatureMapChangeConflictSearch.java index c3bb489f9..b42c2e5b0 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/FeatureMapChangeConflictSearch.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/FeatureMapChangeConflictSearch.java @@ -22,6 +22,7 @@ import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; import static org.eclipse.emf.compare.utils.EMFComparePredicates.possiblyConflictingWith; import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueMatches; +import static org.eclipse.emf.compare.utils.MatchUtil.matchingIndices; import com.google.common.collect.Iterables; diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/NonContainmentRefChangeConflictSearch.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/NonContainmentRefChangeConflictSearch.java index 185b618ac..0a220f043 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/NonContainmentRefChangeConflictSearch.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/NonContainmentRefChangeConflictSearch.java @@ -21,6 +21,7 @@ import static org.eclipse.emf.compare.DifferenceKind.MOVE; import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; import static org.eclipse.emf.compare.utils.EMFComparePredicates.possiblyConflictingWith; +import static org.eclipse.emf.compare.utils.MatchUtil.matchingIndices; import com.google.common.collect.Iterables; diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java index d005ebfb6..1c480d426 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java @@ -19,6 +19,8 @@ import static com.google.common.base.Predicates.not; import static com.google.common.base.Predicates.or; import static com.google.common.collect.Iterables.all; import static com.google.common.collect.Iterables.any; +import static org.eclipse.emf.compare.ConflictKind.REAL; +import static org.eclipse.emf.compare.DifferenceKind.ADD; import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isDeleteOrUnsetDiff; import static org.eclipse.emf.compare.internal.utils.DiffUtil.getAllAtomicRefiningDiffs; import static org.eclipse.emf.compare.internal.utils.DiffUtil.getAllRefiningDiffs; @@ -350,6 +352,30 @@ public final class EMFComparePredicates { } /** + * Indicates whether a diff is part of a real add/add conflict. + * + * @return a predicate to check if a diff belongs to an add/add conflict. + * @since 3.4 + */ + public static Predicate<Diff> isInRealAddAddConflict() { + return new Predicate<Diff>() { + public boolean apply(Diff input) { + Conflict conflict = input.getConflict(); + if (conflict != null) { + if (conflict.getKind() != REAL) { + return false; + } else { + if (all(conflict.getDifferences(), ofKind(ADD))) { + return true; + } + } + } + return false; + } + }; + } + + /** * This predicate can be used to check whether a given Diff represents the deletion of a value from a * multi-valued reference going by {@code referenceName} on an EObject which name matches * {@code qualifiedName}. diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/MatchUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/MatchUtil.java index c50133d1c..d8a526b16 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/MatchUtil.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/MatchUtil.java @@ -14,12 +14,18 @@ package org.eclipse.emf.compare.utils; import static com.google.common.base.Predicates.and; import static org.eclipse.emf.compare.DifferenceKind.ADD; import static org.eclipse.emf.compare.DifferenceKind.DELETE; +import static org.eclipse.emf.compare.DifferenceSource.LEFT; +import static org.eclipse.emf.compare.DifferenceSource.RIGHT; import static org.eclipse.emf.compare.utils.EMFComparePredicates.CONTAINMENT_REFERENCE_CHANGE; import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.onFeature; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.valueIs; import static org.eclipse.emf.compare.utils.ReferenceUtil.getAsList; import com.google.common.collect.Iterables; +import java.util.List; + import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.AttributeChange; import org.eclipse.emf.compare.Comparison; @@ -74,6 +80,127 @@ public final class MatchUtil { } /** + * This will be used whenever we check for conflictual MOVEs in order to determine whether we have a + * pseudo conflict or a real conflict. + * <p> + * Namely, this will retrieve the value of the given {@code feature} on the right and left sides of the + * given {@code match}, then check whether the two given values are on the same index. + * </p> + * <p> + * Note that no sanity checks will be made on either the match's sides or the feature. + * </p> + * + * @param match + * Match for which we need to check a feature. + * @param feature + * The feature which values we need to check. + * @param value1 + * First of the two values which index we are to compare. + * @param value2 + * Second of the two values which index we are to compare. + * @return {@code true} if the two given values are located at the same index in the given feature's + * values list, {@code false} otherwise. + * @since 3.4 + */ + public static boolean matchingIndices(Match match, EStructuralFeature feature, Object value1, + Object value2) { + boolean matching = false; + if (feature.isMany()) { + // FIXME the detection _will_ fail for non-unique lists with multiple identical values... + int leftIndex = computeIndex(match, feature, value1, LEFT); + int rightIndex = computeIndex(match, feature, value2, RIGHT); + matching = leftIndex == rightIndex; + } else { + matching = true; + } + return matching; + } + + /** + * Compute the index of an object in the list of elements of a given match+feature on a given side. This + * index is computed without taking objects that have a diff into account, except if this diff is an ADD. + * + * @param match + * The match + * @param feature + * The structural feature + * @param value + * The object the index of which must be computed + * @param side + * The side on which to compute the index + * @return The index of the given object. + * @since 3.4 + */ + public static int computeIndex(Match match, EStructuralFeature feature, Object value, + DifferenceSource side) { + Comparison comparison = match.getComparison(); + int result = -1; + @SuppressWarnings("unchecked") + final List<Object> sideValues = (List<Object>)ReferenceUtil + .safeEGet(MatchUtil.getMatchedObject(match, side), feature); + for (int i = 0; i < sideValues.size(); i++) { + final Object sideObject = sideValues.get(i); + if (comparison.getEqualityHelper().matchingValues(sideObject, value)) { + break; + } else if ((hasDiff(match, feature, sideObject) && match.getOrigin() != null) + || hasDeleteDiff(match, feature, sideObject)) { + // Do not increment. + } else { + result++; + } + } + return result; + } + + /** + * Checks whether the given {@code value} has been deleted from the given {@code feature} of {@code match} + * . + * + * @param match + * The match which differences we'll check. + * @param feature + * The feature on which we expect a difference. + * @param value + * The value we expect to have been removed from {@code feature}. + * @return <code>true</code> if there is such a Diff on {@code match}, <code>false</code> otherwise. + * @since 3.4 + */ + @SuppressWarnings("unchecked") + public static boolean hasDeleteDiff(Match match, EStructuralFeature feature, Object value) { + Comparison comparison = match.getComparison(); + final Object expectedValue; + if (value instanceof EObject && comparison.isThreeWay()) { + final Match valueMatch = comparison.getMatch((EObject)value); + if (valueMatch != null) { + expectedValue = valueMatch.getOrigin(); + } else { + expectedValue = value; + } + } else { + expectedValue = value; + } + return Iterables.any(match.getDifferences(), + and(onFeature(feature.getName()), valueIs(expectedValue), ofKind(DELETE))); + } + + /** + * Checks whether the given {@code match} presents a difference of any kind on the given {@code feature}'s + * {@code value}. + * + * @param match + * The match which differences we'll check. + * @param feature + * The feature on which we expect a difference. + * @param value + * The value we expect to have changed inside {@code feature}. + * @return <code>true</code> if there is such a Diff on {@code match}, <code>false</code> otherwise. + * @since 3.4 + */ + public static boolean hasDiff(Match match, EStructuralFeature feature, Object value) { + return Iterables.any(match.getDifferences(), and(onFeature(feature.getName()), valueIs(value))); + } + + /** * From a given mono-valued reference change, get the origin value. * * @param comparison |