From 8fcb916fb0f3ae13630d9b4603e2286767d0878a Mon Sep 17 00:00:00 2001 From: Tanja Mayerhofer Date: Thu, 22 Sep 2016 00:03:02 +0200 Subject: [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 --- .../groups/ConflictsGroupTest.java | 207 +++++++++++++++++++++ .../emf/compare/rcp/ui/tests/suite/AllTests.java | 13 +- .../impl/ThreeWayComparisonGroupProvider.java | 138 ++++++++++++-- .../rcp/ui/tests/groups/ConflictsGroupTest.java | 82 ++++++++ ...ningConflictsWithOverlappingDiffsInputData.java | 32 ++++ .../groups/data/uml/compositeconflict/left.uml | 13 ++ .../groups/data/uml/compositeconflict/origin.uml | 13 ++ .../groups/data/uml/compositeconflict/right.uml | 5 + 8 files changed, 481 insertions(+), 22 deletions(-) create mode 100644 plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java create mode 100644 plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/UMLJoiningConflictsWithOverlappingDiffsInputData.java create mode 100644 plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/left.uml create mode 100644 plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/origin.uml create mode 100644 plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/data/uml/compositeconflict/right.uml 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 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 + */ + @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 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 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 + */ + @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 + */ + @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; @@ -91,6 +96,16 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv */ public static class ConflictsGroupImpl extends BasicDifferenceGroupImpl { + /** + * Conflict groups to show in SMV. + */ + private final List compositeConflicts = newArrayList(); + + /** + * Maps conflicting differences to their composite conflicts. + */ + private final Map diffToCompositeConflictMap = newHashMap(); + /** * {@inheritDoc}. * @@ -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 compositeConflictsToJoin = new HashSet(); + 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 Tanja Mayerhofer */ - public static class ConflictGroup extends ConflictImpl { + public static class CompositeConflict extends ConflictImpl { - private final Conflict conflict; + /** The joined conflicts. */ + private Set conflicts = new LinkedHashSet(); + /** The diffs of all composed conflicts. */ private EList diffs = new BasicEList(); - 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 computeDiffs() { - LinkedHashSet computedDiffs = new LinkedHashSet(); + private Set computeRefinedDiffs(Conflict conflict) { + final LinkedHashSet computedDiffs = new LinkedHashSet(); 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 getRootRefinedDiffs(Diff diff) { - List rootRefinedDiffs = newArrayList(); + final List 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 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 joinedDiffs = new LinkedHashSet( + Sets.union(new LinkedHashSet(this.diffs), + new LinkedHashSet(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 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 + * + * + * @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 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 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 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 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 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 @@ + + + + + + + + +
+ + + + 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 @@ + + + + + + + + +
+ + + + 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 @@ + + + + + -- cgit v1.2.3