Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Goubet2014-10-01 10:01:33 +0000
committerLaurent Goubet2014-10-10 07:47:11 +0000
commit0986e2ac6e2ba95b0299e6a7822bf1df8689f74c (patch)
treed6ddf688c97a3890090b474740d40a71b569a39b /plugins
parente39698b4f20d73041f6cf17af49f633c5094d835 (diff)
downloadorg.eclipse.emf.compare-0986e2ac6e2ba95b0299e6a7822bf1df8689f74c.tar.gz
org.eclipse.emf.compare-0986e2ac6e2ba95b0299e6a7822bf1df8689f74c.tar.xz
org.eclipse.emf.compare-0986e2ac6e2ba95b0299e6a7822bf1df8689f74c.zip
Modify merge to handle implications properly
This introduces an overhaul of the merging process as a whole in order to properly handle implications (a diff implied by another does not need merging in itself, we only need to merge the implying), most notably for the UML comparison support. The same algorithm is now used to merge differences in a batch (or to simply merge implications, results and equivalences) and to display the impact analysis of a merge in the user interface (what will be merged and what will be rejected when I merge such or such difference). Change-Id: I39f69c05f94b5f18cb3a3c3b1f7089700f138282
Diffstat (limited to 'plugins')
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java28
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java2
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java4
-rw-r--r--plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java34
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/AbstractUMLTest.java4
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsAssociationTest.java22
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsInterfaceRealizationTest.java18
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsTransitionTest.java14
-rw-r--r--plugins/org.eclipse.emf.compare.uml2/META-INF/MANIFEST.MF2
-rw-r--r--plugins/org.eclipse.emf.compare.uml2/plugin.xml4
-rw-r--r--plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/merge/UMLReferenceChangeMerger.java55
-rw-r--r--plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/UMLPostProcessor.java22
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java166
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java371
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IMerger2.java56
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java38
-rw-r--r--plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java54
17 files changed, 638 insertions, 256 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java
index 539adcf23..f6e0c3639 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/DependencyData.java
@@ -23,14 +23,13 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil;
import org.eclipse.emf.compare.internal.merge.MergeMode;
import org.eclipse.emf.compare.merge.IMerger;
-import org.eclipse.emf.compare.merge.IMerger2;
import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.ISelection;
@@ -47,7 +46,7 @@ public class DependencyData {
private Set<Diff> requires;
- private Set<Diff> unmergeables;
+ private Set<Diff> rejectedDiffs;
private final WrappableTreeViewer treeViewer;
@@ -58,7 +57,7 @@ public class DependencyData {
this.compareConfiguration = compareConfiguration;
this.treeViewer = treeViewer;
requires = newHashSet();
- unmergeables = newHashSet();
+ rejectedDiffs = newHashSet();
diffToItemsMappings = HashMultimap.create();
}
@@ -74,18 +73,15 @@ public class DependencyData {
MergeMode mergePreviewMode = compareConfiguration.getMergePreviewMode();
requires = newHashSet();
- unmergeables = newHashSet();
+ rejectedDiffs = newHashSet();
for (Diff diff : selectedDiffs) {
boolean leftToRight = mergePreviewMode.isLeftToRight(diff, leftEditable, rightEditable);
- final IMerger diffMerger = mergerRegistry.getHighestRankingMerger(diff);
- if (diffMerger instanceof IMerger2) {
- addAll(requires, ((IMerger2)diffMerger).getResultingMerges(diff, leftToRight, Collections
- .<Diff> emptySet()));
- requires.remove(diff);
- addAll(unmergeables, ((IMerger2)diffMerger).getResultingRejections(diff, leftToRight,
- Collections.<Diff> emptySet()));
- unmergeables.remove(diff);
- }
+ requires.addAll(MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry,
+ !leftToRight));
+ requires.remove(diff);
+ rejectedDiffs.addAll(MergeDependenciesUtil.getAllResultingRejections(diff, mergerRegistry,
+ !leftToRight));
+ rejectedDiffs.remove(diff);
}
}
}
@@ -160,8 +156,8 @@ public class DependencyData {
/**
* @return the unmergeables
*/
- public Set<Diff> getUnmergeables() {
- return unmergeables;
+ public Set<Diff> getRejections() {
+ return rejectedDiffs;
}
public Collection<TreeItem> getTreeItems(Diff diff) {
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java
index a4a39f61a..fc0bd5120 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java
@@ -173,7 +173,7 @@ public class EMFCompareDiffTreeRuler extends Canvas {
.getRequiredStrokeColor());
}
}
- for (Diff diff : dependencyData.getUnmergeables()) {
+ for (Diff diff : dependencyData.getRejections()) {
for (TreeItem item : dependencyData.getTreeItems(diff)) {
createAnnotation(e, item, compareColor.getUnmergeableFillColor(), compareColor
.getUnmergeableStrokeColor());
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
index e94505920..dc5bc3529 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewer.java
@@ -1082,11 +1082,11 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap
EObject dataItem = EMFCompareStructureMergeViewer.getDataOfTreeNodeOfAdapter(item.getData());
if (dataItem != null) {
final Set<Diff> requires = dependencyData.getRequires();
- final Set<Diff> unmergeables = dependencyData.getUnmergeables();
+ final Set<Diff> rejectedDiffs = dependencyData.getRejections();
final GC g = event.gc;
if (requires.contains(dataItem)) {
paintItemBackground(g, item, fColors.getRequiredFillColor());
- } else if (unmergeables.contains(dataItem)) {
+ } else if (rejectedDiffs.contains(dataItem)) {
paintItemBackground(g, item, fColors.getUnmergeableFillColor());
}
}
diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java
index 1ff11f994..3db0d66b4 100644
--- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java
+++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeRunnableImpl.java
@@ -25,6 +25,7 @@ import org.eclipse.emf.compare.DifferenceState;
import org.eclipse.emf.compare.domain.IMergeRunnable;
import org.eclipse.emf.compare.internal.merge.IMergeData;
import org.eclipse.emf.compare.internal.merge.MergeDataImpl;
+import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil;
import org.eclipse.emf.compare.internal.merge.MergeMode;
import org.eclipse.emf.compare.internal.merge.MergeOperation;
import org.eclipse.emf.compare.merge.BatchMerger;
@@ -91,27 +92,19 @@ public final class MergeRunnableImpl implements IMergeRunnable {
private void markAllAsMerged(Collection<Diff> diffToMarkAsMerged, MergeMode mode, Registry mergerRegistry) {
for (Diff diff : diffToMarkAsMerged) {
boolean isLeftToRight = mode.isLeftToRight(diff, isLeftEditable, isRightEditable);
- markAsMerged(diff, mode, isLeftToRight, mergerRegistry);
+ markAsMerged(diff, mode, !isLeftToRight, mergerRegistry);
}
}
- private void markAsMerged(Diff diff, MergeMode mode, boolean leftToRight, Registry mergerRegistry) {
+ private void markAsMerged(Diff diff, MergeMode mode, boolean mergeRightToLeft, Registry mergerRegistry) {
if (diff.getState() == DifferenceState.MERGED) {
return;
}
- IMerger diffMerger = mergerRegistry.getHighestRankingMerger(diff);
- final Set<Diff> implied;
- final Set<Diff> rejections;
- if (diffMerger instanceof IMerger2) {
- implied = ((IMerger2)diffMerger)
- .getResultingMerges(diff, leftToRight, Collections.<Diff> emptySet());
- rejections = ((IMerger2)diffMerger).getResultingRejections(diff, leftToRight, Collections
- .<Diff> emptySet());
- } else {
- implied = Collections.singleton(diff);
- rejections = Collections.emptySet();
- }
+ final Set<Diff> implied = MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry,
+ mergeRightToLeft);
+ final Set<Diff> rejections = MergeDependenciesUtil.getAllResultingRejections(diff, mergerRegistry,
+ mergeRightToLeft);
for (Diff req : implied) {
req.setState(DifferenceState.MERGED);
addOrUpdateMergeData(req, mode);
@@ -155,14 +148,15 @@ public final class MergeRunnableImpl implements IMergeRunnable {
for (Diff difference : differences) {
final IMerger diffMerger = mergerRegistry.getHighestRankingMerger(difference);
if (diffMerger instanceof IMerger2) {
- addOrUpdateMergeData(((IMerger2)diffMerger).getResultingMerges(difference, leftToRight,
- Collections.<Diff> emptySet()), mergeMode);
+ final Set<Diff> resultingMerges = MergeDependenciesUtil.getAllResultingMerges(difference,
+ mergerRegistry, !leftToRight);
+ final Set<Diff> resultingRejections = MergeDependenciesUtil.getAllResultingRejections(
+ difference, mergerRegistry, !leftToRight);
+ addOrUpdateMergeData(resultingMerges, mergeMode);
if (mergeMode == MergeMode.LEFT_TO_RIGHT || mergeMode == MergeMode.RIGHT_TO_LEFT) {
- addOrUpdateMergeData(((IMerger2)diffMerger).getResultingRejections(difference, leftToRight,
- Collections.<Diff> emptySet()), mergeMode);
+ addOrUpdateMergeData(resultingRejections, mergeMode);
} else {
- addOrUpdateMergeData(((IMerger2)diffMerger).getResultingRejections(difference, leftToRight,
- Collections.<Diff> emptySet()), mergeMode.inverse());
+ addOrUpdateMergeData(resultingRejections, mergeMode.inverse());
}
} else {
addOrUpdateMergeData(Collections.singleton(difference), mergeMode);
diff --git a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/AbstractUMLTest.java b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/AbstractUMLTest.java
index 38f732b5a..f125fffa0 100644
--- a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/AbstractUMLTest.java
+++ b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/AbstractUMLTest.java
@@ -48,6 +48,7 @@ import org.eclipse.emf.compare.tests.postprocess.data.TestPostProcessor;
import org.eclipse.emf.compare.uml2.internal.StereotypedElementChange;
import org.eclipse.emf.compare.uml2.internal.UMLDiff;
import org.eclipse.emf.compare.uml2.internal.merge.UMLMerger;
+import org.eclipse.emf.compare.uml2.internal.merge.UMLReferenceChangeMerger;
import org.eclipse.emf.compare.uml2.internal.postprocessor.UMLPostProcessor;
import org.eclipse.emf.compare.uml2.profile.test.uml2comparetestprofile.UML2CompareTestProfilePackage;
import org.eclipse.emf.compare.utils.ReferenceUtil;
@@ -111,8 +112,11 @@ public abstract class AbstractUMLTest {
builder.setPostProcessorRegistry(postProcessorRegistry);
mergerRegistry = IMerger.RegistryImpl.createStandaloneInstance();
final IMerger umlMerger = new UMLMerger();
+ final IMerger umlReferenceChangeMerger = new UMLReferenceChangeMerger();
umlMerger.setRanking(11);
+ umlReferenceChangeMerger.setRanking(25);
mergerRegistry.add(umlMerger);
+ mergerRegistry.add(umlReferenceChangeMerger);
emfCompare = builder.build();
}
diff --git a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsAssociationTest.java b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsAssociationTest.java
index d8071d1a5..48c7b64b7 100644
--- a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsAssociationTest.java
+++ b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsAssociationTest.java
@@ -243,8 +243,14 @@ public class ImplicationsAssociationTest extends AbstractUMLTest {
}
private void checkMergeDeleteNavigableOwnedEnd(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 1, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 7, comparison.getDifferences().size());
assertNull(diffs.addNavigableOwnedEndClass1InAssociation);
+ assertNull(diffs.addOwnedEndClass1InAssociation);
+ assertNull(diffs.addMemberEndClass1InAssociation);
+ assertNull(diffs.addRefAssociationInPropertyClass1);
+ assertNull(diffs.addLiteralIntegerInClass1);
+ assertNull(diffs.addUnlimitedNaturalInClass1);
+ assertNull(diffs.addRefTypeInPropertyClass1);
}
@Test
@@ -267,9 +273,10 @@ public class ImplicationsAssociationTest extends AbstractUMLTest {
}
private void checkMergeAddOwnedEnd(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 5, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 6, comparison.getDifferences().size());
assertNull(diffs.addOwnedEndClass1InAssociation);
assertNull(diffs.addMemberEndClass1InAssociation);
+ assertNull(diffs.addNavigableOwnedEndClass1InAssociation);
assertNull(diffs.addRefAssociationInPropertyClass1);
assertNull(diffs.addAssociation);
assertNull(diffs.addUMLAssociation);
@@ -344,10 +351,11 @@ public class ImplicationsAssociationTest extends AbstractUMLTest {
}
private void checkMergeAddMemberEnd(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 5, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 6, comparison.getDifferences().size());
assertNull(diffs.addMemberEndClass1InAssociation);
assertNull(diffs.addRefAssociationInPropertyClass1);
assertNull(diffs.addOwnedEndClass1InAssociation);
+ assertNull(diffs.addNavigableOwnedEndClass1InAssociation);
assertNull(diffs.addAssociation);
assertNull(diffs.addUMLAssociation);
}
@@ -949,9 +957,11 @@ public class ImplicationsAssociationTest extends AbstractUMLTest {
assertEquals(1, diffs.addOwnedEndClass1InAssociation.getImplies().size());
assertTrue(diffs.addOwnedEndClass1InAssociation.getImplies().contains(
diffs.addNavigableOwnedEndClass1InAssociation));
- assertEquals(1, diffs.addOwnedEndClass1InAssociation.getImpliedBy().size());
+ assertEquals(2, diffs.addOwnedEndClass1InAssociation.getImpliedBy().size());
assertTrue(diffs.addOwnedEndClass1InAssociation.getImpliedBy().contains(
diffs.addMemberEndClass1InAssociation));
+ assertTrue(diffs.addOwnedEndClass1InAssociation.getImpliedBy().contains(
+ diffs.addRefAssociationInPropertyClass1));
assertEquals(1, diffs.addMemberEndClass1InAssociation.getImplies().size());
assertTrue(diffs.addMemberEndClass1InAssociation.getImplies().contains(
@@ -963,9 +973,11 @@ public class ImplicationsAssociationTest extends AbstractUMLTest {
diffs.addOwnedEndClass1InAssociation));
assertEquals(0, diffs.addNavigableOwnedEndClass1InAssociation.getImpliedBy().size());
- assertEquals(1, diffs.addOwnedEndClass1InAssociation.getImplies().size());
+ assertEquals(2, diffs.addOwnedEndClass1InAssociation.getImplies().size());
assertTrue(diffs.addOwnedEndClass1InAssociation.getImplies().contains(
diffs.addMemberEndClass1InAssociation));
+ assertTrue(diffs.addOwnedEndClass1InAssociation.getImplies().contains(
+ diffs.addRefAssociationInPropertyClass1));
assertEquals(1, diffs.addOwnedEndClass1InAssociation.getImpliedBy().size());
assertTrue(diffs.addOwnedEndClass1InAssociation.getImpliedBy().contains(
diffs.addNavigableOwnedEndClass1InAssociation));
diff --git a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsInterfaceRealizationTest.java b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsInterfaceRealizationTest.java
index 0a808a474..5d6d6b89b 100644
--- a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsInterfaceRealizationTest.java
+++ b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsInterfaceRealizationTest.java
@@ -7,6 +7,7 @@ import static org.eclipse.emf.compare.utils.EMFComparePredicates.changedReferenc
import static org.eclipse.emf.compare.utils.EMFComparePredicates.removedFromReference;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
@@ -654,15 +655,18 @@ public class ImplicationsInterfaceRealizationTest extends AbstractUMLTest {
DiffsOfInterest diffs = getDiffs(comparison, kind);
if (kind.equals(TestKind.DELETE)) {
- // there were tests for UML4, no longer valid for 5.0 with clientDependency being
- // marked as derived.
+ assertEquals(1, diffs.addInterfaceRealization.getImpliedBy().size());
+ assertTrue(diffs.addInterfaceRealization.getImpliedBy().contains(
+ diffs.addClientInInterfaceRealization));
+ assertEquals(1, diffs.addSubstitution.getImpliedBy().size());
+ assertTrue(diffs.addSubstitution.getImpliedBy().contains(diffs.addClientInSubstitution));
} else {
- assertEquals(0, diffs.addInterfaceRealization.getImplies().size());
- assertEquals(0, diffs.addSubstitution.getImplies().size());
+ assertEquals(1, diffs.addInterfaceRealization.getImplies().size());
+ assertTrue(diffs.addInterfaceRealization.getImplies().contains(
+ diffs.addClientInInterfaceRealization));
+ assertEquals(1, diffs.addSubstitution.getImplies().size());
+ assertTrue(diffs.addSubstitution.getImplies().contains(diffs.addClientInSubstitution));
}
- assertEquals(0, diffs.addClientInInterfaceRealization.getImplies().size());
- assertEquals(0, diffs.addClientInSubstitution.getImplies().size());
-
}
@Override
diff --git a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsTransitionTest.java b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsTransitionTest.java
index 27e3b2af6..6ebac78ab 100644
--- a/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsTransitionTest.java
+++ b/plugins/org.eclipse.emf.compare.uml2.tests/src/org/eclipse/emf/compare/uml2/tests/implications/ImplicationsTransitionTest.java
@@ -160,8 +160,10 @@ public class ImplicationsTransitionTest extends AbstractUMLTest {
}
private void checkMergeDeletePrecondition(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 1, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 3, comparison.getDifferences().size());
assertNull(diffs.addPrecondition);
+ assertNull(diffs.addGuard);
+ assertNull(diffs.addOwnedRule);
}
@Test
@@ -184,9 +186,10 @@ public class ImplicationsTransitionTest extends AbstractUMLTest {
}
private void checkMergeAddGuard(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 3, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 4, comparison.getDifferences().size());
assertNull(diffs.addGuard);
assertNull(diffs.addOwnedRule);
+ assertNull(diffs.addPrecondition);
assertNull(diffs.addTransition);
}
@@ -229,9 +232,10 @@ public class ImplicationsTransitionTest extends AbstractUMLTest {
}
private void checkMergeDeleteGuard(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 2, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 3, comparison.getDifferences().size());
assertNull(diffs.addGuard);
assertNull(diffs.addPrecondition);
+ assertNull(diffs.addOwnedRule);
}
@Test
@@ -254,9 +258,11 @@ public class ImplicationsTransitionTest extends AbstractUMLTest {
}
private void checkMergeAddOwnedRule(Comparison comparison, DiffsOfInterest diffs) {
- assertEquals(NB_DIFFS - 2, comparison.getDifferences().size());
+ assertEquals(NB_DIFFS - 4, comparison.getDifferences().size());
assertNull(diffs.addOwnedRule);
+ assertNull(diffs.addGuard);
assertNull(diffs.addTransition);
+ assertNull(diffs.addPrecondition);
}
@Test
diff --git a/plugins/org.eclipse.emf.compare.uml2/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare.uml2/META-INF/MANIFEST.MF
index 2c77447e5..d58dda05b 100644
--- a/plugins/org.eclipse.emf.compare.uml2/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.compare.uml2/META-INF/MANIFEST.MF
@@ -27,7 +27,7 @@ Export-Package: org.eclipse.emf.compare.uml2.internal;
org.eclipse.emf.compare.uml2.rcp,
org.eclipse.emf.compare.uml2.rcp.ui,
org.eclipse.emf.compare.uml2.tests",
- org.eclipse.emf.compare.uml2.internal.merge;x-internal:=true,
+ org.eclipse.emf.compare.uml2.internal.merge;x-friends:="org.eclipse.emf.compare.uml2.tests",
org.eclipse.emf.compare.uml2.internal.postprocessor;x-friends:="org.eclipse.emf.compare.diagram.papyrus.tests,org.eclipse.emf.compare.uml2.tests,org.eclipse.emf.compare.uml2.rcp.ui.tests",
org.eclipse.emf.compare.uml2.internal.postprocessor.extension;x-internal:=true,
org.eclipse.emf.compare.uml2.internal.postprocessor.extension.clazz;x-internal:=true,
diff --git a/plugins/org.eclipse.emf.compare.uml2/plugin.xml b/plugins/org.eclipse.emf.compare.uml2/plugin.xml
index 097b9792b..c9f90ebea 100644
--- a/plugins/org.eclipse.emf.compare.uml2/plugin.xml
+++ b/plugins/org.eclipse.emf.compare.uml2/plugin.xml
@@ -47,6 +47,10 @@ Contributors:
class="org.eclipse.emf.compare.uml2.internal.merge.UMLMerger"
ranking="20">
</merger>
+ <merger
+ class="org.eclipse.emf.compare.uml2.internal.merge.UMLReferenceChangeMerger"
+ ranking="25">
+ </merger>
</extension>
</plugin>
diff --git a/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/merge/UMLReferenceChangeMerger.java b/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/merge/UMLReferenceChangeMerger.java
new file mode 100644
index 000000000..a4c49b5ee
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/merge/UMLReferenceChangeMerger.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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.uml2.internal.merge;
+
+import java.util.List;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.ReferenceChange;
+import org.eclipse.emf.compare.merge.ReferenceChangeMerger;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.uml2.uml.UMLPackage;
+
+/**
+ * UML does not support "EList#addUnique" correctly, especially with references that are part of a
+ * subset/superset relationship. This specific merger will intercept calls to list additions in order to user
+ * the under-performing "EList#add" instead.
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+public class UMLReferenceChangeMerger extends ReferenceChangeMerger {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
+ */
+ @Override
+ public boolean isMergerFor(Diff target) {
+ if (target instanceof ReferenceChange) {
+ final EObject container = ((ReferenceChange)target).getReference().eContainer();
+ if (container instanceof EClass) {
+ return ((EClass)container).getEPackage() instanceof UMLPackage;
+ }
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected <E> void addAt(List<E> list, E value, int insertionIndex) {
+ if (insertionIndex < 0 || insertionIndex > list.size()) {
+ list.add(value);
+ } else {
+ list.add(insertionIndex, value);
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/UMLPostProcessor.java b/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/UMLPostProcessor.java
index bb421951b..18d1e16f9 100644
--- a/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/UMLPostProcessor.java
+++ b/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/UMLPostProcessor.java
@@ -344,5 +344,27 @@ public class UMLPostProcessor implements IPostProcessor {
}
}
}
+ // If we have an eOpposite and we are a containment reference, then we also imply/are implied by the
+ // changes to this opposite's subset-superset reference.
+ // "interfaceRealization" is an eOpposite of "implementingClassifier" which in turn is a super set of
+ // "InterfaceRealization#client". Adding the InterfaceRealization into a Class#interfaceRealizations
+ // reference implies the setting of this particular realization's "client" reference.
+ if (reference.isContainment() && reference.getEOpposite() != null) {
+ for (EReference superSet : UMLCompareUtil.getNonUnionSupersetReferences(reference.getEOpposite())) {
+ Comparison comparison = diff.getMatch().getComparison();
+ for (Diff superSetDiff : comparison.getDifferences(superSet)) {
+ if (superSetDiff instanceof ReferenceChange
+ && ((ReferenceChange)superSetDiff).getReference() == superSet
+ && ((ReferenceChange)superSetDiff).getMatch() == comparison.getMatch(diff
+ .getValue())) {
+ if (isAddOrSetDiff(diff) && isAddOrSetDiff(superSetDiff)) {
+ diff.getImplies().add(superSetDiff);
+ } else if (isDeleteOrUnsetDiff(diff) && isDeleteOrUnsetDiff(superSetDiff)) {
+ diff.getImpliedBy().add(superSetDiff);
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java
new file mode 100644
index 000000000..022f3d7b3
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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.internal.merge;
+
+import com.google.common.collect.Sets;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.merge.IMerger;
+import org.eclipse.emf.compare.merge.IMerger2;
+
+/**
+ * Factorizes utilities used throughout EMF Compare to explore merge dependencies.
+ *
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+public final class MergeDependenciesUtil {
+ /** Hides default constructor. */
+ private MergeDependenciesUtil() {
+ // Hides default constructor
+ }
+
+ /**
+ * Retrieves the set of all diffs related to the given <code>diff</code> when merging in the given
+ * direction.
+ * <p>
+ * This is expected to return the set of all differences that will be need to merged along when a user
+ * wishes to merge <code>diff</code>, either because they are required by it or because they are implied
+ * by it one way or another.
+ * </p>
+ * <p>
+ * Note that <code>diff</code> will be included in the returned set.
+ * </p>
+ *
+ * @param diff
+ * The difference for which we seek all related ones.
+ * @param mergerRegistry
+ * The {@link IMerger.Registry merger registry} currently in use.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return The set of all diffs related to the given <code>diff</code> when merging in the given
+ * direction.
+ */
+ public static Set<Diff> getAllResultingMerges(Diff diff, IMerger.Registry mergerRegistry,
+ boolean mergeRightToLeft) {
+ final Set<Diff> resultingMerges = new LinkedHashSet<Diff>();
+ resultingMerges.add(diff);
+
+ Set<Diff> relations = internalGetResultingMerges(diff, mergerRegistry, mergeRightToLeft);
+ Set<Diff> difference = Sets.difference(relations, resultingMerges);
+ while (!difference.isEmpty()) {
+ final Set<Diff> newRelations = new LinkedHashSet<Diff>(difference);
+ resultingMerges.addAll(newRelations);
+ relations = new LinkedHashSet<Diff>();
+ for (Diff newRelation : newRelations) {
+ relations.addAll(internalGetResultingMerges(newRelation, mergerRegistry, mergeRightToLeft));
+ }
+ difference = Sets.difference(relations, resultingMerges);
+ }
+
+ return resultingMerges;
+ }
+
+ /**
+ * Returns the set of all differences <b>directly</b> related to the given one, either as dependencies or
+ * as implications.
+ *
+ * @param diff
+ * The difference for which we seek all directly related others.
+ * @param mergerRegistry
+ * The {@link IMerger.Registry merger registry} currently in use.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return The set of all differences <b>directly</b> related to the given one.
+ */
+ private static Set<Diff> internalGetResultingMerges(Diff diff, IMerger.Registry mergerRegistry,
+ boolean mergeRightToLeft) {
+ final IMerger merger = mergerRegistry.getHighestRankingMerger(diff);
+
+ final Set<Diff> directParents;
+ final Set<Diff> directImplications;
+ if (merger instanceof IMerger2) {
+ directParents = ((IMerger2)merger).getDirectMergeDependencies(diff, mergeRightToLeft);
+ directImplications = ((IMerger2)merger).getDirectResultingMerges(diff, mergeRightToLeft);
+ } else {
+ directParents = Collections.emptySet();
+ directImplications = Collections.emptySet();
+ }
+
+ return Sets.newLinkedHashSet(Sets.union(directParents, directImplications));
+ }
+
+ /**
+ * Retrieves the set of all diffs that will be rejected if the given <code>diff</code> is merged, either
+ * because of unresolveable conflicts or because of unreachable requirements.
+ *
+ * @param diff
+ * The difference for which we seek all opposite ones.
+ * @param mergerRegistry
+ * The {@link IMerger.Registry merger registry} currently in use.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return The set of all diffs that will be rejected if the given <code>diff</code> is merged in the
+ * given direction.
+ */
+ public static Set<Diff> getAllResultingRejections(Diff diff, IMerger.Registry mergerRegistry,
+ boolean mergeRightToLeft) {
+ final Set<Diff> resultingRejections = new LinkedHashSet<Diff>();
+
+ final Set<Diff> allResultingMerges = getAllResultingMerges(diff, mergerRegistry, mergeRightToLeft);
+ for (Diff resulting : allResultingMerges) {
+ Set<Diff> rejections = internalGetResultingRejections(resulting, mergerRegistry, mergeRightToLeft);
+ Set<Diff> difference = Sets.difference(rejections, resultingRejections);
+ while (!difference.isEmpty()) {
+ final Set<Diff> newRejections = new LinkedHashSet<Diff>(difference);
+ resultingRejections.addAll(newRejections);
+ rejections = new LinkedHashSet<Diff>();
+ for (Diff rejected : newRejections) {
+ final IMerger merger = mergerRegistry.getHighestRankingMerger(diff);
+ if (merger instanceof IMerger2) {
+ rejections.addAll(((IMerger2)merger).getDirectMergeDependencies(rejected,
+ mergeRightToLeft));
+ rejections.addAll(((IMerger2)merger).getDirectResultingMerges(rejected,
+ mergeRightToLeft));
+ }
+ }
+ difference = Sets.difference(rejections, resultingRejections);
+ }
+ }
+
+ return resultingRejections;
+ }
+
+ /**
+ * Returns the set of differences directly related to <code>diff</code> that will be rejected if it is
+ * merged.
+ *
+ * @param diff
+ * The difference for which we seek all opposite ones.
+ * @param mergerRegistry
+ * The {@link IMerger.Registry merger registry} currently in use.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return The set of all directly related differences that will be rejected if <code>diff</code> is
+ * merged in the given direction.
+ */
+ private static Set<Diff> internalGetResultingRejections(Diff diff, IMerger.Registry mergerRegistry,
+ boolean mergeRightToLeft) {
+ final IMerger merger = mergerRegistry.getHighestRankingMerger(diff);
+ if (merger instanceof IMerger2) {
+ return ((IMerger2)merger).getDirectResultingRejections(diff, mergeRightToLeft);
+ }
+ return Collections.emptySet();
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java
index 345699bd9..9cf097c1c 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java
@@ -11,15 +11,12 @@
package org.eclipse.emf.compare.merge;
import static com.google.common.base.Predicates.in;
-import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.any;
import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -96,72 +93,64 @@ public abstract class AbstractMerger implements IMerger2 {
*
* @since 3.1
*/
- public Set<Diff> getResultingMerges(Diff diff, boolean mergeLeftToRight, Set<Diff> knownImplications) {
- final Set<Diff> relations = new LinkedHashSet<Diff>();
- relations.add(diff);
- if (mergeLeftToRight) {
+ public Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) {
+ final Set<Diff> dependencies = new LinkedHashSet<Diff>();
+ if (mergeRightToLeft) {
if (DifferenceSource.LEFT == diff.getSource()) {
- relations.addAll(getImpliedDiffsOf(diff.getRequires(), mergeLeftToRight, Sets.union(
- relations, knownImplications)));
- relations.addAll(getRecursiveImplies(diff));
+ dependencies.addAll(diff.getImplies());
+ dependencies.addAll(diff.getRequiredBy());
} else {
- relations.addAll(getImpliedDiffsOf(diff.getRequiredBy(), mergeLeftToRight, Sets.union(
- relations, knownImplications)));
- relations.addAll(getRecursiveImpliedBy(diff));
+ dependencies.addAll(diff.getImpliedBy());
+ dependencies.addAll(diff.getRequires());
}
} else {
if (DifferenceSource.LEFT == diff.getSource()) {
- relations.addAll(getImpliedDiffsOf(diff.getRequiredBy(), mergeLeftToRight, Sets.union(
- relations, knownImplications)));
- relations.addAll(getRecursiveImpliedBy(diff));
+ dependencies.addAll(diff.getImpliedBy());
+ dependencies.addAll(diff.getRequires());
} else {
- relations.addAll(getImpliedDiffsOf(diff.getRequires(), mergeLeftToRight, Sets.union(
- relations, knownImplications)));
- relations.addAll(getRecursiveImplies(diff));
+ dependencies.addAll(diff.getImplies());
+ dependencies.addAll(diff.getRequiredBy());
}
}
- relations.addAll(getImpliedDiffsOf(diff.getRefinedBy(), mergeLeftToRight, Sets.union(relations,
- knownImplications)));
+ dependencies.addAll(diff.getRefinedBy());
if (diff.getEquivalence() != null) {
- relations.addAll(diff.getEquivalence().getDifferences());
- }
- if (diff.getConflict() != null && diff.getConflict().getKind() == ConflictKind.PSEUDO) {
- relations.addAll(diff.getConflict().getDifferences());
+ final Diff masterEquivalence = findMasterEquivalence(diff, mergeRightToLeft);
+ if (masterEquivalence != null && masterEquivalence != diff) {
+ dependencies.add(masterEquivalence);
+ }
}
- return relations;
+ return dependencies;
}
/**
- * Prompt the merger of each of the given diffs to tell us what will be implied by their merge. This will
- * recursively call {@link #getImpliedDiffs(Diff, boolean)}.
- * <p>
- * Note that the diffs themselves are included in the returned set (i.e. a diff is considered to be
- * implying itself as far as the merge is concerned).
- * </p>
+ * {@inheritDoc}
*
- * @param subDiffs
- * The diffs for which merges we need the implications.
- * @param mergeLeftToRight
- * The direction in which we're considering a merge.
- * @param knownImplications
- * The set of Diffs already known as being implied by our starting point. Since there may be
- * implication cycles, this can be used to break free. This method is not supposed to add
- * anything to this set.
- * @return All of the recursive implications of merging the given differences in the given direction.
+ * @since 3.1
*/
- private Set<Diff> getImpliedDiffsOf(Iterable<Diff> subDiffs, boolean mergeLeftToRight,
- Set<Diff> knownImplications) {
- final Set<Diff> relations = new LinkedHashSet<Diff>();
- for (Diff dependency : Iterables.filter(subDiffs, not(in(knownImplications)))) {
- IMerger dependencyMerger = getRegistry().getHighestRankingMerger(dependency);
- if (dependencyMerger instanceof IMerger2) {
- relations.addAll(((IMerger2)dependencyMerger).getResultingMerges(dependency, mergeLeftToRight,
- Sets.union(relations, knownImplications)));
+ public Set<Diff> getDirectResultingMerges(Diff target, boolean mergeRightToLeft) {
+ final Set<Diff> resulting = new LinkedHashSet<Diff>();
+ if (mergeRightToLeft) {
+ if (DifferenceSource.LEFT == target.getSource()) {
+ resulting.addAll(target.getImpliedBy());
+ } else {
+ resulting.addAll(target.getImplies());
+ }
+ } else {
+ if (DifferenceSource.LEFT == target.getSource()) {
+ resulting.addAll(target.getImplies());
} else {
- relations.add(dependency);
+ resulting.addAll(target.getImpliedBy());
}
}
- return relations;
+ if (target.getEquivalence() != null) {
+ resulting.addAll(target.getEquivalence().getDifferences());
+ resulting.remove(target);
+ }
+ if (target.getConflict() != null && target.getConflict().getKind() == ConflictKind.PSEUDO) {
+ resulting.addAll(target.getConflict().getDifferences());
+ resulting.remove(target);
+ }
+ return resulting;
}
/**
@@ -169,80 +158,76 @@ public abstract class AbstractMerger implements IMerger2 {
*
* @since 3.1
*/
- public Set<Diff> getResultingRejections(Diff diff, boolean mergeLeftToRight, Set<Diff> knownRejections) {
- final Set<Diff> impliedRejections = new LinkedHashSet<Diff>();
-
- /*
- * Consider that we are merging "diff" from left to right. For all the diffs implied by this merge
- * (including "diff"), we need to look at possible conflicts. For any of these diffs that does have a
- * real conflict, we are in essence rejecting the diffs from the opposite side (say we are merging a
- * diff detected on the left side, all diffs from the right side conflicting with it won't be
- * mergeable once all is said and done). Transitively, a diff that was requiring one of the rejected
- * ones will also be rejected. We do not need to check for conflicts on the rejected ones though :
- * "rejecting" a diff in conflict means nothing for the other diffs of said conflict, except that they
- * can now be merged on demand.
- */
- /*
- * "accepting" a diff is merging a diff from the DifferenceSource#LEFT side from left to right, or a
- * diff from the DifferenceSource#RIGHT side from right to left. Rejecting a diff is ... the reverse
- * of that.
- */
-
- final Set<Diff> impliedDiffs = getResultingMerges(diff, mergeLeftToRight, Collections.<Diff> emptySet());
- for (Diff implied : impliedDiffs) {
- final Conflict conflict = implied.getConflict();
- if (conflict != null && conflict.getKind() == ConflictKind.REAL) {
- final Iterable<Diff> directlyImpliedRejections;
- if (mergeLeftToRight && implied.getSource() == DifferenceSource.LEFT) {
- directlyImpliedRejections = Iterables.filter(conflict.getDifferences(),
- fromSide(DifferenceSource.RIGHT));
- } else if (!mergeLeftToRight && implied.getSource() == DifferenceSource.RIGHT) {
- directlyImpliedRejections = Iterables.filter(conflict.getDifferences(),
- fromSide(DifferenceSource.LEFT));
- } else {
- directlyImpliedRejections = new HashSet<Diff>();
- }
- // And the diffs requiring our rejections are, in turn, rejected.
- Iterables.addAll(impliedRejections, getImpliedDiffsOf(directlyImpliedRejections,
- mergeLeftToRight, knownRejections));
+ public Set<Diff> getDirectResultingRejections(Diff target, boolean mergeRightToLeft) {
+ final Set<Diff> directlyImpliedRejections = new LinkedHashSet<Diff>();
+ final Conflict conflict = target.getConflict();
+ if (conflict != null && conflict.getKind() == ConflictKind.REAL) {
+ if (mergeRightToLeft && target.getSource() == DifferenceSource.RIGHT) {
+ Iterables.addAll(directlyImpliedRejections, Iterables.filter(conflict.getDifferences(),
+ fromSide(DifferenceSource.LEFT)));
+ } else if (!mergeRightToLeft && target.getSource() == DifferenceSource.LEFT) {
+ Iterables.addAll(directlyImpliedRejections, Iterables.filter(conflict.getDifferences(),
+ fromSide(DifferenceSource.RIGHT)));
}
}
-
- return impliedRejections;
+ return directlyImpliedRejections;
}
/**
- * Returns all of the differences "implied" as in {@link Diff#getImplies()} differences of the given
- * starting point, recursively.
+ * Even within 'equivalent' differences, there might be one that we need to consider as the "master", one
+ * part of the equivalence that should take precedence over the others when merging.
+ * <p>
+ * There are two main cases for which this happens :
+ * <ol>
+ * <li>Equivalent differences regarding two "eOpposite" sides, with one side being a multiple valued
+ * reference while the other side is a single valued reference. In such a case, we need the 'many' side of
+ * that equivalence to be merged over the 'single' side, so as to avoid potential ordering issues.</li>
+ * <li>Equivalent differences with conflicts. Basically, if one of the diffs of an equivalence relation is
+ * in conflict while the others are not, then none of the equivalent differences can be automatically
+ * merged. We need to consider the conflict to be taking precedence over the others to make sure that the
+ * conflict is resolved before even trying to merge anything.</li>
+ * </ol>
+ * </p>
*
* @param diff
- * The diff for which we need all recursive implications.
- * @return all of the differences implied by the given starting point.
+ * The diff we need to check the equivalence for a 'master' difference.
+ * @param mergeRightToLeft
+ * Direction of the merge operation.
+ * @return The master difference of this equivalence relation. May be <code>null</code> if there are none.
*/
- private Set<Diff> getRecursiveImplies(Diff diff) {
- final Set<Diff> implies = new LinkedHashSet<Diff>();
- implies.addAll(diff.getImplies());
- for (Diff implied : diff.getImplies()) {
- implies.addAll(getRecursiveImplies(implied));
+ private Diff findMasterEquivalence(Diff diff, boolean mergeRightToLeft) {
+ Diff masterDiff = null;
+ final Iterator<Diff> equivalentDiffs = diff.getEquivalence().getDifferences().iterator();
+ while (masterDiff == null && equivalentDiffs.hasNext()) {
+ final Diff candidate = equivalentDiffs.next();
+ if (hasRealConflict(candidate)) {
+ masterDiff = candidate;
+ } else if (diff instanceof ReferenceChange && candidate instanceof ReferenceChange) {
+ final EReference reference = ((ReferenceChange)diff).getReference();
+ final EReference equivalentReference = ((ReferenceChange)candidate).getReference();
+
+ if (reference.getEOpposite() == equivalentReference
+ && candidate.getState() == DifferenceState.UNRESOLVED) {
+ // This equivalence is on our eOpposite. Is it an addition in a multivalued reference?
+ if (!reference.isMany() && equivalentReference.isMany()
+ && isAdd((ReferenceChange)candidate, mergeRightToLeft)) {
+ masterDiff = candidate;
+ }
+ }
+ }
}
- return implies;
+ return masterDiff;
}
/**
- * Returns all of the differences "impliedBy" as in {@link Diff#getImpliedBY()} differences of the given
- * starting point, recursively.
+ * Returns <code>true</code> if this diff is in a <u>real</u> conflict with another.
*
* @param diff
- * The diff for which we need all recursive implying diffs.
- * @return all of the differences implying the given starting point.
+ * The diff we are to check for conflicts.
+ * @return <code>true</code> if this diff is in a <u>real</u> conflict with another.
*/
- private Set<Diff> getRecursiveImpliedBy(Diff diff) {
- final Set<Diff> implying = new LinkedHashSet<Diff>();
- implying.addAll(diff.getImpliedBy());
- for (Diff impliedBy : diff.getImpliedBy()) {
- implying.addAll(getRecursiveImpliedBy(impliedBy));
- }
- return implying;
+ private boolean hasRealConflict(Diff diff) {
+ return diff.getConflict() != null && diff.getConflict().getKind() == ConflictKind.REAL;
}
/**
@@ -261,26 +246,20 @@ public abstract class AbstractMerger implements IMerger2 {
// Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
target.setState(DifferenceState.MERGED);
- if (target.getSource() == DifferenceSource.LEFT) {
- // merge all "requires" diffs
- mergeRequires(target, false, monitor);
- handleImplies(target, false, monitor);
- } else {
- // merge all "required by" diffs
- mergeRequiredBy(target, false, monitor);
- handleImpliedBy(target, false, monitor);
+ final Set<Diff> dependencies = getDirectMergeDependencies(target, false);
+ for (Diff mergeMe : dependencies) {
+ mergeDiff(mergeMe, false, monitor);
}
- for (Diff refining : target.getRefinedBy()) {
- mergeDiff(refining, false, monitor);
+ for (Diff transitiveMerge : getDirectResultingMerges(target, false)) {
+ transitiveMerge.setState(DifferenceState.MERGED);
}
- boolean hasToBeMerged = true;
- if (target.getEquivalence() != null) {
- hasToBeMerged = handleEquivalences(target, false, monitor);
- }
+ // We'll redo some of the work from getDirectMergeDependencies here in order to ensure we haven't been
+ // merged by another diff (equivalence or implication)
+ boolean requiresMerging = requiresMerging(target, false);
- if (hasToBeMerged) {
+ if (requiresMerging) {
if (target.getSource() == DifferenceSource.LEFT) {
accept(target, false);
} else {
@@ -305,26 +284,20 @@ public abstract class AbstractMerger implements IMerger2 {
// Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
target.setState(DifferenceState.MERGED);
- if (target.getSource() == DifferenceSource.LEFT) {
- // merge all "required by" diffs
- mergeRequiredBy(target, true, monitor);
- handleImpliedBy(target, true, monitor);
- } else {
- // merge all "requires" diffs
- mergeRequires(target, true, monitor);
- handleImplies(target, true, monitor);
+ final Set<Diff> dependencies = getDirectMergeDependencies(target, true);
+ for (Diff mergeMe : dependencies) {
+ mergeDiff(mergeMe, true, monitor);
}
- for (Diff refining : target.getRefinedBy()) {
- mergeDiff(refining, true, monitor);
+ for (Diff transitiveMerge : getDirectResultingMerges(target, true)) {
+ transitiveMerge.setState(DifferenceState.MERGED);
}
- boolean hasToBeMerged = true;
- if (target.getEquivalence() != null) {
- hasToBeMerged = handleEquivalences(target, true, monitor);
- }
+ // We'll redo some of the work from getDirectMergeDependencies here in order to ensure we haven't been
+ // merged by another diff (equivalence or implication)
+ boolean requiresMerging = requiresMerging(target, true);
- if (hasToBeMerged) {
+ if (requiresMerging) {
if (target.getSource() == DifferenceSource.LEFT) {
reject(target, true);
} else {
@@ -334,6 +307,104 @@ public abstract class AbstractMerger implements IMerger2 {
}
/**
+ * Checks whether the given diff still needs to be merged or if it has been merged because of an
+ * implication or 'master' equivalence.
+ *
+ * @param target
+ * The difference we are considering merging.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return <code>true</code> if the <code>target</code> diff hasn't been merged yet and requires handling
+ * of its own.
+ */
+ private boolean requiresMerging(Diff target, boolean mergeRightToLeft) {
+ boolean requiresMerging = true;
+ if (isImpliedMerge(target, mergeRightToLeft)) {
+ // first, if we are implied by something, then we're already merged
+ requiresMerging = false;
+ } else if (target.getEquivalence() != null) {
+ final Diff masterEquivalence = findMasterEquivalence(target, false);
+ if (masterEquivalence != null && masterEquivalence != target) {
+ // If we have a "master" equivalence (see doc on findMasterEquivalence) then we've been merged
+ // along with it
+ requiresMerging = false;
+ } else {
+ // We also need to check for implications on our equivalence (dependency loops)
+ requiresMerging = !hasTransitiveImplicationBeenMerged(target, mergeRightToLeft);
+ }
+ }
+ return requiresMerging;
+ }
+
+ /**
+ * Checks if the given diff is implied by another in the given merge direction, which means that it
+ * doesn't need to be merged individually.
+ *
+ * @param target
+ * The diff we're considering merging.
+ * @param mergeRightToLeft
+ * The direction in which we're currently merging.
+ * @return <code>true</code> if the given diff will be implicitely merged by another in that direction.
+ */
+ private boolean isImpliedMerge(Diff target, boolean mergeRightToLeft) {
+ final boolean isImpliedForDirection;
+ if (mergeRightToLeft) {
+ if (DifferenceSource.LEFT == target.getSource()) {
+ isImpliedForDirection = !target.getImplies().isEmpty();
+ } else {
+ isImpliedForDirection = !target.getImpliedBy().isEmpty();
+ }
+ } else {
+ if (DifferenceSource.LEFT == target.getSource()) {
+ isImpliedForDirection = !target.getImpliedBy().isEmpty();
+ } else {
+ isImpliedForDirection = !target.getImplies().isEmpty();
+ }
+ }
+ return isImpliedForDirection;
+ }
+
+ // FIXME find a use case and check whether this is still required.
+ /**
+ * Checks whether the given diff has been merged through a dependency cycle on its equivalence relations
+ * (this diff requires the merging of a diff that implies one of its equivalences).
+ * <p>
+ * This should only be called on differences that have equivalences.
+ * </p>
+ *
+ * @param target
+ * The difference we are considering merging.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return <code>true</code> if the <code>target</code> diff has already been merged.
+ */
+ private boolean hasTransitiveImplicationBeenMerged(Diff target, boolean mergeRightToLeft) {
+ boolean mergedThroughEquivalentImplication = false;
+ final Iterator<Diff> equivalenceIterator = target.getEquivalence().getDifferences().iterator();
+ while (!mergedThroughEquivalentImplication && equivalenceIterator.hasNext()) {
+ final Diff equivalent = equivalenceIterator.next();
+ if (equivalent != target && mergeRightToLeft) {
+ if (target.getSource() == DifferenceSource.LEFT) {
+ mergedThroughEquivalentImplication = any(equivalent.getImplies(), in(target
+ .getRequiredBy()));
+ } else {
+ mergedThroughEquivalentImplication = any(equivalent.getImpliedBy(), in(target
+ .getRequires()));
+ }
+ } else if (equivalent != target) {
+ if (target.getSource() == DifferenceSource.LEFT) {
+ mergedThroughEquivalentImplication = any(equivalent.getImpliedBy(), in(target
+ .getRequires()));
+ } else {
+ mergedThroughEquivalentImplication = any(equivalent.getImplies(), in(target
+ .getRequiredBy()));
+ }
+ }
+ }
+ return mergedThroughEquivalentImplication;
+ }
+
+ /**
* Accept the given difference. This may be overridden by clients.
*
* @param diff
@@ -370,7 +441,9 @@ public abstract class AbstractMerger implements IMerger2 {
* {@code diff}. Otherwise, {@link #copyLeftToRight(Diff, Monitor) revert} them.
* @param monitor
* The monitor we should use to report progress.
+ * @Deprecated
*/
+ @Deprecated
protected void mergeRequiredBy(Diff diff, boolean rightToLeft, Monitor monitor) {
// TODO log back to the user what we will merge along?
for (Diff dependency : diff.getRequiredBy()) {
@@ -390,10 +463,13 @@ public abstract class AbstractMerger implements IMerger2 {
* @param monitor
* Monitor.
* @since 3.1
+ * @deprecated
*/
+ @Deprecated
protected void handleImplies(Diff diff, boolean rightToLeft, Monitor monitor) {
- for (Diff implied : getRecursiveImplies(diff)) {
+ for (Diff implied : diff.getImplies()) {
implied.setState(DifferenceState.MERGED);
+ handleImplies(implied, rightToLeft, monitor);
}
}
@@ -408,11 +484,11 @@ public abstract class AbstractMerger implements IMerger2 {
* @param monitor
* Monitor.
* @since 3.1
+ * @deprecated
*/
+ @Deprecated
protected void handleImpliedBy(Diff diff, boolean rightToLeft, Monitor monitor) {
- for (Diff impliedBy : getRecursiveImpliedBy(diff)) {
- impliedBy.setState(DifferenceState.MERGED);
- }
+ // Do nothing, this was an implementation error
}
/**
@@ -426,7 +502,9 @@ public abstract class AbstractMerger implements IMerger2 {
* Otherwise, {@link #copyLeftToRight(Diff, Monitor) revert} them.
* @param monitor
* The monitor we should use to report progress.
+ * @deprecated
*/
+ @Deprecated
protected void mergeRequires(Diff diff, boolean rightToLeft, Monitor monitor) {
// TODO log back to the user what we will merge along?
for (Diff dependency : diff.getRequires()) {
@@ -477,7 +555,9 @@ public abstract class AbstractMerger implements IMerger2 {
* @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".
* @since 3.1
+ * @Deprecated
*/
+ @Deprecated
protected boolean handleEquivalences(Diff diff, boolean rightToLeft, Monitor monitor) {
boolean continueMerge = true;
for (Diff equivalent : diff.getEquivalence().getDifferences()) {
@@ -538,6 +618,7 @@ public abstract class AbstractMerger implements IMerger2 {
* Direction of the merge.
* @return <code>true</code> if {@code diff} will add a value with this merge, <code>false</code>
* otherwise.
+ * @since 3.1
*/
protected boolean isAdd(ReferenceChange diff, boolean rightToLeft) {
if (rightToLeft) {
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IMerger2.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IMerger2.java
index 230e13ce6..f735a10e0 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IMerger2.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IMerger2.java
@@ -31,45 +31,39 @@ import org.eclipse.emf.compare.Diff;
*/
public interface IMerger2 extends IMerger {
/**
- * Retrieves the set of all diffs related to the given <code>diff</code> when merging in the given
- * direction.
- * <p>
- * This is expected to return the set of all differences that will be merged along when a user wishes to
- * merge <code>diff</code>, either because they are related or because they are equivalent one way or
- * another.
- * </p>
- * <p>
- * Note that as far as the merged is concerned a given diff is considered to be implying itself.
- * <code>diff</code> will thus be included in the returned set.
- * </p>
+ * Retrieves the set of <b>directly</b> required diffs in order to merge the current one. This may
+ * includes the diff's {@link Diff#getRequires() requirements}, its {@link Diff#getImpliedBy() implying
+ * diff}, or any other diff that we need to merge <u>before</u> the given one.
*
* @param diff
- * The difference for which we seek all related ones.
+ * The diff which direct requirements we need.
* @param mergeLeftToRight
* The direction in which we're considering a merge.
- * @param knownImplications
- * The set of Diffs already known as being implied by our starting point. Since there may be
- * implication cycles, this can be used to break free. Callees are not supposed to add the new
- * implications they find within this set.
- * @return The set of all diffs related to the given <code>diff</code> when merging in the given
- * direction.
+ * @return The set of <b>directly</b> required diffs in order to merge the current one.
*/
- Set<Diff> getResultingMerges(Diff diff, boolean mergeLeftToRight, Set<Diff> knownImplications);
+ Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeLeftToRight);
/**
- * Retrieves the set of all diffs that will be rejected if the given <code>diff</code> is merged, either
- * because of unresolveable conflicts or because of unreachable requirements.
+ * Returns all differences that will be merged because of our merging the given <code>target</code>
+ * difference.
*
- * @param diff
- * The difference for which we seek all opposite ones.
- * @param mergeLeftToRight
+ * @param target
+ * The difference we're considering merging.
+ * @param mergeRightToLeft
+ * The direction in which we're considering a merge.
+ * @return The Set of all differences that will be merged because we've merged <code>target</code>.
+ */
+ Set<Diff> getDirectResultingMerges(Diff target, boolean mergeRightToLeft);
+
+ /**
+ * Returns the set of all differences that need to be rejected if <code>target</code> is merged in the
+ * given direction.
+ *
+ * @param target
+ * The difference we're considering merging.
+ * @param mergeRightToLeft
* The direction in which we're considering a merge.
- * @param knownRejections
- * The set of Diffs already known as being rejected by our starting point. Since there may be
- * rejection cycles, this can be used to break free. Callees are not supposed to add the new
- * rejections they find within this set.
- * @return The set of all diffs that will be rejected if the given <code>diff</code> is merged in the
- * given direction.
+ * @return The Set of all differences that will be rejected if we are to merge merged <code>target</code>.
*/
- Set<Diff> getResultingRejections(Diff diff, boolean mergeLeftToRight, Set<Diff> knownRejections);
+ Set<Diff> getDirectResultingRejections(Diff target, boolean mergeRightToLeft);
}
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 484a35410..0ba26e4eb 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
@@ -17,9 +17,12 @@ import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEIsSet;
import static org.eclipse.emf.compare.utils.ReferenceUtil.safeESet;
import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.compare.Comparison;
@@ -405,14 +408,14 @@ public class ReferenceChangeMerger extends AbstractMerger {
expectedValue = diff.getValue();
}
} else if (rightToLeft) {
- if (reference.isContainment()) {
+ if (reference.isContainment() || isImplyingContainmentAddition(diff, rightToLeft)) {
expectedValue = createCopy(diff.getValue());
valueMatch.setLeft(expectedValue);
} else {
expectedValue = valueMatch.getLeft();
}
} else {
- if (reference.isContainment()) {
+ if (reference.isContainment() || isImplyingContainmentAddition(diff, rightToLeft)) {
expectedValue = createCopy(diff.getValue());
valueMatch.setRight(expectedValue);
} else {
@@ -430,7 +433,7 @@ public class ReferenceChangeMerger extends AbstractMerger {
safeESet(expectedContainer, reference, expectedValue);
}
- if (reference.isContainment()) {
+ if (reference.isContainment() || isImplyingContainmentAddition(diff, rightToLeft)) {
// Copy XMI ID when applicable.
final Resource initialResource = diff.getValue().eResource();
final Resource targetResource = expectedValue.eResource();
@@ -444,6 +447,35 @@ public class ReferenceChangeMerger extends AbstractMerger {
}
/**
+ * Checks whether the given diff implies the merge of a containment reference, in which case we need to
+ * create the object (it hasn't been created through our requirements yet).
+ *
+ * @param target
+ * The diff we're merging.
+ * @param mergeRightToLeft
+ * The direction in which we're merging.
+ * @return <code>true</code> if this diff implies the merge of a containment reference.
+ */
+ private boolean isImplyingContainmentAddition(Diff target, boolean mergeRightToLeft) {
+ final Set<Diff> implications = new LinkedHashSet<Diff>();
+ Set<Diff> newImplications = getDirectResultingMerges(target, mergeRightToLeft);
+ while (!newImplications.isEmpty()) {
+ final Set<Diff> copy = new LinkedHashSet<Diff>(newImplications);
+ implications.addAll(newImplications);
+ newImplications = new LinkedHashSet<Diff>();
+ for (Diff implied : copy) {
+ if (implied instanceof ReferenceChange
+ && ((ReferenceChange)implied).getReference().isContainment()) {
+ return true;
+ }
+ newImplications.addAll(getDirectResultingMerges(implied, mergeRightToLeft));
+ }
+ newImplications = Sets.difference(newImplications, implications);
+ }
+ return false;
+ }
+
+ /**
* 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
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java
index 65f2462eb..08c724eeb 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/req/DefaultReqEngine.java
@@ -30,6 +30,7 @@ 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.FeatureMapChange;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.ReferenceChange;
@@ -89,7 +90,7 @@ public class DefaultReqEngine implements IReqEngine {
// -> requires ADD on the container of the object
requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, value.eContainer(),
- DifferenceKind.ADD));
+ difference.getSource(), DifferenceKind.ADD));
// -> requires DELETE of the origin value on the same containment mono-valued reference
requiredDifferences.addAll(getDELOriginValueOnContainmentRefSingle(comparison, difference));
@@ -98,13 +99,14 @@ public class DefaultReqEngine implements IReqEngine {
} else if (isAddition && !isFeatureMapContainment(difference)) {
// -> requires ADD of the value of the reference (target object)
- requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, value, DifferenceKind.ADD));
+ requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, value, difference
+ .getSource(), DifferenceKind.ADD));
// -> requires ADD of the object containing the reference
final EObject container = MatchUtil.getContainer(comparison, difference);
if (container != null) {
- requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, container,
- DifferenceKind.ADD));
+ requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, container, difference
+ .getSource(), DifferenceKind.ADD));
}
requiredDifferences.addAll(Collections2.filter(match.getDifferences(), and(
instanceOf(ResourceAttachmentChange.class), ofKind(DifferenceKind.ADD))));
@@ -115,7 +117,7 @@ public class DefaultReqEngine implements IReqEngine {
// -> requires DELETE of the outgoing references and contained objects
requiredDifferences.addAll(getDELOutgoingReferences(comparison, difference));
requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, value.eContents(),
- DifferenceKind.DELETE));
+ difference.getSource(), DifferenceKind.DELETE));
// -> requires MOVE of contained objects
requiredDifferences.addAll(getMOVEContainedObjects(comparison, difference));
@@ -127,8 +129,8 @@ public class DefaultReqEngine implements IReqEngine {
} else if (isDeletion && !isFeatureMapContainment(difference)) {
// -> is required by DELETE of the target object
- requiredByDifferences.addAll(getDifferenceOnGivenObject(comparison, value,
- DifferenceKind.DELETE));
+ requiredByDifferences.addAll(getDifferenceOnGivenObject(comparison, value, difference
+ .getSource(), DifferenceKind.DELETE));
// MOVE object
} else if (kind == DifferenceKind.MOVE && isReferenceContainment(difference)) {
@@ -136,12 +138,12 @@ public class DefaultReqEngine implements IReqEngine {
EObject container = value.eContainer();
// -> requires ADD on the container of the object
- requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, container,
- DifferenceKind.ADD));
+ requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, container, difference
+ .getSource(), DifferenceKind.ADD));
// -> requires MOVE of the container of the object
- requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, container,
- DifferenceKind.MOVE));
+ requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, container, difference
+ .getSource(), DifferenceKind.MOVE));
// CHANGE reference
} else if (kind == DifferenceKind.CHANGE && !isAddition && !isDeletion
@@ -149,10 +151,12 @@ public class DefaultReqEngine implements IReqEngine {
// -> is required by DELETE of the origin target object
requiredByDifferences.addAll(getDifferenceOnGivenObject(comparison, MatchUtil.getOriginValue(
- comparison, (ReferenceChange)difference), DifferenceKind.DELETE));
+ comparison, (ReferenceChange)difference), difference.getSource(),
+ DifferenceKind.DELETE));
// -> requires ADD of the value of the reference (target object) if required
- requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, value, DifferenceKind.ADD));
+ requiredDifferences.addAll(getDifferenceOnGivenObject(comparison, value, difference
+ .getSource(), DifferenceKind.ADD));
}
difference.getRequires().addAll(requiredDifferences);
@@ -182,8 +186,8 @@ public class DefaultReqEngine implements IReqEngine {
if (originContainer != null) {
Object originValue = ReferenceUtil.safeEGet(originContainer, reference);
if (originValue instanceof EObject) {
- result = getDifferenceOnGivenObject(comparison, (EObject)originValue,
- DifferenceKind.DELETE);
+ result = getDifferenceOnGivenObject(comparison, (EObject)originValue, sourceDifference
+ .getSource(), DifferenceKind.DELETE);
}
}
}
@@ -198,13 +202,17 @@ public class DefaultReqEngine implements IReqEngine {
* the comparison to search in.
* @param object
* The given object.
+ * @param source
+ * source of the differences. A diff from the left cannot "require" a diff from the right...
* @param kind
* The given kind.
* @return The found differences.
*/
- private Set<Diff> getDifferenceOnGivenObject(Comparison comparison, EObject object, DifferenceKind kind) {
+ private Set<Diff> getDifferenceOnGivenObject(Comparison comparison, EObject object,
+ DifferenceSource source, DifferenceKind kind) {
final Set<Diff> result = new LinkedHashSet<Diff>();
- for (Diff diff : filter(comparison.getDifferences(object), isRequiredContainmentChange(object, kind))) {
+ for (Diff diff : filter(comparison.getDifferences(object), isRequiredContainmentChange(object,
+ source, kind))) {
result.add(diff);
}
return result;
@@ -217,15 +225,17 @@ public class DefaultReqEngine implements IReqEngine {
*
* @param object
* The object for which we seek containmnent differences.
+ * @param source
+ * source of the differences. A diff from the left cannot "require" a diff from the right...
* @param kind
* The kind of difference we seek.
* @return The created predicate.
*/
private Predicate<? super Diff> isRequiredContainmentChange(final EObject object,
- final DifferenceKind kind) {
+ final DifferenceSource source, final DifferenceKind kind) {
return new Predicate<Diff>() {
public boolean apply(Diff input) {
- if (input.getKind() != kind) {
+ if (input == null || input.getKind() != kind || input.getSource() != source) {
return false;
}
@@ -249,15 +259,17 @@ public class DefaultReqEngine implements IReqEngine {
* the comparison to search in.
* @param objects
* The given objects.
+ * @param source
+ * source of the differences. A diff from the left cannot "require" a diff from the right...
* @param kind
* The kind of requested differences.
* @return The found differences.
*/
private Set<Diff> getDifferenceOnGivenObject(Comparison comparison, List<EObject> objects,
- DifferenceKind kind) {
+ DifferenceSource source, DifferenceKind kind) {
Set<Diff> result = new HashSet<Diff>();
for (EObject object : objects) {
- result.addAll(getDifferenceOnGivenObject(comparison, object, kind));
+ result.addAll(getDifferenceOnGivenObject(comparison, object, source, kind));
}
return result;
}

Back to the top