Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTanja Mayerhofer2016-09-21 18:03:02 -0400
committerPhilip Langer2016-10-05 06:22:16 -0400
commit8fcb916fb0f3ae13630d9b4603e2286767d0878a (patch)
treeeeb36899d9a4e81cecf7e5b0dffeac3ad10dacd5
parentcd602d97ac023dc2cac936e946e0e5c45cd5ecad (diff)
downloadorg.eclipse.emf.compare-8fcb916fb0f3ae13630d9b4603e2286767d0878a.tar.gz
org.eclipse.emf.compare-8fcb916fb0f3ae13630d9b4603e2286767d0878a.tar.xz
org.eclipse.emf.compare-8fcb916fb0f3ae13630d9b4603e2286767d0878a.zip
[501864] Join conflicts with overlapping diffs in SMV
If multiple conflicts contain overlapping diffs, these conflicts are joined into one conflict node shown in the SMV. Overlapping diffs may occur due to the replacement of refining diffs with their refined diffs for conflict nodes. Bug: 501864 Change-Id: Ie5cb460ede7f9f7157796e6a95173b10d4fe50a2 Signed-off-by: Tanja Mayerhofer <tmayerhofer@eclipsesource.com>
-rw-r--r--plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java207
-rw-r--r--plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/suite/AllTests.java13
-rw-r--r--plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java138
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java82
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/UMLJoiningConflictsWithOverlappingDiffsInputData.java32
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/left.uml13
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/origin.uml13
-rw-r--r--plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/right.uml5
8 files changed, 481 insertions, 22 deletions
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java
new file mode 100644
index 000000000..d0c5cf89b
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Obeo and others.
+ * 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
+ * Tanja Mayerhofer - bug 501864
+ *******************************************************************************/
+package org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups;
+
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.compare.CompareFactory;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Conflict;
+import org.eclipse.emf.compare.ConflictKind;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.provider.spec.CompareItemProviderAdapterFactorySpec;
+import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareRCPUIMessages;
+import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl;
+import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl.CompositeConflict;
+import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.ConflictNode;
+import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups.provider.AbstractTestTreeNodeItemProviderAdapter;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory;
+import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import org.eclipse.emf.edit.provider.AdapterFactoryItemDelegator;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
+import org.eclipse.emf.edit.tree.TreeNode;
+import org.eclipse.emf.edit.tree.TreePackage;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+@SuppressWarnings("restriction")
+public class ConflictsGroupTest extends AbstractTestTreeNodeItemProviderAdapter {
+
+ private static final CompareFactory FACTORY = CompareFactory.eINSTANCE;
+
+ private AdapterFactoryItemDelegator itemDelegator;
+
+ @Override
+ public void before() throws IOException {
+ super.before();
+
+ final Collection<AdapterFactory> factories = Lists.newArrayList();
+ factories.add(new CompareItemProviderAdapterFactorySpec());
+ factories.add(treeItemProviderAdapterFactory);
+ factories.add(new EcoreItemProviderAdapterFactory());
+ factories.add(new ReflectiveItemProviderAdapterFactory());
+
+ final AdapterFactory composedAdapterFactory = new ComposedAdapterFactory(factories);
+ itemDelegator = new AdapterFactoryItemDelegator(composedAdapterFactory);
+ }
+
+ /**
+ * Tests that multiple conflicts containing overlapping diffs are merged in the SMV. Overlapping diffs can
+ * happen due to the replacement of refining diffs with their refined diffs in the SMV. This test is
+ * related to <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=501864"</a>
+ */
+ @Test
+ public void testMergingMultipleConflictsWithOverlappingDiffs() {
+ // Create diffs
+ final Diff diff1 = FACTORY.createDiff();
+ final Diff diff1a = FACTORY.createDiff();
+ final Diff diff1b = FACTORY.createDiff();
+ diff1.getRefinedBy().add(diff1a);
+ diff1.getRefinedBy().add(diff1b);
+
+ final Diff diff2 = FACTORY.createDiff();
+
+ final Diff diff3 = FACTORY.createDiff();
+ final Diff diff3a = FACTORY.createDiff();
+ final Diff diff3b = FACTORY.createDiff();
+ diff3.getRefinedBy().add(diff3a);
+ diff3.getRefinedBy().add(diff3b);
+
+ final Diff diff4 = FACTORY.createDiff();
+
+ // Create overlapping conflicts
+ Conflict conflict1 = FACTORY.createConflict();
+ conflict1.getDifferences().add(diff1a);
+ conflict1.getDifferences().add(diff2);
+
+ Conflict conflict2 = FACTORY.createConflict();
+ conflict2.getDifferences().add(diff3a);
+ conflict2.getDifferences().add(diff4);
+
+ Conflict conflict3 = FACTORY.createConflict();
+ conflict3.getDifferences().add(diff1b);
+ conflict3.getDifferences().add(diff3b);
+
+ // Create comparison
+ final Comparison comparison = FACTORY.createComparison();
+ comparison.getConflicts().add(conflict1);
+ comparison.getConflicts().add(conflict2);
+ comparison.getConflicts().add(conflict3);
+
+ // Build conflict nodes
+ ECrossReferenceAdapter crossReferenceAdapter = new ECrossReferenceAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.ecore.util.ECrossReferenceAdapter#isIncluded(org.eclipse.emf.ecore.EReference)
+ */
+ @Override
+ protected boolean isIncluded(EReference eReference) {
+ return eReference == TreePackage.Literals.TREE_NODE__DATA;
+ }
+ };
+ final ConflictsGroupImpl conflictsGroup = new ConflictsGroupImpl(comparison,
+ hasConflict(ConflictKind.REAL, ConflictKind.PSEUDO),
+ EMFCompareRCPUIMessages.getString("ThreeWayComparisonGroupProvider.conflicts.label"), //$NON-NLS-1$
+ crossReferenceAdapter);
+ conflictsGroup.buildSubTree();
+
+ // One conflict node was created
+ List<? extends TreeNode> conflictNodes = conflictsGroup.getChildren();
+ assertEquals(1, conflictNodes.size());
+
+ // The conflict node groups the three created conflicts
+ ConflictNode conflictNode = (ConflictNode)conflictNodes.get(0);
+ CompositeConflict compositeConflict = (CompositeConflict)conflictNode.basicGetData();
+ assertEquals(3, compositeConflict.getConflicts().size());
+
+ // The composite conflict contains all refined diffs
+ EList<Diff> differences = compositeConflict.getDifferences();
+ assertEquals(4, differences.size());
+ assertTrue(differences.contains(diff1));
+ assertTrue(differences.contains(diff2));
+ assertTrue(differences.contains(diff3));
+ assertTrue(differences.contains(diff4));
+ }
+
+ /**
+ * Tests that a composite conflict has the conflict kind {@link ConflictKind#REAL} if it contains at least
+ * one conflict of conflict kind {@link ConflictKind#REAL}. This test is related to <a
+ * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=501864"</a>
+ */
+ @Test
+ public void testDerivationOfConflictGroupKindReal() {
+ final Conflict realConflict = FACTORY.createConflict();
+ realConflict.setKind(ConflictKind.REAL);
+
+ final Conflict pseudoConflict = FACTORY.createConflict();
+ pseudoConflict.setKind(ConflictKind.PSEUDO);
+
+ final CompositeConflict realCompositeConflict = new CompositeConflict(realConflict);
+ assertEquals(ConflictKind.REAL, realCompositeConflict.getKind());
+
+ final CompositeConflict pseudoCompositeConflict = new CompositeConflict(pseudoConflict);
+ assertEquals(ConflictKind.PSEUDO, pseudoCompositeConflict.getKind());
+
+ // The union of one real conflict and one pseudo conflict results in a composite conflict with
+ // conflict kind real
+ pseudoCompositeConflict.join(realCompositeConflict);
+ final CompositeConflict mergedConflictGroup = pseudoCompositeConflict;
+ assertEquals(2, mergedConflictGroup.getConflicts().size());
+ assertTrue(mergedConflictGroup.getConflicts().contains(realConflict));
+ assertTrue(mergedConflictGroup.getConflicts().contains(pseudoConflict));
+ assertEquals(ConflictKind.REAL, mergedConflictGroup.getKind());
+ }
+
+ /**
+ * Tests that a composite conflict has the conflict kind {@link ConflictKind#PSEUDO} if it contains only
+ * conflicts of conflict kind {@link ConflictKind#PSEUDO}. This test is related to <a
+ * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=501864"</a>
+ */
+ @Test
+ public void testDerivationOfConflictGroupKindPseudo() {
+ final Conflict pseudoConflict1 = FACTORY.createConflict();
+ pseudoConflict1.setKind(ConflictKind.PSEUDO);
+
+ final Conflict pseudoConflict2 = FACTORY.createConflict();
+ pseudoConflict2.setKind(ConflictKind.PSEUDO);
+
+ final CompositeConflict pseudoCompositeConflict1 = new CompositeConflict(pseudoConflict1);
+ assertEquals(ConflictKind.PSEUDO, pseudoCompositeConflict1.getKind());
+
+ final CompositeConflict pseudoCompositeConflict2 = new CompositeConflict(pseudoConflict2);
+ assertEquals(ConflictKind.PSEUDO, pseudoCompositeConflict2.getKind());
+
+ // The union of pseudo conflicts results in a composite conflict with conflict kind pseudo
+ pseudoCompositeConflict2.join(pseudoCompositeConflict1);
+ final CompositeConflict mergedConflictGroup = pseudoCompositeConflict2;
+ assertEquals(2, mergedConflictGroup.getConflicts().size());
+ assertTrue(mergedConflictGroup.getConflicts().contains(pseudoConflict1));
+ assertTrue(mergedConflictGroup.getConflicts().contains(pseudoConflict2));
+ assertEquals(ConflictKind.PSEUDO, mergedConflictGroup.getKind());
+ }
+
+ protected void checkText(TreeNode childNode, String expected) {
+ assertEquals(expected, itemDelegator.getText(childNode));
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/suite/AllTests.java
index 4447864a0..8b2d0fe64 100644
--- a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/suite/AllTests.java
+++ b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/suite/AllTests.java
@@ -8,19 +8,17 @@
* Contributors:
* Obeo - initial API and implementation
* Philip Langer - bug 486923
+ * Tanja Mayerhofer - bug 501864
*******************************************************************************/
package org.eclipse.emf.compare.rcp.ui.tests.suite;
-import junit.framework.JUnit4TestAdapter;
-import junit.framework.Test;
-import junit.textui.TestRunner;
-
import org.eclipse.emf.compare.ComparePackage;
import org.eclipse.emf.compare.rcp.ui.tests.match.RCPMatchEngineFactoryRegistryTest;
import org.eclipse.emf.compare.rcp.ui.tests.mergeviewer.item.MergeViewerItemFeatureMapsTest;
import org.eclipse.emf.compare.rcp.ui.tests.mergeviewer.item.MergeViewerItemPseudoConflictTest;
import org.eclipse.emf.compare.rcp.ui.tests.mergeviewer.item.MergeViewerItemTest;
import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.filters.TestFeatureMapDifferencesFilter;
+import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups.ConflictsGroupTest;
import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups.TestBasicDifferenceGroupImpl;
import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups.provider.TestComparisonTreeNodeItemProviderSpec;
import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups.provider.TestMatchTreeNodeItemProviderSpec;
@@ -35,12 +33,17 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
@RunWith(Suite.class)
@SuiteClasses({TestComparisonTreeNodeItemProviderSpec.class, TestMatchTreeNodeItemProviderSpec.class,
TestReferenceChangeTreeNodeItemProviderSpec.class, MergeViewerItemTest.class,
MergeViewerItemPseudoConflictTest.class, MergeViewerItemFeatureMapsTest.class,
TestBasicDifferenceGroupImpl.class, BugsTestSuite.class, TestFeatureMapDifferencesFilter.class,
- RCPMatchEngineFactoryRegistryTest.class, ThreeWayComparisonGroupProviderTest.class, })
+ RCPMatchEngineFactoryRegistryTest.class, ThreeWayComparisonGroupProviderTest.class,
+ ConflictsGroupTest.class, })
public class AllTests {
/**
* Launches the test with the given arguments.
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java
index a5f162cc0..c31ee5d4d 100644
--- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java
@@ -20,6 +20,7 @@ import static com.google.common.collect.Iterators.concat;
import static com.google.common.collect.Iterators.filter;
import static com.google.common.collect.Iterators.transform;
import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide;
import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict;
import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasState;
@@ -27,13 +28,16 @@ import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasState;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
@@ -56,6 +60,7 @@ import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.Confli
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.AbstractDifferenceGroupProvider;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup;
import org.eclipse.emf.compare.scope.IComparisonScope;
+import org.eclipse.emf.compare.utils.EMFComparePredicates;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
@@ -92,6 +97,16 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv
public static class ConflictsGroupImpl extends BasicDifferenceGroupImpl {
/**
+ * Conflict groups to show in SMV.
+ */
+ private final List<CompositeConflict> compositeConflicts = newArrayList();
+
+ /**
+ * Maps conflicting differences to their composite conflicts.
+ */
+ private final Map<Diff, CompositeConflict> diffToCompositeConflictMap = newHashMap();
+
+ /**
* {@inheritDoc}.
*
* @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.BasicDifferenceGroupImpl#BasicDifferenceGroupImpl(org.eclipse.emf.compare.Comparison,
@@ -120,38 +135,94 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv
@Override
protected void doBuildSubTrees() {
for (Conflict conflict : getComparison().getConflicts()) {
- ConflictGroup conflictGroup = new ConflictGroup(conflict);
- ConflictNodeBuilder builder = new ConflictNodeBuilder(conflictGroup, this);
- ConflictNode conflictNode = builder.buildNode();
+ final CompositeConflict compositeConflict = new CompositeConflict(conflict);
+ compositeConflicts.add(compositeConflict);
+ joinOverlappingCompositeConflicts(compositeConflict);
+ updateDiffToCompositeConflictMap(compositeConflict);
+ }
+
+ for (CompositeConflict conflict : compositeConflicts) {
+ final ConflictNodeBuilder builder = new ConflictNodeBuilder(conflict, this);
+ final ConflictNode conflictNode = builder.buildNode();
children.add(conflictNode);
}
}
/**
- * This implementation of {@link Conflict} is used to re-define conflicts for the SMV. Conflicts are
- * re-define to contain refined diffs instead of refining diffs.
+ * Joins the given composite conflict with existing overlapping composite conflicts.
+ *
+ * @param compositeConflict
+ * The composite conflict into which overlapping composite conflicts should be joined
+ */
+ private void joinOverlappingCompositeConflicts(final CompositeConflict compositeConflict) {
+ // determine composite conflicts to join
+ final Set<CompositeConflict> compositeConflictsToJoin = new HashSet<CompositeConflict>();
+ for (Diff diff : compositeConflict.getDifferences()) {
+ if (diffToCompositeConflictMap.containsKey(diff)) {
+ compositeConflictsToJoin.add(diffToCompositeConflictMap.get(diff));
+ }
+ }
+ // join conflict groups
+ for (CompositeConflict conflictGroupToJoin : compositeConflictsToJoin) {
+ compositeConflict.join(conflictGroupToJoin);
+ compositeConflicts.remove(conflictGroupToJoin);
+ }
+ }
+
+ /**
+ * Updates the diff to composite conflict map with the given composite conflict.
+ *
+ * @param compositeConflict
+ * The composite conflict that should be added to the diff to composite conflict map
+ */
+ private void updateDiffToCompositeConflictMap(final CompositeConflict compositeConflict) {
+ for (Diff diff : compositeConflict.getDifferences()) {
+ diffToCompositeConflictMap.put(diff, compositeConflict);
+ }
+ }
+
+ /**
+ * This extension of {@link Conflict} is used to handle {@link Diff#getRefinedBy() refined} diffs and
+ * to join conflicts for the SMV. If refining diffs are part of a conflict, we show their refined
+ * diffs instead. As we show refined diffs instead of the refining diffs, multiple conflicts may
+ * consequently include the same refined diffs. To avoid that, this extension of a conflict also joins
+ * such overlapping conflicts.
*
* @author <a href="mailto:tmayerhofer@eclipsesource.com">Tanja Mayerhofer</a>
*/
- public static class ConflictGroup extends ConflictImpl {
+ public static class CompositeConflict extends ConflictImpl {
- private final Conflict conflict;
+ /** The joined conflicts. */
+ private Set<Conflict> conflicts = new LinkedHashSet<Conflict>();
+ /** The diffs of all composed conflicts. */
private EList<Diff> diffs = new BasicEList<Diff>();
- public ConflictGroup(Conflict conflict) {
- this.conflict = checkNotNull(conflict);
- this.diffs.addAll(computeDiffs());
+ /** The conflict kind of this composite conflict. */
+ private ConflictKind conflictKind = ConflictKind.REAL;
+
+ /**
+ * Creates a new composite conflict for the given conflict.
+ *
+ * @param conflict
+ * The conflict to create a composite conflict for.
+ */
+ public CompositeConflict(Conflict conflict) {
+ this.conflicts.add(checkNotNull(conflict));
+ this.conflictKind = conflict.getKind();
+ this.diffs.addAll(computeRefinedDiffs(conflict));
}
/**
- * Computes the re-defined diffs of the conflict. In particular, refining diffs are replaces by
+ * Computes the refined diffs of the conflict. In particular, refining diffs are replaced by
* refined diffs.
*
- * @return The set of re-defined diffs of the conflict
+ * @param conflict
+ * The conflict to compute its refined diffs for.
+ * @return The set of refined diffs of the conflict
*/
- private Set<Diff> computeDiffs() {
- LinkedHashSet<Diff> computedDiffs = new LinkedHashSet<Diff>();
+ private Set<Diff> computeRefinedDiffs(Conflict conflict) {
+ final LinkedHashSet<Diff> computedDiffs = new LinkedHashSet<Diff>();
for (Diff diff : conflict.getDifferences()) {
if (diff.getRefines().isEmpty()) {
computedDiffs.add(diff);
@@ -171,7 +242,7 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv
* @return The leaf refined diff of the provided (refining diff)
*/
private List<Diff> getRootRefinedDiffs(Diff diff) {
- List<Diff> rootRefinedDiffs = newArrayList();
+ final List<Diff> rootRefinedDiffs = newArrayList();
for (Diff refinedDiff : diff.getRefines()) {
if (refinedDiff.getRefines().isEmpty()) {
rootRefinedDiffs.add(refinedDiff);
@@ -184,7 +255,7 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv
@Override
public ConflictKind getKind() {
- return this.conflict.getKind();
+ return this.conflictKind;
}
@Override
@@ -192,6 +263,37 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv
return this.diffs;
}
+ /**
+ * Returns the joined conflicts.
+ *
+ * @return The joined conflicts
+ */
+ public Set<Conflict> getConflicts() {
+ return conflicts;
+ }
+
+ /**
+ * Joins the provided composite conflict with this composite conflict.
+ *
+ * @param conflict
+ * The conflict to be joined with this composite conflict
+ */
+ public void join(CompositeConflict conflict) {
+ final LinkedHashSet<Diff> joinedDiffs = new LinkedHashSet<Diff>(
+ Sets.union(new LinkedHashSet<Diff>(this.diffs),
+ new LinkedHashSet<Diff>(conflict.getDifferences())));
+ this.diffs.clear();
+ this.diffs.addAll(joinedDiffs);
+ this.conflicts.addAll(conflict.getConflicts());
+
+ final Conflict realConflict = Iterators.find(this.conflicts.iterator(),
+ EMFComparePredicates.containsConflictOfTypes(ConflictKind.REAL), null);
+ if (realConflict != null) {
+ this.conflictKind = ConflictKind.REAL;
+ } else {
+ this.conflictKind = ConflictKind.PSEUDO;
+ }
+ }
}
/**
@@ -225,7 +327,9 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv
protected Collection<? extends IDifferenceGroup> buildGroups(Comparison comparison2) {
Adapter adapter = EcoreUtil.getAdapter(getComparison().eAdapters(), SideLabelProvider.class);
- final String leftLabel, rightLabel;
+ final String leftLabel;
+ final String rightLabel;
+
if (adapter instanceof SideLabelProvider) {
SideLabelProvider labelProvider = (SideLabelProvider)adapter;
leftLabel = labelProvider.getLeftLabel();
diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java
index f526960c8..fc072b076 100644
--- a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java
+++ b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java
@@ -13,6 +13,7 @@ package org.eclipse.emf.compare.uml2.rcp.ui.tests.groups;
import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.google.common.collect.Lists;
@@ -22,14 +23,17 @@ import java.util.List;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Conflict;
import org.eclipse.emf.compare.ConflictKind;
import org.eclipse.emf.compare.provider.spec.CompareItemProviderAdapterFactorySpec;
import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareRCPUIMessages;
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl;
+import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl.CompositeConflict;
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.ConflictNode;
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.DiffNode;
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.MatchNode;
import org.eclipse.emf.compare.rcp.ui.tests.structuremergeviewer.groups.provider.AbstractTestTreeNodeItemProviderAdapter;
+import org.eclipse.emf.compare.uml2.rcp.ui.tests.groups.data.uml.compositeconflict.UMLJoiningConflictsWithOverlappingDiffsInputData;
import org.eclipse.emf.compare.uml2.rcp.ui.tests.groups.data.uml.conflictrefiningdiffs.UMLConflictWithRefiningDiffInputData;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory;
@@ -124,6 +128,84 @@ public class ConflictsGroupTest extends AbstractTestTreeNodeItemProviderAdapter
assertEquals(0, diffNodeLanguageAdd.getChildren().size());
}
+ /**
+ * Tests that conflicts containing overlapping diffs are joined in the SMV. Overlapping diffs can happen
+ * due to the replacement of refining diffs with their refined diffs in the SMV. This test is related to
+ * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=501864"</a>
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testJoiningConflictsWithOverlappingDiffs() throws IOException {
+ Comparison comparison = getComparison(new UMLJoiningConflictsWithOverlappingDiffsInputData());
+ ECrossReferenceAdapter crossReferenceAdapter = new ECrossReferenceAdapter() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.emf.ecore.util.ECrossReferenceAdapter#isIncluded(org.eclipse.emf.ecore.EReference)
+ */
+ @Override
+ protected boolean isIncluded(EReference eReference) {
+ return eReference == TreePackage.Literals.TREE_NODE__DATA;
+ }
+ };
+ final ConflictsGroupImpl conflictsGroup = new ConflictsGroupImpl(comparison,
+ hasConflict(ConflictKind.REAL, ConflictKind.PSEUDO),
+ EMFCompareRCPUIMessages.getString("ThreeWayComparisonGroupProvider.conflicts.label"), //$NON-NLS-1$
+ crossReferenceAdapter);
+ conflictsGroup.buildSubTree();
+
+ // There is one conflict node
+ List<? extends TreeNode> conflictNodes = conflictsGroup.getChildren();
+ assertEquals(1, conflictNodes.size());
+
+ // The conflict node groups two conflicts
+ ConflictNode conflictNode = (ConflictNode)conflictNodes.get(0);
+ Conflict conflict = conflictNode.getConflict();
+ assertTrue(conflict instanceof CompositeConflict);
+ CompositeConflict compositeConflict = (CompositeConflict)conflict;
+ assertEquals(2, compositeConflict.getConflicts().size());
+
+ List<? extends TreeNode> matchNodes = conflictNode.getChildren();
+ assertEquals(2, matchNodes.size());
+
+ // Match node for association
+ MatchNode matchNodeAssociation = (MatchNode)matchNodes.get(0);
+ checkText(matchNodeAssociation, "Association"); //$NON-NLS-1$
+
+ // Match node for association property owned by class1
+ MatchNode matchNodePropertyClass1 = (MatchNode)matchNodes.get(1);
+ checkText(matchNodePropertyClass1, "Property class2_"); //$NON-NLS-1$
+
+ List<? extends TreeNode> childrenMatchNodeAssociation = matchNodeAssociation.getChildren();
+ assertEquals(2, childrenMatchNodeAssociation.size());
+
+ // Diff node for deletion of association
+ DiffNode diffNodeDeletion = (DiffNode)childrenMatchNodeAssociation.get(0);
+ checkText(diffNodeDeletion, "Association Change DELETE"); //$NON-NLS-1$
+ assertTrue(diffNodeDeletion.getChildren().size() > 0);
+
+ // Match node for association property owned by association
+ MatchNode matchNodePropertyAssociation = (MatchNode)childrenMatchNodeAssociation.get(1);
+ checkText(matchNodePropertyAssociation, "Property class1_"); //$NON-NLS-1$
+ List<? extends TreeNode> childrenMatchNodePropertyAssociation = matchNodePropertyAssociation
+ .getChildren();
+ assertEquals(1, childrenMatchNodePropertyAssociation.size());
+
+ // Diff node for name update of association property owned by association
+ DiffNode diffNodeUpdatePropertyAssociation = (DiffNode)childrenMatchNodePropertyAssociation.get(0);
+ checkText(diffNodeUpdatePropertyAssociation, "class1_ [name set]"); //$NON-NLS-1$
+ assertEquals(0, diffNodeUpdatePropertyAssociation.getChildren().size());
+
+ List<? extends TreeNode> childrenMatchNodePropertyClass1 = matchNodePropertyClass1.getChildren();
+ assertEquals(1, childrenMatchNodePropertyClass1.size());
+
+ // Diff node for name update of association property owned by class1
+ DiffNode diffNodeUpdatePropertyClass1 = (DiffNode)childrenMatchNodePropertyClass1.get(0);
+ checkText(diffNodeUpdatePropertyClass1, "class2_ [name set]"); //$NON-NLS-1$
+ assertEquals(0, diffNodeUpdatePropertyClass1.getChildren().size());
+ }
+
protected void checkText(TreeNode childNode, String expected) {
assertEquals(expected, itemDelegator.getText(childNode));
}
diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/UMLJoiningConflictsWithOverlappingDiffsInputData.java b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/UMLJoiningConflictsWithOverlappingDiffsInputData.java
new file mode 100644
index 000000000..787dca21b
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/UMLJoiningConflictsWithOverlappingDiffsInputData.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EclipseSource Services GmbH and others.
+ * 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:
+ * Tanja Mayerhofer - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.uml2.rcp.ui.tests.groups.data.uml.compositeconflict;
+
+import java.io.IOException;
+
+import org.eclipse.emf.compare.tests.edit.data.ResourceScopeProvider;
+import org.eclipse.emf.compare.tests.framework.AbstractInputData;
+import org.eclipse.emf.ecore.resource.Resource;
+
+public class UMLJoiningConflictsWithOverlappingDiffsInputData extends AbstractInputData implements ResourceScopeProvider {
+
+ public Resource getLeft() throws IOException {
+ return loadFromClassLoader("left.uml"); //$NON-NLS-1$
+ }
+
+ public Resource getRight() throws IOException {
+ return loadFromClassLoader("right.uml"); //$NON-NLS-1$
+ }
+
+ public Resource getOrigin() throws IOException {
+ return loadFromClassLoader("origin.uml"); //$NON-NLS-1$
+ }
+}
diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/left.uml b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/left.uml
new file mode 100644
index 000000000..a189e5262
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/left.uml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_gPPNkH_3EeaBipewnLNB5Q" name="model">
+ <packagedElement xmi:type="uml:Class" xmi:id="_hjK6oH_3EeaBipewnLNB5Q" name="Class1">
+ <ownedAttribute xmi:type="uml:Property" xmi:id="_RgcocH_4EeaBipewnLNB5Q" name="class2_" type="_iTQTwH_3EeaBipewnLNB5Q" association="_RgaMMH_4EeaBipewnLNB5Q"/>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_iTQTwH_3EeaBipewnLNB5Q" name="Class2"/>
+ <packagedElement xmi:type="uml:Association" xmi:id="_RgaMMH_4EeaBipewnLNB5Q" memberEnd="_RgcocH_4EeaBipewnLNB5Q _RgcocX_4EeaBipewnLNB5Q">
+ <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_RgcBYH_4EeaBipewnLNB5Q" source="org.eclipse.papyrus">
+ <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_RgcBYX_4EeaBipewnLNB5Q" key="nature" value="UML_Nature"/>
+ </eAnnotations>
+ <ownedEnd xmi:type="uml:Property" xmi:id="_RgcocX_4EeaBipewnLNB5Q" name="class1_" type="_hjK6oH_3EeaBipewnLNB5Q" association="_RgaMMH_4EeaBipewnLNB5Q"/>
+ </packagedElement>
+</uml:Model>
diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/origin.uml b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/origin.uml
new file mode 100644
index 000000000..4d7c8be8e
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/origin.uml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_gPPNkH_3EeaBipewnLNB5Q" name="model">
+ <packagedElement xmi:type="uml:Class" xmi:id="_hjK6oH_3EeaBipewnLNB5Q" name="Class1">
+ <ownedAttribute xmi:type="uml:Property" xmi:id="_RgcocH_4EeaBipewnLNB5Q" name="class2" type="_iTQTwH_3EeaBipewnLNB5Q" association="_RgaMMH_4EeaBipewnLNB5Q"/>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_iTQTwH_3EeaBipewnLNB5Q" name="Class2"/>
+ <packagedElement xmi:type="uml:Association" xmi:id="_RgaMMH_4EeaBipewnLNB5Q" memberEnd="_RgcocH_4EeaBipewnLNB5Q _RgcocX_4EeaBipewnLNB5Q">
+ <eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_RgcBYH_4EeaBipewnLNB5Q" source="org.eclipse.papyrus">
+ <details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_RgcBYX_4EeaBipewnLNB5Q" key="nature" value="UML_Nature"/>
+ </eAnnotations>
+ <ownedEnd xmi:type="uml:Property" xmi:id="_RgcocX_4EeaBipewnLNB5Q" name="class1" type="_hjK6oH_3EeaBipewnLNB5Q" association="_RgaMMH_4EeaBipewnLNB5Q"/>
+ </packagedElement>
+</uml:Model>
diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/right.uml b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/right.uml
new file mode 100644
index 000000000..dca1cd5fe
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/right.uml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_gPPNkH_3EeaBipewnLNB5Q" name="model">
+ <packagedElement xmi:type="uml:Class" xmi:id="_hjK6oH_3EeaBipewnLNB5Q" name="Class1"/>
+ <packagedElement xmi:type="uml:Class" xmi:id="_iTQTwH_3EeaBipewnLNB5Q" name="Class2"/>
+</uml:Model>

Back to the top