Moves with non-unique values and addition/deletions of duplicates
We cannot always ignore the "moved" values when computing the LCS. ADD
and DELETE of duplicates of the moved one need to be taken into account.
Change-Id: I5e97b89ae34f9dfd1ef7b9db69c1bf7feb3a5f54
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java
index 706a744..23ea9b9 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java
@@ -16,11 +16,9 @@
import static com.google.common.collect.Lists.newArrayList;
import com.google.common.base.Predicate;
-import com.google.common.collect.HashMultiset;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import java.lang.ref.WeakReference;
@@ -917,23 +915,22 @@
}
if (lcsIndexOfSubsequenceStart > -1) {
- // Do we have duplicates before A in the lcs?
- final Multiset<E> dupesLCS = HashMultiset.create(lcs.subList(0, lcsIndexOfSubsequenceStart + 1));
- final E subsequenceStart = lcs.get(lcsIndexOfSubsequenceStart);
- int duplicatesToGo = dupesLCS.count(subsequenceStart) - 1;
+ // Now browse our target list to find the index of that lcs element in it
+ int targetCursor = 0;
+ for (int i = 0; i <= lcsIndexOfSubsequenceStart; i++) {
+ E lcsElement = lcs.get(i);
- // Then, find the index of "A" in target
- for (int i = 0; i < target.size() && insertionIndex == -1; i++) {
- final E targetElement = target.get(i);
+ boolean isInLCS = false;
+ for (int j = targetCursor; j < target.size() && !isInLCS; j++) {
+ E targetElement = target.get(j);
- if (equalityHelper.matchingValues(subsequenceStart, targetElement)) {
- if (duplicatesToGo > 0) {
- duplicatesToGo--;
- } else {
- insertionIndex = i + 1;
+ if (equalityHelper.matchingValues(lcsElement, targetElement)) {
+ isInLCS = true;
+ targetCursor = j + 1;
}
}
}
+ insertionIndex = targetCursor;
}
return insertionIndex;
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AttributeChangeMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AttributeChangeMerger.java
index 200e362..c7ac57d 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AttributeChangeMerger.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AttributeChangeMerger.java
@@ -25,7 +25,9 @@
import org.eclipse.emf.compare.AttributeChange;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.DifferenceSource;
+import org.eclipse.emf.compare.DifferenceState;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.internal.ThreeWayTextDiff;
import org.eclipse.emf.compare.internal.utils.DiffUtil;
@@ -372,10 +374,39 @@
Set<Object> ignoredElements = DiffUtil.computeIgnoredElements(comparison, equalityHelper, targetList,
diff, rightToLeft);
- if (ignoredElements.isEmpty()) {
- ignoredElements = Collections.singleton(valueToMove);
- } else {
- ignoredElements.add(valueToMove);
+ // We're "moving" an element, so it is present on both sides and should not be part of the LCS
+ // computation since it is our move target. However, if we also have a diff on that same value, we do
+ // not have to ignore it.
+ Iterator<Diff> siblingDiffs = diff.getMatch().getDifferences().stream()
+ .filter(AttributeChange.class::isInstance)
+ .filter(d -> d.getState() == DifferenceState.UNRESOLVED && equalityHelper
+ .matchingAttributeValues(valueToMove, ((AttributeChange)d).getValue()))
+ .iterator();
+ boolean ignoreValue = true;
+ while (siblingDiffs.hasNext()) {
+ Diff sibling = siblingDiffs.next();
+ if (sibling.getKind() == DifferenceKind.ADD) {
+ // We have another duplicate of that value on this side that corresponds to none on the other
+ if (sibling.getSource() == DifferenceSource.LEFT && rightToLeft) {
+ ignoreValue = false;
+ } else if (sibling.getSource() == DifferenceSource.RIGHT && !rightToLeft) {
+ ignoreValue = false;
+ }
+ } else if (sibling.getKind() == DifferenceKind.DELETE) {
+ // reverse the above
+ if (sibling.getSource() == DifferenceSource.LEFT && !rightToLeft) {
+ ignoreValue = false;
+ } else if (sibling.getSource() == DifferenceSource.RIGHT && rightToLeft) {
+ ignoreValue = false;
+ }
+ }
+ }
+ if (ignoreValue) {
+ if (ignoredElements.isEmpty()) {
+ ignoredElements = Collections.singleton(valueToMove);
+ } else {
+ ignoredElements.add(valueToMove);
+ }
}
List<Object> lcs = DiffUtil.longestCommonSubsequence(comparison, ignoredElements, sourceList,
copyTarget);