Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Delaigue2016-10-14 09:20:06 -0400
committerLaurent Delaigue2016-10-17 07:19:43 -0400
commitcd75f7c48dafd01d6dd2e28ebd353867d73f008b (patch)
tree716623edc2cab739dff3182ca3939c5a8d324657
parentfcdec72a8076a82bb99a3e24a295ac7446864b7a (diff)
downloadorg.eclipse.emf.compare-cd75f7c48dafd01d6dd2e28ebd353867d73f008b.tar.gz
org.eclipse.emf.compare-cd75f7c48dafd01d6dd2e28ebd353867d73f008b.tar.xz
org.eclipse.emf.compare-cd75f7c48dafd01d6dd2e28ebd353867d73f008b.zip
[505983] Pseudo-conflicts should be real on ADD/ADD
When adding matching objects in differents indices in a list, all the diffs were marked as pseudo-conflicts. At least some of them should be real conflicts. This patch fixes the issue by marking all such diffs as real conflicts but it should be possible to improve it to have some of them marked as pseudo where it would make sense. Code shared by default conflict detector and match-based conflict detector is extracted into MatchUtil to reduce duplication. Bug: 505983 Change-Id: Icca55a1fcccc08cd5d858639d4c6dd8734f327a9 Also-by: Mathieu Cartaud <mathieu.cartaud@obeo.fr> Signed-off-by: Laurent Delaigue <laurent.delaigue@obeo.fr>
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.papyrus.tests.git/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/AdditiveMergeDiagramTests.java15
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui.tests.git/src/org/eclipse/emf/compare/ide/ui/tests/merge/AdditiveMergeTests.java16
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/conflict/DefaultConflictDetector.java125
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AbstractConflictSearch.java112
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/AttributeChangeConflictSearch.java1
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/ContainmentRefChangeConflictSearch.java1
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/FeatureMapChangeConflictSearch.java1
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/conflict/NonContainmentRefChangeConflictSearch.java1
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java26
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/MatchUtil.java127
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

Back to the top