[561825] Merge a Sirius Node doesn't merge "actualMapping" difference.

The implies/impliedBy relations of Sirius Nodes should not be
automatically merged and just noted as "MERGED". These differences
should be merged just like the others.

- Add a new Merger, SiriusReferenceChangeMerger, to manage these
"implies/impliedBy" relations.

- Add unit tests.

Bug: 561825
Change-Id: If191702dc31ec00cce0410ecc39a0ee3e5dac0dd
Signed-off-by: Glenn Plouhinec <glenn.plouhinec@obeo.fr>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/TestBug561458.java b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/TestBug561458.java
index 94d6e5c..b09504a 100644
--- a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/TestBug561458.java
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/TestBug561458.java
@@ -17,7 +17,6 @@
 
 import java.io.IOException;
 import java.util.List;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -25,17 +24,8 @@
 import org.eclipse.emf.compare.Diff;
 import org.eclipse.emf.compare.EMFCompare;
 import org.eclipse.emf.compare.ReferenceChange;
-import org.eclipse.emf.compare.diagram.internal.CompareDiagramPostProcessor;
-import org.eclipse.emf.compare.domain.IMergeRunnable;
-import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl;
-import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.diagram.sirius.tests.AbstractSiriusTest;
 import org.eclipse.emf.compare.merge.AbstractMerger;
-import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer;
-import org.eclipse.emf.compare.merge.IMerger;
-import org.eclipse.emf.compare.postprocessor.BasicPostProcessorDescriptorImpl;
-import org.eclipse.emf.compare.postprocessor.IPostProcessor;
-import org.eclipse.emf.compare.postprocessor.PostProcessorDescriptorRegistryImpl;
-import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
 import org.eclipse.emf.compare.scope.DefaultComparisonScope;
 import org.eclipse.emf.compare.tests.framework.AbstractInputData;
 import org.eclipse.emf.ecore.resource.Resource;
@@ -49,8 +39,7 @@
  * 
  * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
  */
-@SuppressWarnings("restriction")
-public class TestBug561458 {
+public class TestBug561458 extends AbstractSiriusTest {
 
 	/**
 	 * The main comparison.
@@ -83,21 +72,6 @@
 	private Resource origin;
 
 	/**
-	 * The PostProcessors registry.
-	 */
-	private IPostProcessor.Descriptor.Registry<String> registry;
-
-	/**
-	 * Used to initialize a default merger registry for merge action.
-	 */
-	private IMerger.Registry mergerRegistry;
-
-	/**
-	 * Used to merge differences.
-	 */
-	private IMergeRunnable mergeRunnable;
-
-	/**
 	 * Set up the test models.
 	 * <p>
 	 * addedContainers.nodes: In this model we have a tree nodes structured like this:
@@ -139,13 +113,12 @@
 	@Before
 	public void setUp() throws IOException {
 		Bug561458 inputData = new Bug561458();
+		fillRegistries();
 		addedContainers = inputData.getResource("addedContainers.aird"); //$NON-NLS-1$
 		deletedContainers = inputData.getResource("deletedContainers.aird"); //$NON-NLS-1$
 		singleAddedContainer = inputData.getResource("singleAddedContainer.aird"); //$NON-NLS-1$
 		singleDeletedContainer = inputData.getResource("singleDeletedContainer.aird"); //$NON-NLS-1$
 		origin = null;
-		registry = registerPostProcessors();
-		mergerRegistry = EMFCompareRCPPlugin.getDefault().getMergerRegistry();
 	}
 
 	/**
@@ -155,7 +128,8 @@
 	public void testSingleAddedContainerImpliesMapping() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(singleAddedContainer,
 				singleDeletedContainer, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 		List<ReferenceChange> decorators = getAddedOrRemovedDecorators();
 
 		ReferenceChange addedDiff = decorators.get(0);
@@ -173,7 +147,8 @@
 	public void testSingleDeletedContainerImpliedByMapping() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(singleDeletedContainer,
 				singleAddedContainer, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 		List<ReferenceChange> decorators = getAddedOrRemovedDecorators();
 
 		ReferenceChange deletedDiff = decorators.get(0);
@@ -190,7 +165,8 @@
 	@Test
 	public void testAddedContainersImpliesMapping() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(addedContainers, deletedContainers, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 		List<ReferenceChange> decorators = getAddedOrRemovedDecorators();
 
 		ReferenceChange firstAddedDiff = decorators.get(0);
@@ -213,7 +189,8 @@
 	@Test
 	public void testDeletedContainersImpliedByMapping() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(deletedContainers, addedContainers, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 		List<ReferenceChange> decorators = getAddedOrRemovedDecorators();
 
 		ReferenceChange firstDeletedDiff = decorators.get(0);
@@ -237,10 +214,10 @@
 	public void testMergeRightToLeftDeletedContainer() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(singleDeletedContainer,
 				singleAddedContainer, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
-		boolean leftToRight = false;
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 
-		mergeDiffs(comparison.getDifferences(), leftToRight);
+		mergeDiffsRightToLeft(comparison.getDifferences());
 		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
 	}
 
@@ -251,10 +228,10 @@
 	public void testMergeLeftToRightDeletedContainer() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(singleDeletedContainer,
 				singleAddedContainer, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
-		boolean leftToRight = true;
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 
-		mergeDiffs(comparison.getDifferences(), leftToRight);
+		mergeDiffsLeftToRight(comparison.getDifferences());
 		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
 	}
 
@@ -265,12 +242,11 @@
 	public void testMergeRightToLeftAddedContainer() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(singleAddedContainer,
 				singleDeletedContainer, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
-		boolean leftToRight = false;
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 
-		mergeDiffs(comparison.getDifferences(), leftToRight);
+		mergeDiffsRightToLeft(comparison.getDifferences());
 		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
-
 	}
 
 	/**
@@ -280,10 +256,10 @@
 	public void testMergeLeftToRightAddedContainer() {
 		DefaultComparisonScope scope = new DefaultComparisonScope(singleAddedContainer,
 				singleDeletedContainer, origin);
-		comparison = EMFCompare.builder().setPostProcessorRegistry(registry).build().compare(scope);
-		boolean leftToRight = true;
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
 
-		mergeDiffs(comparison.getDifferences(), leftToRight);
+		mergeDiffsLeftToRight(comparison.getDifferences());
 		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
 	}
 
@@ -305,47 +281,6 @@
 	}
 
 	/**
-	 * Used to register new post processors.
-	 * 
-	 * @see org.eclipse.emf.compare.diagram.sirius.internal.SiriusDiffPostProcessor
-	 * @see org.eclipse.emf.compare.diagram.internal.CompareDiagramPostProcessor
-	 * @return the register composed of SiriusDiffPostProcessor and CompareDiagramPostProcessor.
-	 */
-	protected IPostProcessor.Descriptor.Registry<String> registerPostProcessors() {
-		final IPostProcessor.Descriptor.Registry<String> postProcessorRegistry = new PostProcessorDescriptorRegistryImpl<String>();
-
-		postProcessorRegistry.put(SiriusDiffPostProcessor.class.getName(),
-				new BasicPostProcessorDescriptorImpl(new SiriusDiffPostProcessor(),
-						Pattern.compile("http://www.eclipse.org/sirius/1.1.0"), //$NON-NLS-1$
-						Pattern.compile(""))); //$NON-NLS-1$
-		postProcessorRegistry.put(CompareDiagramPostProcessor.class.getName(),
-				new BasicPostProcessorDescriptorImpl(new CompareDiagramPostProcessor(),
-						Pattern.compile("http://www.eclipse.org/gmf/runtime/1.0.2/notation"), //$NON-NLS-1$
-						Pattern.compile(""))); //$NON-NLS-1$
-		return postProcessorRegistry;
-	}
-
-	/**
-	 * Executes a right to left or left to right merge of a list of differences.
-	 * 
-	 * @param differences
-	 *            the list of differences.
-	 * @param leftToRight
-	 *            the merge direction.
-	 */
-	protected void mergeDiffs(List<? extends Diff> differences, boolean leftToRight) {
-		if (leftToRight) {
-			mergeRunnable = new MergeRunnableImpl(true, true, MergeMode.LEFT_TO_RIGHT,
-					new CachingDiffRelationshipComputer(mergerRegistry));
-			mergeRunnable.merge(differences, true, mergerRegistry);
-		} else {
-			mergeRunnable = new MergeRunnableImpl(true, true, MergeMode.RIGHT_TO_LEFT,
-					new CachingDiffRelationshipComputer(mergerRegistry));
-			mergeRunnable.merge(differences, false, mergerRegistry);
-		}
-	}
-
-	/**
 	 * Input data for this bug.
 	 * 
 	 * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/TestBug561825.java b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/TestBug561825.java
new file mode 100644
index 0000000..f7bf448
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/TestBug561825.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.sirius.internal.merge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.EMFCompare;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.ReferenceChange;
+import org.eclipse.emf.compare.diagram.sirius.tests.AbstractSiriusTest;
+import org.eclipse.emf.compare.merge.AbstractMerger;
+import org.eclipse.emf.compare.scope.DefaultComparisonScope;
+import org.eclipse.emf.compare.tests.framework.AbstractInputData;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.sirius.viewpoint.DMappingBased;
+import org.eclipse.sirius.viewpoint.description.RepresentationElementMapping;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * This test is related to the bug <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=561825">561825.</a>
+ * 
+ * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
+ */
+public class TestBug561825 extends AbstractSiriusTest {
+
+	/**
+	 * The main comparison.
+	 */
+	private Comparison comparison;
+
+	/**
+	 * The "singleAddedContainer.aird" data resource.
+	 */
+	private Resource singleAddedContainer;
+
+	/**
+	 * The "singleDeletedContainer.aird" data resource.
+	 */
+	private Resource singleDeletedContainer;
+
+	/**
+	 * Used to initialize the comparison scope.
+	 */
+	private Resource origin;
+
+	/**
+	 * Set up the test models.
+	 * <p>
+	 * singleAddedContainer.nodes: In this model we have a tree nodes structured like this:
+	 * <ul>
+	 * <li>A Node "A" that holds a Node "B".</li>
+	 * </ul>
+	 * singleDeletedContainer.nodes: In this model "B" has been deleted.
+	 * </p>
+	 * <p>
+	 * A 2-way comparison between their representations files singleAddedContainer.aird and
+	 * singleDeletedContainer.aird gives us these differences in particular:
+	 * <ul>
+	 * <li>A ReferenceChange difference for the addition/deletion of "B" DNodeContainer.</li>
+	 * <li>A ReferenceChange difference for the modification of "B.actualMapping" ContainerMapping.</li>
+	 * </ul>
+	 * </p>
+	 * 
+	 * @throws IOException
+	 *             Thrown if we could not access either this class' resource, or the file towards which
+	 *             <code>string</code> points.
+	 */
+	@Before
+	public void setUp() throws IOException {
+		Bug561825 inputData = new Bug561825();
+		fillRegistries();
+		singleAddedContainer = inputData.getResource("singleAddedContainer.aird"); //$NON-NLS-1$
+		singleDeletedContainer = inputData.getResource("singleDeletedContainer.aird"); //$NON-NLS-1$
+		origin = null;
+	}
+
+	/**
+	 * Tests that the merge from right to left of a deleted container on the left correctly sets the left
+	 * value of the match corresponding to the mapping, and also the mapping of this value, without failing.
+	 */
+	@Test
+	public void testSingleDeletedContainerMappingSet() {
+		DefaultComparisonScope scope = new DefaultComparisonScope(singleDeletedContainer,
+				singleAddedContainer, origin);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
+		ReferenceChange mappingDiff = getRepresentationElementMappingDiffs(comparison.getDifferences())
+				.get(0);
+		ReferenceChange nodeDiff = getDMappingBasedDiffs(comparison.getDifferences()).get(0);
+
+		mergeDiffsRightToLeft(comparison.getDifferences());
+
+		assertEquals(comparison.getMatch(nodeDiff.getValue()), mappingDiff.getMatch());
+		assertNotNull(mappingDiff.getMatch().getLeft());
+		assertTrue(mappingDiff.getMatch().getLeft() instanceof DMappingBased);
+		assertNotNull(((DMappingBased)mappingDiff.getMatch().getLeft()).getMapping());
+		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
+	}
+
+	/**
+	 * Tests that the merge from left to right of a deleted container on the left correctly unsets the right
+	 * value of the match corresponding to the mapping, without failing.
+	 */
+	@Test
+	public void testSingleDeletedContainerMappingUnset() {
+		DefaultComparisonScope scope = new DefaultComparisonScope(singleDeletedContainer,
+				singleAddedContainer, origin);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
+		ReferenceChange mappingDiff = getRepresentationElementMappingDiffs(comparison.getDifferences())
+				.get(0);
+		ReferenceChange nodeDiff = getDMappingBasedDiffs(comparison.getDifferences()).get(0);
+
+		Match valueMatch = comparison.getMatch(nodeDiff.getValue());
+		mergeDiffsLeftToRight(comparison.getDifferences());
+
+		assertEquals(valueMatch, mappingDiff.getMatch());
+		assertNull(comparison.getMatch(nodeDiff.getValue()));
+		assertNull(mappingDiff.getMatch().getRight());
+		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
+	}
+
+	/**
+	 * Tests that the merge from left to right of an added container on the left correctly sets the right
+	 * value of the match corresponding to the mapping, and also the mapping of this value, without failing.
+	 */
+	@Test
+	public void testSingleAddedContainerMappingSet() {
+		DefaultComparisonScope scope = new DefaultComparisonScope(singleAddedContainer,
+				singleDeletedContainer, origin);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
+		ReferenceChange mappingDiff = getRepresentationElementMappingDiffs(comparison.getDifferences())
+				.get(0);
+		ReferenceChange nodeDiff = getDMappingBasedDiffs(comparison.getDifferences()).get(0);
+
+		mergeDiffsLeftToRight(comparison.getDifferences());
+
+		assertEquals(comparison.getMatch(nodeDiff.getValue()), mappingDiff.getMatch());
+		assertNotNull(mappingDiff.getMatch().getRight());
+		assertTrue(mappingDiff.getMatch().getRight() instanceof DMappingBased);
+		assertNotNull(((DMappingBased)mappingDiff.getMatch().getRight()).getMapping());
+		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
+	}
+
+	/**
+	 * Tests that the merge from right to left of an added container on the left correctly unsets the left
+	 * value of the match corresponding to the mapping, without failing.
+	 */
+	@Test
+	public void testSingleAddedContainerMappingUnset() {
+		DefaultComparisonScope scope = new DefaultComparisonScope(singleAddedContainer,
+				singleDeletedContainer, origin);
+		comparison = EMFCompare.builder().setPostProcessorRegistry(getPostProcessorRegistry()).build()
+				.compare(scope);
+		ReferenceChange mappingDiff = getRepresentationElementMappingDiffs(comparison.getDifferences())
+				.get(0);
+		ReferenceChange nodeDiff = getDMappingBasedDiffs(comparison.getDifferences()).get(0);
+
+		Match valueMatch = comparison.getMatch(nodeDiff.getValue());
+		mergeDiffsRightToLeft(comparison.getDifferences());
+
+		assertEquals(valueMatch, mappingDiff.getMatch());
+		assertNull(comparison.getMatch(nodeDiff.getValue()));
+		assertNull(mappingDiff.getMatch().getLeft());
+		comparison.getDifferences().forEach(diff -> assertTrue(AbstractMerger.isInTerminalState(diff)));
+	}
+
+	/**
+	 * Filters a list of differences by keeping only the differences concerned by a mapping in their "implied"
+	 * relation.
+	 * 
+	 * @param differences
+	 *            the list of differences to filter.
+	 * @return the differences concerned by a mapping in their "implied" relation.
+	 */
+	private Stream<ReferenceChange> filterMappingDifferences(List<Diff> differences) {
+		Stream<ReferenceChange> refChanges = differences.stream()
+				.filter(diff -> diff instanceof ReferenceChange).map(ReferenceChange.class::cast);
+		Stream<ReferenceChange> mappingDifferences = refChanges
+				.filter(diff -> (!diff.getImpliedBy().isEmpty() || !diff.getImplies().isEmpty())
+						&& (diff.getValue() instanceof DMappingBased
+								|| diff.getValue() instanceof RepresentationElementMapping));
+		return mappingDifferences;
+
+	}
+
+	/**
+	 * Used to filter a list by keeping only the differences with RepresentationElementMapping type values.
+	 * 
+	 * @see org.eclipse.sirius.viewpoint.description.RepresentationElementMapping
+	 * @param differences
+	 *            the list of differences to filter.
+	 * @return the list of mappings differences.
+	 */
+	private List<ReferenceChange> getRepresentationElementMappingDiffs(List<Diff> differences) {
+		return filterMappingDifferences(differences)
+				.filter(diff -> diff.getValue() instanceof RepresentationElementMapping)
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * Used to filter a list by keeping only the differences with DMappingBased type values, such as DNode,
+	 * DNodeContainer and DEdge.
+	 * 
+	 * @see org.eclipse.sirius.viewpoint.DMappingBased
+	 * @param differences
+	 *            the list of differences to filter.
+	 * @return the list of DMappingBased differences.
+	 */
+	private List<ReferenceChange> getDMappingBasedDiffs(List<Diff> differences) {
+		return filterMappingDifferences(differences).filter(diff -> diff.getValue() instanceof DMappingBased)
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * Input data for this bug.
+	 * 
+	 * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
+	 */
+	public class Bug561825 extends AbstractInputData {
+
+		/**
+		 * The data path.
+		 */
+		private static final String PATH_PREFIX = "data/_561825/"; //$NON-NLS-1$
+
+		/**
+		 * Used to load a resource.
+		 * 
+		 * @param resourceName
+		 *            the resource to load.
+		 * @return the resource loaded.
+		 * @throws IOException
+		 *             Thrown if we could not access either this class' resource, or the file towards which
+		 *             <code>string</code> points.
+		 */
+		public Resource getResource(String resourceName) throws IOException {
+			StringBuilder resourceURL = new StringBuilder(PATH_PREFIX);
+			resourceURL.append(resourceName);
+			return loadFromClassLoader(resourceURL.toString());
+		}
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleAddedContainer.aird b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleAddedContainer.aird
new file mode 100644
index 0000000..a8c1a81
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleAddedContainer.aird
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:description="http://www.eclipse.org/sirius/description/1.1.0" xmlns:description_1="http://www.eclipse.org/sirius/diagram/description/1.1.0" xmlns:diagram="http://www.eclipse.org/sirius/diagram/1.1.0" xmlns:nodes="http://www.eclipse.org/emf/compare/tests/nodes" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmlns:style="http://www.eclipse.org/sirius/diagram/description/style/1.1.0" xmlns:viewpoint="http://www.eclipse.org/sirius/1.1.0" xsi:schemaLocation="http://www.eclipse.org/sirius/description/1.1.0 http://www.eclipse.org/sirius/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/style/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description/style">
+  <viewpoint:DAnalysis uid="_4LbGcHAvEeqXy8bBNMW8cQ" selectedViews="_I9pGcHAwEeqXy8bBNMW8cQ" version="14.3.0.201909031200">
+    <semanticResources>singleAddedContainer.nodes</semanticResources>
+    <ownedViews xmi:type="viewpoint:DView" uid="_I9pGcHAwEeqXy8bBNMW8cQ">
+      <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']"/>
+      <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_JRkIAXAwEeqXy8bBNMW8cQ" name="new Nodes Diagram" repPath="#_JQLA4HAwEeqXy8bBNMW8cQ" changeId="b0a77288-bf38-43c5-942e-b53e26d9dbee">
+        <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']"/>
+        <target xmi:type="nodes:Node" href="singleAddedContainer.nodes#__XENwHAvEeqXy8bBNMW8cQ"/>
+      </ownedRepresentationDescriptors>
+    </ownedViews>
+  </viewpoint:DAnalysis>
+  <diagram:DSemanticDiagram uid="_JQLA4HAwEeqXy8bBNMW8cQ">
+    <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_JRhrwHAwEeqXy8bBNMW8cQ" source="DANNOTATION_CUSTOMIZATION_KEY">
+      <data xmi:type="diagram:ComputedStyleDescriptionRegistry" uid="_JRhrwXAwEeqXy8bBNMW8cQ"/>
+    </ownedAnnotationEntries>
+    <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_JSCCEHAwEeqXy8bBNMW8cQ" source="GMF_DIAGRAMS">
+      <data xmi:type="notation:Diagram" xmi:id="_JSCpIHAwEeqXy8bBNMW8cQ" type="Sirius" element="_JQLA4HAwEeqXy8bBNMW8cQ" measurementUnit="Pixel">
+        <children xmi:type="notation:Node" xmi:id="_JSQrkHAwEeqXy8bBNMW8cQ" type="2002" element="_JROw0HAwEeqXy8bBNMW8cQ">
+          <children xmi:type="notation:Node" xmi:id="_JSYAUHAwEeqXy8bBNMW8cQ" type="5006"/>
+          <children xmi:type="notation:Node" xmi:id="_JSZOcHAwEeqXy8bBNMW8cQ" type="7001">
+            <styles xmi:type="notation:SortingStyle" xmi:id="_JSZOcXAwEeqXy8bBNMW8cQ"/>
+            <styles xmi:type="notation:FilteringStyle" xmi:id="_JSZOcnAwEeqXy8bBNMW8cQ"/>
+          </children>
+          <styles xmi:type="notation:ShapeStyle" xmi:id="_JSQrkXAwEeqXy8bBNMW8cQ" fontName="Segoe UI" fontHeight="12"/>
+          <layoutConstraint xmi:type="notation:Bounds" xmi:id="_JSQrknAwEeqXy8bBNMW8cQ"/>
+        </children>
+        <styles xmi:type="notation:DiagramStyle" xmi:id="_JSCpIXAwEeqXy8bBNMW8cQ"/>
+      </data>
+    </ownedAnnotationEntries>
+    <ownedDiagramElements xmi:type="diagram:DNodeContainer" uid="_JROw0HAwEeqXy8bBNMW8cQ" name="B">
+      <target xmi:type="nodes:Node" href="singleAddedContainer.nodes#_EOwVcHAwEeqXy8bBNMW8cQ"/>
+      <semanticElements xmi:type="nodes:Node" href="singleAddedContainer.nodes#_EOwVcHAwEeqXy8bBNMW8cQ"/>
+      <ownedStyle xmi:type="diagram:FlatContainerStyle" uid="_JRSbMHAwEeqXy8bBNMW8cQ" labelSize="12" borderSize="1" borderSizeComputationExpression="1">
+        <description xmi:type="style:FlatContainerStyleDescription" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']/@defaultLayer/@containerMappings[name='ND_Node']/@style"/>
+      </ownedStyle>
+      <actualMapping xmi:type="description_1:ContainerMapping" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']/@defaultLayer/@containerMappings[name='ND_Node']"/>
+    </ownedDiagramElements>
+    <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']"/>
+    <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_JQZqYHAwEeqXy8bBNMW8cQ"/>
+    <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']/@defaultLayer"/>
+    <target xmi:type="nodes:Node" href="singleAddedContainer.nodes#__XENwHAvEeqXy8bBNMW8cQ"/>
+  </diagram:DSemanticDiagram>
+</xmi:XMI>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleAddedContainer.nodes b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleAddedContainer.nodes
new file mode 100644
index 0000000..3e48cff
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleAddedContainer.nodes
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodes:Node xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:nodes="http://www.eclipse.org/emf/compare/tests/nodes" xmi:id="__XENwHAvEeqXy8bBNMW8cQ" name="A">
+  <containmentRef1 xmi:id="_EOwVcHAwEeqXy8bBNMW8cQ" name="B"/>
+</nodes:Node>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleDeletedContainer.aird b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleDeletedContainer.aird
new file mode 100644
index 0000000..e71f2f1
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleDeletedContainer.aird
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:description="http://www.eclipse.org/sirius/description/1.1.0" xmlns:description_1="http://www.eclipse.org/sirius/diagram/description/1.1.0" xmlns:diagram="http://www.eclipse.org/sirius/diagram/1.1.0" xmlns:nodes="http://www.eclipse.org/emf/compare/tests/nodes" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmlns:viewpoint="http://www.eclipse.org/sirius/1.1.0" xsi:schemaLocation="http://www.eclipse.org/sirius/description/1.1.0 http://www.eclipse.org/sirius/1.1.0#//description http://www.eclipse.org/sirius/diagram/description/1.1.0 http://www.eclipse.org/sirius/diagram/1.1.0#//description">
+  <viewpoint:DAnalysis uid="_4LbGcHAvEeqXy8bBNMW8cQ" selectedViews="_I9pGcHAwEeqXy8bBNMW8cQ" version="14.3.0.201909031200">
+    <semanticResources>singleDeletedContainer.nodes</semanticResources>
+    <ownedViews xmi:type="viewpoint:DView" uid="_I9pGcHAwEeqXy8bBNMW8cQ">
+      <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']"/>
+      <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_JRkIAXAwEeqXy8bBNMW8cQ" name="new Nodes Diagram" repPath="#_JQLA4HAwEeqXy8bBNMW8cQ" changeId="005005d3-0211-4680-ba81-333f77b59742">
+        <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']"/>
+        <target xmi:type="nodes:Node" href="singleDeletedContainer.nodes#__XENwHAvEeqXy8bBNMW8cQ"/>
+      </ownedRepresentationDescriptors>
+    </ownedViews>
+  </viewpoint:DAnalysis>
+  <diagram:DSemanticDiagram uid="_JQLA4HAwEeqXy8bBNMW8cQ">
+    <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_JRhrwHAwEeqXy8bBNMW8cQ" source="DANNOTATION_CUSTOMIZATION_KEY">
+      <data xmi:type="diagram:ComputedStyleDescriptionRegistry" uid="_JRhrwXAwEeqXy8bBNMW8cQ"/>
+    </ownedAnnotationEntries>
+    <ownedAnnotationEntries xmi:type="description:AnnotationEntry" uid="_JSCCEHAwEeqXy8bBNMW8cQ" source="GMF_DIAGRAMS">
+      <data xmi:type="notation:Diagram" xmi:id="_JSCpIHAwEeqXy8bBNMW8cQ" type="Sirius" element="_JQLA4HAwEeqXy8bBNMW8cQ" measurementUnit="Pixel">
+        <styles xmi:type="notation:DiagramStyle" xmi:id="_JSCpIXAwEeqXy8bBNMW8cQ"/>
+      </data>
+    </ownedAnnotationEntries>
+    <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']"/>
+    <filterVariableHistory xmi:type="diagram:FilterVariableHistory" uid="_JQZqYHAwEeqXy8bBNMW8cQ"/>
+    <activatedLayers xmi:type="description_1:Layer" href="platform:/plugin/org.eclipse.emf.compare.diagram.sirius.tests.design/description/nodes.odesign#//@ownedViewpoints[name='Nodes%20Design']/@ownedRepresentations[name='Nodes%20Diagram']/@defaultLayer"/>
+    <target xmi:type="nodes:Node" href="singleDeletedContainer.nodes#__XENwHAvEeqXy8bBNMW8cQ"/>
+  </diagram:DSemanticDiagram>
+</xmi:XMI>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleDeletedContainer.nodes b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleDeletedContainer.nodes
new file mode 100644
index 0000000..2f28552
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/data/_561825/singleDeletedContainer.nodes
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodes:Node xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:nodes="http://www.eclipse.org/emf/compare/tests/nodes" xmi:id="__XENwHAvEeqXy8bBNMW8cQ" name="A"/>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/AbstractSiriusTest.java b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/AbstractSiriusTest.java
new file mode 100644
index 0000000..f0814c4
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/AbstractSiriusTest.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.sirius.tests;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.diagram.internal.CompareDiagramPostProcessor;
+import org.eclipse.emf.compare.diagram.sirius.internal.SiriusDiffPostProcessor;
+import org.eclipse.emf.compare.domain.IMergeRunnable;
+import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeRunnableImpl;
+import org.eclipse.emf.compare.internal.merge.MergeMode;
+import org.eclipse.emf.compare.merge.CachingDiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IMerger;
+import org.eclipse.emf.compare.postprocessor.BasicPostProcessorDescriptorImpl;
+import org.eclipse.emf.compare.postprocessor.IPostProcessor;
+import org.eclipse.emf.compare.postprocessor.PostProcessorDescriptorRegistryImpl;
+import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
+
+/**
+ * This class helps to initialize and use the registers for Sirius tests.
+ * 
+ * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
+ */
+@SuppressWarnings("restriction")
+public abstract class AbstractSiriusTest {
+
+	/**
+	 * The PostProcessors registry.
+	 */
+	private IPostProcessor.Descriptor.Registry<String> postProcessorRegistry;
+
+	/**
+	 * Used to initialize a default merger registry for merge action.
+	 */
+	private IMerger.Registry mergerRegistry;
+
+	/**
+	 * Used to merge differences.
+	 */
+	private IMergeRunnable mergeRunnable;
+
+	/**
+	 * Used to initialize the registers.
+	 */
+	protected void fillRegistries() {
+		postProcessorRegistry = registerSiriusPostProcessors();
+		mergerRegistry = EMFCompareRCPPlugin.getDefault().getMergerRegistry();
+	}
+
+	/**
+	 * Used to register new post processors.
+	 * 
+	 * @see org.eclipse.emf.compare.diagram.sirius.internal.SiriusDiffPostProcessor
+	 * @see org.eclipse.emf.compare.diagram.internal.CompareDiagramPostProcessor
+	 * @return the register composed of SiriusDiffPostProcessor and CompareDiagramPostProcessor.
+	 */
+	protected IPostProcessor.Descriptor.Registry<String> registerSiriusPostProcessors() {
+		final IPostProcessor.Descriptor.Registry<String> registry = new PostProcessorDescriptorRegistryImpl<String>();
+
+		registry.put(SiriusDiffPostProcessor.class.getName(),
+				new BasicPostProcessorDescriptorImpl(new SiriusDiffPostProcessor(),
+						Pattern.compile("http://www.eclipse.org/sirius/1.1.0"), //$NON-NLS-1$
+						Pattern.compile(""))); //$NON-NLS-1$
+		registry.put(CompareDiagramPostProcessor.class.getName(),
+				new BasicPostProcessorDescriptorImpl(new CompareDiagramPostProcessor(),
+						Pattern.compile("http://www.eclipse.org/gmf/runtime/1.0.2/notation"), //$NON-NLS-1$
+						Pattern.compile(""))); //$NON-NLS-1$
+		return registry;
+	}
+
+	/**
+	 * Executes a right to left merge of a list of differences.
+	 * 
+	 * @param differences
+	 *            the list of differences.
+	 */
+	protected void mergeDiffsRightToLeft(List<? extends Diff> differences) {
+		mergeDiffs(differences, false);
+	}
+
+	/**
+	 * Executes a left to right merge of a list of differences.
+	 * 
+	 * @param differences
+	 *            the list of differences.
+	 */
+	protected void mergeDiffsLeftToRight(List<? extends Diff> differences) {
+		mergeDiffs(differences, true);
+	}
+
+	/**
+	 * Executes a right to left or left to right merge of a list of differences.
+	 * 
+	 * @param differences
+	 *            the list of differences.
+	 * @param leftToRight
+	 *            the merge direction.
+	 */
+	private void mergeDiffs(List<? extends Diff> differences, boolean leftToRight) {
+		IDiffRelationshipComputer computer = new CachingDiffRelationshipComputer(mergerRegistry);
+		final MergeMode mergeMode;
+		if (leftToRight) {
+			mergeMode = MergeMode.LEFT_TO_RIGHT;
+		} else {
+			mergeMode = MergeMode.RIGHT_TO_LEFT;
+		}
+		mergeRunnable = new MergeRunnableImpl(true, true, mergeMode, computer);
+		mergeRunnable.merge(differences, leftToRight, mergerRegistry);
+	}
+
+	/**
+	 * Returns the post processor registry.
+	 * 
+	 * @return postProcessorRegistry.
+	 */
+	protected IPostProcessor.Descriptor.Registry<String> getPostProcessorRegistry() {
+		return postProcessorRegistry;
+	}
+
+	/**
+	 * Returns the merger registry.
+	 * 
+	 * @return mergerRegistry.
+	 */
+	protected IMerger.Registry getMergerRegistry() {
+		return mergerRegistry;
+	}
+
+	/**
+	 * Returns the merge runnable.
+	 * 
+	 * @return mergeRunnable.
+	 */
+	protected IMergeRunnable getMergeRunnable() {
+		return mergeRunnable;
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/suite/BugsTestSuite.java b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/suite/BugsTestSuite.java
index 2f5d526..4e47ed3 100644
--- a/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/suite/BugsTestSuite.java
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius.tests/src/org/eclipse/emf/compare/diagram/sirius/tests/suite/BugsTestSuite.java
@@ -11,6 +11,7 @@
 package org.eclipse.emf.compare.diagram.sirius.tests.suite;
 
 import org.eclipse.emf.compare.diagram.sirius.internal.TestBug561458;
+import org.eclipse.emf.compare.diagram.sirius.internal.merge.TestBug561825;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
@@ -22,6 +23,6 @@
  * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
  */
 @RunWith(Suite.class)
-@SuiteClasses({TestBug561458.class })
+@SuiteClasses({TestBug561458.class, TestBug561825.class })
 public class BugsTestSuite {
 }
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius/plugin.xml b/plugins/org.eclipse.emf.compare.diagram.sirius/plugin.xml
index bcb4989..93f64f3 100644
--- a/plugins/org.eclipse.emf.compare.diagram.sirius/plugin.xml
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius/plugin.xml
@@ -2,7 +2,7 @@
 <?eclipse version="3.4"?>
 
 <!--
- Copyright (c) 2015 Obeo.
+ Copyright (c) 2015, 2020 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
@@ -24,5 +24,12 @@
          </nsURI>
       </processor>
    </extension>
+  <extension
+        point="org.eclipse.emf.compare.rcp.merger">
+     <merger
+           class="org.eclipse.emf.compare.diagram.sirius.internal.merge.SiriusReferenceChangeMerger"
+           ranking="25">
+     </merger>
+  </extension>
    
 </plugin>
diff --git a/plugins/org.eclipse.emf.compare.diagram.sirius/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/SiriusReferenceChangeMerger.java b/plugins/org.eclipse.emf.compare.diagram.sirius/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/SiriusReferenceChangeMerger.java
new file mode 100644
index 0000000..b226d9c
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.diagram.sirius/src/org/eclipse/emf/compare/diagram/sirius/internal/merge/SiriusReferenceChangeMerger.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.diagram.sirius.internal.merge;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceState;
+import org.eclipse.emf.compare.ReferenceChange;
+import org.eclipse.emf.compare.merge.ComputeDiffsToMerge;
+import org.eclipse.emf.compare.merge.DiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.IDiffRelationshipComputer;
+import org.eclipse.emf.compare.merge.ReferenceChangeMerger;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.sirius.viewpoint.DMappingBased;
+import org.eclipse.sirius.viewpoint.description.RepresentationElementMapping;
+
+/**
+ * This specific implementation of {@link ReferenceChangeMerger} will be used to merge Sirius changes.
+ * 
+ * @author <a href="mailto:glenn.plouhinec@obeo.fr">Glenn Plouhinec</a>
+ */
+public class SiriusReferenceChangeMerger extends ReferenceChangeMerger {
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
+	 */
+	@Override
+	public boolean isMergerFor(Diff target) {
+		boolean result = false;
+		if (target instanceof ReferenceChange) {
+			EObject value = ((ReferenceChange)target).getValue();
+			result = (value instanceof DMappingBased || value instanceof RepresentationElementMapping)
+					&& (!target.getImpliedBy().isEmpty() || !target.getImplies().isEmpty());
+		}
+		return result;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.AbstractMerger#copyDiff(org.eclipse.emf.compare.Diff,
+	 *      org.eclipse.emf.common.util.Monitor, boolean)
+	 */
+	@Override
+	protected void copyDiff(Diff target, Monitor monitor, boolean rightToLeft) {
+		super.copyDiff(target, monitor, rightToLeft);
+
+		Set<Diff> impliedMerges = getImpliedMerges(target, rightToLeft);
+		Set<Diff> impliedMappings = new HashSet<Diff>();
+		while (!impliedMerges.isEmpty()) {
+			Diff impliedMerge = impliedMerges.iterator().next();
+			impliedMerges.addAll(getImpliedMerges(impliedMerge, rightToLeft));
+			if (impliedMerge instanceof ReferenceChange) {
+				EObject value = ((ReferenceChange)impliedMerge).getValue();
+				if (value instanceof DMappingBased || value instanceof RepresentationElementMapping) {
+					impliedMerge.setState(DifferenceState.UNRESOLVED);
+					impliedMappings.add(impliedMerge);
+				}
+			}
+			impliedMerges.remove(impliedMerge);
+		}
+
+		mergeImpliedMappings(impliedMappings, rightToLeft);
+	}
+
+	/**
+	 * Used to merge implied differences which are related to a RepresentationElementMapping or DMappingBased,
+	 * in the correct order, according to the required dependencies.
+	 * 
+	 * @param impliedMappings
+	 *            The set of implied differences which have a mapping.
+	 * @param rightToLeft
+	 *            Merge direction.
+	 */
+	private void mergeImpliedMappings(Set<Diff> impliedMappings, boolean rightToLeft) {
+		IDiffRelationshipComputer relationshipComputer = new DiffRelationshipComputer(getRegistry());
+		ComputeDiffsToMerge computer = new ComputeDiffsToMerge(rightToLeft, relationshipComputer);
+		for (Diff impliedMapping : impliedMappings) {
+			for (Diff toMerge : computer.getAllDiffsToMerge(impliedMapping)) {
+				if (!isInTerminalState(toMerge)) {
+					mergeDiff(toMerge, rightToLeft, null);
+				}
+			}
+		}
+	}
+}