diff options
Diffstat (limited to 'plugins')
31 files changed, 1677 insertions, 301 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java index 21e3d10ac..c47d2183b 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/command/MergeAllCommandTests.java @@ -39,6 +39,7 @@ import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain; import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeNonConflictingRunnable; import org.eclipse.emf.compare.ide.ui.tests.command.data.MergeAllCommandInputData; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.scope.DefaultComparisonScope; import org.eclipse.emf.compare.scope.IComparisonScope; @@ -86,7 +87,7 @@ public class MergeAllCommandTests { .addAll(ImmutableList.of(leftResource, rightResource, originResource)).build(); MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable, - isRightEditable, mergeMode); + isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry)); MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers, comparison, leftToRight, mergerRegistry, runnable); @@ -155,7 +156,7 @@ public class MergeAllCommandTests { .addAll(ImmutableList.of(leftResource, rightResource, originResource)).build(); MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable, - isRightEditable, mergeMode); + isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry)); MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers, comparison, leftToRight, mergerRegistry, runnable); @@ -224,7 +225,7 @@ public class MergeAllCommandTests { .addAll(ImmutableList.of(leftResource, rightResource, originResource)).build(); MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable, - isRightEditable, mergeMode); + isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry)); MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers, comparison, leftToRight, mergerRegistry, runnable); @@ -293,7 +294,7 @@ public class MergeAllCommandTests { .addAll(ImmutableList.of(leftResource, rightResource, originResource)).build(); MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable, - isRightEditable, mergeMode); + isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry)); MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers, comparison, leftToRight, mergerRegistry, runnable); @@ -369,7 +370,7 @@ public class MergeAllCommandTests { .addAll(ImmutableList.of(leftResource, originResource)).build(); MergeNonConflictingRunnable runnable = new MergeNonConflictingRunnable(isLeftEditable, - isRightEditable, mergeMode); + isRightEditable, mergeMode, new DiffRelationshipComputer(mergerRegistry)); MergeAllNonConflictingCommand command = new MergeAllNonConflictingCommand(changeRecorder, notifiers, comparison, mergeMode.isLeftToRight(isLeftEditable, isRightEditable), mergerRegistry, diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java index a52055198..61f72fe82 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/CascadingFilterRefinementTest.java @@ -33,7 +33,8 @@ import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.ide.ui.tests.framework.RuntimeTestRunner; import org.eclipse.emf.compare.ide.ui.tests.framework.annotations.Compare; import org.eclipse.emf.compare.ide.ui.tests.framework.internal.CompareTestSupport; -import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMergeOptionAware; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; @@ -46,7 +47,7 @@ import org.junit.runner.RunWith; * * @author Martin Fleck <mfleck@eclipsesource.com> */ -@SuppressWarnings({"restriction", "nls" }) +@SuppressWarnings({"nls" }) @RunWith(RuntimeTestRunner.class) public class CascadingFilterRefinementTest { @@ -172,8 +173,8 @@ public class CascadingFilterRefinementTest { */ public void verifyRefinement(List<Diff> differences, boolean mergeRightToLeft) { for (Diff diff : differences) { - Set<Diff> resultingMerges = MergeDependenciesUtil.getAllResultingMerges(diff, MERGER_REGISTRY, - mergeRightToLeft); + IDiffRelationshipComputer computer = new DiffRelationshipComputer(MERGER_REGISTRY); + Set<Diff> resultingMerges = computer.getAllResultingMerges(diff, mergeRightToLeft); assertTrue("Not all refined diffs are in resulting merges.", resultingMerges.containsAll(diff.getRefines())); assertTrue("Not all refining diffs are in resulting merges.", diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java index e1877bcad..e0e07ecf6 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableRefinementTest.java @@ -32,6 +32,7 @@ import org.eclipse.emf.compare.internal.spec.ConflictSpec; import org.eclipse.emf.compare.internal.spec.DiffSpec; import org.eclipse.emf.compare.internal.spec.MatchSpec; import org.eclipse.emf.compare.merge.AbstractMerger; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.junit.Assert; import org.junit.Test; @@ -570,7 +571,7 @@ public class MergeNonConflictingRunnableRefinementTest { throw new IllegalArgumentException(); } MergeNonConflictingRunnable mergeNonConflicting = new MergeNonConflictingRunnable(isLeftEditable, - isRightEditable, mergeMode); + isRightEditable, mergeMode, new DiffRelationshipComputer(MERGER_REGISTRY)); mergeNonConflicting.merge(comparison, leftToRight, MERGER_REGISTRY); } } diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java index b1d5ad030..c460c197d 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/MergeNonConflictingRunnableTest.java @@ -30,6 +30,7 @@ import com.google.common.collect.Iterators; import java.util.Iterator; +import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.Monitor; @@ -44,6 +45,7 @@ import org.eclipse.emf.compare.Match; import org.eclipse.emf.compare.ReferenceChange; import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeNonConflictingRunnable; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMergeCriterion; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.merge.IMerger.Registry; @@ -502,6 +504,7 @@ public class MergeNonConflictingRunnableTest { when(diff.getRefinedBy()).thenReturn(new BasicEList<Diff>()); when(diff.getRefines()).thenReturn(new BasicEList<Diff>()); when(diff.getState()).thenReturn(UNRESOLVED); + when(diff.eAdapters()).thenReturn(new BasicEList<Adapter>()); return diff; } @@ -634,7 +637,8 @@ public class MergeNonConflictingRunnableTest { default: throw new IllegalArgumentException(); } - return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mergeMode) { + return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mergeMode, + new DiffRelationshipComputer(mergerRegistry)) { @Override protected void markAsMerged(Diff diff, MergeMode mode, boolean mergeRightToLeft, Registry registry) { diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java index 953cc124f..e881dae12 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434822.java @@ -28,6 +28,7 @@ import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl; import org.eclipse.emf.compare.internal.merge.MergeDataImpl; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.scope.DefaultComparisonScope; @@ -112,7 +113,8 @@ public class TestBug434822 { public void testMergeDataAfterAcceptingDeletion() { ArrayList<Diff> uiDiff = Lists.newArrayList(rightDelete); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); // Assert merge data and diff states @@ -131,7 +133,8 @@ public class TestBug434822 { // filter activated). ArrayList<Diff> uiDiff = Lists.newArrayList(rightDelete, leftMove); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); // Assert merge data and diff states @@ -152,7 +155,8 @@ public class TestBug434822 { */ ArrayList<Diff> uiDiff = Lists.newArrayList(rightDelete); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); // Assert merge data and diff states @@ -169,7 +173,8 @@ public class TestBug434822 { // Mocks the UI behavior of UI if the movement diff is selected for merging. ArrayList<Diff> uiDiff = Lists.newArrayList(leftMove); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); // Assert merge data @@ -187,7 +192,8 @@ public class TestBug434822 { // Mocks the UI behavior of UI if the movement diff is selected for merging. ArrayList<Diff> uiDiff = Lists.newArrayList(leftMove); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); // Assert merge data diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java index 9f74b0e4a..1be009ab5 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434827.java @@ -32,6 +32,7 @@ import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.Merg import org.eclipse.emf.compare.internal.merge.IMergeData; import org.eclipse.emf.compare.internal.merge.MergeDataImpl; import org.eclipse.emf.compare.internal.spec.ReferenceChangeSpec; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.scope.DefaultComparisonScope; @@ -133,7 +134,8 @@ public class TestBug434827 { // The subDiff is not added in this list because it is not considered as a cascading diff. ArrayList<Diff> uiDiff = Lists.newArrayList(deletionDiff); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); Node rootNode = (Node)left.getContents().get(0); @@ -168,7 +170,8 @@ public class TestBug434827 { // The subDiff is not added in this list because it is not considered as a cascading diff. ArrayList<Diff> uiDiff = Lists.newArrayList(deletionDiff); - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, REJECT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(uiDiff, false, mergerRegistry); Node rootNode = (Node)left.getContents().get(0); diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java index e4af7d616..89bdd0901 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828.java @@ -30,6 +30,7 @@ import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl; import org.eclipse.emf.compare.internal.merge.MergeDataImpl; import org.eclipse.emf.compare.merge.BatchMerger; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.scope.DefaultComparisonScope; @@ -126,7 +127,8 @@ public class TestBug434828 { */ @Test public void testAcceptConflictDiffWithConflictingDiffWithRequiredBy() { - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(Collections.singletonList(refChangeDiff), false, mergerRegistry); assertEquals(MERGED, refChangeDiff.getState()); diff --git a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java index 32c4d830c..d92e86846 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java +++ b/plugins/org.eclipse.emf.compare.ide.ui.tests/src/org/eclipse/emf/compare/ide/ui/tests/structuremergeviewer/actions/TestBug434828_2.java @@ -30,6 +30,7 @@ import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl; import org.eclipse.emf.compare.internal.merge.MergeDataImpl; import org.eclipse.emf.compare.merge.BatchMerger; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.scope.DefaultComparisonScope; @@ -127,7 +128,8 @@ public class TestBug434828_2 { */ @Test public void testAcceptConflictDiffWithConflictingDiffWithRequiredBy() { - MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT); + MergeRunnableImpl mergeRunnable = new MergeRunnableImpl(true, false, ACCEPT, + new DiffRelationshipComputer(mergerRegistry)); mergeRunnable.merge(Collections.singletonList(refChangeDiff), false, mergerRegistry); assertEquals(MERGED, refChangeDiff.getState()); diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java index cefe2265f..38969b3a0 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/configuration/EMFCompareConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2016 Obeo and others. + * Copyright (c) 2013, 2017 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 @@ -9,6 +9,7 @@ * Obeo - initial API and implementation * Conor O'Mahony - bug 507465 * Martin Fleck - bug 483798 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.configuration; @@ -29,11 +30,15 @@ import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.domain.ICompareEditingDomain; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; +import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.rcp.ui.EMFCompareRCPUIPlugin; import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.AdapterFactoryChange; import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.CompareEditingDomainChange; import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.ComparisonAndScopeChange; +import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.DiffRelationshipComputerChange; import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.EMFComparatorChange; import org.eclipse.emf.compare.rcp.ui.internal.configuration.impl.MergePreviewModeChange; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter; @@ -61,6 +66,9 @@ public class EMFCompareConfiguration extends ForwardingCompareConfiguration impl private static final String ADAPTER_FACTORY = EMFCompareIDEUIPlugin.PLUGIN_ID + ".ADAPTER_FACTORY"; //$NON-NLS-1$ + private static final String DIFF_RELATIONSHIP_COMPUTER = EMFCompareIDEUIPlugin.PLUGIN_ID + + ".DIFF_RELATIONSHIP_COMPUTER"; //$NON-NLS-1$ + private static final String PREVIEW_MERGE_MODE = EMFCompareIDEUIPlugin.PLUGIN_ID + ".PREVIEW_MERGE_MODE"; //$NON-NLS-1$ private static final String COMPARISON_SCOPE = EMFCompareIDEUIPlugin.PLUGIN_ID + ".COMPARISON_SCOPE"; //$NON-NLS-1$ ; @@ -125,6 +133,11 @@ public class EMFCompareConfiguration extends ForwardingCompareConfiguration impl if (getProperty(DISPLAY_SAVE_ACTION) == null) { setProperty(DISPLAY_SAVE_ACTION, Boolean.TRUE); } + + if (getProperty(DIFF_RELATIONSHIP_COMPUTER) == null) { + setProperty(DIFF_RELATIONSHIP_COMPUTER, + new DiffRelationshipComputer(EMFCompareRCPPlugin.getDefault().getMergerRegistry())); + } } /** @@ -163,6 +176,7 @@ public class EMFCompareConfiguration extends ForwardingCompareConfiguration impl compareConfiguration.setProperty(SMV_FILTERS, null); compareConfiguration.setProperty(EDITING_DOMAIN, null); compareConfiguration.setProperty(ADAPTER_FACTORY, null); + compareConfiguration.setProperty(DIFF_RELATIONSHIP_COMPUTER, null); compareConfiguration.setProperty(SMV_GROUP_PROVIDERS, null); compareConfiguration.setProperty(PREVIEW_MERGE_MODE, null); compareConfiguration.setProperty(DISPLAY_GROUP_PROVIDERS, null); @@ -201,6 +215,10 @@ public class EMFCompareConfiguration extends ForwardingCompareConfiguration impl return (AdapterFactory)getProperty(ADAPTER_FACTORY); } + public IDiffRelationshipComputer getDiffRelationshipComputer() { + return (IDiffRelationshipComputer)getProperty(DIFF_RELATIONSHIP_COMPUTER); + } + /** * {@inheritDoc} * @@ -293,6 +311,12 @@ public class EMFCompareConfiguration extends ForwardingCompareConfiguration impl getEventBus().post(new AdapterFactoryChange(oldValue, adapterFactory)); } + public void setDiffRelationshipComputer(IDiffRelationshipComputer diffRelationshipComputer) { + IDiffRelationshipComputer oldValue = getDiffRelationshipComputer(); + setProperty(DIFF_RELATIONSHIP_COMPUTER, diffRelationshipComputer); + getEventBus().post(new DiffRelationshipComputerChange(oldValue, diffRelationshipComputer)); + } + private class PropertyChangeListener implements IPropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { fireChange(event.getProperty(), event.getOldValue(), event.getNewValue()); diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java index 53f2866f2..dde9c88e5 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/AdditiveResourceMappingMerger.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2017 Obeo. + * Copyright (c) 2016, 2017 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 @@ -8,6 +8,7 @@ * Contributors: * Obeo - initial API and implementation * Martin Fleck - bug 512562 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.logical; @@ -60,6 +61,7 @@ import org.eclipse.emf.compare.ide.ui.logical.SynchronizationModel; import org.eclipse.emf.compare.ide.utils.ResourceUtil; import org.eclipse.emf.compare.internal.utils.DiffUtil; import org.eclipse.emf.compare.merge.AdditiveMergeCriterion; +import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer; import org.eclipse.emf.compare.merge.ComputeDiffsToMerge; import org.eclipse.emf.compare.merge.DelegatingMerger; import org.eclipse.emf.compare.merge.MergeBlockedByConflictException; @@ -111,11 +113,14 @@ public class AdditiveResourceMappingMerger extends EMFResourceMappingMerger impl private Set<URI> performPreMerge(Comparison comparison, SubMonitor subMonitor) { final Monitor emfMonitor = BasicMonitor.toMonitor(subMonitor); final Set<URI> conflictingURIs = new LinkedHashSet<URI>(); - ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, MERGER_REGISTRY, - AdditiveMergeCriterion.INSTANCE).failOnRealConflictUnless(isAdditiveConflict()); + CachingDiffRelationshipComputer relationshipComputer = new CachingDiffRelationshipComputer( + MERGER_REGISTRY, AdditiveMergeCriterion.INSTANCE); + ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, relationshipComputer) + .failOnRealConflictUnless(isAdditiveConflict()); for (Diff next : comparison.getDifferences()) { doMergeForDiff(next, computer, emfMonitor, conflictingURIs); } + relationshipComputer.invalidate(); return conflictingURIs; } diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java index 788272b53..2078dcc17 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/logical/EMFResourceMappingMerger.java @@ -11,6 +11,7 @@ * Alexandra Buzila - bugs 470332, 478620 * Stefan Dirix - bug 507050 * Martin Fleck - bug 512562, 514382 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.logical; @@ -76,8 +77,10 @@ import org.eclipse.emf.compare.ide.ui.logical.SynchronizationModel; import org.eclipse.emf.compare.ide.utils.ResourceUtil; import org.eclipse.emf.compare.ide.utils.StorageTraversal; import org.eclipse.emf.compare.merge.BatchMerger; +import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer; import org.eclipse.emf.compare.merge.ComputeDiffsToMerge; import org.eclipse.emf.compare.merge.IBatchMerger; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.merge.IMerger.Registry2; import org.eclipse.emf.compare.merge.MergeBlockedByConflictException; @@ -325,15 +328,19 @@ public class EMFResourceMappingMerger implements IResourceMappingMerger { */ private Set<URI> performPreMerge(Comparison comparison, SubMonitor subMonitor) { final Monitor emfMonitor = BasicMonitor.toMonitor(subMonitor); + final CachingDiffRelationshipComputer relationshipComputer = new CachingDiffRelationshipComputer( + MERGER_REGISTRY); final Set<URI> conflictingURIs = new LinkedHashSet<URI>(); for (Diff next : filter(comparison.getDifferences(), fromSide(DifferenceSource.RIGHT))) { - doMergeForDiff(emfMonitor, conflictingURIs, next); + doMergeForDiff(emfMonitor, conflictingURIs, next, relationshipComputer); } + relationshipComputer.invalidate(); return conflictingURIs; } - protected void doMergeForDiff(Monitor emfMonitor, Set<URI> conflictingURIs, Diff diff) { - ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, MERGER_REGISTRY) + protected void doMergeForDiff(Monitor emfMonitor, Set<URI> conflictingURIs, Diff diff, + IDiffRelationshipComputer relationshipComputer) { + ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, relationshipComputer) .failOnRealConflictUnless(alwaysFalse()); try { Set<Diff> diffsToMerge = computer.getAllDiffsToMerge(diff); 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 59d98d29c..2ad486ebe 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 @@ -8,6 +8,7 @@ * Contributors: * Obeo - initial API and implementation * Martin Fleck - bug 514767 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer; @@ -27,8 +28,9 @@ 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.DiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroupProvider; @@ -55,6 +57,24 @@ public class DependencyData { } /** + * Returns the diff relationship computer instance from the compare configuration with the given merger + * registry. If no computer instance has been set, a default instance will be created. + * + * @param mergerRegistry + * merger registry used to compute diff relationships. + * @return a non-null diff relationship computer. + */ + protected IDiffRelationshipComputer getDiffRelationshipComputer(IMerger.Registry mergerRegistry) { + if (compareConfiguration == null || compareConfiguration.getDiffRelationshipComputer() == null) { + return new DiffRelationshipComputer(mergerRegistry); + } + IDiffRelationshipComputer diffRelationshipComputer = compareConfiguration + .getDiffRelationshipComputer(); + diffRelationshipComputer.setMergerRegistry(mergerRegistry); + return diffRelationshipComputer; + } + + /** * @param selection */ public void updateDependencies(ISelection selection, IMerger.Registry mergerRegistry) { @@ -69,11 +89,10 @@ public class DependencyData { rejectedDiffs = newHashSet(); for (Diff diff : selectedDiffs) { boolean leftToRight = mergePreviewMode.isLeftToRight(diff, leftEditable, rightEditable); - requires.addAll( - MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry, !leftToRight)); + IDiffRelationshipComputer computer = getDiffRelationshipComputer(mergerRegistry); + requires.addAll(computer.getAllResultingMerges(diff, !leftToRight)); requires.remove(diff); - rejectedDiffs.addAll( - MergeDependenciesUtil.getAllResultingRejections(diff, mergerRegistry, !leftToRight)); + rejectedDiffs.addAll(computer.getAllResultingRejections(diff, !leftToRight)); rejectedDiffs.remove(diff); requires.removeAll(rejectedDiffs); } 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 3538a3bac..db93e117e 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 @@ -13,6 +13,7 @@ * Martin Fleck - bug 497066 * Martin Fleck - bug 483798 * Martin Fleck - bug 514767 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer; @@ -117,6 +118,7 @@ import org.eclipse.emf.compare.ide.ui.internal.util.PlatformElementUtil; import org.eclipse.emf.compare.internal.merge.MergeDataImpl; import org.eclipse.emf.compare.internal.merge.MergeMode; import org.eclipse.emf.compare.merge.AbstractMerger; +import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMergeOptionAware; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; @@ -240,6 +242,9 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap /** The adapter factory. */ private ComposedAdapterFactory fAdapterFactory; + /** The diff relationship computer. */ + private CachingDiffRelationshipComputer fDiffRelationshipComputer; + /** The tree ruler associated with this viewer. */ private EMFCompareDiffTreeRuler treeRuler; @@ -579,6 +584,10 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap fAdapterFactory = initAdapterFactory(getCompareConfiguration().getComparison()); getCompareConfiguration().setAdapterFactory(fAdapterFactory); + fDiffRelationshipComputer = new CachingDiffRelationshipComputer( + EMFCompareRCPPlugin.getDefault().getMergerRegistry()); + getCompareConfiguration().setDiffRelationshipComputer(fDiffRelationshipComputer); + inputChangedTask = new CompareInputChangedJob(EMFCompareIDEUIMessages .getString("EMFCompareStructureMergeViewer.computingModelDifferences")); //$NON-NLS-1$ } @@ -934,6 +943,7 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap compareInputChanged((ICompareInput)null); treeRuler.handleDispose(); fAdapterFactory.dispose(); + fDiffRelationshipComputer.invalidate(); toolBar.dispose(); fColors.dispose(); @@ -1089,6 +1099,11 @@ public class EMFCompareStructureMergeViewer extends AbstractStructuredViewerWrap } fAdapterFactory = initAdapterFactory(comparison); + // clear cache for new comparison + if (fDiffRelationshipComputer != null) { + fDiffRelationshipComputer.invalidate(); + } + // propagate new adapter factory config.setAdapterFactory(fAdapterFactory); getContentProvider().setAdapterFactory(fAdapterFactory); diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java index cdec275b8..9c0dd7337 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/AbstractMergeRunnable.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2017 Obeo. + * Copyright (c) 2014, 2017 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 @@ -7,6 +7,7 @@ * * Contributors: * Obeo - initial API and implementation + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions; @@ -21,8 +22,10 @@ import java.util.Collection; 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.DiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.merge.IMerger.Registry; /** @@ -31,6 +34,7 @@ import org.eclipse.emf.compare.merge.IMerger.Registry; * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> */ public abstract class AbstractMergeRunnable { + /** Tells us whether the left side of the comparison we're operating on is editable. */ private final boolean isLeftEditable; @@ -40,6 +44,9 @@ public abstract class AbstractMergeRunnable { /** Current merging mode. */ private final MergeMode mergeMode; + /** Computer to calculate the relationship between diffs. */ + private IDiffRelationshipComputer diffRelationshipComputer; + /** * Default constructor. * @@ -49,11 +56,15 @@ public abstract class AbstractMergeRunnable { * Whether the right side of the comparison we're operating on is editable. * @param mergeMode * Merge mode for this operation. + * @param diffRelationshipComputer + * The diff relationship computer used to find resulting merges and rejections. */ - public AbstractMergeRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode) { + public AbstractMergeRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode, + IDiffRelationshipComputer diffRelationshipComputer) { this.isLeftEditable = isLeftEditable; this.isRightEditable = isRightEditable; this.mergeMode = mergeMode; + this.diffRelationshipComputer = diffRelationshipComputer; } protected boolean isLeftEditable() { @@ -69,6 +80,22 @@ public abstract class AbstractMergeRunnable { } /** + * Returns the diff relationship computer instance from the compare configuration with the given merger + * registry. If no computer instance has been set, a default instance will be created. + * + * @param mergerRegistry + * merger registry used to compute diff relationships. + * @return a non-null diff relationship computer. + */ + protected IDiffRelationshipComputer getDiffRelationshipComputer(IMerger.Registry mergerRegistry) { + if (diffRelationshipComputer == null) { + diffRelationshipComputer = new DiffRelationshipComputer(mergerRegistry); + } + diffRelationshipComputer.setMergerRegistry(mergerRegistry); + return diffRelationshipComputer; + } + + /** * Marks all of the given diffs as merged, keeping track of the merged mode used for the operation. * * @param diffToMarkAsMerged @@ -81,7 +108,7 @@ public abstract class AbstractMergeRunnable { protected void markAllAsMerged(Collection<? extends Diff> diffToMarkAsMerged, MergeMode mode, Registry mergerRegistry) { for (Diff diff : diffToMarkAsMerged) { - boolean isLeftToRight = mode.isLeftToRight(diff, isLeftEditable, isRightEditable); + boolean isLeftToRight = mode.isLeftToRight(diff, isLeftEditable(), isRightEditable()); markAsMerged(diff, mode, !isLeftToRight, mergerRegistry); } } @@ -103,11 +130,10 @@ public abstract class AbstractMergeRunnable { if (isInTerminalState(diff)) { return; } + IDiffRelationshipComputer computer = getDiffRelationshipComputer(mergerRegistry); if (isAccepting(diff, mergeRightToLeft)) { - final Set<Diff> implied = MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry, - mergeRightToLeft); - final Set<Diff> rejections = MergeDependenciesUtil.getAllResultingRejections(diff, mergerRegistry, - mergeRightToLeft); + final Set<Diff> implied = computer.getAllResultingMerges(diff, mergeRightToLeft); + final Set<Diff> rejections = computer.getAllResultingRejections(diff, mergeRightToLeft); for (Diff impliedDiff : Sets.difference(implied, rejections)) { impliedDiff.setState(MERGED); } @@ -115,8 +141,7 @@ public abstract class AbstractMergeRunnable { impliedRejection.setState(DISCARDED); } } else { - final Set<Diff> implied = MergeDependenciesUtil.getAllResultingMerges(diff, mergerRegistry, - mergeRightToLeft); + final Set<Diff> implied = computer.getAllResultingMerges(diff, mergeRightToLeft); for (Diff impliedDiff : implied) { impliedDiff.setState(DISCARDED); } diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java index c8b8828f1..9b1c7ac7a 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAction.java @@ -34,6 +34,7 @@ import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.Navigatable; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.merge.IMerger.Registry; import org.eclipse.emf.compare.provider.ITooltipLabelProvider; @@ -81,6 +82,8 @@ public class MergeAction extends BaseSelectionListenerAction { */ private AdapterFactory adapterFactory; + private IDiffRelationshipComputer diffRelationshipComputer; + private boolean isMirrored; private final boolean isLeftEditable; @@ -98,6 +101,7 @@ public class MergeAction extends BaseSelectionListenerAction { super(""); //$NON-NLS-1$ adapterFactory = compareConfiguration.getAdapterFactory(); + diffRelationshipComputer = compareConfiguration.getDiffRelationshipComputer(); isLeftEditable = compareConfiguration.isLeftEditable(); isRightEditable = compareConfiguration.isRightEditable(); @@ -118,7 +122,8 @@ public class MergeAction extends BaseSelectionListenerAction { this.editingDomain = compareConfiguration.getEditingDomain(); this.mergerRegistry = mergerRegistry; this.leftToRight = mode.isLeftToRight(isLeftEditable, isRightEditable); - this.mergeRunnable = createMergeRunnable(mode, isLeftEditable, isRightEditable); + this.mergeRunnable = createMergeRunnable(mode, isLeftEditable, isRightEditable, + diffRelationshipComputer); this.selectedDifferences = newArrayList(); this.selectedMode = mode; @@ -131,9 +136,9 @@ public class MergeAction extends BaseSelectionListenerAction { updateSelection(selection); } - protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean leftEditable, - boolean rightEditable) { - return new MergeRunnableImpl(leftEditable, rightEditable, mode); + protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean leftEditable, boolean rightEditable, + IDiffRelationshipComputer relationshipComputer) { + return new MergeRunnableImpl(leftEditable, rightEditable, mode, relationshipComputer); } protected void initToolTipAndImage(MergeMode mode) { @@ -300,10 +305,12 @@ public class MergeAction extends BaseSelectionListenerAction { if (mirrored) { MergeMode mirroredMode = selectedMode.inverse(); leftToRight = mirroredMode.isLeftToRight(isRightEditable, isLeftEditable); - mergeRunnable = createMergeRunnable(mirroredMode, isRightEditable, isLeftEditable); + mergeRunnable = createMergeRunnable(mirroredMode, isRightEditable, isLeftEditable, + diffRelationshipComputer); } else { leftToRight = selectedMode.isLeftToRight(isLeftEditable, isRightEditable); - mergeRunnable = createMergeRunnable(selectedMode, isLeftEditable, isRightEditable); + mergeRunnable = createMergeRunnable(selectedMode, isLeftEditable, isRightEditable, + diffRelationshipComputer); } } } diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java index ed8b885ee..16a7e5bd8 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeAllNonConflictingAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2015 Obeo. + * Copyright (c) 2013, 2017 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 @@ -8,6 +8,7 @@ * Contributors: * Obeo - initial API and implementation * Martin Fleck - bug 514079 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions; @@ -21,6 +22,7 @@ import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; import org.eclipse.emf.compare.internal.domain.IMergeAllNonConflictingRunnable; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; import org.eclipse.jface.viewers.IStructuredSelection; @@ -50,8 +52,8 @@ public class MergeAllNonConflictingAction extends MergeAction { @Override protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean isLeftEditable, - boolean isRightEditable) { - return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode); + boolean isRightEditable, IDiffRelationshipComputer relationshipComputer) { + return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode, relationshipComputer); } @Override diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java index 89152d77e..15135fa55 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeContainedNonConflictingAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 EclipseSource Munich and others. + * Copyright (c) 2015, 2017 EclipseSource Munich 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 @@ -7,6 +7,7 @@ * * Contributors: * Philip Langer - initial API and implementation + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions; @@ -30,6 +31,7 @@ import org.eclipse.emf.compare.domain.IMergeRunnable; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger.Registry; import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup; @@ -124,8 +126,8 @@ public class MergeContainedNonConflictingAction extends MergeAction { @Override protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean isLeftEditable, - boolean isRightEditable) { - return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode); + boolean isRightEditable, IDiffRelationshipComputer relationshipComputer) { + return new MergeNonConflictingRunnable(isLeftEditable, isRightEditable, mode, relationshipComputer); } @Override diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java index de5972c33..445f45294 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/actions/MergeNonConflictingRunnable.java @@ -9,6 +9,7 @@ * Obeo - initial API and implementation * Philip Langer - bug 469355, bug 462884, refactorings * Martin Fleck - bug 507177 + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions; @@ -53,9 +54,9 @@ import org.eclipse.emf.compare.internal.utils.ComparisonUtil; import org.eclipse.emf.compare.merge.BatchMerger; import org.eclipse.emf.compare.merge.ComputeDiffsToMerge; import org.eclipse.emf.compare.merge.IBatchMerger; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.merge.IMerger.Registry; -import org.eclipse.emf.compare.merge.IMerger.Registry2; import org.eclipse.emf.compare.merge.MergeBlockedByConflictException; /** @@ -77,9 +78,12 @@ public class MergeNonConflictingRunnable extends AbstractMergeRunnable implement * Whether the right side of the comparison we're operating on is editable. * @param mergeMode * Merge mode for this operation. + * @param diffRelationshipComputer + * The diff relationship computer used to find resulting merges and rejections. */ - public MergeNonConflictingRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode) { - super(isLeftEditable, isRightEditable, mergeMode); + public MergeNonConflictingRunnable(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode, + IDiffRelationshipComputer diffRelationshipComputer) { + super(isLeftEditable, isRightEditable, mergeMode, diffRelationshipComputer); } /** @@ -151,7 +155,7 @@ public class MergeNonConflictingRunnable extends AbstractMergeRunnable implement private Iterable<Diff> mergeThreeWayWithoutConflicts(Collection<Diff> differences, boolean leftToRight, Registry mergerRegistry) { final List<Diff> affectedDiffs; - final IBatchMerger merger = new BatchMerger(mergerRegistry); + final IBatchMerger merger = new BatchMerger(getDiffRelationshipComputer(mergerRegistry)); if (getMergeMode() == MergeMode.LEFT_TO_RIGHT) { affectedDiffs = Lists @@ -199,7 +203,7 @@ public class MergeNonConflictingRunnable extends AbstractMergeRunnable implement private Iterable<Diff> mergeTwoWay(Collection<Diff> differences, boolean leftToRight, Registry mergerRegistry) { final List<Diff> affectedDiffs; - final IBatchMerger merger = new BatchMerger(mergerRegistry); + final IBatchMerger merger = new BatchMerger(getDiffRelationshipComputer(mergerRegistry)); // in two-way comparison, difference source is always LEFT if (getMergeMode() == MergeMode.LEFT_TO_RIGHT) { @@ -234,8 +238,8 @@ public class MergeNonConflictingRunnable extends AbstractMergeRunnable implement Registry mergerRegistry) { final List<Diff> affectedDiffs = new ArrayList<Diff>(); final Monitor emfMonitor = new BasicMonitor(); - ComputeDiffsToMerge computer = new ComputeDiffsToMerge(!leftToRight, (Registry2)mergerRegistry) - .failOnRealConflictUnless(alwaysFalse()); + ComputeDiffsToMerge computer = new ComputeDiffsToMerge(!leftToRight, + getDiffRelationshipComputer(mergerRegistry)).failOnRealConflictUnless(alwaysFalse()); final Predicate<? super Diff> filter; if (getMergeMode() == RIGHT_TO_LEFT) { 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 f692550b5..234b7fff6 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2017 Obeo. + * Copyright (c) 2013, 2017 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 @@ -7,6 +7,7 @@ * * Contributors: * Obeo - initial API and implementation + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions; @@ -24,6 +25,7 @@ import org.eclipse.emf.compare.internal.merge.MergeMode; import org.eclipse.emf.compare.internal.merge.MergeOperation; import org.eclipse.emf.compare.merge.BatchMerger; import org.eclipse.emf.compare.merge.IBatchMerger; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger.Registry; /** @@ -33,8 +35,9 @@ import org.eclipse.emf.compare.merge.IMerger.Registry; */ // Visible for testing public final class MergeRunnableImpl extends AbstractMergeRunnable implements IMergeRunnable { - public MergeRunnableImpl(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode) { - super(isLeftEditable, isRightEditable, mergeMode); + public MergeRunnableImpl(boolean isLeftEditable, boolean isRightEditable, MergeMode mergeMode, + IDiffRelationshipComputer diffRelationshipComputer) { + super(isLeftEditable, isRightEditable, mergeMode, diffRelationshipComputer); } public void merge(List<? extends Diff> differences, boolean leftToRight, Registry mergerRegistry) { @@ -70,7 +73,7 @@ public final class MergeRunnableImpl extends AbstractMergeRunnable implements IM private void mergeAll(Collection<? extends Diff> differences, boolean leftToRight, Registry mergerRegistry) { - final IBatchMerger merger = new BatchMerger(mergerRegistry); + final IBatchMerger merger = new BatchMerger(getDiffRelationshipComputer(mergerRegistry)); if (leftToRight) { merger.copyAllLeftToRight(differences, new BasicMonitor()); } else { diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IDiffRelationshipComputerChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IDiffRelationshipComputerChange.java new file mode 100644 index 000000000..05ea62926 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IDiffRelationshipComputerChange.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2017 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: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.rcp.ui.internal.configuration; + +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; +import org.eclipse.emf.compare.rcp.ui.configuration.ICompareEvent; + +/** + * Event indicating a change in {@link IDiffRelationshipComputer}. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +public interface IDiffRelationshipComputerChange extends ICompareEvent { + + /** + * Previous value of the {@link IDiffRelationshipComputer}. + * + * @return previous value of the {@link IDiffRelationshipComputer}. + */ + IDiffRelationshipComputer getOldValue(); + + /** + * New value of the {@link IDiffRelationshipComputer}. + * + * @return new value of the {@link IDiffRelationshipComputer}. + */ + IDiffRelationshipComputer getNewValue(); +} diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java index c14d27b6a..6beff7ef5 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/IEMFCompareConfiguration.java @@ -17,6 +17,7 @@ import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.domain.ICompareEditingDomain; import org.eclipse.emf.compare.internal.merge.MergeMode; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.StructureMergeViewerGrouper; import org.eclipse.emf.compare.scope.IComparisonScope; @@ -58,5 +59,9 @@ public interface IEMFCompareConfiguration { void setMergePreviewMode(MergeMode mergePreviewMode); + IDiffRelationshipComputer getDiffRelationshipComputer(); + + void setDiffRelationshipComputer(IDiffRelationshipComputer diffRelationshipComputer); + boolean getBooleanProperty(String key, boolean dflt); } diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/impl/DiffRelationshipComputerChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/impl/DiffRelationshipComputerChange.java new file mode 100644 index 000000000..302f47da3 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/configuration/impl/DiffRelationshipComputerChange.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2017 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: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.rcp.ui.internal.configuration.impl; + +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; +import org.eclipse.emf.compare.rcp.ui.internal.configuration.IDiffRelationshipComputerChange; + +/** + * A change for {@link IDiffRelationshipComputer}. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +public class DiffRelationshipComputerChange extends CompareEvent<IDiffRelationshipComputer> implements IDiffRelationshipComputerChange { + public DiffRelationshipComputerChange(IDiffRelationshipComputer oldValue, + IDiffRelationshipComputer newValue) { + super(oldValue, newValue); + } +} diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java index 872a55dcb..e0e6d4055 100644 --- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java +++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/checkers/MergeDependenciesChecker.java @@ -7,10 +7,10 @@ import com.google.common.collect.Sets; import java.util.Set; import org.eclipse.emf.compare.Diff; -import org.eclipse.emf.compare.internal.merge.MergeDependenciesUtil; +import org.eclipse.emf.compare.merge.DiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; import org.eclipse.emf.compare.merge.IMerger; -@SuppressWarnings("restriction") public class MergeDependenciesChecker { private IMerger.Registry registry; @@ -58,10 +58,9 @@ public class MergeDependenciesChecker { } public void check() { - Set<Diff> allResultingMerges = MergeDependenciesUtil.getAllResultingMerges(diff, registry, - this.rightToLeft); - Set<Diff> allResultingRejections = MergeDependenciesUtil.getAllResultingRejections(diff, registry, - this.rightToLeft); + IDiffRelationshipComputer computer = new DiffRelationshipComputer(registry); + Set<Diff> allResultingMerges = computer.getAllResultingMerges(diff, this.rightToLeft); + Set<Diff> allResultingRejections = computer.getAllResultingRejections(diff, this.rightToLeft); assertEquals(this.nbMerges, Sets.difference(allResultingMerges, allResultingRejections).size()); assertEquals(this.nbRejections, allResultingRejections.size()); } diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/diff/DiffRelationshipComputerTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/diff/DiffRelationshipComputerTest.java new file mode 100644 index 000000000..252f30c3a --- /dev/null +++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/diff/DiffRelationshipComputerTest.java @@ -0,0 +1,420 @@ +/******************************************************************************* + * Copyright (c) 2017 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: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.tests.diff; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Sets; + +import java.util.Set; + +import org.eclipse.emf.compare.CompareFactory; +import org.eclipse.emf.compare.Comparison; +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.Match; +import org.eclipse.emf.compare.internal.spec.DiffSpec; +import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer; +import org.eclipse.emf.compare.merge.ComputeDiffsToMerge; +import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; +import org.eclipse.emf.compare.merge.IMerger; +import org.eclipse.emf.compare.merge.IMerger.Registry2; +import org.eclipse.emf.compare.merge.IMerger2; +import org.junit.Before; +import org.junit.Test; + +/** + * This class holds test cases that are related to the {@link IDiffRelationshipComputer} and specifically, the + * {@link CachingDiffRelationshipComputer}. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +@SuppressWarnings({"boxing" }) +public class DiffRelationshipComputerTest { + + /** Merge direction. */ + protected static boolean MERGE_RIGHT_TO_LEFT = true; + + /** Merger registry with the mock merger. */ + protected Registry2 mergerRegistry; + + /** Mock merger with a high ranking applying to the testing diff. */ + protected IMerger2 merger; + + /** Comparison. */ + protected Comparison comparison; + + /** Match. */ + protected Match match; + + /** Testing diff. */ + protected Diff diff; + + /** Dependency of the testing diff. */ + protected Diff mergeDependency; + + /** Resulting diff of the testing diff. */ + protected Diff resultingMerge; + + /** Resulting rejection of the testing diff. */ + protected Diff resultingRejection; + + /** + * Creates a new merger registry, merger and relevant diffs. + */ + @Before + public void setupClass() { + mergerRegistry = createMergerRegistry(); + comparison = createComparison(); + comparison.getMatches().add(match = createMatch()); + comparison.getDifferences().add(diff = createDiff(match)); + comparison.getDifferences().add(mergeDependency = createDiff(match)); + comparison.getDifferences().add(resultingMerge = createDiff(match)); + comparison.getDifferences().add(resultingRejection = createDiff(match)); + merger = mockMerger(diff, mergeDependency, resultingMerge, resultingRejection, 1000, mergerRegistry); + mergerRegistry.add(merger); + } + + protected CachingDiffRelationshipComputer getDiffRelationshipComputer() { + return new CachingDiffRelationshipComputer(mergerRegistry); + } + + /** + * Tests that the computer is working correctly for direct merge dependencies. + */ + @Test + public void testDirectMergeDependencies() { + IDiffRelationshipComputer computer = getDiffRelationshipComputer(); + + // call calculation method a few times + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods was called exactly once + verifyDirectMergeDependenciesCalledExactly(1); + } + + /** + * Tests that the computer is working correctly for direct resulting merges. + */ + @Test + public void testDirectResultingMerges() { + IDiffRelationshipComputer computer = getDiffRelationshipComputer(); + + // call calculation method a few times + assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectResultingMerges(computer.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods was called exactly once + verifyDirectResultingMergesCalledExactly(1); + } + + /** + * Tests that the computer is working correctly for direct resulting rejections. + */ + @Test + public void testDirectResultingRejections() { + IDiffRelationshipComputer computer = getDiffRelationshipComputer(); + + // call calculation method a few times + assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectResultingRejections(computer.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods was called exactly once + verifyDirectResultingRejectionsCalledExactly(1); + } + + /** + * Tests that the computer is working correctly for all resulting merges. + */ + @Test + public void testAllResultingMerges() { + IDiffRelationshipComputer computer = getDiffRelationshipComputer(); + + // call calculation method a few times + assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + assertAllResultingMerges(computer.getAllResultingMerges(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods of the diffs are called at most once + verifyMergerCalculationsCalledAtMostOnce(); + } + + /** + * Tests that the computer is working correctly for all resulting rejections. + */ + @Test + public void testAllResultingRejections() { + IDiffRelationshipComputer computer = getDiffRelationshipComputer(); + + // call resulting rejections a few more times + assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + assertAllResultingRejections(computer.getAllResultingRejections(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods of the diffs are called at most once + verifyMergerCalculationsCalledAtMostOnce(); + } + + /** + * Tests that a diff state change invalidates the cached relationships and leads to a re-calculation of + * the relationships. + */ + @Test + public void testInvalidateCache() { + CachingDiffRelationshipComputer computer = getDiffRelationshipComputer(); + + // trigger first time caching + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation method was called exactly once + verifyDirectMergeDependenciesCalledExactly(1); + + // invalidate cache + computer.invalidate(); + + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods was called twice now, because we needed to recalculate the relationships + verifyDirectMergeDependenciesCalledExactly(2); + + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods was called twice now, same as before + verifyDirectMergeDependenciesCalledExactly(2); + + // invalidate cache + computer.invalidate(); + + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + assertDirectMergeDependencies(computer.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)); + + // the calculation methods was called three times now, because of recalculation + verifyDirectMergeDependenciesCalledExactly(3); + } + + /** + * Tests that the is correctly used in {@link ComputeDiffsToMerge}. + */ + @Test + public void testComputeDiffsToMergeIntegration() { + ComputeDiffsToMerge computer = new ComputeDiffsToMerge(MERGE_RIGHT_TO_LEFT, + new CachingDiffRelationshipComputer(mergerRegistry)); + + // call resulting merges a few more times + assertAllResultingMerges(computer.getAllDiffsToMerge(diff)); + assertAllResultingMerges(computer.getAllDiffsToMerge(diff)); + assertAllResultingMerges(computer.getAllDiffsToMerge(diff)); + assertAllResultingMerges(computer.getAllDiffsToMerge(diff)); + + // the calculation methods of the diffs are called at most once + verifyMergerCalculationsCalledAtMostOnce(); + } + + /** + * Verifies that any of the relationship calculating methods of the merger were called at most once. + */ + protected void verifyMergerCalculationsCalledAtMostOnce() { + verify(merger, atMost(1)).getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT); + verify(merger, atMost(1)).getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT); + verify(merger, atMost(1)).getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT); + } + + /** + * Verifies that the calculation method for the direct merge dependencies was called exactly the given + * number of times. + * + * @param times + * number of times the method should have been called. + */ + protected void verifyDirectMergeDependenciesCalledExactly(int times) { + verify(merger, times(times)).getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT); + } + + /** + * Verifies that the calculation method for the direct resulting merges was called exactly the given + * number of times. + * + * @param times + * number of times the method should have been called. + */ + protected void verifyDirectResultingMergesCalledExactly(int times) { + verify(merger, times(times)).getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT); + } + + /** + * Verifies that the calculation method for the direct resulting rejections was called exactly the given + * number of times. + * + * @param times + * number of times the method should have been called. + */ + protected void verifyDirectResultingRejectionsCalledExactly(int times) { + verify(merger, times(times)).getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT); + } + + /** + * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only + * diffs available in the given set. + * + * @param resultingMerges + * calculated resulting merges + */ + protected void assertDirectMergeDependencies(Set<Diff> mergeDependencies) { + assertEquals(1, mergeDependencies.size()); + assertTrue(mergeDependencies.contains(mergeDependency)); + } + + /** + * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only + * diffs available in the given set. + * + * @param resultingMerges + * calculated resulting merges + */ + protected void assertDirectResultingRejections(Set<Diff> resultingRejections) { + assertEquals(1, resultingRejections.size()); + assertTrue(resultingRejections.contains(resultingRejection)); + } + + /** + * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only + * diffs available in the given set. + * + * @param resultingMerges + * calculated resulting merges + */ + protected void assertDirectResultingMerges(Set<Diff> resultingMerges) { + assertEquals(1, resultingMerges.size()); + assertTrue(resultingMerges.contains(resultingMerge)); + } + + /** + * Asserts that the known resulting merges (dependency, diff itself, direct resulting merge) are the only + * diffs available in the given set. + * + * @param resultingMerges + * calculated resulting merges + */ + protected void assertAllResultingMerges(Set<Diff> resultingMerges) { + assertEquals(3, resultingMerges.size()); + assertTrue(resultingMerges.contains(mergeDependency)); + assertTrue(resultingMerges.contains(diff)); + assertTrue(resultingMerges.contains(resultingMerge)); + } + + /** + * Asserts that the known resulting rejection is the only diff available in the given set. + * + * @param resultingRejections + * calculated resulting rejections + */ + protected void assertAllResultingRejections(Set<Diff> resultingRejections) { + assertEquals(1, resultingRejections.size()); + assertTrue(resultingRejections.contains(resultingRejection)); + } + + /** + * Creates a new comparison. + * + * @return new comparison instance + */ + private static Comparison createComparison() { + return CompareFactory.eINSTANCE.createComparison(); + } + + /** + * Creates a new match. + * + * @return new match instance + */ + private static Match createMatch() { + return CompareFactory.eINSTANCE.createMatch(); + } + + /** + * Creates a new diff instance. + * + * @param match + * @return new diff instance + */ + private static Diff createDiff(Match match) { + Diff diff = new DiffSpec(); + diff.setMatch(match); + return diff; + } + + /** + * Creates a new standalone instance of a merger registry. + * + * @return new merger registry instance + */ + private static IMerger.Registry2 createMergerRegistry() { + return (Registry2)IMerger.RegistryImpl.createStandaloneInstance(); + } + + /** + * Creates a mock merger that returns the given diffs as relationships for the testing diff. + * + * @param diff + * testing diff + * @param mergeDependency + * dependency of the testing diff + * @param resultingMerge + * resulting diff for the testing diff + * @param resultingRejection + * resulting rejection for the testing diff + * @param ranking + * ranking of the mock merger + * @param registry + * registry of the mock merger + * @return a mock merger + */ + private static IMerger2 mockMerger(Diff diff, Diff mergeDependency, Diff resultingMerge, + Diff resultingRejection, int ranking, IMerger.Registry registry) { + IMerger2 mockMerger = mock(IMerger2.class); + when(mockMerger.getDirectMergeDependencies(diff, MERGE_RIGHT_TO_LEFT)) + .thenReturn(Sets.newHashSet(mergeDependency)); + when(mockMerger.getDirectResultingMerges(diff, MERGE_RIGHT_TO_LEFT)) + .thenReturn(Sets.newHashSet(resultingMerge)); + when(mockMerger.getDirectResultingRejections(diff, MERGE_RIGHT_TO_LEFT)) + .thenReturn(Sets.newHashSet(resultingRejection)); + when(mockMerger.isMergerFor(any(Diff.class))).thenReturn(Boolean.TRUE); + when(mockMerger.getRanking()).thenReturn(ranking); + when(mockMerger.getRegistry()).thenReturn(registry); + return mockMerger; + } +} diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java index a69af8112..21a233f10 100644 --- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java +++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java @@ -11,6 +11,7 @@ * Stefan Dirix - Adds additional test classes * Michael Borkowski - Adds additional test classes * Martin Fleck - Add ImplicationMergeTest, GraphTest + * Martin Fleck - Add DiffCacheAdapterTest *******************************************************************************/ package org.eclipse.emf.compare.tests.suite; @@ -22,6 +23,7 @@ import org.eclipse.emf.compare.tests.conflict.MultiLineAttributeConflictDetectio import org.eclipse.emf.compare.tests.conflict.PseudoConflictDetectionTest; import org.eclipse.emf.compare.tests.conflict.data.bug484557.Bug484557ConflictTest; import org.eclipse.emf.compare.tests.diff.ComparisonUtilTest; +import org.eclipse.emf.compare.tests.diff.DiffRelationshipComputerTest; import org.eclipse.emf.compare.tests.diff.DiffUtilTest; import org.eclipse.emf.compare.tests.diff.FeatureFilterTest; import org.eclipse.emf.compare.tests.diff.FeatureMapMoveDiffTest; @@ -108,7 +110,7 @@ import junit.textui.TestRunner; RankedAdapterFactoryRegistryTest.class, ComparisonScopeAdapterTest.class, EMFComparePredicatesTest.class, ImplicationsMergeTest.class, GraphTest.class, ConflictImplicationsTest_Bug484579.class, PseudoConflictDetectionTest.class, ComplexMergeTest.class, - ConflictSearchTest.class, }) + ConflictSearchTest.class, DiffRelationshipComputerTest.class }) public class AllTests { /** * Standalone launcher for all of compare's tests. 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 deleted file mode 100644 index ed5285634..000000000 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDependenciesUtil.java +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 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 - * Philip Langer - bug 462884 - * Martin Fleck - bug 507177 - *******************************************************************************/ -package org.eclipse.emf.compare.internal.merge; - -import static com.google.common.base.Predicates.and; -import static com.google.common.base.Predicates.not; -import static org.eclipse.emf.compare.ConflictKind.PSEUDO; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.sameSideAs; - -import com.google.common.collect.Sets; -import com.google.common.collect.Sets.SetView; - -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> - * <p> - * Also note that the resulting merges will contain the resulting rejections (diffs from the other side - * that will be rejected) - * </p> - * - * @param diff - * The difference for which we seek all related ones. - * @param mergerRegistry - * The {@link IMerger.Registry merger registry} currently in use. - * @param rightToLeft - * 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 rightToLeft) { - final Set<Diff> resultingMerges = new LinkedHashSet<Diff>(); - resultingMerges.add(diff); - - Set<Diff> relations = internalGetResultingMerges(diff, mergerRegistry, rightToLeft); - // We don't want to take in account pseudo conflicts since there is nothing to do with them - // and their dependencies may cause incorrect merge dependencies computation. - Set<Diff> difference = Sets.filter(Sets.difference(relations, resultingMerges), - not(hasConflict(PSEUDO))); - while (!difference.isEmpty()) { - final Set<Diff> newRelations = new LinkedHashSet<Diff>(difference); - resultingMerges.addAll(newRelations); - relations = new LinkedHashSet<Diff>(); - for (Diff newRelation : newRelations) { - Set<Diff> internalResultingMerges = internalGetResultingMerges(newRelation, mergerRegistry, - rightToLeft); - // We don't want to take in account pseudo conflicts since there is nothing to do with them - // and there dependencies may cause incorrect merge dependencies computation. - relations.addAll(Sets.filter(internalResultingMerges, not(hasConflict(PSEUDO)))); - } - difference = Sets.difference(relations, resultingMerges); - } - - // If a pseudo conflict is directly selected, we want to display other diffs of the pseudo conflict as - // resulting merge for the user - if (diff.getConflict() != null && diff.getConflict().getKind() == PSEUDO) { - resultingMerges.addAll(diff.getConflict().getDifferences()); - } - - 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 rightToLeft - * 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 rightToLeft) { - final IMerger merger = mergerRegistry.getHighestRankingMerger(diff); - final Set<Diff> directParents; - final Set<Diff> directImplications; - if (merger instanceof IMerger2) { - directParents = ((IMerger2)merger).getDirectMergeDependencies(diff, rightToLeft); - directImplications = ((IMerger2)merger).getDirectResultingMerges(diff, rightToLeft); - } else { - directParents = Collections.emptySet(); - directImplications = Collections.emptySet(); - } - - final SetView<Diff> directRelated = Sets.union(directParents, directImplications); - - return directRelated; - } - - /** - * 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); - resultingRejections.addAll( - Sets.filter(allResultingMerges, and(not(hasConflict(PSEUDO)), not(sameSideAs(diff))))); - // Only search rejections caused by diffs on the same side - for (Diff resulting : Sets.filter(allResultingMerges, sameSideAs(diff))) { - Set<Diff> rejections = internalGetResultingRejections(resulting, mergerRegistry, - mergeRightToLeft); - // We don't want to take in account pseudo conflicts since there is nothing to do with them - // and their dependencies may cause incorrect merge dependencies computation. - Set<Diff> difference = Sets.filter(rejections, not(hasConflict(PSEUDO))); - 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(rejected); - if (merger instanceof IMerger2) { - Set<Diff> directMergeDependencies = ((IMerger2)merger) - .getDirectMergeDependencies(rejected, mergeRightToLeft); - // We don't want to take in account pseudo conflicts since there is nothing to do with - // them and their dependencies may cause incorrect merge dependencies computation. - // We also don't want to consider diffs on the same side for rejections - rejections.addAll(Sets.filter(directMergeDependencies, - and(not(hasConflict(PSEUDO)), not(sameSideAs(diff))))); - Set<Diff> directResultingMerges = ((IMerger2)merger) - .getDirectResultingMerges(rejected, mergeRightToLeft); - rejections.addAll(Sets.filter(directResultingMerges, - and(not(hasConflict(PSEUDO)), not(sameSideAs(diff))))); - } - } - 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 rightToLeft - * 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 rightToLeft) { - final IMerger merger = mergerRegistry.getHighestRankingMerger(diff); - if (merger instanceof IMerger2) { - return ((IMerger2)merger).getDirectResultingRejections(diff, rightToLeft); - } - return Collections.emptySet(); - } -} diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java index b49795edd..f129cc3de 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/BatchMerger.java @@ -40,6 +40,9 @@ public class BatchMerger implements IBatchMerger { /** Filter the differences that should be merged. */ private final Predicate<? super Diff> filter; + /** The relationship computer used to calculate dependencies and requirements of diffs. */ + private IDiffRelationshipComputer relationshipComputer; + /** * Constructs our batch merger provided the registry from which to retrieve the delegate mergers. Using * such a merger will merge every differences passed to its "copy" methods : conflictual or not. @@ -49,7 +52,19 @@ public class BatchMerger implements IBatchMerger { * IMerger.Registry2. */ public BatchMerger(IMerger.Registry registry) { - this(registry, alwaysTrue()); + this(new DiffRelationshipComputer(registry), alwaysTrue()); + } + + /** + * Constructs our batch merger provided the registry from which to retrieve the delegate mergers. Using + * such a merger will merge every differences passed to its "copy" methods : conflictual or not. + * + * @param relationshipComputer + * The relationship computer used to calculate dependencies and requirements of diffs. + * @since 3.5 + */ + public BatchMerger(IDiffRelationshipComputer relationshipComputer) { + this(relationshipComputer, alwaysTrue()); } /** @@ -79,7 +94,38 @@ public class BatchMerger implements IBatchMerger { * @see org.eclipse.emf.compare.utils.EMFComparePredicates */ public BatchMerger(IMerger.Registry registry, Predicate<? super Diff> filter) { - this.registry = (IMerger.Registry2)checkNotNull(registry); + this(new DiffRelationshipComputer(registry), filter); + } + + /** + * Constructs our batch merger provided the registry from which to retrieve the delegate mergers, and a + * filter if you only wish to merge specific differences. + * <p> + * <b>Note</b> that the filter indicates differences that will be merged, not those that will be ignored. + * </p> + * <p> + * For example, if you wish to ignore all differences in conflict, you can use : + * + * <pre> + * IMerger.Registry registry = IMerger.RegistryImpl.createStandaloneInstance(); + * IBatchMerger bathMerger = new BatchMerger(registry, {@link com.google.common.base.Predicates#not(Predicate) not}({@link org.eclipse.emf.compare.utils.EMFComparePredicates#hasConflict(org.eclipse.emf.compare.ConflictKind...) hasConflict}(ConflictKind.PSEUDO, ConflictKind.REAL))); + * bathMerger.copyAll... + * </pre> + * </p> + * + * @param filter + * Additional filter for the differences. This could be set in order to ignore diffs + * originating from a given side. Note that the filter describes the differences that will be + * merged, not those that will be ignored. + * @param relationshipComputer + * The relationship computer used to calculate dependencies and requirements of diffs. + * @see com.google.common.base.Predicates + * @see org.eclipse.emf.compare.utils.EMFComparePredicates + * @since 3.5 + */ + public BatchMerger(IDiffRelationshipComputer relationshipComputer, Predicate<? super Diff> filter) { + this.relationshipComputer = checkNotNull(relationshipComputer); + this.registry = (IMerger.Registry2)checkNotNull(relationshipComputer.getMergerRegistry()); this.filter = checkNotNull(filter); } @@ -95,7 +141,7 @@ public class BatchMerger implements IBatchMerger { start = System.currentTimeMillis(); LOGGER.debug("copyAllLeftToRight(differences, monitor) - Start"); //$NON-NLS-1$ } - ComputeDiffsToMerge computer = new ComputeDiffsToMerge(false, registry); + ComputeDiffsToMerge computer = new ComputeDiffsToMerge(false, relationshipComputer); for (Diff diff : Iterables.filter(differences, filter)) { if (!AbstractMerger.isInTerminalState(diff)) { Set<Diff> diffsToMerge = computer.getAllDiffsToMerge(diff); @@ -129,7 +175,7 @@ public class BatchMerger implements IBatchMerger { start = System.currentTimeMillis(); LOGGER.debug("copyAllRightToLeft(differences, monitor) - Start"); //$NON-NLS-1$ } - ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, registry); + ComputeDiffsToMerge computer = new ComputeDiffsToMerge(true, relationshipComputer); for (Diff diff : Iterables.filter(differences, filter)) { if (!AbstractMerger.isInTerminalState(diff)) { Set<Diff> diffsToMerge = computer.getAllDiffsToMerge(diff); diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/CachingDiffRelationshipComputer.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/CachingDiffRelationshipComputer.java new file mode 100644 index 000000000..64bdc7734 --- /dev/null +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/CachingDiffRelationshipComputer.java @@ -0,0 +1,524 @@ +/******************************************************************************* + * Copyright (c) 2017 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: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.merge; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.merge.IMerger.Registry; + +/** + * A computer implementation to cache the relationship of diffs. + * + * @since 3.5 + * @author Martin Fleck <mfleck@eclipsesource.com> + * @see IMerger2 + */ +public class CachingDiffRelationshipComputer implements IDiffRelationshipComputer { + + /** A computer instance to calculate the relationships between diffs. */ + protected IDiffRelationshipComputer computer; + + /** Direct merge dependencies: right to left. */ + protected Map<Diff, Set<Diff>> directMergeDependenciesR2L = new ConcurrentHashMap<>(); + + /** Direct merge dependencies: left to right. */ + protected Map<Diff, Set<Diff>> directMergeDependenciesL2R = new ConcurrentHashMap<>(); + + /** Direct resulting merges: right to left. */ + protected Map<Diff, Set<Diff>> directResultingMergesR2L = new ConcurrentHashMap<>(); + + /** Direct resulting merges: left to right. */ + protected Map<Diff, Set<Diff>> directResultingMergesL2R = new ConcurrentHashMap<>(); + + /** Direct resulting rejections: right to left. */ + protected Map<Diff, Set<Diff>> directResultingRejectionsR2L = new ConcurrentHashMap<>(); + + /** Direct resulting rejections: left to right. */ + protected Map<Diff, Set<Diff>> directResultingRejectionsL2R = new ConcurrentHashMap<>(); + + /** All resulting merges: right to left. */ + protected Map<Diff, Set<Diff>> allResultingMergesR2L = new ConcurrentHashMap<>(); + + /** All resulting merges: left to right. */ + protected Map<Diff, Set<Diff>> allResultingMergesL2R = new ConcurrentHashMap<>(); + + /** All resulting rejections: right to left. */ + protected Map<Diff, Set<Diff>> allResultingRejectionsR2L = new ConcurrentHashMap<>(); + + /** All resulting rejections: left to right. */ + protected Map<Diff, Set<Diff>> allResultingRejectionsL2R = new ConcurrentHashMap<>(); + + /** + * Creates a new computer with the given registry. + * + * @param registry + * merger registry + */ + public CachingDiffRelationshipComputer(IMerger.Registry registry) { + this(registry, IMergeCriterion.NONE); + } + + /** + * Creates a new computer with the given registry and merge criterion. + * + * @param registry + * merger registry + * @param criterion + * merge criterion used to get the merger from the registry, use {@link IMergeCriterion#NONE} + * if no special criterion should be set. + */ + public CachingDiffRelationshipComputer(IMerger.Registry registry, IMergeCriterion criterion) { + this(new DiffRelationshipComputer(registry, criterion)); + } + + /** + * Creates a new computer by wrapping the given instance. Any computing calls are delegated to this + * instance and cached. + * + * @param computer + * computer instance used for calculating diff relationships. + */ + public CachingDiffRelationshipComputer(IDiffRelationshipComputer computer) { + this.computer = computer; + } + + /** + * Returns the internal computer instance used to compute the diff relationships. + * + * @return internal computer instance. + */ + protected IDiffRelationshipComputer getComputer() { + return computer; + } + + @Override + public Registry getMergerRegistry() { + return getComputer().getMergerRegistry(); + } + + /** + * {@inheritDoc} WARNING: Setting the merger registry invalidates previously cached results, if another + * registry was set previously! + */ + public void setMergerRegistry(Registry mergerRegistry) { + if (getMergerRegistry() != mergerRegistry) { + getComputer().setMergerRegistry(mergerRegistry); + invalidate(); + } + } + + @Override + public IMergeCriterion getMergeCriterion() { + return getComputer().getMergeCriterion(); + } + + @Override + public IMerger2 getMerger(Diff diff) { + return getComputer().getMerger(diff); + } + + @Override + public boolean hasMerger(Diff diff) { + return getComputer().hasMerger(diff); + } + + /** + * {@inheritDoc} WARNING: Setting the merge criterion invalidates previously cached results, if another + * criterion was set previously. + */ + public void setMergeCriterion(IMergeCriterion mergeCriterion) { + if (getMergeCriterion() != mergeCriterion) { + getComputer().setMergeCriterion(mergeCriterion); + invalidate(); + } + } + + /** + * Caches the given direct merge dependencies. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @param directMergeDependencies + * direct merge dependencies of diff + */ + protected void setCachedDirectMergeDependencies(Diff diff, boolean mergeRightToLeft, + Set<Diff> directMergeDependencies) { + if (mergeRightToLeft) { + directMergeDependenciesR2L.put(diff, directMergeDependencies); + } else { + directMergeDependenciesL2R.put(diff, directMergeDependencies); + } + } + + /** + * Returns the cached direct merge dependencies. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached direct merge dependencies + */ + protected Set<Diff> getCachedDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) { + if (mergeRightToLeft) { + return directMergeDependenciesR2L.get(diff); + } else { + return directMergeDependenciesL2R.get(diff); + } + } + + /** + * Computes direct merge dependencies for the given diff. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return a non-null set of direct merge dependencies + */ + protected Set<Diff> computeDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) { + return getComputer().getDirectMergeDependencies(diff, mergeRightToLeft); + } + + /** + * Returns the cached direct merge dependencies, if present. Otherwise, the direct merge dependencies are + * retrieved and cached using the given merger. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached direct merge dependencies + * @see IMerger2#getDirectMergeDependencies(Diff, boolean) + */ + @Override + public Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) { + Set<Diff> directMergeDependencies = getCachedDirectMergeDependencies(diff, mergeRightToLeft); + if (directMergeDependencies == null) { + directMergeDependencies = computeDirectMergeDependencies(diff, mergeRightToLeft); + setCachedDirectMergeDependencies(diff, mergeRightToLeft, directMergeDependencies); + } + return directMergeDependencies; + } + + /** + * Caches the given direct resulting merges. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @param directResultingMerges + * direct resulting merges + */ + protected void setCachedDirectResultingMerges(Diff diff, boolean mergeRightToLeft, + Set<Diff> directResultingMerges) { + if (mergeRightToLeft) { + directResultingMergesR2L.put(diff, directResultingMerges); + } else { + directResultingMergesL2R.put(diff, directResultingMerges); + } + } + + /** + * Returns the cached direct resulting merges. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached direct resulting merges + */ + protected Set<Diff> getCachedDirectResultingMerges(Diff diff, boolean mergeRightToLeft) { + if (mergeRightToLeft) { + return directResultingMergesR2L.get(diff); + } else { + return directResultingMergesL2R.get(diff); + } + } + + /** + * Computes direct resulting merges for the given diff. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return a non-null set of all resulting merges + */ + protected Set<Diff> computeDirectResultingMerges(Diff diff, boolean mergeRightToLeft) { + return getComputer().getDirectResultingMerges(diff, mergeRightToLeft); + } + + /** + * Returns the cached direct resulting merges, if present. Otherwise, the direct resulting merges are + * retrieved and cached using the given merger. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached direct resulting merges + * @see IMerger2#getDirectResultingMerges(Diff, boolean) + */ + @Override + public Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft) { + Set<Diff> directResultingMerges = getCachedDirectResultingMerges(diff, mergeRightToLeft); + if (directResultingMerges == null) { + directResultingMerges = computeDirectResultingMerges(diff, mergeRightToLeft); + setCachedDirectResultingMerges(diff, mergeRightToLeft, directResultingMerges); + } + return directResultingMerges; + } + + /** + * Caches the given direct resulting rejections. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @param directResultingRejections + * direct resulting rejections + */ + protected void setCachedDirectResultingRejections(Diff diff, boolean mergeRightToLeft, + Set<Diff> directResultingRejections) { + if (mergeRightToLeft) { + directResultingRejectionsR2L.put(diff, directResultingRejections); + } else { + directResultingRejectionsL2R.put(diff, directResultingRejections); + } + } + + /** + * Returns the cached direct resulting rejections. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached direct resulting rejections + */ + protected Set<Diff> getCachedDirectResultingRejections(Diff diff, boolean mergeRightToLeft) { + if (mergeRightToLeft) { + return directResultingRejectionsR2L.get(diff); + } else { + return directResultingRejectionsL2R.get(diff); + } + } + + /** + * Computes the direct resulting rejections. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return a non-null set of direct resulting rejections + */ + protected Set<Diff> computeDirectResultingRejections(Diff diff, boolean mergeRightToLeft) { + return getComputer().getDirectResultingRejections(diff, mergeRightToLeft); + } + + @Override + public Set<Diff> getDirectResultingRejections(Diff diff, boolean mergeRightToLeft) { + Set<Diff> directResultingRejections = getCachedDirectResultingRejections(diff, mergeRightToLeft); + if (directResultingRejections == null) { + directResultingRejections = computeDirectResultingRejections(diff, mergeRightToLeft); + setCachedDirectResultingRejections(diff, mergeRightToLeft, directResultingRejections); + } + return directResultingRejections; + } + + /** + * Caches the given all resulting rejections. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @param allResultingRejections + * all resulting rejections + */ + protected void setCachedAllResultingRejections(Diff diff, boolean mergeRightToLeft, + Set<Diff> allResultingRejections) { + if (mergeRightToLeft) { + allResultingRejectionsR2L.put(diff, allResultingRejections); + } else { + allResultingRejectionsL2R.put(diff, allResultingRejections); + } + } + + /** + * Returns the cached all resulting rejections. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached all resulting rejections + */ + protected Set<Diff> getCachedAllResultingRejections(Diff diff, boolean mergeRightToLeft) { + if (mergeRightToLeft) { + return allResultingRejectionsR2L.get(diff); + } else { + return allResultingRejectionsL2R.get(diff); + } + } + + /** + * Computes all resulting rejections for the given diff. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return a non-null set of all resulting rejections + */ + protected Set<Diff> computeAllResultingRejections(Diff diff, boolean mergeRightToLeft) { + return getComputer().getAllResultingRejections(diff, mergeRightToLeft); + } + + @Override + public Set<Diff> getAllResultingRejections(Diff diff, boolean mergeRightToLeft) { + Set<Diff> allResultingRejections = getCachedAllResultingRejections(diff, mergeRightToLeft); + if (allResultingRejections == null) { + allResultingRejections = computeAllResultingRejections(diff, mergeRightToLeft); + setCachedAllResultingRejections(diff, mergeRightToLeft, allResultingRejections); + } + return allResultingRejections; + } + + /** + * Caches the given all resulting merges. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @param allResultingMerges + * all resulting merges + */ + protected void setCachedAllResultingMerges(Diff diff, boolean mergeRightToLeft, + Set<Diff> allResultingMerges) { + if (mergeRightToLeft) { + allResultingMergesR2L.put(diff, allResultingMerges); + } else { + allResultingMergesL2R.put(diff, allResultingMerges); + } + } + + /** + * Returns the cached all resulting merges. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached all resulting merges + */ + protected Set<Diff> getCachedAllResultingMerges(Diff diff, boolean mergeRightToLeft) { + if (mergeRightToLeft) { + return allResultingMergesR2L.get(diff); + } else { + return allResultingMergesL2R.get(diff); + } + } + + /** + * Computes all resulting merges for the given diff. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return a non-null set of all resulting merges + */ + protected Set<Diff> computeAllResultingMerges(Diff diff, boolean mergeRightToLeft) { + return getComputer().getAllResultingMerges(diff, mergeRightToLeft); + } + + /** + * Returns the cached direct resulting merges, if present. Otherwise, the direct resulting merges are + * retrieved and cached using the given merger. + * + * @param diff + * diff + * @param mergeRightToLeft + * merge direction + * @return cached direct resulting merges + * @see IMerger2#getDirectResultingMerges(Diff, boolean) + */ + @Override + public Set<Diff> getAllResultingMerges(Diff diff, boolean mergeRightToLeft) { + Set<Diff> allResultingMerges = getCachedAllResultingMerges(diff, mergeRightToLeft); + if (allResultingMerges == null) { + allResultingMerges = computeAllResultingMerges(diff, mergeRightToLeft); + setCachedAllResultingMerges(diff, mergeRightToLeft, allResultingMerges); + } + return allResultingMerges; + } + + /** + * Invalidates the cache for the given diff, so that relationships will be re-calculated for this diff the + * next time a respective method is called. + * + * @param diff + * diff for which the cache should be invalidated. + */ + public void invalidate(Diff diff) { + directMergeDependenciesR2L.remove(diff); + directMergeDependenciesL2R.remove(diff); + directResultingRejectionsR2L.remove(diff); + directResultingRejectionsL2R.remove(diff); + directResultingMergesR2L.remove(diff); + directResultingMergesL2R.remove(diff); + allResultingMergesL2R.remove(diff); + allResultingMergesR2L.remove(diff); + allResultingRejectionsL2R.remove(diff); + allResultingRejectionsR2L.remove(diff); + } + + /** + * Invalidates the cache for all given diffs, so that relationships will be re-calculated for each diff + * the next time a respective method is called. + * + * @param diffs + * diffs for which the cache should be invalidated. + */ + public void invalidate(Iterable<Diff> diffs) { + for (Diff diff : diffs) { + invalidate(diff); + } + } + + /** + * Invalidates the complete cache, so that relationships will be re-calculated any diff the next time a + * respective method is called. + */ + public void invalidate() { + directMergeDependenciesR2L.clear(); + directMergeDependenciesL2R.clear(); + directResultingRejectionsR2L.clear(); + directResultingRejectionsL2R.clear(); + directResultingMergesR2L.clear(); + directResultingMergesL2R.clear(); + allResultingMergesL2R.clear(); + allResultingMergesR2L.clear(); + allResultingRejectionsL2R.clear(); + allResultingRejectionsR2L.clear(); + } +} diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java index 02b569291..c1469d570 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ComputeDiffsToMerge.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Obeo. + * Copyright (c) 2017 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 @@ -7,10 +7,10 @@ * * Contributors: * Obeo - initial API and implementation + * Martin Fleck - bug 514415 *******************************************************************************/ package org.eclipse.emf.compare.merge; -import static com.google.common.base.Preconditions.checkNotNull; import static org.eclipse.emf.compare.ConflictKind.REAL; import static org.eclipse.emf.compare.merge.IMergeCriterion.NONE; @@ -54,16 +54,6 @@ public class ComputeDiffsToMerge { private final boolean rightToLeft; /** - * The merge criterion to use, can be <code>null</code>. - */ - private IMergeCriterion criterion; - - /** - * The merger registry to use. - */ - private IMerger.Registry2 registry; - - /** * The ordered set of diffs to merge. */ private Set<Diff> result = new LinkedHashSet<Diff>(); @@ -77,6 +67,9 @@ public class ComputeDiffsToMerge { */ private Predicate<? super Conflict> conflictChecker; + /** The relationship computer used to calculate dependencies and requirements of diffs. */ + private IDiffRelationshipComputer relationshipComputer; + /** * Constructor. * @@ -86,7 +79,7 @@ public class ComputeDiffsToMerge { * The Registry to use. */ public ComputeDiffsToMerge(boolean rightToLeft, IMerger.Registry2 registry) { - this(rightToLeft, registry, NONE); + this(rightToLeft, new DiffRelationshipComputer(registry, NONE)); } /** @@ -100,9 +93,20 @@ public class ComputeDiffsToMerge { * The merge criterion, must not be <code>null</code> */ public ComputeDiffsToMerge(boolean rightToLeft, IMerger.Registry2 registry, IMergeCriterion criterion) { + this(rightToLeft, new DiffRelationshipComputer(registry, criterion)); + } + + /** + * Constructor. + * + * @param rightToLeft + * The merge direction + * @param relationshipComputer + * The relationship computer used to calculate dependencies and requirements of diffs. + */ + public ComputeDiffsToMerge(boolean rightToLeft, IDiffRelationshipComputer relationshipComputer) { this.rightToLeft = rightToLeft; - this.registry = registry; - this.criterion = checkNotNull(criterion); + this.relationshipComputer = relationshipComputer; } /** @@ -179,16 +183,17 @@ public class ComputeDiffsToMerge { diffsThatLedToConflict.add(diff); throw new MergeBlockedByConflictException(diffsThatLedToConflict); } - DelegatingMerger mergerDelegate = AbstractMerger.getMergerDelegate(diff, registry, criterion); - IMerger merger = mergerDelegate.getMerger(); - if (merger instanceof IMerger2) { - Set<Diff> dependencies = ((IMerger2)merger).getDirectMergeDependencies(diff, rightToLeft); + + if (relationshipComputer.hasMerger(diff)) { + Set<Diff> dependencies = relationshipComputer.getDirectMergeDependencies(diff, rightToLeft); for (Diff required : dependencies) { addDiff(required, consequences); } + result.add(diff); computing.remove(diff); - consequences.addAll(((IMerger2)merger).getDirectResultingMerges(diff, rightToLeft)); + + consequences.addAll(relationshipComputer.getDirectResultingMerges(diff, rightToLeft)); } else { result.add(diff); } diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/DiffRelationshipComputer.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/DiffRelationshipComputer.java new file mode 100644 index 000000000..c144f3b7a --- /dev/null +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/DiffRelationshipComputer.java @@ -0,0 +1,233 @@ +/******************************************************************************* + * Copyright (c) 2017 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: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.merge; + +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.not; +import static org.eclipse.emf.compare.ConflictKind.PSEUDO; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.sameSideAs; + +import com.google.common.collect.Sets; +import com.google.common.collect.Sets.SetView; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.merge.IMerger.Registry; +import org.eclipse.emf.compare.merge.IMerger.Registry2; + +/** + * A computer implementation to calculate the relationship of differences in EMF Compare. + * + * @since 3.5 + * @author Martin Fleck <mfleck@eclipsesource.com> + * @see IMerger2 + */ +public class DiffRelationshipComputer implements IDiffRelationshipComputer { + + /** Merger registry used to retrieve the correct merger. */ + protected Registry registry; + + /** Merge criterion used to retrieve the correct merger. */ + protected IMergeCriterion criterion; + + /** + * Creates a new relationship computer. + * + * @param registry + * merger registry + */ + public DiffRelationshipComputer(IMerger.Registry registry) { + this(registry, IMergeCriterion.NONE); + } + + /** + * Creates a new relationship computer. + * + * @param registry + * merger registry + * @param criterion + * merge criterion used to get the merger from the registry, use {@link IMergeCriterion#NONE} + * if no special criterion should be set. + */ + public DiffRelationshipComputer(IMerger.Registry registry, IMergeCriterion criterion) { + this.registry = registry; + this.criterion = criterion; + } + + @Override + public Registry getMergerRegistry() { + return registry; + } + + @Override + public void setMergerRegistry(Registry mergerRegistry) { + this.registry = mergerRegistry; + } + + @Override + public IMergeCriterion getMergeCriterion() { + return criterion; + } + + @Override + public void setMergeCriterion(IMergeCriterion mergeCriterion) { + this.criterion = mergeCriterion; + } + + /** + * Returns the {@link #getMergerRegistry() merger registry} as {@link Registry2}, if possible. If the + * merger registry is not an instance of {@link Registry2}, null is returned. + * + * @return {@link Registry2} instance or null. + */ + protected Registry2 getMergerRegistry2() { + if (registry instanceof Registry2) { + return (Registry2)registry; + } + return null; + } + + @Override + public IMerger2 getMerger(Diff diff) { + if (getMergerRegistry2() == null) { + return null; + } + + DelegatingMerger mergerDelegate = AbstractMerger.getMergerDelegate(diff, getMergerRegistry2(), + getMergeCriterion()); + IMerger merger = mergerDelegate.getMerger(); + if (!(merger instanceof IMerger2)) { + return null; + } + + return (IMerger2)merger; + } + + @Override + public boolean hasMerger(Diff diff) { + return getMerger(diff) != null; + } + + @Override + public Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) { + IMerger2 merger = getMerger(diff); + if (merger != null) { + return merger.getDirectMergeDependencies(diff, mergeRightToLeft); + } + return Sets.newHashSet(); + } + + @Override + public Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft) { + IMerger2 merger = getMerger(diff); + if (merger != null) { + return merger.getDirectResultingMerges(diff, mergeRightToLeft); + } + return Sets.newHashSet(); + } + + @Override + public Set<Diff> getDirectResultingRejections(Diff diff, boolean mergeRightToLeft) { + IMerger2 merger = getMerger(diff); + if (merger != null) { + return merger.getDirectResultingRejections(diff, mergeRightToLeft); + } else { + return Sets.newHashSet(); + } + } + + @Override + public Set<Diff> getAllResultingMerges(Diff diff, boolean rightToLeft) { + final Set<Diff> resultingMerges = new LinkedHashSet<Diff>(); + resultingMerges.add(diff); + + Set<Diff> relations = internalGetAllResultingMerges(diff, rightToLeft); + // We don't want to take in account pseudo conflicts since there is nothing to do with them + // and their dependencies may cause incorrect merge dependencies computation. + Set<Diff> difference = Sets.filter(Sets.difference(relations, resultingMerges), + not(hasConflict(PSEUDO))); + while (!difference.isEmpty()) { + final Set<Diff> newRelations = new LinkedHashSet<Diff>(difference); + resultingMerges.addAll(newRelations); + relations = new LinkedHashSet<Diff>(); + for (Diff newRelation : newRelations) { + Set<Diff> internalResultingMerges = internalGetAllResultingMerges(newRelation, rightToLeft); + // We don't want to take in account pseudo conflicts since there is nothing to do with them + // and there dependencies may cause incorrect merge dependencies computation. + relations.addAll(Sets.filter(internalResultingMerges, not(hasConflict(PSEUDO)))); + } + difference = Sets.difference(relations, resultingMerges); + } + + // If a pseudo conflict is directly selected, we want to display other diffs of the pseudo conflict as + // resulting merge for the user + if (diff.getConflict() != null && diff.getConflict().getKind() == PSEUDO) { + resultingMerges.addAll(diff.getConflict().getDifferences()); + } + + 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 rightToLeft + * The direction in which we're considering a merge. + * @return The set of all differences <b>directly</b> related to the given one. + */ + protected Set<Diff> internalGetAllResultingMerges(Diff diff, boolean rightToLeft) { + final Set<Diff> directParents = getDirectMergeDependencies(diff, rightToLeft); + final Set<Diff> directImplications = getDirectResultingMerges(diff, rightToLeft); + final SetView<Diff> directRelated = Sets.union(directParents, directImplications); + return directRelated; + } + + @Override + public Set<Diff> getAllResultingRejections(Diff diff, boolean mergeRightToLeft) { + final Set<Diff> resultingRejections = new LinkedHashSet<Diff>(); + final Set<Diff> allResultingMerges = getAllResultingMerges(diff, mergeRightToLeft); + resultingRejections.addAll( + Sets.filter(allResultingMerges, and(not(hasConflict(PSEUDO)), not(sameSideAs(diff))))); + // Only search rejections caused by diffs on the same side + for (Diff resulting : Sets.filter(allResultingMerges, sameSideAs(diff))) { + Set<Diff> rejections = getDirectResultingRejections(resulting, mergeRightToLeft); + // We don't want to take in account pseudo conflicts since there is nothing to do with them + // and their dependencies may cause incorrect merge dependencies computation. + Set<Diff> difference = Sets.filter(rejections, not(hasConflict(PSEUDO))); + while (!difference.isEmpty()) { + final Set<Diff> newRejections = new LinkedHashSet<Diff>(difference); + resultingRejections.addAll(newRejections); + rejections = new LinkedHashSet<Diff>(); + for (Diff rejected : newRejections) { + Set<Diff> directMergeDependencies = getDirectMergeDependencies(rejected, + mergeRightToLeft); + // We don't want to take in account pseudo conflicts since there is nothing to do with + // them and their dependencies may cause incorrect merge dependencies computation. + // We also don't want to consider diffs on the same side for rejections + rejections.addAll(Sets.filter(directMergeDependencies, + and(not(hasConflict(PSEUDO)), not(sameSideAs(diff))))); + Set<Diff> directResultingMerges = getDirectResultingMerges(rejected, mergeRightToLeft); + rejections.addAll(Sets.filter(directResultingMerges, + and(not(hasConflict(PSEUDO)), not(sameSideAs(diff))))); + } + difference = Sets.difference(rejections, resultingRejections); + } + } + return resultingRejections; + } + +} diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IDiffRelationshipComputer.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IDiffRelationshipComputer.java new file mode 100644 index 000000000..56ceaabe8 --- /dev/null +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/IDiffRelationshipComputer.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2017 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: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.emf.compare.merge; + +import java.util.Set; + +import org.eclipse.emf.compare.Diff; + +/** + * A computer to retrieve the dependent diffs, resulting merges and resulting rejections for a diff. + * + * @since 3.5 + * @author Martin Fleck <mfleck@eclipsesource.com> + * @see IMerger2 + */ +public interface IDiffRelationshipComputer { + /** + * Retrieves the set of directly required diffs needed in order to merge the given <code>diff</code>. This + * may includes the diff's requirements or any other diff that we need to merge before the given one. + * + * @param diff + * The difference for which we seek the direct merge dependencies. + * @param mergeRightToLeft + * The direction in which we're considering a merge. + * @return A non-null set of direct merge dependencies. + * @see IMerger2#getDirectMergeDependencies(Diff, boolean) + */ + Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft); + + /** + * Returns all differences that will be merged because of our merging the given <code>diff</code>. This + * may include the diff's implications, the diff's equivalences, the diff's refinements or any other diff + * that we need to merge together with the given diff. + * + * @param diff + * The difference for which we seek the direct merge dependencies. + * @param mergeRightToLeft + * The direction in which we're considering a merge. + * @return A non-null set of direct resulting merges. + * @see IMerger2#getDirectResultingMerges(Diff, boolean) + */ + Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft); + + /** + * Returns the set of all differences that need to be rejected if <code>diff</code> is merged in the given + * direction. + * + * @param diff + * The difference for which we seek the direct resulting rejections. + * @param mergeRightToLeft + * The direction in which we're considering a merge. + * @return A non-null set of direct resulting rejections. + * @see #getAllResultingRejections(Diff, boolean) + * @see IMerger2#getDirectResultingRejections(Diff, boolean) + */ + Set<Diff> getDirectResultingRejections(Diff diff, boolean mergeRightToLeft); + + /** + * 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> + * <p> + * Also note that the resulting merges will contain the resulting rejections (diffs from the other side + * that will be rejected) + * </p> + * + * @param diff + * The difference for which we seek all related ones. + * @param rightToLeft + * The direction in which we're considering a merge. + * @return A non-null set of all diffs related to the given <code>diff</code> when merging in the given + * direction. + */ + Set<Diff> getAllResultingMerges(Diff diff, boolean rightToLeft); + + /** + * 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 mergeRightToLeft + * The direction in which we're considering a merge. + * @return A non-null set of all diffs that will be rejected if the given <code>diff</code> is merged in + * the given direction. + */ + Set<Diff> getAllResultingRejections(Diff diff, boolean mergeRightToLeft); + + /** + * Returns the merger registry used for calculating the diff relationships. + * + * @return The merger registry. + */ + IMerger.Registry getMergerRegistry(); + + /** + * Sets the merger registry used for calculating the diff relationships. + * + * @param mergerRegistry + * The merger registry. + */ + void setMergerRegistry(IMerger.Registry mergerRegistry); + + /** + * Returns the merge criterion considered for calculating the diff relationships. + * + * @return The merge criterion. + */ + IMergeCriterion getMergeCriterion(); + + /** + * Sets the merge criterion considered for calculating the diff relationships. + * + * @param mergeCriterion + * The merger criterion. + */ + void setMergeCriterion(IMergeCriterion mergeCriterion); + + /** + * Returns the best-fitting merger for the given diff according to the {@link #getMergerRegistry() merger + * registry} and the {@link #getMergeCriterion() merge criterion}. + * + * @param diff + * The difference for which we seek the merger. + * @return The best-fitting merger for the given diff or null if no merger fits. + */ + IMerger2 getMerger(Diff diff); + + /** + * Indicates whether a best-fitting merger for the given diff is available. + * + * @param diff + * The difference for which we seek the merger. + * @return Returns true if a best-fitting merger is available, false otherwise. + */ + boolean hasMerger(Diff diff); +} |