Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlgoubet2013-06-24 15:16:48 +0000
committerLaurent Goubet2013-07-12 06:35:14 +0000
commit2b6957c24d3554cf936d2c49410962293ec7d446 (patch)
tree4630f0fa882a2e24e51be5ced8f9bb9120ccff76 /plugins
parent2a49b81c24888b6e1158d946ec25d520a53c35a6 (diff)
downloadorg.eclipse.emf.compare-2b6957c24d3554cf936d2c49410962293ec7d446.tar.gz
org.eclipse.emf.compare-2b6957c24d3554cf936d2c49410962293ec7d446.tar.xz
org.eclipse.emf.compare-2b6957c24d3554cf936d2c49410962293ec7d446.zip
[410737] preserve ordering even for equivalent differences.
We need to check both equivalent and implied (or implied "by") differences. This allows us to "catch" issues with EMF automatically merging elements in their target lists while disregarding order. Bug:410737 Change-Id: I32b7c703d8069973ab8d6d90c0eb6a14ccee35a3
Diffstat (limited to 'plugins')
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/AbstractTest.java18
-rw-r--r--plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/EdgeMergeTest.java94
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/association/AddAssociation2Test.java9
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java39
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java1626
5 files changed, 922 insertions, 864 deletions
diff --git a/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/AbstractTest.java b/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/AbstractTest.java
index 317e8923d..e06ee019e 100644
--- a/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/AbstractTest.java
+++ b/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/AbstractTest.java
@@ -152,24 +152,6 @@ public abstract class AbstractTest {
testIntersections(comparison);
}
- // FIXME: See Bug 410737
- protected void diffsCheckingBug410737(Comparison comparison, int totalDiffsNb, ExpectedStat ... expectedStats) {
- List<Diff> diffs = comparison.getDifferences();
- Collection<Diff> differences = Collections2.filter(diffs, Predicates.not(EMFComparePredicates.ofKind(DifferenceKind.MOVE)));
-
- assertEquals(totalDiffsNb, differences.size());
- for (ExpectedStat expectedStat : expectedStats) {
- Predicate<Diff> p = expectedStat.p;
- int nb = expectedStat.nb;
- int result = Collections2.filter(differences, p).size();
-
- String message = buildAssertMessage(differences, p);
-
- assertEquals(message, nb, result);
- }
- testIntersections(comparison);
- }
-
private String buildAssertMessage(Collection<Diff> differences, Predicate<Diff> p) {
Diff diff = Iterables.find(differences, p, null);
String message = "";
diff --git a/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/EdgeMergeTest.java b/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/EdgeMergeTest.java
index 5a2b3a1fe..797a04777 100644
--- a/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/EdgeMergeTest.java
+++ b/plugins/org.eclipse.emf.compare.diagram.papyrus.tests/src/org/eclipse/emf/compare/diagram/papyrus/tests/merge/EdgeMergeTest.java
@@ -143,7 +143,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -165,7 +165,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A1_DIFFS_NB - A1_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0)); // only graphical diffs merged
+ diffsChecking(comparison, A1_DIFFS_NB - A1_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0)); // only graphical diffs merged
}
@Test
@@ -187,7 +187,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A1_DIFFS_NB - A1_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0)); // only graphical diffs merged
+ diffsChecking(comparison, A1_DIFFS_NB - A1_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0)); // only graphical diffs merged
}
@Test
@@ -209,7 +209,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -231,7 +231,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A1_DIFFS_NB - A1_ASSOCHANGE_NB); // only 13 semantic diffs merged
+ diffsChecking(comparison, A1_DIFFS_NB - A1_ASSOCHANGE_NB); // only 13 semantic diffs merged
}
@Test
@@ -253,7 +253,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -275,7 +275,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -297,7 +297,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A1_DIFFS_NB - A1_ASSOCHANGE_NB); // only 13 semantic diffs merged
+ diffsChecking(comparison, A1_DIFFS_NB - A1_ASSOCHANGE_NB); // only 13 semantic diffs merged
}
@Test
@@ -320,7 +320,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -343,7 +343,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A2_DIFFS_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
+ diffsChecking(comparison, A2_DIFFS_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
}
@Test
@@ -366,7 +366,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A2_DIFFS_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
+ diffsChecking(comparison, A2_DIFFS_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
}
@Test
@@ -389,7 +389,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -412,7 +412,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 1));
+ diffsChecking(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 1));
}
@Test
@@ -435,7 +435,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
+ diffsChecking(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
}
@Test
@@ -458,7 +458,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
+ diffsChecking(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 1));
}
@Test
@@ -481,7 +481,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 1));
+ diffsChecking(comparison, A2_DIFFS_NB - A2_ASSOCHANGE_NB - A2_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 1));
}
@Test
@@ -504,7 +504,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -527,7 +527,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A3_DIFFS_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
+ diffsChecking(comparison, A3_DIFFS_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
}
@Test
@@ -550,7 +550,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A3_DIFFS_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
+ diffsChecking(comparison, A3_DIFFS_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
}
@Test
@@ -573,7 +573,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, 0); // all diffs merged
+ diffsChecking(comparison, 0); // all diffs merged
}
@Test
@@ -596,7 +596,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 2));
+ diffsChecking(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 2));
}
@Test
@@ -619,7 +619,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
+ diffsChecking(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
}
@Test
@@ -642,7 +642,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
+ diffsChecking(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_EDGECHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 0), new ExpectedStat(node, 2));
}
@Test
@@ -665,7 +665,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 2));
+ diffsChecking(comparison, A3_DIFFS_NB - A3_ASSOCHANGE_NB - A3_CLASSCHANGE_NB, new ExpectedStat(association, 0), new ExpectedStat(edge, 1), new ExpectedStat(node, 2));
}
@Test
@@ -689,7 +689,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB - A4_NODECHANGE1_NB - A4_PKGNODECHANGE_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 3));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB - A4_NODECHANGE1_NB - A4_PKGNODECHANGE_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 3));
}
@Test
@@ -713,7 +713,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB, new ExpectedStat(association, 2), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB, new ExpectedStat(association, 2), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
}
@Test
@@ -737,7 +737,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB, new ExpectedStat(association, 2), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB, new ExpectedStat(association, 2), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
}
@Test
@@ -761,7 +761,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB - A4_NODECHANGE1_NB - A4_PKGNODECHANGE_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 3));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_EDGECHANGE1_NB - A4_NODECHANGE1_NB - A4_PKGNODECHANGE_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(association, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 3));
}
@Test
@@ -786,7 +786,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 1), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 1), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
}
@Test
@@ -811,7 +811,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_EDGECHANGE1_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_EDGECHANGE1_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
}
@Test
@@ -836,7 +836,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_EDGECHANGE1_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_EDGECHANGE1_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
}
@Test
@@ -861,7 +861,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 1), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_ASSOCHANGE1_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(asso1, 0), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 1), new ExpectedStat(edge2, 1), new ExpectedStat(node, 5));
}
@Test
@@ -888,7 +888,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_NODECHANGE1_NB - A4_PKGNODECHANGE_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(asso1, 1), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 1), new ExpectedStat(edge2, 1), new ExpectedStat(nodeB, 0), new ExpectedStat(nodeSubB, 1), new ExpectedStat(nodePropB, 1));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_NODECHANGE1_NB - A4_PKGNODECHANGE_NB - A4_CLASSCHANGE1_NB - A4_PKGCHANGE_NB, new ExpectedStat(asso1, 1), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 1), new ExpectedStat(edge2, 1), new ExpectedStat(nodeB, 0), new ExpectedStat(nodeSubB, 1), new ExpectedStat(nodePropB, 1));
}
@Test
@@ -915,7 +915,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A4_DIFFS_NB - A4_NODECHANGE1_NB - 2 * A4_EDGECHANGE1_NB - A4_PROPNODECHANGE_NB - A4_SUBNODECHANGE_NB, new ExpectedStat(asso1, 1), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 0), new ExpectedStat(nodeB, 0), new ExpectedStat(nodeSubB, 0), new ExpectedStat(nodePropB, 0));
+ diffsChecking(comparison, A4_DIFFS_NB - A4_NODECHANGE1_NB - 2 * A4_EDGECHANGE1_NB - A4_PROPNODECHANGE_NB - A4_SUBNODECHANGE_NB, new ExpectedStat(asso1, 1), new ExpectedStat(asso2, 1), new ExpectedStat(edge1, 0), new ExpectedStat(edge2, 0), new ExpectedStat(nodeB, 0), new ExpectedStat(nodeSubB, 0), new ExpectedStat(nodePropB, 0));
}
@Test
@@ -968,7 +968,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1036,7 +1036,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_ASSO_EDGE_CHANGE1_NB - A6_ASSO_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_ASSO_EDGE_CHANGE1_NB - A6_ASSO_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 1), // ** -1
@@ -1104,7 +1104,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1174,7 +1174,7 @@ public class EdgeMergeTest extends AbstractTest {
comparison = buildComparison(left, right);
//FIXME: ElementImport.importedElement is not merged => should create a UMLDiff for this DirectedRelationship (A6_IMPORT_CHANGE1_NB += 1)
// See Bug 406405
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_IMPORT_EDGE_CHANGE1_NB - A6_IMPORT_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_IMPORT_EDGE_CHANGE1_NB - A6_IMPORT_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1244,7 +1244,7 @@ public class EdgeMergeTest extends AbstractTest {
comparison = buildComparison(left, right);
//FIXME: Generalization.general is not merged => should create a UMLDiff for this DirectedRelationship (A6_GENERAL_CHANGE1_NB += 1)
// See Bug 406405
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_GENERAL_EDGE_CHANGE1_NB - A6_GENERAL_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_GENERAL_EDGE_CHANGE1_NB - A6_GENERAL_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1382,7 +1382,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_IREAL_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_IREAL_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 0), // ** -1
new ExpectedStat(associations, 2),
@@ -1450,7 +1450,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1518,7 +1518,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_SUBSTITUTION_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_SUBSTITUTION_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 1), // ** -1
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1586,7 +1586,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
+ diffsChecking(comparison, A6_DIFFS_NB - A6_DEPENDENCY_EDGE_CHANGE1_NB - A6_DEPENDENCY_CHANGE1_NB - A6_NODECHANGE1_NB - A6_CLASSCHANGE1_NB,
new ExpectedStat(substitutions, 2),
new ExpectedStat(interfaceRealizations, 1),
new ExpectedStat(associations, 2),
@@ -1631,7 +1631,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A7_DIFFS_NB - A7_EDGECHANGE1_NB,
+ diffsChecking(comparison, A7_DIFFS_NB - A7_EDGECHANGE1_NB,
new ExpectedStat(edges, 2), // ** -1
new ExpectedStat(edgeTarget, 1),
new ExpectedStat(assoTarget, 1));
@@ -1664,7 +1664,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A7_DIFFS_NB - A7_EDGECHANGE1_NB,
+ diffsChecking(comparison, A7_DIFFS_NB - A7_EDGECHANGE1_NB,
new ExpectedStat(edges, 2), // ** -1
new ExpectedStat(edgeTarget, 1),
new ExpectedStat(assoTarget, 1));
@@ -1697,7 +1697,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A7_DIFFS_NB - A7_EDGECHANGE1_NB,
+ diffsChecking(comparison, A7_DIFFS_NB - A7_EDGECHANGE1_NB,
new ExpectedStat(edges, 2), // ** -1
new ExpectedStat(edgeTarget, 1),
new ExpectedStat(assoTarget, 1));
@@ -1730,7 +1730,7 @@ public class EdgeMergeTest extends AbstractTest {
// ** MERGE CHECKING **
comparison = buildComparison(left, right);
- diffsCheckingBug410737(comparison, A7_DIFFS_NB - A7_EDGE_TARGET_CHANGE_NB,
+ diffsChecking(comparison, A7_DIFFS_NB - A7_EDGE_TARGET_CHANGE_NB,
new ExpectedStat(edges, 3),
new ExpectedStat(edgeTarget, 0), // ** -1
new ExpectedStat(assoTarget, 1));
diff --git a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/association/AddAssociation2Test.java b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/association/AddAssociation2Test.java
index 4dc748179..b773db30f 100644
--- a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/association/AddAssociation2Test.java
+++ b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/association/AddAssociation2Test.java
@@ -28,7 +28,6 @@ import org.eclipse.emf.compare.uml2.internal.AssociationChange;
import org.eclipse.emf.compare.uml2.tests.AbstractTest;
import org.eclipse.emf.compare.uml2.tests.association.data.AssociationInputData;
import org.eclipse.emf.ecore.resource.Resource;
-import org.junit.Ignore;
import org.junit.Test;
@SuppressWarnings("nls")
@@ -54,8 +53,6 @@ public class AddAssociation2Test extends AbstractTest {
}
@Test
- @Ignore
- // FIXME: See Bug 410737
public void testMergeLtRA30UseCase() throws IOException {
final Resource left = input.getA3Left();
final Resource right = input.getA3Right();
@@ -73,8 +70,6 @@ public class AddAssociation2Test extends AbstractTest {
}
@Test
- @Ignore
- // FIXME: See Bug 410737
public void testMergeRtLA31UseCase() throws IOException {
final Resource left = input.getA3Left();
final Resource right = input.getA3Right();
@@ -108,8 +103,6 @@ public class AddAssociation2Test extends AbstractTest {
}
@Test
- @Ignore
- // FIXME: See Bug 410737
public void testMergeLtRA30UseCase3way() throws IOException {
final Resource left = input.getA3Left();
final Resource right = input.getA3Right();
@@ -134,8 +127,6 @@ public class AddAssociation2Test extends AbstractTest {
}
@Test
- @Ignore
- // FIXME: See Bug 410737
public void testMergeLtRA31UseCase3way() throws IOException {
final Resource left = input.getA3Left();
final Resource right = input.getA3Right();
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 8dedf68c9..7211e2051 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
@@ -46,8 +46,6 @@ import org.eclipse.emf.compare.utils.ReferenceUtil;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.EcorePackage;
-import org.eclipse.emf.ecore.util.InternalEList;
/**
* This utility class will be used to provide similarity implementations.
@@ -540,7 +538,6 @@ public final class DiffUtil {
List<E> source, List<E> target, E newElement) {
final IEqualityHelper equalityHelper = comparison.getEqualityHelper();
- // We assume that "newElement" is in source but not in the target yet
final List<E> lcs;
if (ignoredElements != null) {
lcs = longestCommonSubsequence(comparison, ignoredElements, source, target);
@@ -797,7 +794,6 @@ public final class DiffUtil {
* inferred from {@code rightToLeft}.
* @see #findInsertionIndex(Comparison, Iterable, List, List, Object)
*/
- @SuppressWarnings("unchecked")
public static int findInsertionIndex(Comparison comparison, Diff diff, boolean rightToLeft) {
final EStructuralFeature feature;
final Object value;
@@ -846,23 +842,7 @@ public final class DiffUtil {
}
final List<Object> sourceList = getSourceList(diff, feature, rightToLeft);
- final List<Object> targetList;
-
- if (expectedContainer != null) {
- final List<Object> temp = (List<Object>)ReferenceUtil.safeEGet(expectedContainer, feature);
- if (feature == EcorePackage.Literals.ECLASS__ESUPER_TYPES
- || feature == EcorePackage.Literals.EOPERATION__EEXCEPTIONS) {
- // workaround 394286
- targetList = temp;
- } else if (temp instanceof InternalEList<?>) {
- // EMF ignores the "resolve" flag for containment lists...
- targetList = ((InternalEList<Object>)temp).basicList();
- } else {
- targetList = temp;
- }
- } else {
- targetList = ImmutableList.of();
- }
+ final List<Object> targetList = ReferenceUtil.getAsList(expectedContainer, feature);
Iterable<Object> ignoredElements = Iterables.concat(computeIgnoredElements(targetList, diff),
Collections.singleton(value));
@@ -1007,7 +987,6 @@ public final class DiffUtil {
* @return The list that should be used as a source for this merge. May be empty, but never
* <code>null</code>.
*/
- @SuppressWarnings("unchecked")
private static List<Object> getSourceList(Diff diff, EStructuralFeature feature, boolean rightToLeft) {
final Match match = diff.getMatch();
final List<Object> sourceList;
@@ -1035,21 +1014,7 @@ public final class DiffUtil {
}
}
- if (expectedContainer != null) {
- final List<Object> temp = (List<Object>)ReferenceUtil.safeEGet(expectedContainer, feature);
- if (feature == EcorePackage.Literals.ECLASS__ESUPER_TYPES
- || feature == EcorePackage.Literals.EOPERATION__EEXCEPTIONS) {
- // workaround 394286
- sourceList = temp;
- } else if (temp instanceof InternalEList<?>) {
- // EMF ignores the "resolve" flag for containment lists...
- sourceList = ((InternalEList<Object>)temp).basicList();
- } else {
- sourceList = temp;
- }
- } else {
- sourceList = ImmutableList.of();
- }
+ sourceList = ReferenceUtil.getAsList(expectedContainer, feature);
return sourceList;
}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java
index ce9d162b4..cbb74564c 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java
@@ -1,753 +1,873 @@
-/*******************************************************************************
- * Copyright (c) 2012, 2013 Obeo.
- * 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:
- * Obeo - initial API and implementation
- *******************************************************************************/
-package org.eclipse.emf.compare.merge;
-
-import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEIsSet;
-
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-
-import java.util.Iterator;
-import java.util.List;
-
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.common.util.Monitor;
-import org.eclipse.emf.compare.Comparison;
-import org.eclipse.emf.compare.Diff;
-import org.eclipse.emf.compare.DifferenceSource;
-import org.eclipse.emf.compare.DifferenceState;
-import org.eclipse.emf.compare.Match;
-import org.eclipse.emf.compare.ReferenceChange;
-import org.eclipse.emf.compare.internal.utils.DiffUtil;
-import org.eclipse.emf.compare.utils.IEqualityHelper;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.emf.ecore.xmi.XMIResource;
-
-/**
- * This specific implementation of {@link AbstractMerger} will be used to merge reference changes.
- *
- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
- */
-public class ReferenceChangeMerger extends AbstractMerger {
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
- */
- public boolean isMergerFor(Diff target) {
- return target instanceof ReferenceChange;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.merge.IMerger#copyLeftToRight(org.eclipse.emf.compare.Diff,
- * org.eclipse.emf.common.util.Monitor)
- */
- public void copyLeftToRight(Diff target, Monitor monitor) {
- // Don't merge an already merged (or discarded) diff
- if (target.getState() != DifferenceState.UNRESOLVED) {
- return;
- }
- final ReferenceChange diff = (ReferenceChange)target;
-
- // Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
- diff.setState(DifferenceState.MERGED);
-
- if (diff.getSource() == DifferenceSource.LEFT) {
- // merge all "requires" diffs
- mergeRequires(diff, false, monitor);
- handleImplies(diff, false, monitor);
- } else {
- // merge all "required by" diffs
- mergeRequiredBy(diff, false, monitor);
- handleImpliedBy(diff, false, monitor);
- }
-
- boolean hasToBeMerged = true;
- if (diff.getEquivalence() != null) {
- hasToBeMerged = handleEquivalences(diff, false, monitor);
- }
-
- if (hasToBeMerged) {
- if (diff.getSource() == DifferenceSource.LEFT) {
- accept(diff, false);
- } else {
- reject(diff, false);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.eclipse.emf.compare.merge.IMerger#copyRightToLeft(org.eclipse.emf.compare.Diff,
- * org.eclipse.emf.common.util.Monitor)
- */
- public void copyRightToLeft(Diff target, Monitor monitor) {
- // Don't merge an already merged (or discarded) diff
- if (target.getState() != DifferenceState.UNRESOLVED) {
- return;
- }
- final ReferenceChange diff = (ReferenceChange)target;
-
- // Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
- diff.setState(DifferenceState.MERGED);
-
- if (diff.getSource() == DifferenceSource.LEFT) {
- // merge all "required by" diffs
- mergeRequiredBy(diff, true, monitor);
- handleImpliedBy(diff, true, monitor);
- } else {
- // merge all "requires" diffs
- mergeRequires(diff, true, monitor);
- handleImplies(diff, true, monitor);
- }
-
- boolean hasToBeMerged = true;
- if (diff.getEquivalence() != null) {
- hasToBeMerged = handleEquivalences(diff, true, monitor);
- }
-
- if (hasToBeMerged) {
- if (diff.getSource() == DifferenceSource.LEFT) {
- reject(diff, true);
- } else {
- accept(diff, true);
- }
- }
- }
-
- /**
- * Merge the given difference rejecting it.
- *
- * @param diff
- * The difference to merge.
- * @param rightToLeft
- * The direction of the merge.
- */
- private void reject(final ReferenceChange diff, boolean rightToLeft) {
- DifferenceSource source = diff.getSource();
- switch (diff.getKind()) {
- case ADD:
- // We have a ADD on left, thus nothing in right. We need to revert the addition
- removeFromTarget(diff, rightToLeft);
- break;
- case DELETE:
- // DELETE in the left, thus an element in right. We need to re-create that element
- addInTarget(diff, rightToLeft);
- break;
- case MOVE:
- moveElement(diff, rightToLeft);
- break;
- case CHANGE:
- EObject container = null;
- if (source == DifferenceSource.LEFT) {
- container = diff.getMatch().getLeft();
-
- } else {
- container = diff.getMatch().getRight();
- }
- // Is it an unset?
- if (container != null) {
- final EObject leftValue = (EObject)container.eGet(diff.getReference(), false);
- if (leftValue == null) {
- // Value has been unset in the right, and we are merging towards right.
- // We need to re-add this element
- addInTarget(diff, rightToLeft);
- } else {
- // We'll actually need to "reset" this reference to its original value
- resetInTarget(diff, rightToLeft);
- }
- } else {
- // we have no left, and the source is on the left. Can only be an unset
- addInTarget(diff, rightToLeft);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Merge the given difference accepting it.
- *
- * @param diff
- * The difference to merge.
- * @param rightToLeft
- * The direction of the merge.
- */
- private void accept(final ReferenceChange diff, boolean rightToLeft) {
- DifferenceSource source = diff.getSource();
- switch (diff.getKind()) {
- case ADD:
- // Create the same element in right
- addInTarget(diff, rightToLeft);
- break;
- case DELETE:
- // Delete that same element from right
- removeFromTarget(diff, rightToLeft);
- break;
- case MOVE:
- moveElement(diff, rightToLeft);
- break;
- case CHANGE:
- EObject container = null;
- if (source == DifferenceSource.LEFT) {
- container = diff.getMatch().getLeft();
- } else {
- container = diff.getMatch().getRight();
- }
- // Is it an unset?
- if (container != null) {
- final EObject leftValue = (EObject)container.eGet(diff.getReference(), false);
- if (leftValue == null) {
- removeFromTarget(diff, rightToLeft);
- } else {
- addInTarget(diff, rightToLeft);
- }
- } else {
- // we have no left, and the source is on the left. Can only be an unset
- removeFromTarget(diff, rightToLeft);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Mark as MERGED all the implied differences recursively from the given one.
- *
- * @param diff
- * The difference from which the implications have to be marked.
- * @param rightToLeft
- * The direction of the merge.
- * @param monitor
- * Monitor.
- */
- private void handleImplies(Diff diff, boolean rightToLeft, Monitor monitor) {
- for (Diff implied : diff.getImplies()) {
- implied.setState(DifferenceState.MERGED);
- handleImplies(implied, rightToLeft, monitor);
- }
- }
-
- /**
- * Mark as MERGED all the implying differences recursively from the given one.
- *
- * @param diff
- * The difference from which the implications have to be marked.
- * @param rightToLeft
- * The direction of the merge.
- * @param monitor
- * Monitor.
- */
- private void handleImpliedBy(Diff diff, boolean rightToLeft, Monitor monitor) {
- for (Diff impliedBy : diff.getImpliedBy()) {
- impliedBy.setState(DifferenceState.MERGED);
- handleImpliedBy(impliedBy, rightToLeft, monitor);
- }
- }
-
- /**
- * This will be called when trying to copy a "MOVE" diff.
- *
- * @param diff
- * The diff we are currently merging.
- * @param rightToLeft
- * Whether we should move the value in the left or right side.
- */
- protected void moveElement(ReferenceChange diff, boolean rightToLeft) {
- final Comparison comparison = diff.getMatch().getComparison();
- final Match valueMatch = comparison.getMatch(diff.getValue());
- final EReference reference = diff.getReference();
-
- final EObject expectedContainer;
- if (reference.isContainment()) {
- /*
- * We cannot "trust" the holding match (getMatch) in this case. However, "valueMatch" cannot be
- * null : we cannot have detected a move if the moved element is not matched on both sides. Use
- * that information to retrieve the proper "target" container.
- */
- final Match targetContainerMatch;
- // If it exists, use the source side's container as reference
- if (rightToLeft && valueMatch.getRight() != null) {
- targetContainerMatch = comparison.getMatch(valueMatch.getRight().eContainer());
- } else if (!rightToLeft && valueMatch.getLeft() != null) {
- targetContainerMatch = comparison.getMatch(valueMatch.getLeft().eContainer());
- } else {
- // Otherwise, the value we're moving on one side has been removed from its source side.
- targetContainerMatch = comparison.getMatch(valueMatch.getOrigin().eContainer());
- }
- if (rightToLeft) {
- expectedContainer = targetContainerMatch.getLeft();
- } else {
- expectedContainer = targetContainerMatch.getRight();
- }
- } else if (rightToLeft) {
- expectedContainer = diff.getMatch().getLeft();
- } else {
- expectedContainer = diff.getMatch().getRight();
- }
- if (expectedContainer == null) {
- // FIXME throw exception? log? re-try to merge our requirements?
- // one of the "required" diffs should have created our container.
- return;
- }
-
- final EObject expectedValue;
- if (valueMatch == null) {
- // The value being moved is out of the scope
- /*
- * Note : there should not be a way to end up with a "move" for an out of scope value : a move can
- * only be detected if the object is matched on both sides, otherwise all we can see is "add" and
- * "delete"... Is this "fallback" code even reachable? If so, how?
- */
- // We need to look it up
- if (reference.isMany()) {
- @SuppressWarnings("unchecked")
- final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
- expectedValue = findMatchIn(comparison, targetList, diff.getValue());
- } else {
- expectedValue = (EObject)expectedContainer.eGet(reference);
- }
- } else {
- if (rightToLeft) {
- expectedValue = valueMatch.getLeft();
- } else {
- expectedValue = valueMatch.getRight();
- }
- }
- // We now know the target container, target reference and target value.
- doMove(diff, comparison, expectedContainer, expectedValue, rightToLeft);
- }
-
- /**
- * This will do the actual work of moving the element into its reference. All sanity checks were made in
- * {@link #moveElement(boolean)} and no more verification will be made here.
- *
- * @param diff
- * The diff we are currently merging.
- * @param comparison
- * Comparison holding this Diff.
- * @param expectedContainer
- * The container in which we are reorganizing a reference.
- * @param expectedValue
- * The value that is to be moved within its reference.
- * @param rightToLeft
- * Whether we should move the value in the left or right side.
- */
- @SuppressWarnings("unchecked")
- protected void doMove(ReferenceChange diff, Comparison comparison, EObject expectedContainer,
- EObject expectedValue, boolean rightToLeft) {
- final EReference reference = diff.getReference();
- if (reference.isMany()) {
- // Element to move cannot be part of the LCS... or there would not be a MOVE diff
- int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
-
- /*
- * However, it could still have been located "before" its new index, in which case we need to take
- * it into account.
- */
- final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
- final int currentIndex = targetList.indexOf(expectedValue);
- if (insertionIndex > currentIndex && currentIndex >= 0) {
- insertionIndex--;
- }
-
- if (currentIndex == -1) {
- // happens for container changes for example.
- if (!reference.isContainment()) {
- targetList.remove(expectedValue);
- }
- if (insertionIndex < 0 && insertionIndex > targetList.size()) {
- targetList.add(expectedValue);
- } else {
- targetList.add(insertionIndex, expectedValue);
- }
- } else if (targetList instanceof EList<?>) {
- if (insertionIndex < 0 && insertionIndex > targetList.size()) {
- ((EList<EObject>)targetList).move(targetList.size() - 1, expectedValue);
- } else {
- ((EList<EObject>)targetList).move(insertionIndex, expectedValue);
- }
- } else {
- targetList.remove(expectedValue);
- if (insertionIndex < 0 && insertionIndex > targetList.size()) {
- targetList.add(expectedValue);
- } else {
- targetList.add(insertionIndex, expectedValue);
- }
- }
- } else {
- expectedContainer.eSet(reference, expectedValue);
- }
- }
-
- /**
- * This will be called when we need to create an element in the target side.
- * <p>
- * All necessary sanity checks have been made to ensure that the current operation is one that should
- * create an object in its side or add an objet to a reference. In other words, either :
- * <ul>
- * <li>We are copying from right to left and
- * <ul>
- * <li>we are copying an addition to the right side (we need to create the same object in the left), or</li>
- * <li>we are copying a deletion from the left side (we need to revert the deletion).</li>
- * </ul>
- * </li>
- * <li>We are copying from left to right and
- * <ul>
- * <li>we are copying a deletion from the right side (we need to revert the deletion), or</li>
- * <li>we are copying an addition to the left side (we need to create the same object in the right).</li>
- * </ul>
- * </li>
- * </ul>
- * </p>
- *
- * @param diff
- * The diff we are currently merging.
- * @param rightToLeft
- * Tells us whether we are to add an object on the left or right side.
- */
- @SuppressWarnings("unchecked")
- protected void addInTarget(ReferenceChange diff, boolean rightToLeft) {
- final Match match = diff.getMatch();
- final EObject expectedContainer;
- if (rightToLeft) {
- expectedContainer = match.getLeft();
- } else {
- expectedContainer = match.getRight();
- }
-
- if (expectedContainer == null) {
- // FIXME throw exception? log? re-try to merge our requirements?
- // one of the "required" diffs should have created our container.
- return;
- }
-
- final Comparison comparison = match.getComparison();
- final EReference reference = diff.getReference();
- final EObject expectedValue;
- final Match valueMatch = comparison.getMatch(diff.getValue());
- if (valueMatch == null) {
- // This is an out of scope value.
- if (diff.getValue().eIsProxy()) {
- // Copy the proxy
- expectedValue = EcoreUtil.copy(diff.getValue());
- } else {
- // Use the same value.
- expectedValue = diff.getValue();
- }
- } else if (rightToLeft) {
- if (reference.isContainment()) {
- expectedValue = createCopy(diff.getValue());
- valueMatch.setLeft(expectedValue);
- } else {
- expectedValue = valueMatch.getLeft();
- }
- } else {
- if (reference.isContainment()) {
- expectedValue = createCopy(diff.getValue());
- valueMatch.setRight(expectedValue);
- } else {
- expectedValue = valueMatch.getRight();
- }
- }
-
- // We have the container, reference and value. We need to know the insertion index.
- if (reference.isMany()) {
- final int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
-
- final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
- addAt(targetList, expectedValue, insertionIndex);
- } else {
- expectedContainer.eSet(reference, expectedValue);
- }
-
- if (reference.isContainment()) {
- // Copy XMI ID when applicable.
- final Resource initialResource = diff.getValue().eResource();
- final Resource targetResource = expectedValue.eResource();
- if (initialResource instanceof XMIResource && targetResource instanceof XMIResource) {
- ((XMIResource)targetResource).setID(expectedValue, ((XMIResource)initialResource).getID(diff
- .getValue()));
- }
- }
- }
-
- /**
- * This will be called when we need to remove an element from the target side.
- * <p>
- * All necessary sanity checks have been made to ensure that the current operation is one that should
- * delete an object. In other words, we are :
- * <ul>
- * <li>Copying from right to left and either
- * <ul>
- * <li>we are copying a deletion from the right side (we need to remove the same object in the left) or,</li>
- * <li>we are copying an addition to the left side (we need to revert the addition).</li>
- * </ul>
- * </li>
- * <li>Copying from left to right and either
- * <ul>
- * <li>we are copying an addition to the right side (we need to revert the addition), or.</li>
- * <li>we are copying a deletion from the left side (we need to remove the same object in the right).</li>
- * </ul>
- * </li>
- * </ul>
- * </p>
- *
- * @param diff
- * The diff we are currently merging.
- * @param rightToLeft
- * Tells us whether we are to add an object on the left or right side.
- */
- @SuppressWarnings("unchecked")
- protected void removeFromTarget(ReferenceChange diff, boolean rightToLeft) {
- final Match match = diff.getMatch();
- final EReference reference = diff.getReference();
- final EObject currentContainer;
- if (rightToLeft) {
- currentContainer = match.getLeft();
- } else {
- currentContainer = match.getRight();
- }
- final Comparison comparison = match.getComparison();
- final Match valueMatch = comparison.getMatch(diff.getValue());
-
- if (currentContainer == null) {
- // FIXME throw exception? log? re-try to merge our requirements?
- // one of the "required" diffs should have created our container.
- return;
- }
-
- final EObject expectedValue;
- if (valueMatch == null) {
- // value is out of the scope... we need to look it up
- if (reference.isMany()) {
- final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);
- expectedValue = findMatchIn(comparison, targetList, diff.getValue());
- } else {
- // the value will not be needed anyway
- expectedValue = null;
- }
- } else if (rightToLeft) {
- expectedValue = valueMatch.getLeft();
- } else {
- expectedValue = valueMatch.getRight();
- }
-
- // We have the container, reference and value to remove. Expected value can be null when the
- // deletion was made on both side (i.e. a pseudo delete)
- if (reference.isContainment() && expectedValue != null) {
- EcoreUtil.remove(expectedValue);
- if (rightToLeft && valueMatch != null) {
- valueMatch.setLeft(null);
- } else if (valueMatch != null) {
- valueMatch.setRight(null);
- }
- // TODO remove dangling? remove empty Match?
- } else if (reference.isMany()) {
- /*
- * TODO if the same value appears twice, should we try and find the one that has actually been
- * deleted? Can it happen? For now, remove the first occurence we find.
- */
- final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);
- targetList.remove(expectedValue);
- } else {
- currentContainer.eUnset(reference);
- }
- }
-
- /**
- * This will be called by the merge operations in order to reset a reference to its original value, be
- * that the left or right side.
- * <p>
- * Should never be called on multi-valued references.
- * </p>
- *
- * @param diff
- * The diff we are currently merging.
- * @param rightToLeft
- * Tells us the direction of this merge operation.
- */
- protected void resetInTarget(ReferenceChange diff, boolean rightToLeft) {
- final Match match = diff.getMatch();
- final EReference reference = diff.getReference();
- final EObject targetContainer;
- if (rightToLeft) {
- targetContainer = match.getLeft();
- } else {
- targetContainer = match.getRight();
- }
-
- final EObject originContainer;
- if (match.getComparison().isThreeWay()) {
- originContainer = match.getOrigin();
- } else if (rightToLeft) {
- originContainer = match.getRight();
- } else {
- originContainer = match.getLeft();
- }
-
- if (originContainer == null || !safeEIsSet(targetContainer, reference)
- || !safeEIsSet(originContainer, reference)) {
- targetContainer.eUnset(reference);
- } else {
- final EObject originalValue = (EObject)originContainer.eGet(reference);
- final Match valueMatch = match.getComparison().getMatch(originalValue);
- final EObject expectedValue;
- if (valueMatch == null) {
- // Value is out of the scope, use it as-is
- expectedValue = originalValue;
- } else if (rightToLeft) {
- expectedValue = valueMatch.getLeft();
- } else {
- expectedValue = valueMatch.getRight();
- }
- targetContainer.eSet(reference, expectedValue);
- }
- }
-
- /**
- * Handles the equivalences of this difference.
- * <p>
- * Note that in certain cases, we'll merge our opposite instead of merging this diff. Specifically, we'll
- * do that for one-to-many eOpposites : we'll merge the 'many' side instead of the 'unique' one. This
- * allows us not to worry about the order of the references on that 'many' side.
- * </p>
- * <p>
- * This is called before the merge of <code>this</code>. In short, if this returns <code>false</code>, we
- * won't carry on merging <code>this</code> after returning.
- * </p>
- *
- * @param diff
- * The diff we are currently merging.
- * @param rightToLeft
- * Direction of the merge.
- * @param monitor
- * The monitor to use in order to report progress information.
- * @return <code>true</code> if the current difference should still be merged after handling its
- * equivalences, <code>false</code> if it should be considered "already merged".
- */
- protected boolean handleEquivalences(ReferenceChange diff, boolean rightToLeft, Monitor monitor) {
- final EReference reference = diff.getReference();
- boolean continueMerge = true;
- for (Diff equivalent : diff.getEquivalence().getDifferences()) {
- // For 1..*, merge diff on many-valued to preserve ordering
- if (equivalent instanceof ReferenceChange
- && reference.getEOpposite() == ((ReferenceChange)equivalent).getReference()
- && equivalent.getState() == DifferenceState.UNRESOLVED) {
- // This equivalence is on our eOpposite. Should we merge it instead of 'this'?
- final boolean mergeEquivalence = !reference.isMany()
- && ((ReferenceChange)equivalent).getReference().isMany();
- if (mergeEquivalence) {
- mergeDiff(equivalent, rightToLeft, monitor);
- continueMerge = false;
- }
- }
-
- /*
- * If one of the equivalent differences is implied or implying (depending on the merge direction)
- * a merged diff, then we have a dependency loop : the "current" difference has already been
- * merged because of this implication. This will allow us to break out of that loop.
- */
- if (rightToLeft) {
- if (diff.getSource() == DifferenceSource.LEFT) {
- continueMerge = continueMerge
- && !containsAny(diff.getRequiredBy(), equivalent.getImplies());
- } else {
- continueMerge = continueMerge
- && !containsAny(diff.getRequires(), equivalent.getImpliedBy());
- }
- } else {
- if (diff.getSource() == DifferenceSource.LEFT) {
- continueMerge = continueMerge
- && !containsAny(diff.getRequires(), equivalent.getImpliedBy());
- } else {
- continueMerge = continueMerge
- && !containsAny(diff.getRequiredBy(), equivalent.getImplies());
- }
- }
-
- equivalent.setState(DifferenceState.MERGED);
- }
- return continueMerge;
- }
-
- /**
- * Utility method to check that the first sequence contains one of the elements of the second sequence at
- * least.
- *
- * @param sequence1
- * The first sequence.
- * @param sequence2
- * The second sequence.
- * @return True if the given first sequence contains one of the elements of the second sequence at least.
- * false otherwise.
- */
- private boolean containsAny(List<? extends EObject> sequence1, List<? extends EObject> sequence2) {
- return Iterables.any(sequence2, Predicates.in(sequence1));
- }
-
- /**
- * Seeks a match of the given {@code element} in the given list, using the equality helper to find it.
- * This is only used when moving or deleting proxies for now.
- *
- * @param comparison
- * The comparison which Diff we are currently merging.
- * @param list
- * The list from which we seek a value.
- * @param element
- * The value for which we need a match in {@code list}.
- * @return The match of {@code element} in {@code list}, {@code null} if none.
- */
- protected EObject findMatchIn(Comparison comparison, List<EObject> list, EObject element) {
- final IEqualityHelper helper = comparison.getEqualityHelper();
- final Iterator<EObject> it = list.iterator();
- while (it.hasNext()) {
- final EObject next = it.next();
- if (helper.matchingValues(next, element)) {
- return next;
- }
- }
- return null;
- }
-
- /**
- * This will be used by the distinct merge actions in order to find the index at which a value should be
- * inserted in its target list. See {@link DiffUtil#findInsertionIndex(Comparison, Diff, boolean)} for
- * more on this.
- * <p>
- * Sub-classes can override this if the insertion order is irrelevant. A return value of {@code -1} will
- * be considered as "no index" and the value will be inserted at the end of its target list.
- * </p>
- *
- * @param comparison
- * This will be used in order to retrieve the Match for EObjects when comparing them.
- * @param diff
- * The diff which merging will trigger the need for an insertion index in its target list.
- * @param rightToLeft
- * {@code true} if the merging will be done into the left list, so that we should consider the
- * right model as the source and the left as the target.
- * @return The index at which this {@code diff}'s value should be inserted into the 'target' list, as
- * inferred from {@code rightToLeft}. {@code -1} if the value should be inserted at the end of its
- * target list.
- * @see DiffUtil#findInsertionIndex(Comparison, Diff, boolean)
- */
- protected int findInsertionIndex(Comparison comparison, Diff diff, boolean rightToLeft) {
- return DiffUtil.findInsertionIndex(comparison, diff, rightToLeft);
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2012, 2013 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.merge;
+
+import static com.google.common.collect.Iterators.filter;
+import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEIsSet;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.Monitor;
+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.ReferenceChange;
+import org.eclipse.emf.compare.internal.utils.DiffUtil;
+import org.eclipse.emf.compare.utils.IEqualityHelper;
+import org.eclipse.emf.compare.utils.ReferenceUtil;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.xmi.XMIResource;
+
+/**
+ * This specific implementation of {@link AbstractMerger} will be used to merge reference changes.
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+public class ReferenceChangeMerger extends AbstractMerger {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
+ */
+ public boolean isMergerFor(Diff target) {
+ return target instanceof ReferenceChange;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.merge.IMerger#copyLeftToRight(org.eclipse.emf.compare.Diff,
+ * org.eclipse.emf.common.util.Monitor)
+ */
+ public void copyLeftToRight(Diff target, Monitor monitor) {
+ // Don't merge an already merged (or discarded) diff
+ if (target.getState() != DifferenceState.UNRESOLVED) {
+ return;
+ }
+ final ReferenceChange diff = (ReferenceChange)target;
+
+ // Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
+ diff.setState(DifferenceState.MERGED);
+
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ // merge all "requires" diffs
+ mergeRequires(diff, false, monitor);
+ handleImplies(diff, false, monitor);
+ } else {
+ // merge all "required by" diffs
+ mergeRequiredBy(diff, false, monitor);
+ handleImpliedBy(diff, false, monitor);
+ }
+
+ boolean hasToBeMerged = true;
+ if (diff.getEquivalence() != null) {
+ hasToBeMerged = handleEquivalences(diff, false, monitor);
+ }
+
+ if (hasToBeMerged) {
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ accept(diff, false);
+ } else {
+ reject(diff, false);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.merge.IMerger#copyRightToLeft(org.eclipse.emf.compare.Diff,
+ * org.eclipse.emf.common.util.Monitor)
+ */
+ public void copyRightToLeft(Diff target, Monitor monitor) {
+ // Don't merge an already merged (or discarded) diff
+ if (target.getState() != DifferenceState.UNRESOLVED) {
+ return;
+ }
+ final ReferenceChange diff = (ReferenceChange)target;
+
+ // Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
+ diff.setState(DifferenceState.MERGED);
+
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ // merge all "required by" diffs
+ mergeRequiredBy(diff, true, monitor);
+ handleImpliedBy(diff, true, monitor);
+ } else {
+ // merge all "requires" diffs
+ mergeRequires(diff, true, monitor);
+ handleImplies(diff, true, monitor);
+ }
+
+ boolean hasToBeMerged = true;
+ if (diff.getEquivalence() != null) {
+ hasToBeMerged = handleEquivalences(diff, true, monitor);
+ }
+
+ if (hasToBeMerged) {
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ reject(diff, true);
+ } else {
+ accept(diff, true);
+ }
+ }
+ }
+
+ /**
+ * Merge the given difference rejecting it.
+ *
+ * @param diff
+ * The difference to merge.
+ * @param rightToLeft
+ * The direction of the merge.
+ */
+ private void reject(final ReferenceChange diff, boolean rightToLeft) {
+ DifferenceSource source = diff.getSource();
+ switch (diff.getKind()) {
+ case ADD:
+ // We have a ADD on left, thus nothing in right. We need to revert the addition
+ removeFromTarget(diff, rightToLeft);
+ break;
+ case DELETE:
+ // DELETE in the left, thus an element in right. We need to re-create that element
+ addInTarget(diff, rightToLeft);
+ break;
+ case MOVE:
+ moveElement(diff, rightToLeft);
+ break;
+ case CHANGE:
+ EObject container = null;
+ if (source == DifferenceSource.LEFT) {
+ container = diff.getMatch().getLeft();
+
+ } else {
+ container = diff.getMatch().getRight();
+ }
+ // Is it an unset?
+ if (container != null) {
+ final EObject leftValue = (EObject)container.eGet(diff.getReference(), false);
+ if (leftValue == null) {
+ // Value has been unset in the right, and we are merging towards right.
+ // We need to re-add this element
+ addInTarget(diff, rightToLeft);
+ } else {
+ // We'll actually need to "reset" this reference to its original value
+ resetInTarget(diff, rightToLeft);
+ }
+ } else {
+ // we have no left, and the source is on the left. Can only be an unset
+ addInTarget(diff, rightToLeft);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Merge the given difference accepting it.
+ *
+ * @param diff
+ * The difference to merge.
+ * @param rightToLeft
+ * The direction of the merge.
+ */
+ private void accept(final ReferenceChange diff, boolean rightToLeft) {
+ DifferenceSource source = diff.getSource();
+ switch (diff.getKind()) {
+ case ADD:
+ // Create the same element in right
+ addInTarget(diff, rightToLeft);
+ break;
+ case DELETE:
+ // Delete that same element from right
+ removeFromTarget(diff, rightToLeft);
+ break;
+ case MOVE:
+ moveElement(diff, rightToLeft);
+ break;
+ case CHANGE:
+ EObject container = null;
+ if (source == DifferenceSource.LEFT) {
+ container = diff.getMatch().getLeft();
+ } else {
+ container = diff.getMatch().getRight();
+ }
+ // Is it an unset?
+ if (container != null) {
+ final EObject leftValue = (EObject)container.eGet(diff.getReference(), false);
+ if (leftValue == null) {
+ removeFromTarget(diff, rightToLeft);
+ } else {
+ addInTarget(diff, rightToLeft);
+ }
+ } else {
+ // we have no left, and the source is on the left. Can only be an unset
+ removeFromTarget(diff, rightToLeft);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Mark as MERGED all the implied differences recursively from the given one.
+ *
+ * @param diff
+ * The difference from which the implications have to be marked.
+ * @param rightToLeft
+ * The direction of the merge.
+ * @param monitor
+ * Monitor.
+ */
+ private void handleImplies(Diff diff, boolean rightToLeft, Monitor monitor) {
+ for (Diff implied : diff.getImplies()) {
+ implied.setState(DifferenceState.MERGED);
+ handleImplies(implied, rightToLeft, monitor);
+ }
+ }
+
+ /**
+ * Mark as MERGED all the implying differences recursively from the given one.
+ *
+ * @param diff
+ * The difference from which the implications have to be marked.
+ * @param rightToLeft
+ * The direction of the merge.
+ * @param monitor
+ * Monitor.
+ */
+ private void handleImpliedBy(Diff diff, boolean rightToLeft, Monitor monitor) {
+ for (Diff impliedBy : diff.getImpliedBy()) {
+ impliedBy.setState(DifferenceState.MERGED);
+ handleImpliedBy(impliedBy, rightToLeft, monitor);
+ }
+ }
+
+ /**
+ * This will be called when trying to copy a "MOVE" diff.
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param rightToLeft
+ * Whether we should move the value in the left or right side.
+ */
+ protected void moveElement(ReferenceChange diff, boolean rightToLeft) {
+ final Comparison comparison = diff.getMatch().getComparison();
+ final Match valueMatch = comparison.getMatch(diff.getValue());
+ final EReference reference = diff.getReference();
+
+ final EObject expectedContainer;
+ if (reference.isContainment()) {
+ /*
+ * We cannot "trust" the holding match (getMatch) in this case. However, "valueMatch" cannot be
+ * null : we cannot have detected a move if the moved element is not matched on both sides. Use
+ * that information to retrieve the proper "target" container.
+ */
+ final Match targetContainerMatch;
+ // If it exists, use the source side's container as reference
+ if (rightToLeft && valueMatch.getRight() != null) {
+ targetContainerMatch = comparison.getMatch(valueMatch.getRight().eContainer());
+ } else if (!rightToLeft && valueMatch.getLeft() != null) {
+ targetContainerMatch = comparison.getMatch(valueMatch.getLeft().eContainer());
+ } else {
+ // Otherwise, the value we're moving on one side has been removed from its source side.
+ targetContainerMatch = comparison.getMatch(valueMatch.getOrigin().eContainer());
+ }
+ if (rightToLeft) {
+ expectedContainer = targetContainerMatch.getLeft();
+ } else {
+ expectedContainer = targetContainerMatch.getRight();
+ }
+ } else if (rightToLeft) {
+ expectedContainer = diff.getMatch().getLeft();
+ } else {
+ expectedContainer = diff.getMatch().getRight();
+ }
+ if (expectedContainer == null) {
+ // FIXME throw exception? log? re-try to merge our requirements?
+ // one of the "required" diffs should have created our container.
+ return;
+ }
+
+ final EObject expectedValue;
+ if (valueMatch == null) {
+ // The value being moved is out of the scope
+ /*
+ * Note : there should not be a way to end up with a "move" for an out of scope value : a move can
+ * only be detected if the object is matched on both sides, otherwise all we can see is "add" and
+ * "delete"... Is this "fallback" code even reachable? If so, how?
+ */
+ // We need to look it up
+ if (reference.isMany()) {
+ @SuppressWarnings("unchecked")
+ final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
+ expectedValue = findMatchIn(comparison, targetList, diff.getValue());
+ } else {
+ expectedValue = (EObject)expectedContainer.eGet(reference);
+ }
+ } else {
+ if (rightToLeft) {
+ expectedValue = valueMatch.getLeft();
+ } else {
+ expectedValue = valueMatch.getRight();
+ }
+ }
+ // We now know the target container, target reference and target value.
+ doMove(diff, comparison, expectedContainer, expectedValue, rightToLeft);
+ }
+
+ /**
+ * This will do the actual work of moving the element into its reference. All sanity checks were made in
+ * {@link #moveElement(boolean)} and no more verification will be made here.
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param comparison
+ * Comparison holding this Diff.
+ * @param expectedContainer
+ * The container in which we are reorganizing a reference.
+ * @param expectedValue
+ * The value that is to be moved within its reference.
+ * @param rightToLeft
+ * Whether we should move the value in the left or right side.
+ */
+ @SuppressWarnings("unchecked")
+ protected void doMove(ReferenceChange diff, Comparison comparison, EObject expectedContainer,
+ EObject expectedValue, boolean rightToLeft) {
+ final EReference reference = diff.getReference();
+ if (reference.isMany()) {
+ // Element to move cannot be part of the LCS... or there would not be a MOVE diff
+ int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
+
+ /*
+ * However, it could still have been located "before" its new index, in which case we need to take
+ * it into account.
+ */
+ final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
+ final int currentIndex = targetList.indexOf(expectedValue);
+ if (insertionIndex > currentIndex && currentIndex >= 0) {
+ insertionIndex--;
+ }
+
+ if (currentIndex == -1) {
+ // happens for container changes for example.
+ if (!reference.isContainment()) {
+ targetList.remove(expectedValue);
+ }
+ if (insertionIndex < 0 && insertionIndex > targetList.size()) {
+ targetList.add(expectedValue);
+ } else {
+ targetList.add(insertionIndex, expectedValue);
+ }
+ } else if (targetList instanceof EList<?>) {
+ if (insertionIndex < 0 && insertionIndex > targetList.size()) {
+ ((EList<EObject>)targetList).move(targetList.size() - 1, expectedValue);
+ } else {
+ ((EList<EObject>)targetList).move(insertionIndex, expectedValue);
+ }
+ } else {
+ targetList.remove(expectedValue);
+ if (insertionIndex < 0 && insertionIndex > targetList.size()) {
+ targetList.add(expectedValue);
+ } else {
+ targetList.add(insertionIndex, expectedValue);
+ }
+ }
+ } else {
+ expectedContainer.eSet(reference, expectedValue);
+ }
+ }
+
+ /**
+ * This will be called when we need to create an element in the target side.
+ * <p>
+ * All necessary sanity checks have been made to ensure that the current operation is one that should
+ * create an object in its side or add an objet to a reference. In other words, either :
+ * <ul>
+ * <li>We are copying from right to left and
+ * <ul>
+ * <li>we are copying an addition to the right side (we need to create the same object in the left), or</li>
+ * <li>we are copying a deletion from the left side (we need to revert the deletion).</li>
+ * </ul>
+ * </li>
+ * <li>We are copying from left to right and
+ * <ul>
+ * <li>we are copying a deletion from the right side (we need to revert the deletion), or</li>
+ * <li>we are copying an addition to the left side (we need to create the same object in the right).</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param rightToLeft
+ * Tells us whether we are to add an object on the left or right side.
+ */
+ @SuppressWarnings("unchecked")
+ protected void addInTarget(ReferenceChange diff, boolean rightToLeft) {
+ final Match match = diff.getMatch();
+ final EObject expectedContainer;
+ if (rightToLeft) {
+ expectedContainer = match.getLeft();
+ } else {
+ expectedContainer = match.getRight();
+ }
+
+ if (expectedContainer == null) {
+ // FIXME throw exception? log? re-try to merge our requirements?
+ // one of the "required" diffs should have created our container.
+ return;
+ }
+
+ final Comparison comparison = match.getComparison();
+ final EReference reference = diff.getReference();
+ final EObject expectedValue;
+ final Match valueMatch = comparison.getMatch(diff.getValue());
+ if (valueMatch == null) {
+ // This is an out of scope value.
+ if (diff.getValue().eIsProxy()) {
+ // Copy the proxy
+ expectedValue = EcoreUtil.copy(diff.getValue());
+ } else {
+ // Use the same value.
+ expectedValue = diff.getValue();
+ }
+ } else if (rightToLeft) {
+ if (reference.isContainment()) {
+ expectedValue = createCopy(diff.getValue());
+ valueMatch.setLeft(expectedValue);
+ } else {
+ expectedValue = valueMatch.getLeft();
+ }
+ } else {
+ if (reference.isContainment()) {
+ expectedValue = createCopy(diff.getValue());
+ valueMatch.setRight(expectedValue);
+ } else {
+ expectedValue = valueMatch.getRight();
+ }
+ }
+
+ // We have the container, reference and value. We need to know the insertion index.
+ if (reference.isMany()) {
+ final int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
+
+ final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
+ addAt(targetList, expectedValue, insertionIndex);
+ } else {
+ expectedContainer.eSet(reference, expectedValue);
+ }
+
+ if (reference.isContainment()) {
+ // Copy XMI ID when applicable.
+ final Resource initialResource = diff.getValue().eResource();
+ final Resource targetResource = expectedValue.eResource();
+ if (initialResource instanceof XMIResource && targetResource instanceof XMIResource) {
+ ((XMIResource)targetResource).setID(expectedValue, ((XMIResource)initialResource).getID(diff
+ .getValue()));
+ }
+ }
+
+ checkImpliedDiffsOrdering(diff, rightToLeft);
+ }
+
+ /**
+ * This will be called when we need to remove an element from the target side.
+ * <p>
+ * All necessary sanity checks have been made to ensure that the current operation is one that should
+ * delete an object. In other words, we are :
+ * <ul>
+ * <li>Copying from right to left and either
+ * <ul>
+ * <li>we are copying a deletion from the right side (we need to remove the same object in the left) or,</li>
+ * <li>we are copying an addition to the left side (we need to revert the addition).</li>
+ * </ul>
+ * </li>
+ * <li>Copying from left to right and either
+ * <ul>
+ * <li>we are copying an addition to the right side (we need to revert the addition), or.</li>
+ * <li>we are copying a deletion from the left side (we need to remove the same object in the right).</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param rightToLeft
+ * Tells us whether we are to add an object on the left or right side.
+ */
+ @SuppressWarnings("unchecked")
+ protected void removeFromTarget(ReferenceChange diff, boolean rightToLeft) {
+ final Match match = diff.getMatch();
+ final EReference reference = diff.getReference();
+ final EObject currentContainer;
+ if (rightToLeft) {
+ currentContainer = match.getLeft();
+ } else {
+ currentContainer = match.getRight();
+ }
+ final Comparison comparison = match.getComparison();
+ final Match valueMatch = comparison.getMatch(diff.getValue());
+
+ if (currentContainer == null) {
+ // FIXME throw exception? log? re-try to merge our requirements?
+ // one of the "required" diffs should have created our container.
+ return;
+ }
+
+ final EObject expectedValue;
+ if (valueMatch == null) {
+ // value is out of the scope... we need to look it up
+ if (reference.isMany()) {
+ final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);
+ expectedValue = findMatchIn(comparison, targetList, diff.getValue());
+ } else {
+ // the value will not be needed anyway
+ expectedValue = null;
+ }
+ } else if (rightToLeft) {
+ expectedValue = valueMatch.getLeft();
+ } else {
+ expectedValue = valueMatch.getRight();
+ }
+
+ // We have the container, reference and value to remove. Expected value can be null when the
+ // deletion was made on both side (i.e. a pseudo delete)
+ if (reference.isContainment() && expectedValue != null) {
+ EcoreUtil.remove(expectedValue);
+ if (rightToLeft && valueMatch != null) {
+ valueMatch.setLeft(null);
+ } else if (valueMatch != null) {
+ valueMatch.setRight(null);
+ }
+ // TODO remove dangling? remove empty Match?
+ } else if (reference.isMany()) {
+ /*
+ * TODO if the same value appears twice, should we try and find the one that has actually been
+ * deleted? Can it happen? For now, remove the first occurence we find.
+ */
+ final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);
+ targetList.remove(expectedValue);
+ } else {
+ currentContainer.eUnset(reference);
+ }
+ }
+
+ /**
+ * This will be called by the merge operations in order to reset a reference to its original value, be
+ * that the left or right side.
+ * <p>
+ * Should never be called on multi-valued references.
+ * </p>
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param rightToLeft
+ * Tells us the direction of this merge operation.
+ */
+ protected void resetInTarget(ReferenceChange diff, boolean rightToLeft) {
+ final Match match = diff.getMatch();
+ final EReference reference = diff.getReference();
+ final EObject targetContainer;
+ if (rightToLeft) {
+ targetContainer = match.getLeft();
+ } else {
+ targetContainer = match.getRight();
+ }
+
+ final EObject originContainer;
+ if (match.getComparison().isThreeWay()) {
+ originContainer = match.getOrigin();
+ } else if (rightToLeft) {
+ originContainer = match.getRight();
+ } else {
+ originContainer = match.getLeft();
+ }
+
+ if (originContainer == null || !safeEIsSet(targetContainer, reference)
+ || !safeEIsSet(originContainer, reference)) {
+ targetContainer.eUnset(reference);
+ } else {
+ final EObject originalValue = (EObject)originContainer.eGet(reference);
+ final Match valueMatch = match.getComparison().getMatch(originalValue);
+ final EObject expectedValue;
+ if (valueMatch == null) {
+ // Value is out of the scope, use it as-is
+ expectedValue = originalValue;
+ } else if (rightToLeft) {
+ expectedValue = valueMatch.getLeft();
+ } else {
+ expectedValue = valueMatch.getRight();
+ }
+ targetContainer.eSet(reference, expectedValue);
+ }
+ }
+
+ /**
+ * Handles the equivalences of this difference.
+ * <p>
+ * Note that in certain cases, we'll merge our opposite instead of merging this diff. Specifically, we'll
+ * do that for one-to-many eOpposites : we'll merge the 'many' side instead of the 'unique' one. This
+ * allows us not to worry about the order of the references on that 'many' side.
+ * </p>
+ * <p>
+ * This is called before the merge of <code>this</code>. In short, if this returns <code>false</code>, we
+ * won't carry on merging <code>this</code> after returning.
+ * </p>
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param rightToLeft
+ * Direction of the merge.
+ * @param monitor
+ * The monitor to use in order to report progress information.
+ * @return <code>true</code> if the current difference should still be merged after handling its
+ * equivalences, <code>false</code> if it should be considered "already merged".
+ */
+ protected boolean handleEquivalences(ReferenceChange diff, boolean rightToLeft, Monitor monitor) {
+ final EReference reference = diff.getReference();
+ boolean continueMerge = true;
+ for (Diff equivalent : diff.getEquivalence().getDifferences()) {
+ // For 1..*, merge diff on many-valued to preserve ordering
+ if (equivalent instanceof ReferenceChange
+ && reference.getEOpposite() == ((ReferenceChange)equivalent).getReference()
+ && equivalent.getState() == DifferenceState.UNRESOLVED) {
+ // This equivalence is on our eOpposite. Should we merge it instead of 'this'?
+ final boolean mergeEquivalence = !reference.isMany()
+ && ((ReferenceChange)equivalent).getReference().isMany();
+ if (mergeEquivalence) {
+ mergeDiff(equivalent, rightToLeft, monitor);
+ continueMerge = false;
+ }
+ }
+
+ /*
+ * If one of the equivalent differences is implied or implying (depending on the merge direction)
+ * a merged diff, then we have a dependency loop : the "current" difference has already been
+ * merged because of this implication. This will allow us to break out of that loop.
+ */
+ if (rightToLeft) {
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ continueMerge = continueMerge
+ && !containsAny(diff.getRequiredBy(), equivalent.getImplies());
+ } else {
+ continueMerge = continueMerge
+ && !containsAny(diff.getRequires(), equivalent.getImpliedBy());
+ }
+ } else {
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ continueMerge = continueMerge
+ && !containsAny(diff.getRequires(), equivalent.getImpliedBy());
+ } else {
+ continueMerge = continueMerge
+ && !containsAny(diff.getRequiredBy(), equivalent.getImplies());
+ }
+ }
+
+ equivalent.setState(DifferenceState.MERGED);
+ }
+ return continueMerge;
+ }
+
+ /**
+ * In the case of many-to-many eOpposite references, EMF will simply report the difference made on one
+ * side of the equivalence to the other, without considering ordering in any way. In such cases, we'll
+ * iterate over our equivalences after the merge, and double-check the ordering ourselves, fixing it as
+ * needed.
+ * <p>
+ * Note that both implied and equivalent diffs will be double-checked from here.
+ * </p>
+ *
+ * @param diff
+ * The diff we are currently merging.
+ * @param rightToLeft
+ * Direction of the merge.
+ */
+ protected void checkImpliedDiffsOrdering(ReferenceChange diff, boolean rightToLeft) {
+ final EReference reference = diff.getReference();
+ final List<Diff> mergedImplications;
+ if (rightToLeft) {
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ mergedImplications = diff.getImpliedBy();
+ } else {
+ mergedImplications = diff.getImplies();
+ }
+ } else {
+ if (diff.getSource() == DifferenceSource.LEFT) {
+ mergedImplications = diff.getImplies();
+ } else {
+ mergedImplications = diff.getImpliedBy();
+ }
+ }
+
+ Iterator<Diff> impliedDiffs = mergedImplications.iterator();
+ if (reference.isMany() && diff.getEquivalence() != null) {
+ impliedDiffs = Iterators.concat(impliedDiffs, diff.getEquivalence().getDifferences().iterator());
+ }
+ final Iterator<ReferenceChange> impliedReferenceChanges = filter(impliedDiffs, ReferenceChange.class);
+
+ while (impliedReferenceChanges.hasNext()) {
+ final ReferenceChange implied = impliedReferenceChanges.next();
+ if (implied != diff && implied.getState() == DifferenceState.MERGED) {
+ if (implied.getReference().isMany() && implied.getKind() != DifferenceKind.MOVE) {
+ internalCheckOrdering(implied, rightToLeft);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks a particular difference for the ordering of its target values. This will be used to double-check
+ * that equivalent differences haven't been "broken" by EMF by not preserving their value order.
+ * <p>
+ * Should only be used on <u>merged</u> differences which target <u>many-valued</u> references.
+ * </p>
+ *
+ * @param diff
+ * The diff that is to be checked.
+ * @param rightToLeft
+ * Direction of the merge that took place.
+ */
+ private void internalCheckOrdering(ReferenceChange diff, boolean rightToLeft) {
+ final EStructuralFeature feature = diff.getReference();
+ final EObject value = diff.getValue();
+ final Match match = diff.getMatch();
+ final Comparison comparison = match.getComparison();
+ final Match valueMatch = comparison.getMatch(value);
+
+ final EObject sourceContainer;
+ final EObject targetContainer;
+ final EObject newValue;
+ if (rightToLeft) {
+ sourceContainer = match.getRight();
+ targetContainer = match.getLeft();
+ newValue = valueMatch.getLeft();
+ } else {
+ sourceContainer = match.getLeft();
+ targetContainer = match.getRight();
+ newValue = valueMatch.getRight();
+ }
+
+ final List<Object> sourceList = ReferenceUtil.getAsList(sourceContainer, feature);
+ final List<Object> targetList = ReferenceUtil.getAsList(targetContainer, feature);
+
+ final List<Object> lcs = DiffUtil.longestCommonSubsequence(comparison, sourceList, targetList);
+ if (lcs.contains(valueMatch.getLeft()) || lcs.contains(valueMatch.getRight())) {
+ // Ordering is correct on this one
+ return;
+ }
+
+ int insertionIndex = DiffUtil.findInsertionIndex(comparison, sourceList, targetList, value);
+ if (insertionIndex >= 0) {
+ /*
+ * We've used unresolving views of the eobject lists since we didn't know whether there was
+ * actually any work to do. Use the real list now.
+ */
+ @SuppressWarnings("unchecked")
+ final List<EObject> changedList = (List<EObject>)targetContainer.eGet(feature);
+ if (changedList instanceof EList<?>) {
+ if (insertionIndex > changedList.size()) {
+ ((EList<EObject>)changedList).move(changedList.size() - 1, newValue);
+ } else {
+ ((EList<EObject>)changedList).move(insertionIndex, newValue);
+ }
+ } else {
+ changedList.remove(newValue);
+ if (insertionIndex > changedList.size()) {
+ changedList.add(newValue);
+ } else {
+ changedList.add(insertionIndex, newValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility method to check that the first sequence contains one of the elements of the second sequence at
+ * least.
+ *
+ * @param sequence1
+ * The first sequence.
+ * @param sequence2
+ * The second sequence.
+ * @return True if the given first sequence contains one of the elements of the second sequence at least.
+ * false otherwise.
+ */
+ private boolean containsAny(List<? extends EObject> sequence1, List<? extends EObject> sequence2) {
+ return Iterables.any(sequence2, Predicates.in(sequence1));
+ }
+
+ /**
+ * Seeks a match of the given {@code element} in the given list, using the equality helper to find it.
+ * This is only used when moving or deleting proxies for now.
+ *
+ * @param comparison
+ * The comparison which Diff we are currently merging.
+ * @param list
+ * The list from which we seek a value.
+ * @param element
+ * The value for which we need a match in {@code list}.
+ * @return The match of {@code element} in {@code list}, {@code null} if none.
+ */
+ protected EObject findMatchIn(Comparison comparison, List<EObject> list, EObject element) {
+ final IEqualityHelper helper = comparison.getEqualityHelper();
+ final Iterator<EObject> it = list.iterator();
+ while (it.hasNext()) {
+ final EObject next = it.next();
+ if (helper.matchingValues(next, element)) {
+ return next;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This will be used by the distinct merge actions in order to find the index at which a value should be
+ * inserted in its target list. See {@link DiffUtil#findInsertionIndex(Comparison, Diff, boolean)} for
+ * more on this.
+ * <p>
+ * Sub-classes can override this if the insertion order is irrelevant. A return value of {@code -1} will
+ * be considered as "no index" and the value will be inserted at the end of its target list.
+ * </p>
+ *
+ * @param comparison
+ * This will be used in order to retrieve the Match for EObjects when comparing them.
+ * @param diff
+ * The diff which merging will trigger the need for an insertion index in its target list.
+ * @param rightToLeft
+ * {@code true} if the merging will be done into the left list, so that we should consider the
+ * right model as the source and the left as the target.
+ * @return The index at which this {@code diff}'s value should be inserted into the 'target' list, as
+ * inferred from {@code rightToLeft}. {@code -1} if the value should be inserted at the end of its
+ * target list.
+ * @see DiffUtil#findInsertionIndex(Comparison, Diff, boolean)
+ */
+ protected int findInsertionIndex(Comparison comparison, Diff diff, boolean rightToLeft) {
+ return DiffUtil.findInsertionIndex(comparison, diff, rightToLeft);
+ }
+}

Back to the top