[398361] Model Merge UI

New model merge UI
diff --git a/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/domain/impl/EMFCompareEditingDomain.java b/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/domain/impl/EMFCompareEditingDomain.java
index 5ed82f1..439ca85 100644
--- a/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/domain/impl/EMFCompareEditingDomain.java
+++ b/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/domain/impl/EMFCompareEditingDomain.java
@@ -20,7 +20,9 @@
 import org.eclipse.emf.common.command.Command;
 import org.eclipse.emf.common.command.CommandStack;
 import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.compare.ComparePackage;
 import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceState;
 import org.eclipse.emf.compare.command.ICompareCommandStack;
 import org.eclipse.emf.compare.command.impl.CompareCommandStack;
 import org.eclipse.emf.compare.command.impl.CopyAllNonConflictingCommand;
@@ -32,6 +34,7 @@
 import org.eclipse.emf.ecore.change.util.ChangeRecorder;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.edit.command.SetCommand;
 import org.eclipse.emf.transaction.TransactionalEditingDomain;
 
 /**
@@ -164,6 +167,19 @@
 	/**
 	 * {@inheritDoc}
 	 * 
+	 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#createChangeStateCommand(org.eclipse.emf.compare.Diff,
+	 *      boolean)
+	 * @since 3.0
+	 */
+	public Command createChangeStateCommand(Diff diff, boolean leftToRight) {
+
+		return SetCommand
+				.create(null, diff, ComparePackage.eINSTANCE.getDiff_State(), DifferenceState.MERGED);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
 	 * @see org.eclipse.emf.compare.domain.ICompareEditingDomain#createCopyAllNonConflictingCommand(java.util.List,
 	 *      boolean, org.eclipse.emf.compare.merge.IMerger.Registry)
 	 * @since 3.0
diff --git a/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/provider/spec/OverlayImageProvider.java b/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/provider/spec/OverlayImageProvider.java
index 3659329..bdeb757 100644
--- a/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/provider/spec/OverlayImageProvider.java
+++ b/plugins/org.eclipse.emf.compare.edit/src/org/eclipse/emf/compare/provider/spec/OverlayImageProvider.java
@@ -1,196 +1,216 @@
-/*******************************************************************************

- * Copyright (c) 2012 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.provider.spec;

-

-import static com.google.common.collect.Lists.newArrayList;

-

-import java.util.Collection;

-import java.util.List;

-

-import org.eclipse.emf.common.util.ResourceLocator;

-import org.eclipse.emf.compare.Comparison;

-import org.eclipse.emf.compare.Conflict;

-import org.eclipse.emf.compare.ConflictKind;

-import org.eclipse.emf.compare.Diff;

-import org.eclipse.emf.compare.DifferenceKind;

-import org.eclipse.emf.compare.DifferenceSource;

-import org.eclipse.emf.compare.DifferenceState;

-import org.eclipse.emf.compare.Match;

-import org.eclipse.emf.edit.provider.ComposedImage;

-

-/**

- * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>

- */

-public class OverlayImageProvider {

-

-	private final ResourceLocator fResourceLocator;

-

-	/**

-	 * 

-	 */

-	public OverlayImageProvider(ResourceLocator resourceLocator) {

-		this.fResourceLocator = resourceLocator;

-	}

-

-	public Object getComposedImage(Diff diff, Object imageToCompose) {

-		String overlay = getImageOverlay(diff);

-		return getComposedImage(imageToCompose, overlay);

-	}

-

-	public Object getComposedImage(Match match, Object imageToCompose) {

-		String overlay = getImageOverlay(match);

-		return getComposedImage(imageToCompose, overlay);

-	}

-

-	private Object getComposedImage(Object imageToCompose, String overlay) {

-		Collection<Object> images = newArrayList();

-		images.add(imageToCompose);

-		if (overlay != null) {

-			Object image = fResourceLocator.getImage(overlay);

-			images.add(image);

-		}

-		return new ComposedImageExtension(images);

-	}

-

-	// Nothing here has to be externalized

-	@SuppressWarnings("nls")

-	private String getImageOverlay(Diff diff) {

-		final DifferenceSource source = diff.getSource();

-		final Match match = diff.getMatch();

-		final Conflict conflict = diff.getConflict();

-		final DifferenceKind diffKind = diff.getKind();

-		final Comparison comparison = match.getComparison();

-		String path = "full/ovr16/";

-

-		if (diff.getState() == DifferenceState.MERGED) {

-			path += "merged_ov";

-		} else if (diff.getState() == DifferenceState.DISCARDED) {

-			path += "removed_ov";

-		} else if (comparison.isThreeWay()) {

-			// "png" needs explicit declaration, "gif" does not

-			String extension = "";

-			if (conflict != null) {

-				extension = ".png";

-				if (conflict.getKind() == ConflictKind.PSEUDO) {

-					path += "p";

-				}

-				path += "conf";

-				if (source == DifferenceSource.RIGHT) {

-					path += "r_";

-				}

-			} else {

-				switch (source) {

-					case LEFT:

-						path += "r_out";

-						break;

-					case RIGHT:

-						path += "r_in";

-						break;

-					default:

-						// Cannot happen ... for now

-						break;

-				}

-			}

-

-			switch (diffKind) {

-				case ADD:

-					path += "add_ov";

-					break;

-				case DELETE:

-					path += "del_ov";

-					break;

-				case CHANGE:

-					// fallthrough

-				case MOVE:

-					path += "chg_ov";

-					break;

-				default:

-					// Cannot happen ... for now

-					break;

-			}

-			path += extension;

-		} else {

-			switch (diffKind) {

-				case ADD:

-					path += "add_ov";

-					break;

-				case DELETE:

-					path += "del_ov";

-					break;

-				case CHANGE:

-					// fallthrough

-				case MOVE:

-					path += "chg_ov";

-					break;

-				default:

-					break;

-			}

-		}

-		return path;

-	}

-

-	// Nothing here has to be externalized

-	@SuppressWarnings("nls")

-	private String getImageOverlay(Match match) {

-		return "full/ovr16/match_ov.png";

-	}

-

-	private final class ComposedImageExtension extends ComposedImage {

-

-		/**

-		 * 

-		 */

-		private static final int X_OFFSET = 10;

-

-		/**

-		 * @param images

-		 */

-		ComposedImageExtension(Collection<?> images) {

-			super(images);

-		}

-

-		/**

-		 * {@inheritDoc}

-		 * 

-		 * @see org.eclipse.emf.edit.provider.ComposedImage#getDrawPoints(org.eclipse.emf.edit.provider.ComposedImage.Size)

-		 */

-		@Override

-		public List<Point> getDrawPoints(Size size) {

-			List<ComposedImage.Point> result = super.getDrawPoints(size);

-			if (result.size() > 1) {

-				result.get(1).x = X_OFFSET;

-				result.get(1).y = 2;

-			}

-			return result;

-		}

-

-		/**

-		 * {@inheritDoc}

-		 * 

-		 * @see org.eclipse.emf.edit.provider.ComposedImage#getSize(java.util.Collection)

-		 */

-		@Override

-		public Size getSize(Collection<? extends Size> sizes) {

-			this.imageSizes = newArrayList(sizes);

-			List<Point> drawPoints = getDrawPoints(null);

-

-			Size result = new Size();

-			for (int i = 0; i < sizes.size(); i++) {

-				Size size = this.imageSizes.get(i);

-				Point point = drawPoints.get(i);

-

-				result.width = Math.max(result.width, size.width + Math.abs(point.x));

-				result.height = Math.max(result.height, size.height + Math.abs(point.y));

-			}

-			return result;

-		}

-	}

-}

+/*******************************************************************************
+ * Copyright (c) 2012 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.provider.spec;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.util.ResourceLocator;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Conflict;
+import org.eclipse.emf.compare.ConflictKind;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceKind;
+import org.eclipse.emf.compare.DifferenceSource;
+import org.eclipse.emf.compare.DifferenceState;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.internal.merge.IMergeData;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.edit.provider.ComposedImage;
+
+/**
+ * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
+ */
+public class OverlayImageProvider {
+
+	private final ResourceLocator fResourceLocator;
+
+	/**
+	 * 
+	 */
+	public OverlayImageProvider(ResourceLocator resourceLocator) {
+		this.fResourceLocator = resourceLocator;
+	}
+
+	public Object getComposedImage(Diff diff, Object imageToCompose) {
+		String overlay = getImageOverlay(diff);
+		return getComposedImage(imageToCompose, overlay);
+	}
+
+	public Object getComposedImage(Match match, Object imageToCompose) {
+		String overlay = getImageOverlay(match);
+		return getComposedImage(imageToCompose, overlay);
+	}
+
+	private Object getComposedImage(Object imageToCompose, String overlay) {
+		Collection<Object> images = newArrayList();
+		images.add(imageToCompose);
+		if (overlay != null) {
+			Object image = fResourceLocator.getImage(overlay);
+			images.add(image);
+		}
+		return new ComposedImageExtension(images);
+	}
+
+	// Nothing here has to be externalized
+	@SuppressWarnings("nls")
+	private String getImageOverlay(Diff diff) {
+		final DifferenceSource source = diff.getSource();
+		final Match match = diff.getMatch();
+		final Conflict conflict = diff.getConflict();
+		final DifferenceKind diffKind = diff.getKind();
+		final Comparison comparison = match.getComparison();
+		String path = "full/ovr16/";
+
+		if (diff.getState() == DifferenceState.MERGED) {
+			Adapter adapter = EcoreUtil.getExistingAdapter(diff, IMergeData.class);
+			if (adapter != null) {
+				if (((IMergeData)adapter).hasBeenMergedToLeft()) {
+					if (source == DifferenceSource.LEFT) {
+						path += "removed_ov";
+					} else {
+						path += "merged_ov";
+					}
+				} else {
+					if (source == DifferenceSource.LEFT) {
+						path += "merged_ov";
+					} else {
+						path += "removed_ov";
+					}
+				}
+			} else {
+				path += "merged_ov";
+			}
+		} else if (diff.getState() == DifferenceState.DISCARDED) {
+			path += "removed_ov";
+		} else if (comparison.isThreeWay()) {
+			// "png" needs explicit declaration, "gif" does not
+			String extension = "";
+			if (conflict != null) {
+				extension = ".png";
+				if (conflict.getKind() == ConflictKind.PSEUDO) {
+					path += "p";
+				}
+				path += "conf";
+				if (source == DifferenceSource.RIGHT) {
+					path += "r_";
+				}
+			} else {
+				switch (source) {
+					case LEFT:
+						path += "r_out";
+						break;
+					case RIGHT:
+						path += "r_in";
+						break;
+					default:
+						// Cannot happen ... for now
+						break;
+				}
+			}
+
+			switch (diffKind) {
+				case ADD:
+					path += "add_ov";
+					break;
+				case DELETE:
+					path += "del_ov";
+					break;
+				case CHANGE:
+					// fallthrough
+				case MOVE:
+					path += "chg_ov";
+					break;
+				default:
+					// Cannot happen ... for now
+					break;
+			}
+			path += extension;
+		} else {
+			switch (diffKind) {
+				case ADD:
+					path += "add_ov";
+					break;
+				case DELETE:
+					path += "del_ov";
+					break;
+				case CHANGE:
+					// fallthrough
+				case MOVE:
+					path += "chg_ov";
+					break;
+				default:
+					break;
+			}
+		}
+		return path;
+	}
+
+	// Nothing here has to be externalized
+	@SuppressWarnings("nls")
+	private String getImageOverlay(Match match) {
+		return "full/ovr16/match_ov.png";
+	}
+
+	private final class ComposedImageExtension extends ComposedImage {
+
+		/**
+		 * 
+		 */
+		private static final int X_OFFSET = 10;
+
+		/**
+		 * @param images
+		 */
+		ComposedImageExtension(Collection<?> images) {
+			super(images);
+		}
+
+		/**
+		 * {@inheritDoc}
+		 * 
+		 * @see org.eclipse.emf.edit.provider.ComposedImage#getDrawPoints(org.eclipse.emf.edit.provider.ComposedImage.Size)
+		 */
+		@Override
+		public List<Point> getDrawPoints(Size size) {
+			List<ComposedImage.Point> result = super.getDrawPoints(size);
+			if (result.size() > 1) {
+				result.get(1).x = X_OFFSET;
+				result.get(1).y = 2;
+			}
+			return result;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 * 
+		 * @see org.eclipse.emf.edit.provider.ComposedImage#getSize(java.util.Collection)
+		 */
+		@Override
+		public Size getSize(Collection<? extends Size> sizes) {
+			this.imageSizes = newArrayList(sizes);
+			List<Point> drawPoints = getDrawPoints(null);
+
+			Size result = new Size();
+			for (int i = 0; i < sizes.size(); i++) {
+				Size size = this.imageSizes.get(i);
+				Point point = drawPoints.get(i);
+
+				result.width = Math.max(result.width, size.width + Math.abs(point.x));
+				result.height = Math.max(result.height, size.height + Math.abs(point.y));
+			}
+			return result;
+		}
+	}
+}
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 9d7d8e6..d4841b3 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
@@ -10,8 +10,13 @@
  *******************************************************************************/
 package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer;
 
+import static com.google.common.base.Predicates.and;
+import static com.google.common.base.Predicates.or;
 import static com.google.common.collect.Iterables.getFirst;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide;
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind;
 
+import com.google.common.collect.Lists;
 import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
 
@@ -25,6 +30,7 @@
 import java.util.EventObject;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 
 import org.eclipse.compare.CompareConfiguration;
 import org.eclipse.compare.CompareViewerSwitchingPane;
@@ -43,10 +49,13 @@
 import org.eclipse.emf.common.command.CommandStack;
 import org.eclipse.emf.common.command.CommandStackListener;
 import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notifier;
 import org.eclipse.emf.common.util.BasicMonitor;
 import org.eclipse.emf.compare.Comparison;
 import org.eclipse.emf.compare.Conflict;
+import org.eclipse.emf.compare.ConflictKind;
 import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceKind;
 import org.eclipse.emf.compare.DifferenceSource;
 import org.eclipse.emf.compare.EMFCompare;
 import org.eclipse.emf.compare.Match;
@@ -81,14 +90,26 @@
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
 import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.ViewerComparator;
 import org.eclipse.jface.viewers.ViewerFilter;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Tree;
 import org.eclipse.swt.widgets.TreeItem;
 import org.eclipse.team.core.subscribers.Subscriber;
 import org.eclipse.team.core.subscribers.SubscriberMergeContext;
@@ -108,6 +129,12 @@
  */
 public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements CommandStackListener {
 
+	private static final String REQUIRED_DIFF_COLOR = "RequiredDiffColor";
+
+	private static final String UNMERGEABLE_DIFF_COLOR = "UnmergeableDiffColor";
+
+	private static final String OPTIONAL_DIFF_COLOR = "OptionalDiffColor";
+
 	private final ICompareInputChangeListener fCompareInputChangeListener;
 
 	private final ComposedAdapterFactory fAdapterFactory;
@@ -142,6 +169,8 @@
 
 	private EventBus eventBus;
 
+	private Listener fEraseItemListener;
+
 	/**
 	 * @param parent
 	 * @param configuration
@@ -172,6 +201,19 @@
 			}
 		};
 
+		addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				getControl().redraw();
+			}
+		});
+
+		fEraseItemListener = new Listener() {
+			public void handleEvent(Event event) {
+				EMFCompareStructureMergeViewer.this.handleEraseItemEvent(event);
+			}
+		};
+		getControl().addListener(SWT.EraseItem, fEraseItemListener);
+
 		// Wrap the defined comparer in our own.
 		setComparer(new DiffNodeComparer(super.getComparer()));
 
@@ -180,6 +222,10 @@
 			eventBus.register(this);
 		}
 
+		JFaceResources.getColorRegistry().put(REQUIRED_DIFF_COLOR, new RGB(215, 255, 200));
+		JFaceResources.getColorRegistry().put(UNMERGEABLE_DIFF_COLOR, new RGB(255, 254, 200));
+		JFaceResources.getColorRegistry().put(OPTIONAL_DIFF_COLOR, new RGB(193, 210, 230));
+
 		inputChangedTask.setPriority(Job.LONG);
 	}
 
@@ -726,6 +772,7 @@
 			ci.removeCompareInputChangeListener(fCompareInputChangeListener);
 		}
 		compareInputChanged((ICompareInput)null);
+		getControl().removeListener(SWT.EraseItem, fEraseItemListener);
 		fAdapterFactory.dispose();
 
 		super.handleDispose(event);
@@ -794,4 +841,125 @@
 		}
 		return result;
 	}
+
+	/**
+	 * Handle the erase item event. When select a difference in the structure merge viewer, highlight required
+	 * differences with a specific color, and highlight unmergeable differences with another color.
+	 * 
+	 * @param event
+	 *            the erase item event.
+	 */
+	protected void handleEraseItemEvent(Event event) {
+		ISelection selection = getSelection();
+		Object firstElement = ((IStructuredSelection)selection).getFirstElement();
+		if (firstElement instanceof Adapter) {
+			Notifier target = ((Adapter)firstElement).getTarget();
+			if (target instanceof Diff) {
+				TreeItem item = (TreeItem)event.item;
+				Object dataTreeItem = item.getData();
+				if (dataTreeItem instanceof Adapter) {
+					final List<Diff> requires = getRequires((Diff)target);
+					final List<Diff> unmergeables = getUnmergeables((Diff)target);
+					final List<Diff> optionals = getOptionals(((Diff)target));
+					final GC g = event.gc;
+					if (requires.contains(((Adapter)dataTreeItem).getTarget())) {
+						paintItemBackground(g, item, JFaceResources.getColorRegistry().get(
+								REQUIRED_DIFF_COLOR));
+					} else if (unmergeables.contains(((Adapter)dataTreeItem).getTarget())) {
+						paintItemBackground(g, item, JFaceResources.getColorRegistry().get(
+								UNMERGEABLE_DIFF_COLOR));
+					} else if (optionals.contains(((Adapter)dataTreeItem).getTarget())) {
+						paintItemBackground(g, item, JFaceResources.getColorRegistry().get(
+								OPTIONAL_DIFF_COLOR));
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Paint the background of the given item with the given color.
+	 * 
+	 * @param g
+	 *            the GC associated to the item.
+	 * @param item
+	 *            the given item.
+	 * @param color
+	 *            the given color.
+	 */
+	private void paintItemBackground(GC g, TreeItem item, Color color) {
+		Rectangle itemBounds = item.getBounds();
+		Tree tree = item.getParent();
+		Rectangle areaBounds = tree.getClientArea();
+		g.setClipping(areaBounds.x, itemBounds.y, areaBounds.width, itemBounds.height);
+		g.setBackground(color);
+		g.fillRectangle(areaBounds.x, itemBounds.y, areaBounds.width, itemBounds.height);
+	}
+
+	/**
+	 * Get the list of all required differences for merge of the given difference (required, required of
+	 * required...).
+	 * 
+	 * @param diff
+	 *            the given difference.
+	 * @return the list of all required differences.
+	 */
+	private List<Diff> getRequires(Diff diff) {
+		List<Diff> requires = Lists.newArrayList();
+		for (Diff require : diff.getRequires()) {
+			requires.add(require);
+			requires.addAll(getRequires(require));
+		}
+		return requires;
+	}
+
+	/**
+	 * Get the list of all optional differences for merge of the given difference (optional, optional of
+	 * optional...).
+	 * 
+	 * @param diff
+	 *            the given difference.
+	 * @return the list of all optional differences.
+	 */
+	private List<Diff> getOptionals(Diff diff) {
+		List<Diff> optionals = Lists.newArrayList();
+		for (Diff optional : diff.getRequiredBy()) {
+			optionals.add(optional);
+			optionals.addAll(getOptionals(optional));
+		}
+		return optionals;
+	}
+
+	/**
+	 * Get the list of unmergeable differences after the merge of the given difference.
+	 * 
+	 * @param diff
+	 *            the given difference.
+	 * @return the list of unmergeable differences.
+	 */
+	private List<Diff> getUnmergeables(Diff diff) {
+		List<Diff> unmergeables = Lists.newArrayList();
+		Conflict conflict = diff.getConflict();
+		if (conflict != null && conflict.getKind() == ConflictKind.REAL) {
+			for (Diff diffConflict : conflict.getDifferences()) {
+				if (and(fromSide(DifferenceSource.LEFT),
+						or(ofKind(DifferenceKind.ADD), ofKind(DifferenceKind.CHANGE))).apply(diff)) {
+					if (and(fromSide(DifferenceSource.RIGHT),
+							or(ofKind(DifferenceKind.DELETE), ofKind(DifferenceKind.CHANGE))).apply(
+							diffConflict)) {
+						unmergeables.add(diffConflict);
+					}
+				} else if (and(fromSide(DifferenceSource.LEFT),
+						or(ofKind(DifferenceKind.DELETE), ofKind(DifferenceKind.CHANGE))).apply(diff)) {
+					if (and(fromSide(DifferenceSource.RIGHT),
+							or(ofKind(DifferenceKind.ADD), ofKind(DifferenceKind.CHANGE)))
+							.apply(diffConflict)) {
+						unmergeables.add(diffConflict);
+					}
+				}
+			}
+		}
+		return unmergeables;
+	}
+
 }
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/icons/full/toolb16/accept_change.gif b/plugins/org.eclipse.emf.compare.rcp.ui/icons/full/toolb16/accept_change.gif
new file mode 100644
index 0000000..23c97f0
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/icons/full/toolb16/accept_change.gif
Binary files differ
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/icons/full/toolb16/reject_change.gif b/plugins/org.eclipse.emf.compare.rcp.ui/icons/full/toolb16/reject_change.gif
new file mode 100644
index 0000000..1aca259
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/icons/full/toolb16/reject_change.gif
Binary files differ
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/plugin.properties b/plugins/org.eclipse.emf.compare.rcp.ui/plugin.properties
index f29dc9c..8dbc9d9 100644
--- a/plugins/org.eclipse.emf.compare.rcp.ui/plugin.properties
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/plugin.properties
@@ -13,4 +13,12 @@
 
 save.model.label = Save Comparison Model
 save.model.tooltip = Save Comparison Model
-save.model.command.name = EMF Compare Save Comparison Model
\ No newline at end of file
+save.model.command.name = EMF Compare Save Comparison Model
+
+accept.change = Accept Change
+accept.change.tooltip = Accept Change
+accept.change.command.name = EMF Compare Accept Change
+
+reject.change = Reject Change
+reject.change.tooltip = Reject Change
+reject.change.command.name = EMF Compare Reject Change
\ No newline at end of file
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/plugin.xml b/plugins/org.eclipse.emf.compare.rcp.ui/plugin.xml
index 015ea80..f56288a 100644
--- a/plugins/org.eclipse.emf.compare.rcp.ui/plugin.xml
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/plugin.xml
@@ -89,6 +89,30 @@
             allPopups="false"
             locationURI="toolbar:org.eclipse.emf.compare.structuremergeviewer.toolbar">
          <command
+               commandId="org.eclipse.emf.compare.rcp.ui.acceptChange"
+               icon="icons/full/toolb16/accept_change.gif"
+               label="%accept.change"
+               style="push"
+               tooltip="%accept.change.tooltip">
+            <visibleWhen
+                  checkEnabled="true">
+            </visibleWhen>
+         </command>
+         <command
+               commandId="org.eclipse.emf.compare.rcp.ui.rejectChange"
+               icon="icons/full/toolb16/reject_change.gif"
+               label="%reject.change"
+               style="push"
+               tooltip="%reject.change.tooltip">
+            <visibleWhen
+                  checkEnabled="true">
+            </visibleWhen>
+         </command>
+         <separator
+               name="org.eclipse.emf.compare.rcp.ui.separatorSaveModel"
+               visible="true">
+         </separator>
+         <command
                commandId="org.eclipse.emf.compare.rcp.ui.saveComparisonModel"
                icon="icons/full/toolb16/saveas_edit.gif"
                label="%save.model.name"
@@ -100,6 +124,14 @@
    <extension
          point="org.eclipse.ui.commands">
       <command
+            id="org.eclipse.emf.compare.rcp.ui.acceptChange"
+            name="%accept.change.command.name">
+      </command>
+      <command
+            id="org.eclipse.emf.compare.rcp.ui.rejectChange"
+            name="%reject.change.command.name">
+      </command>
+      <command
             id="org.eclipse.emf.compare.rcp.ui.saveComparisonModel"
             name="%save.model.command.name">
       </command>
@@ -107,6 +139,30 @@
    <extension
          point="org.eclipse.ui.handlers">
       <handler
+            class="org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AcceptChange"
+            commandId="org.eclipse.emf.compare.rcp.ui.acceptChange">
+         <activeWhen>
+            <with
+                  variable="activeEditor">
+               <test
+                     property="emfcompare.hasReadOnlySide">
+               </test>
+            </with>
+         </activeWhen>
+      </handler>
+      <handler
+            class="org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.RejectChange"
+            commandId="org.eclipse.emf.compare.rcp.ui.rejectChange">
+         <activeWhen>
+            <with
+                  variable="activeEditor">
+               <test
+                     property="emfcompare.hasReadOnlySide">
+               </test>
+            </with>
+         </activeWhen>
+      </handler>
+      <handler
             class="org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.SaveComparisonModel"
             commandId="org.eclipse.emf.compare.rcp.ui.saveComparisonModel">
          <enabledWhen>
@@ -129,5 +185,12 @@
             properties="isSaveable"
             type="java.lang.Object">
       </propertyTester>
+      <propertyTester
+            class="org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AcceptRejectChangePropertyTester"
+            id="org.eclipse.emf.compare.rcp.ui.hasReadOnlySide"
+            namespace="emfcompare"
+            properties="hasReadOnlySide"
+            type="java.lang.Object">
+      </propertyTester>
    </extension>
 </plugin>
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptChange.java
new file mode 100644
index 0000000..ea87eaa
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptChange.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.rcp.ui.internal.structuremergeviewer.handler;
+
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceSource;
+
+/**
+ * Handler that manages a merge of a difference in case of one side of the comparison is in read-only mode.
+ * 
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ * @since 3.0
+ */
+public class AcceptChange extends AcceptRejectChange {
+
+	@Override
+	protected boolean isCopyDiffCase(Diff diff, boolean leftToRight) {
+		if (leftToRight) {
+			return fromSide(DifferenceSource.LEFT).apply(diff);
+		} else {
+			return fromSide(DifferenceSource.RIGHT).apply(diff);
+		}
+	}
+
+}
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptRejectChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptRejectChange.java
new file mode 100644
index 0000000..cea793d
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptRejectChange.java
@@ -0,0 +1,164 @@
+package org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler;
+
+import static com.google.common.collect.Iterables.addAll;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareNavigator;
+import org.eclipse.compare.ICompareNavigator;
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceState;
+import org.eclipse.emf.compare.command.ICompareCopyCommand;
+import org.eclipse.emf.compare.domain.ICompareEditingDomain;
+import org.eclipse.emf.compare.internal.merge.IMergeData;
+import org.eclipse.emf.compare.internal.merge.MergeDataAdapter;
+import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
+import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants;
+import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.IDifferenceFilter;
+import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.impl.CascadingDifferencesFilter;
+import org.eclipse.emf.compare.utils.DiffUtil;
+import org.eclipse.emf.ecore.change.util.ChangeRecorder;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.edit.command.ChangeCommand;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class AcceptRejectChange extends AbstractHandler {
+
+	/** The compare configuration object used to get the compare model. */
+	private CompareConfiguration configuration;
+
+	public Object execute(ExecutionEvent event) throws ExecutionException {
+		Object editorInput = HandlerUtil.getVariable(event, ISources.ACTIVE_EDITOR_INPUT_NAME);
+		if (editorInput instanceof CompareEditorInput) {
+			configuration = ((CompareEditorInput)editorInput).getCompareConfiguration();
+			Object diffNode = ((CompareEditorInput)editorInput).getSelectedEdition();
+			if (diffNode instanceof Adapter) {
+				Notifier diff = ((Adapter)diffNode).getTarget();
+				if (diff instanceof Diff) {
+					boolean leftReadOnly = !configuration.isLeftEditable() && configuration.isRightEditable();
+					boolean rightReadOnly = configuration.isLeftEditable()
+							&& !configuration.isRightEditable();
+					if (rightReadOnly) {
+						if (isCopyDiffCase((Diff)diff, leftReadOnly)) {
+							copyDiff((Diff)diff, leftReadOnly);
+						} else {
+							changeState((Diff)diff, rightReadOnly);
+						}
+						// Select next diff
+						navigate(true);
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	protected abstract boolean isCopyDiffCase(Diff diff, boolean leftToRight);
+
+	private void changeState(Diff diffToChangeState, boolean leftToRight) {
+		if (diffToChangeState != null) {
+			ICompareEditingDomain compareEditingDomain = (ICompareEditingDomain)configuration
+					.getProperty(EMFCompareConstants.EDITING_DOMAIN);
+			Command changeStateCommand = new AcceptRejectChangeCommand(compareEditingDomain
+					.getChangeRecorder(), diffToChangeState, leftToRight);
+			compareEditingDomain.getCommandStack().execute(changeStateCommand);
+		}
+	}
+
+	private void copyDiff(Diff diffToCopy, boolean leftToRight) {
+		if (diffToCopy != null) {
+			List<Diff> diffsToCopy = new ArrayList<Diff>();
+			diffsToCopy.add(diffToCopy);
+			if (isSubDiffFilterActive()) {
+				addAll(diffsToCopy, DiffUtil.getSubDiffs(leftToRight).apply(diffToCopy));
+			}
+			ICompareEditingDomain editingDomain = (ICompareEditingDomain)configuration
+					.getProperty(EMFCompareConstants.EDITING_DOMAIN);
+			Command copyCommand = editingDomain.createCopyCommand(diffsToCopy, leftToRight,
+					EMFCompareRCPPlugin.getDefault().getMergerRegistry());
+
+			editingDomain.getCommandStack().execute(copyCommand);
+			// refresh();
+		}
+	}
+
+	protected boolean isSubDiffFilterActive() {
+		Object property = configuration.getProperty(EMFCompareConstants.SELECTED_FILTERS);
+		final Collection<IDifferenceFilter> selectedFilters;
+		if (property == null) {
+			return false;
+		} else {
+			selectedFilters = (Collection<IDifferenceFilter>)property;
+			for (IDifferenceFilter iDifferenceFilter : selectedFilters) {
+				if (iDifferenceFilter instanceof CascadingDifferencesFilter) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Called by the framework to navigate to the next (or previous) difference. This will open the content
+	 * viewer for the next (or previous) diff displayed in the structure viewer.
+	 * 
+	 * @param next
+	 *            <code>true</code> if we are to open the next structure viewer's diff, <code>false</code> if
+	 *            we should go to the previous instead.
+	 */
+	protected void navigate(boolean next) {
+		// final Control control = getControl();
+		// if (control != null && !control.isDisposed()) {
+		final ICompareNavigator navigator = configuration.getContainer().getNavigator();
+		if (navigator instanceof CompareNavigator && ((CompareNavigator)navigator).hasChange(next)) {
+			navigator.selectChange(next);
+		}
+		// }
+	}
+
+	public class AcceptRejectChangeCommand extends ChangeCommand implements ICompareCopyCommand {
+
+		private Diff difference;
+
+		private boolean leftToRight;
+
+		public AcceptRejectChangeCommand(ChangeRecorder changeRecorder, Diff difference, boolean leftToRight) {
+			super(changeRecorder, difference);
+			this.difference = difference;
+			this.leftToRight = leftToRight;
+		}
+
+		@Override
+		public void doExecute() {
+			Adapter adapter = EcoreUtil.getExistingAdapter(difference, IMergeData.class);
+			if (adapter != null) {
+				if (leftToRight) {
+					((IMergeData)adapter).setMergedToRight();
+				} else {
+					((IMergeData)adapter).setMergedToLeft();
+				}
+			} else {
+				difference.eAdapters().add(new MergeDataAdapter(leftToRight));
+			}
+			difference.setState(DifferenceState.MERGED);
+
+		}
+
+		public boolean isLeftToRight() {
+			return leftToRight;
+		}
+
+	}
+
+}
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptRejectChangePropertyTester.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptRejectChangePropertyTester.java
new file mode 100644
index 0000000..ef70494
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/AcceptRejectChangePropertyTester.java
@@ -0,0 +1,26 @@
+package org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+
+public class AcceptRejectChangePropertyTester extends PropertyTester {
+
+	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+		if (receiver instanceof IEditorPart) {
+			IEditorInput i = ((IEditorPart)receiver).getEditorInput();
+			if (i instanceof CompareEditorInput) {
+				CompareConfiguration configuration = ((CompareEditorInput)i).getCompareConfiguration();
+				if (configuration.isLeftEditable() && !configuration.isRightEditable()) {
+					return true;
+				} else if (!configuration.isLeftEditable() && configuration.isRightEditable()) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+}
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/RejectChange.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/RejectChange.java
new file mode 100644
index 0000000..0d0380f
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/handler/RejectChange.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.rcp.ui.internal.structuremergeviewer.handler;
+
+import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide;
+
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceSource;
+
+/**
+ * Handler that manages a reject of a difference in case of one side of the comparison is in read-only mode.
+ * 
+ * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
+ * @since 3.0
+ */
+public class RejectChange extends AcceptRejectChange {
+
+	@Override
+	protected boolean isCopyDiffCase(Diff diff, boolean leftToRight) {
+		if (leftToRight) {
+			return fromSide(DifferenceSource.RIGHT).apply(diff);
+		} else {
+			return fromSide(DifferenceSource.LEFT).apply(diff);
+		}
+	}
+
+}
diff --git a/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF
index 939f2f9..de8d949 100644
--- a/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF
@@ -13,6 +13,7 @@
  org.eclipse.emf.compare.equi,
  org.eclipse.emf.compare.impl,
  org.eclipse.emf.compare.internal;x-friends:="org.eclipse.emf.compare.logical,org.eclipse.emf.compare.ide",
+ org.eclipse.emf.compare.internal.merge;x-friends:="org.eclipse.emf.compare.rcp.ui,org.eclipse.emf.compare.edit",
  org.eclipse.emf.compare.internal.postprocessor.factories;x-internal:=true,
  org.eclipse.emf.compare.internal.spec;x-friends:="org.eclipse.emf.compare.tests",
  org.eclipse.emf.compare.match,
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/IMergeData.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/IMergeData.java
new file mode 100644
index 0000000..49f9f78
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/IMergeData.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.internal.merge;
+
+public interface IMergeData {
+
+	boolean hasBeenMergedToLeft();
+
+	boolean hasBeenMergedToRight();
+
+	void setMergedToLeft();
+
+	void setMergedToRight();
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDataAdapter.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDataAdapter.java
new file mode 100644
index 0000000..ee891d5
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/MergeDataAdapter.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Obeo.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.compare.internal.merge;
+
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
+
+/**
+ * @since 3.0
+ */
+public class MergeDataAdapter extends AdapterImpl implements IMergeData {
+
+	boolean leftToRight;
+
+	public MergeDataAdapter(boolean leftToRight) {
+		this.leftToRight = leftToRight;
+	}
+
+	public boolean hasBeenMergedToLeft() {
+		return leftToRight == false;
+	}
+
+	public boolean hasBeenMergedToRight() {
+		return leftToRight == true;
+	}
+
+	public void setMergedToLeft() {
+		leftToRight = false;
+	}
+
+	public void setMergedToRight() {
+		leftToRight = true;
+	}
+
+	@Override
+	public boolean isAdapterForType(Object type) {
+		return type == IMergeData.class;
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java
index 8d85231..a89bd10 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/AbstractMerger.java
@@ -1,190 +1,214 @@
-/*******************************************************************************

- * Copyright (c) 2012, 2013 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.merge;

-

-import java.util.List;

-

-import org.eclipse.emf.common.util.Monitor;

-import org.eclipse.emf.compare.Diff;

-import org.eclipse.emf.compare.utils.EMFCompareCopier;

-import org.eclipse.emf.ecore.EObject;

-import org.eclipse.emf.ecore.util.EcoreUtil;

-import org.eclipse.emf.ecore.util.InternalEList;

-

-/**

- * Abstract implementation of an {@link IMerger}. This can be used as a base implementation to avoid

- * re-implementing the whole contract.

- * 

- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>

- * @since 3.0

- */

-public abstract class AbstractMerger implements IMerger {

-	/** Ranking of this merger. */

-	private int ranking;

-

-	/** Registry from which this merger has been created.. */

-	private Registry registry;

-

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#getRanking()

-	 */

-	public int getRanking() {

-		return ranking;

-	}

-

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#setRanking(int)

-	 */

-	public void setRanking(int r) {

-		ranking = r;

-	}

-

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#getRegistry()

-	 */

-	public Registry getRegistry() {

-		return registry;

-	}

-

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#setRegistry(org.eclipse.emf.compare.merge.IMerger.Registry)

-	 */

-	public void setRegistry(Registry registry) {

-		if (this.registry != null && registry != null) {

-			throw new IllegalStateException("The registry has to be set only once."); //$NON-NLS-1$

-		}

-		this.registry = registry;

-	}

-

-	/**

-	 * This will merge all {@link Diff#getRequiredBy() differences that require} {@code diff} in the given

-	 * direction.

-	 * 

-	 * @param diff

-	 *            We need to merge all differences that require this one (see {@link Diff#getRequiredBy()}.

-	 * @param rightToLeft

-	 *            If {@code true}, {@link #copyRightToLeft(Diff, Monitor) apply} all differences that require

-	 *            {@code diff}. Otherwise, {@link #copyLeftToRight(Diff, Monitor) revert} them.

-	 * @param monitor

-	 *            The monitor we should use to report progress.

-	 */

-	protected void mergeRequiredBy(Diff diff, boolean rightToLeft, Monitor monitor) {

-		// TODO log back to the user what we will merge along?

-		for (Diff dependency : diff.getRequiredBy()) {

-			// TODO: what to do when state = Discarded but is required?

-			mergeDiff(dependency, rightToLeft, monitor);

-		}

-	}

-

-	/**

-	 * This will merge all {@link Diff#getRequires() differences required by} {@code diff} in the given

-	 * direction.

-	 * 

-	 * @param diff

-	 *            The difference which requirements we need to merge.

-	 * @param rightToLeft

-	 *            If {@code true}, {@link #copyRightToLeft(Diff, Monitor) apply} all required differences.

-	 *            Otherwise, {@link #copyLeftToRight(Diff, Monitor) revert} them.

-	 * @param monitor

-	 *            The monitor we should use to report progress.

-	 */

-	protected void mergeRequires(Diff diff, boolean rightToLeft, Monitor monitor) {

-		// TODO log back to the user what we will merge along?

-		for (Diff dependency : diff.getRequires()) {

-			// TODO: what to do when state = Discarded but is required?

-			mergeDiff(dependency, rightToLeft, monitor);

-		}

-	}

-

-	/**

-	 * This can be used by mergers to merge another (required, equivalent...) difference using the right

-	 * merger for that diff.

-	 * 

-	 * @param diff

-	 *            The diff we need to merge.

-	 * @param rightToLeft

-	 *            Direction of that merge.

-	 * @param monitor

-	 *            The monitor we should use to report progress.

-	 */

-	protected void mergeDiff(Diff diff, boolean rightToLeft, Monitor monitor) {

-		if (rightToLeft) {

-			final IMerger delegate = getRegistry().getHighestRankingMerger(diff);

-			delegate.copyRightToLeft(diff, monitor);

-		} else {

-			final IMerger delegate = getRegistry().getHighestRankingMerger(diff);

-			delegate.copyLeftToRight(diff, monitor);

-		}

-	}

-

-	/**

-	 * This will create a copy of the given EObject that can be used as the target of an addition (or the

-	 * reverting of a deletion).

-	 * <p>

-	 * The target will be self-contained and will have no reference towards any other EObject set (neither

-	 * containment nor "classic" references). All of its attributes' values will match the given

-	 * {@code referenceObject}'s.

-	 * </p>

-	 * 

-	 * @param referenceObject

-	 *            The EObject for which we'll create a copy.

-	 * @return A self-contained copy of {@code referenceObject}.

-	 * @see EMFCompareCopier#copy(EObject)

-	 */

-	protected EObject createCopy(EObject referenceObject) {

-		/*

-		 * We can't simply use EcoreUtil.copy. References will have their own diffs and will thus be merged

-		 * later on.

-		 */

-		final EcoreUtil.Copier copier = new EMFCompareCopier();

-		return copier.copy(referenceObject);

-	}

-

-	/**

-	 * Adds the given {@code value} into the given {@code list} at the given {@code index}. An {@code index}

-	 * under than zero or above the list's size will mean that the value should be appended at the end of the

-	 * list.

-	 * 

-	 * @param list

-	 *            The list into which {@code value} should be added.

-	 * @param value

-	 *            The value we need to add to {@code list}.

-	 * @param <E>

-	 *            Type of objects contained in the list.

-	 * @param insertionIndex

-	 *            The index at which {@code value} should be inserted into {@code list}. {@code -1} if it

-	 *            should be appended at the end of the list.

-	 */

-	@SuppressWarnings("unchecked")

-	protected <E> void addAt(List<E> list, E value, int insertionIndex) {

-		if (list instanceof InternalEList<?>) {

-			if (insertionIndex < 0 || insertionIndex > list.size()) {

-				((InternalEList<Object>)list).addUnique(value);

-			} else {

-				((InternalEList<Object>)list).addUnique(insertionIndex, value);

-			}

-		} else {

-			if (insertionIndex < 0 || insertionIndex > list.size()) {

-				list.add(value);

-			} else {

-				list.add(insertionIndex, value);

-			}

-		}

-	}

-}

+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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.merge;
+
+import java.util.List;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.internal.merge.IMergeData;
+import org.eclipse.emf.compare.internal.merge.MergeDataAdapter;
+import org.eclipse.emf.compare.utils.EMFCompareCopier;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.util.InternalEList;
+
+/**
+ * Abstract implementation of an {@link IMerger}. This can be used as a base implementation to avoid
+ * re-implementing the whole contract.
+ * 
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ * @since 3.0
+ */
+public abstract class AbstractMerger implements IMerger {
+	/** Ranking of this merger. */
+	private int ranking;
+
+	/** Registry from which this merger has been created.. */
+	private Registry registry;
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#getRanking()
+	 */
+	public int getRanking() {
+		return ranking;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#setRanking(int)
+	 */
+	public void setRanking(int r) {
+		ranking = r;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#getRegistry()
+	 */
+	public Registry getRegistry() {
+		return registry;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#setRegistry(org.eclipse.emf.compare.merge.IMerger.Registry)
+	 */
+	public void setRegistry(Registry registry) {
+		if (this.registry != null && registry != null) {
+			throw new IllegalStateException("The registry has to be set only once."); //$NON-NLS-1$
+		}
+		this.registry = registry;
+	}
+
+	/**
+	 * This will merge all {@link Diff#getRequiredBy() differences that require} {@code diff} in the given
+	 * direction.
+	 * 
+	 * @param diff
+	 *            We need to merge all differences that require this one (see {@link Diff#getRequiredBy()}.
+	 * @param rightToLeft
+	 *            If {@code true}, {@link #copyRightToLeft(Diff, Monitor) apply} all differences that require
+	 *            {@code diff}. Otherwise, {@link #copyLeftToRight(Diff, Monitor) revert} them.
+	 * @param monitor
+	 *            The monitor we should use to report progress.
+	 */
+	protected void mergeRequiredBy(Diff diff, boolean rightToLeft, Monitor monitor) {
+		// TODO log back to the user what we will merge along?
+		for (Diff dependency : diff.getRequiredBy()) {
+			// TODO: what to do when state = Discarded but is required?
+			mergeDiff(dependency, rightToLeft, monitor);
+		}
+	}
+
+	/**
+	 * This will merge all {@link Diff#getRequires() differences required by} {@code diff} in the given
+	 * direction.
+	 * 
+	 * @param diff
+	 *            The difference which requirements we need to merge.
+	 * @param rightToLeft
+	 *            If {@code true}, {@link #copyRightToLeft(Diff, Monitor) apply} all required differences.
+	 *            Otherwise, {@link #copyLeftToRight(Diff, Monitor) revert} them.
+	 * @param monitor
+	 *            The monitor we should use to report progress.
+	 */
+	protected void mergeRequires(Diff diff, boolean rightToLeft, Monitor monitor) {
+		// TODO log back to the user what we will merge along?
+		for (Diff dependency : diff.getRequires()) {
+			// TODO: what to do when state = Discarded but is required?
+			mergeDiff(dependency, rightToLeft, monitor);
+		}
+	}
+
+	/**
+	 * This can be used by mergers to merge another (required, equivalent...) difference using the right
+	 * merger for that diff.
+	 * 
+	 * @param diff
+	 *            The diff we need to merge.
+	 * @param rightToLeft
+	 *            Direction of that merge.
+	 * @param monitor
+	 *            The monitor we should use to report progress.
+	 */
+	protected void mergeDiff(Diff diff, boolean rightToLeft, Monitor monitor) {
+		if (rightToLeft) {
+			final IMerger delegate = getRegistry().getHighestRankingMerger(diff);
+			delegate.copyRightToLeft(diff, monitor);
+		} else {
+			final IMerger delegate = getRegistry().getHighestRankingMerger(diff);
+			delegate.copyLeftToRight(diff, monitor);
+		}
+	}
+
+	/**
+	 * This will create a copy of the given EObject that can be used as the target of an addition (or the
+	 * reverting of a deletion).
+	 * <p>
+	 * The target will be self-contained and will have no reference towards any other EObject set (neither
+	 * containment nor "classic" references). All of its attributes' values will match the given
+	 * {@code referenceObject}'s.
+	 * </p>
+	 * 
+	 * @param referenceObject
+	 *            The EObject for which we'll create a copy.
+	 * @return A self-contained copy of {@code referenceObject}.
+	 * @see EMFCompareCopier#copy(EObject)
+	 */
+	protected EObject createCopy(EObject referenceObject) {
+		/*
+		 * We can't simply use EcoreUtil.copy. References will have their own diffs and will thus be merged
+		 * later on.
+		 */
+		final EcoreUtil.Copier copier = new EMFCompareCopier();
+		return copier.copy(referenceObject);
+	}
+
+	/**
+	 * Adds the given {@code value} into the given {@code list} at the given {@code index}. An {@code index}
+	 * under than zero or above the list's size will mean that the value should be appended at the end of the
+	 * list.
+	 * 
+	 * @param list
+	 *            The list into which {@code value} should be added.
+	 * @param value
+	 *            The value we need to add to {@code list}.
+	 * @param <E>
+	 *            Type of objects contained in the list.
+	 * @param insertionIndex
+	 *            The index at which {@code value} should be inserted into {@code list}. {@code -1} if it
+	 *            should be appended at the end of the list.
+	 */
+	@SuppressWarnings("unchecked")
+	protected <E> void addAt(List<E> list, E value, int insertionIndex) {
+		if (list instanceof InternalEList<?>) {
+			if (insertionIndex < 0 || insertionIndex > list.size()) {
+				((InternalEList<Object>)list).addUnique(value);
+			} else {
+				((InternalEList<Object>)list).addUnique(insertionIndex, value);
+			}
+		} else {
+			if (insertionIndex < 0 || insertionIndex > list.size()) {
+				list.add(value);
+			} else {
+				list.add(insertionIndex, value);
+			}
+		}
+	}
+
+	/**
+	 * Set the merge way for the given diff. after a merge, it allows to know the way of the merge.
+	 * 
+	 * @param diff
+	 *            the given diff.
+	 * @param leftToRight
+	 *            the way of the merge.
+	 */
+	protected void setMergeDataForDiff(Diff diff, boolean leftToRight) {
+		Adapter adapter = EcoreUtil.getExistingAdapter(diff, IMergeData.class);
+		if (adapter != null) {
+			if (leftToRight) {
+				((IMergeData)adapter).setMergedToRight();
+			} else {
+				((IMergeData)adapter).setMergedToLeft();
+			}
+		} else {
+			diff.eAdapters().add(new MergeDataAdapter(leftToRight));
+		}
+	}
+}
diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java
index 398c339..02bb684 100644
--- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java
+++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/merge/ReferenceChangeMerger.java
@@ -1,714 +1,720 @@
-/*******************************************************************************

- * Copyright (c) 2012, 2013 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.merge;

-

-import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEIsSet;

-

-import java.util.Iterator;

-import java.util.List;

-

-import org.eclipse.emf.common.util.EList;

-import org.eclipse.emf.common.util.Monitor;

-import org.eclipse.emf.compare.Comparison;

-import org.eclipse.emf.compare.Diff;

-import org.eclipse.emf.compare.DifferenceSource;

-import org.eclipse.emf.compare.DifferenceState;

-import org.eclipse.emf.compare.Match;

-import org.eclipse.emf.compare.ReferenceChange;

-import org.eclipse.emf.compare.utils.DiffUtil;

-import org.eclipse.emf.compare.utils.IEqualityHelper;

-import org.eclipse.emf.ecore.EObject;

-import org.eclipse.emf.ecore.EReference;

-import org.eclipse.emf.ecore.resource.Resource;

-import org.eclipse.emf.ecore.util.EcoreUtil;

-import org.eclipse.emf.ecore.xmi.XMIResource;

-

-/**

- * This specific implementation of {@link AbstractMerger} will be used to merge reference changes.

- * 

- * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>

- */

-public class ReferenceChangeMerger extends AbstractMerger {

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)

-	 */

-	public boolean isMergerFor(Diff target) {

-		return target instanceof ReferenceChange;

-	}

-

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#copyLeftToRight(org.eclipse.emf.compare.Diff,

-	 *      org.eclipse.emf.common.util.Monitor)

-	 */

-	public void copyLeftToRight(Diff target, Monitor monitor) {

-		// Don't merge an already merged (or discarded) diff

-		if (target.getState() != DifferenceState.UNRESOLVED) {

-			return;

-		}

-		final ReferenceChange diff = (ReferenceChange)target;

-

-		// Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.

-		diff.setState(DifferenceState.MERGED);

-		if (diff.getEquivalence() != null) {

-			boolean continueMerge = handleEquivalences(diff, false, monitor);

-			if (!continueMerge) {

-				return;

-			}

-		}

-

-		if (diff.getSource() == DifferenceSource.LEFT) {

-			// merge all "requires" diffs

-			mergeRequires(diff, false, monitor);

-

-			switch (diff.getKind()) {

-				case ADD:

-					// Create the same element in right

-					addInTarget(diff, false);

-					break;

-				case DELETE:

-					// Delete that same element from right

-					removeFromTarget(diff, false);

-					break;

-				case MOVE:

-					moveElement(diff, false);

-					break;

-				case CHANGE:

-					// Is it an unset?

-					if (diff.getMatch().getLeft() != null) {

-						final EObject leftValue = (EObject)diff.getMatch().getLeft().eGet(

-								diff.getReference(), false);

-						if (leftValue == null) {

-							removeFromTarget(diff, false);

-						} else {

-							addInTarget(diff, false);

-						}

-					} else {

-						// we have no left, and the source is on the left. Can only be an unset

-						removeFromTarget(diff, false);

-					}

-					break;

-				default:

-					break;

-			}

-		} else {

-			// merge all "required by" diffs

-			mergeRequiredBy(diff, false, monitor);

-

-			switch (diff.getKind()) {

-				case ADD:

-					// We have a ADD on right. we need to revert this addition

-					removeFromTarget(diff, false);

-					break;

-				case DELETE:

-					// DELETE in the right. We need to re-create this element

-					addInTarget(diff, false);

-					break;

-				case MOVE:

-					moveElement(diff, false);

-					break;

-				case CHANGE:

-					// Is it an unset?

-					if (diff.getMatch().getRight() != null) {

-						final EObject rightValue = (EObject)diff.getMatch().getRight().eGet(

-								diff.getReference(), false);

-						if (rightValue == null) {

-							// Value has been unset in the right, and we are merging towards right.

-							// We need to re-add this element

-							addInTarget(diff, false);

-						} else {

-							// We'll actually need to "reset" this reference to its original value

-							resetInTarget(diff, false);

-						}

-					} else {

-						// we have no right, and the source is on the right. Can only be an unset

-						addInTarget(diff, false);

-					}

-					break;

-				default:

-					break;

-			}

-		}

-	}

-

-	/**

-	 * {@inheritDoc}

-	 * 

-	 * @see org.eclipse.emf.compare.merge.IMerger#copyRightToLeft(org.eclipse.emf.compare.Diff,

-	 *      org.eclipse.emf.common.util.Monitor)

-	 */

-	public void copyRightToLeft(Diff target, Monitor monitor) {

-		// Don't merge an already merged (or discarded) diff

-		if (target.getState() != DifferenceState.UNRESOLVED) {

-			return;

-		}

-		final ReferenceChange diff = (ReferenceChange)target;

-

-		// Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.

-		diff.setState(DifferenceState.MERGED);

-		if (diff.getEquivalence() != null) {

-			boolean continueMerge = handleEquivalences(diff, true, monitor);

-			if (!continueMerge) {

-				return;

-			}

-		}

-

-		if (diff.getSource() == DifferenceSource.LEFT) {

-			// merge all "required by" diffs

-			mergeRequiredBy(diff, true, monitor);

-

-			switch (diff.getKind()) {

-				case ADD:

-					// We have a ADD on left, thus nothing in right. We need to revert the addition

-					removeFromTarget(diff, true);

-					break;

-				case DELETE:

-					// DELETE in the left, thus an element in right. We need to re-create that element

-					addInTarget(diff, true);

-					break;

-				case MOVE:

-					moveElement(diff, true);

-					break;

-				case CHANGE:

-					// Is it an unset?

-					if (diff.getMatch().getLeft() != null) {

-						final EObject leftValue = (EObject)diff.getMatch().getLeft().eGet(

-								diff.getReference(), false);

-						if (leftValue == null) {

-							// Value has been unset in the right, and we are merging towards right.

-							// We need to re-add this element

-							addInTarget(diff, true);

-						} else {

-							// We'll actually need to "reset" this reference to its original value

-							resetInTarget(diff, true);

-						}

-					} else {

-						// we have no left, and the source is on the left. Can only be an unset

-						addInTarget(diff, true);

-					}

-					break;

-				default:

-					break;

-			}

-		} else {

-			// merge all "requires" diffs

-			mergeRequires(diff, true, monitor);

-

-			switch (diff.getKind()) {

-				case ADD:

-					addInTarget(diff, true);

-					break;

-				case DELETE:

-					removeFromTarget(diff, true);

-					break;

-				case MOVE:

-					moveElement(diff, true);

-					break;

-				case CHANGE:

-					// Is it an unset?

-					if (diff.getMatch().getRight() != null) {

-						final EObject rightValue = (EObject)diff.getMatch().getRight().eGet(

-								diff.getReference(), false);

-						if (rightValue == null) {

-							removeFromTarget(diff, true);

-						} else {

-							addInTarget(diff, true);

-						}

-					} else {

-						// we have no right, and the source is on the right. Can only be an unset

-						removeFromTarget(diff, true);

-					}

-					break;

-				default:

-					break;

-			}

-		}

-	}

-

-	/**

-	 * This will be called when trying to copy a "MOVE" diff.

-	 * 

-	 * @param diff

-	 *            The diff we are currently merging.

-	 * @param rightToLeft

-	 *            Whether we should move the value in the left or right side.

-	 */

-	protected void moveElement(ReferenceChange diff, boolean rightToLeft) {

-		final Comparison comparison = diff.getMatch().getComparison();

-		final Match valueMatch = comparison.getMatch(diff.getValue());

-		final EReference reference = diff.getReference();

-

-		final EObject expectedContainer;

-		if (reference.isContainment()) {

-			/*

-			 * We cannot "trust" the holding match (getMatch) in this case. However, "valueMatch" cannot be

-			 * null : we cannot have detected a move if the moved element is not matched on both sides. Use

-			 * that information to retrieve the proper "target" container.

-			 */

-			final Match targetContainerMatch;

-			// If it exists, use the source side's container as reference

-			if (rightToLeft && valueMatch.getRight() != null) {

-				targetContainerMatch = comparison.getMatch(valueMatch.getRight().eContainer());

-			} else if (!rightToLeft && valueMatch.getLeft() != null) {

-				targetContainerMatch = comparison.getMatch(valueMatch.getLeft().eContainer());

-			} else {

-				// Otherwise, the value we're moving on one side has been removed from its source side.

-				targetContainerMatch = comparison.getMatch(valueMatch.getOrigin().eContainer());

-			}

-			if (rightToLeft) {

-				expectedContainer = targetContainerMatch.getLeft();

-			} else {

-				expectedContainer = targetContainerMatch.getRight();

-			}

-		} else if (rightToLeft) {

-			expectedContainer = diff.getMatch().getLeft();

-		} else {

-			expectedContainer = diff.getMatch().getRight();

-		}

-		if (expectedContainer == null) {

-			// FIXME throw exception? log? re-try to merge our requirements?

-			// one of the "required" diffs should have created our container.

-			return;

-		}

-

-		final EObject expectedValue;

-		if (valueMatch == null) {

-			// The value being moved is out of the scope

-			/*

-			 * Note : there should not be a way to end up with a "move" for an out of scope value : a move can

-			 * only be detected if the object is matched on both sides, otherwise all we can see is "add" and

-			 * "delete"... Is this "fallback" code even reachable? If so, how?

-			 */

-			// We need to look it up

-			if (reference.isMany()) {

-				@SuppressWarnings("unchecked")

-				final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);

-				expectedValue = findMatchIn(comparison, targetList, diff.getValue());

-			} else {

-				expectedValue = (EObject)expectedContainer.eGet(reference);

-			}

-		} else {

-			if (rightToLeft) {

-				expectedValue = valueMatch.getLeft();

-			} else {

-				expectedValue = valueMatch.getRight();

-			}

-		}

-		// We now know the target container, target reference and target value.

-		doMove(diff, comparison, expectedContainer, expectedValue, rightToLeft);

-	}

-

-	/**

-	 * This will do the actual work of moving the element into its reference. All sanity checks were made in

-	 * {@link #moveElement(boolean)} and no more verification will be made here.

-	 * 

-	 * @param diff

-	 *            The diff we are currently merging.

-	 * @param comparison

-	 *            Comparison holding this Diff.

-	 * @param expectedContainer

-	 *            The container in which we are reorganizing a reference.

-	 * @param expectedValue

-	 *            The value that is to be moved within its reference.

-	 * @param rightToLeft

-	 *            Whether we should move the value in the left or right side.

-	 */

-	@SuppressWarnings("unchecked")

-	protected void doMove(ReferenceChange diff, Comparison comparison, EObject expectedContainer,

-			EObject expectedValue, boolean rightToLeft) {

-		final EReference reference = diff.getReference();

-		if (reference.isMany()) {

-			// Element to move cannot be part of the LCS... or there would not be a MOVE diff

-			int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);

-

-			/*

-			 * However, it could still have been located "before" its new index, in which case we need to take

-			 * it into account.

-			 */

-			final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);

-			final int currentIndex = targetList.indexOf(expectedValue);

-			if (insertionIndex > currentIndex && currentIndex >= 0) {

-				insertionIndex--;

-			}

-

-			if (currentIndex == -1) {

-				// happens for container changes for example.

-				if (!reference.isContainment()) {

-					targetList.remove(expectedValue);

-				}

-				if (insertionIndex < 0 && insertionIndex > targetList.size()) {

-					targetList.add(expectedValue);

-				} else {

-					targetList.add(insertionIndex, expectedValue);

-				}

-			} else if (targetList instanceof EList<?>) {

-				if (insertionIndex < 0 && insertionIndex > targetList.size()) {

-					((EList<EObject>)targetList).move(targetList.size() - 1, expectedValue);

-				} else {

-					((EList<EObject>)targetList).move(insertionIndex, expectedValue);

-				}

-			} else {

-				targetList.remove(expectedValue);

-				if (insertionIndex < 0 && insertionIndex > targetList.size()) {

-					targetList.add(expectedValue);

-				} else {

-					targetList.add(insertionIndex, expectedValue);

-				}

-			}

-		} else {

-			expectedContainer.eSet(reference, expectedValue);

-		}

-	}

-

-	/**

-	 * This will be called when we need to create an element in the target side.

-	 * <p>

-	 * All necessary sanity checks have been made to ensure that the current operation is one that should

-	 * create an object in its side or add an objet to a reference. In other words, either :

-	 * <ul>

-	 * <li>We are copying from right to left and

-	 * <ul>

-	 * <li>we are copying an addition to the right side (we need to create the same object in the left), or</li>

-	 * <li>we are copying a deletion from the left side (we need to revert the deletion).</li>

-	 * </ul>

-	 * </li>

-	 * <li>We are copying from left to right and

-	 * <ul>

-	 * <li>we are copying a deletion from the right side (we need to revert the deletion), or</li>

-	 * <li>we are copying an addition to the left side (we need to create the same object in the right).</li>

-	 * </ul>

-	 * </li>

-	 * </ul>

-	 * </p>

-	 * 

-	 * @param diff

-	 *            The diff we are currently merging.

-	 * @param rightToLeft

-	 *            Tells us whether we are to add an object on the left or right side.

-	 */

-	@SuppressWarnings("unchecked")

-	protected void addInTarget(ReferenceChange diff, boolean rightToLeft) {

-		final Match match = diff.getMatch();

-		final EObject expectedContainer;

-		if (rightToLeft) {

-			expectedContainer = match.getLeft();

-		} else {

-			expectedContainer = match.getRight();

-		}

-

-		if (expectedContainer == null) {

-			// FIXME throw exception? log? re-try to merge our requirements?

-			// one of the "required" diffs should have created our container.

-			return;

-		}

-

-		final Comparison comparison = match.getComparison();

-		final EReference reference = diff.getReference();

-		final EObject expectedValue;

-		final Match valueMatch = comparison.getMatch(diff.getValue());

-		if (valueMatch == null) {

-			// This is an out of scope value.

-			if (diff.getValue().eIsProxy()) {

-				// Copy the proxy

-				expectedValue = EcoreUtil.copy(diff.getValue());

-			} else {

-				// Use the same value.

-				expectedValue = diff.getValue();

-			}

-		} else if (rightToLeft) {

-			if (reference.isContainment()) {

-				expectedValue = createCopy(diff.getValue());

-				valueMatch.setLeft(expectedValue);

-			} else {

-				expectedValue = valueMatch.getLeft();

-			}

-		} else {

-			if (reference.isContainment()) {

-				expectedValue = createCopy(diff.getValue());

-				valueMatch.setRight(expectedValue);

-			} else {

-				expectedValue = valueMatch.getRight();

-			}

-		}

-

-		// We have the container, reference and value. We need to know the insertion index.

-		if (reference.isMany()) {

-			final int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);

-

-			final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);

-			addAt(targetList, expectedValue, insertionIndex);

-		} else {

-			expectedContainer.eSet(reference, expectedValue);

-		}

-

-		if (reference.isContainment()) {

-			// Copy XMI ID when applicable.

-			final Resource initialResource = diff.getValue().eResource();

-			final Resource targetResource = expectedValue.eResource();

-			if (initialResource instanceof XMIResource && targetResource instanceof XMIResource) {

-				((XMIResource)targetResource).setID(expectedValue, ((XMIResource)initialResource).getID(diff

-						.getValue()));

-			}

-		}

-	}

-

-	/**

-	 * This will be called when we need to remove an element from the target side.

-	 * <p>

-	 * All necessary sanity checks have been made to ensure that the current operation is one that should

-	 * delete an object. In other words, we are :

-	 * <ul>

-	 * <li>Copying from right to left and either

-	 * <ul>

-	 * <li>we are copying a deletion from the right side (we need to remove the same object in the left) or,</li>

-	 * <li>we are copying an addition to the left side (we need to revert the addition).</li>

-	 * </ul>

-	 * </li>

-	 * <li>Copying from left to right and either

-	 * <ul>

-	 * <li>we are copying an addition to the right side (we need to revert the addition), or.</li>

-	 * <li>we are copying a deletion from the left side (we need to remove the same object in the right).</li>

-	 * </ul>

-	 * </li>

-	 * </ul>

-	 * </p>

-	 * 

-	 * @param diff

-	 *            The diff we are currently merging.

-	 * @param rightToLeft

-	 *            Tells us whether we are to add an object on the left or right side.

-	 */

-	@SuppressWarnings("unchecked")

-	protected void removeFromTarget(ReferenceChange diff, boolean rightToLeft) {

-		final Match match = diff.getMatch();

-		final EReference reference = diff.getReference();

-		final EObject currentContainer;

-		if (rightToLeft) {

-			currentContainer = match.getLeft();

-		} else {

-			currentContainer = match.getRight();

-		}

-		final Comparison comparison = match.getComparison();

-		final Match valueMatch = comparison.getMatch(diff.getValue());

-

-		if (currentContainer == null) {

-			// FIXME throw exception? log? re-try to merge our requirements?

-			// one of the "required" diffs should have created our container.

-			return;

-		}

-

-		final EObject expectedValue;

-		if (valueMatch == null) {

-			// value is out of the scope... we need to look it up

-			if (reference.isMany()) {

-				final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);

-				expectedValue = findMatchIn(comparison, targetList, diff.getValue());

-			} else {

-				// the value will not be needed anyway

-				expectedValue = null;

-			}

-		} else if (rightToLeft) {

-			expectedValue = valueMatch.getLeft();

-		} else {

-			expectedValue = valueMatch.getRight();

-		}

-

-		// We have the container, reference and value to remove. Expected value can be null when the

-		// deletion was made on both side (i.e. a pseudo delete)

-		if (reference.isContainment() && expectedValue != null) {

-			EcoreUtil.remove(expectedValue);

-			if (rightToLeft && valueMatch != null) {

-				valueMatch.setLeft(null);

-			} else if (valueMatch != null) {

-				valueMatch.setRight(null);

-			}

-			// TODO remove dangling? remove empty Match?

-		} else if (reference.isMany()) {

-			/*

-			 * TODO if the same value appears twice, should we try and find the one that has actually been

-			 * deleted? Can it happen? For now, remove the first occurence we find.

-			 */

-			final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);

-			targetList.remove(expectedValue);

-		} else {

-			currentContainer.eUnset(reference);

-		}

-	}

-

-	/**

-	 * This will be called by the merge operations in order to reset a reference to its original value, be

-	 * that the left or right side.

-	 * <p>

-	 * Should never be called on multi-valued references.

-	 * </p>

-	 * 

-	 * @param diff

-	 *            The diff we are currently merging.

-	 * @param rightToLeft

-	 *            Tells us the direction of this merge operation.

-	 */

-	protected void resetInTarget(ReferenceChange diff, boolean rightToLeft) {

-		final Match match = diff.getMatch();

-		final EReference reference = diff.getReference();

-		final EObject targetContainer;

-		if (rightToLeft) {

-			targetContainer = match.getLeft();

-		} else {

-			targetContainer = match.getRight();

-		}

-

-		final EObject originContainer;

-		if (match.getComparison().isThreeWay()) {

-			originContainer = match.getOrigin();

-		} else if (rightToLeft) {

-			originContainer = match.getRight();

-		} else {

-			originContainer = match.getLeft();

-		}

-

-		if (originContainer == null || !safeEIsSet(targetContainer, reference)

-				|| !safeEIsSet(originContainer, reference)) {

-			targetContainer.eUnset(reference);

-		} else {

-			final EObject originalValue = (EObject)originContainer.eGet(reference);

-			final Match valueMatch = match.getComparison().getMatch(originalValue);

-			final EObject expectedValue;

-			if (valueMatch == null) {

-				// Value is out of the scope, use it as-is

-				expectedValue = originalValue;

-			} else if (rightToLeft) {

-				expectedValue = valueMatch.getLeft();

-			} else {

-				expectedValue = valueMatch.getRight();

-			}

-			targetContainer.eSet(reference, expectedValue);

-		}

-	}

-

-	/**

-	 * Handles the equivalences of this difference.

-	 * <p>

-	 * Note that in certain cases, we'll merge our opposite instead of merging this diff. Specifically, we'll

-	 * do that for one-to-many eOpposites : we'll merge the 'many' side instead of the 'unique' one. This

-	 * allows us not to worry about the order of the references on that 'many' side.

-	 * </p>

-	 * <p>

-	 * This is called before the merge of <code>this</code>. In short, if this returns <code>false</code>, we

-	 * won't carry on merging <code>this</code> after returning.

-	 * </p>

-	 * 

-	 * @param diff

-	 *            The diff we are currently merging.

-	 * @param rightToLeft

-	 *            Direction of the merge.

-	 * @param monitor

-	 *            The monitor to use in order to report progress information.

-	 * @return <code>true</code> if the current difference should still be merged after handling its

-	 *         equivalences, <code>false</code> if it should be considered "already merged".

-	 */

-	protected boolean handleEquivalences(ReferenceChange diff, boolean rightToLeft, Monitor monitor) {

-		final EReference reference = diff.getReference();

-		boolean continueMerge = true;

-		for (Diff equivalent : diff.getEquivalence().getDifferences()) {

-			if (equivalent instanceof ReferenceChange

-					&& reference.getEOpposite() == ((ReferenceChange)equivalent).getReference()

-					&& equivalent.getState() == DifferenceState.UNRESOLVED) {

-				// This equivalence is on our eOpposite. Should we merge it instead of 'this'?

-				final boolean mergeEquivalence = !reference.isMany()

-						&& ((ReferenceChange)equivalent).getReference().isMany();

-				if (mergeEquivalence) {

-					mergeDiff(equivalent, rightToLeft, monitor);

-					continueMerge = false;

-				}

-			} else if (diff.getSource() == DifferenceSource.LEFT) {

-				// This can happen when merging subset/supersets... see AddInterfaceTest#testA50UseCase

-				/*

-				 * This should be removed (or we should make sure that we can never be here) when bug 398402

-				 * is fixed.

-				 */

-				if (rightToLeft && diff.getRequiredBy().contains(equivalent)) {

-					mergeDiff(equivalent, rightToLeft, monitor);

-					continueMerge = false;

-				} else if (!rightToLeft && diff.getRequires().contains(equivalent)) {

-					mergeDiff(equivalent, rightToLeft, monitor);

-					continueMerge = false;

-				}

-			} else if (diff.getSource() == DifferenceSource.RIGHT) {

-				// This can happen when merging subset/supersets... see AddInterfaceTest#testA50UseCase

-				/*

-				 * This should be removed (or we should make sure that we can never be here) when bug 398402

-				 * is fixed.

-				 */

-				if (rightToLeft && diff.getRequires().contains(equivalent)) {

-					mergeDiff(equivalent, rightToLeft, monitor);

-					continueMerge = false;

-				} else if (!rightToLeft && diff.getRequiredBy().contains(equivalent)) {

-					mergeDiff(equivalent, rightToLeft, monitor);

-					continueMerge = false;

-				}

-			}

-			equivalent.setState(DifferenceState.MERGED);

-		}

-		return continueMerge;

-	}

-

-	/**

-	 * Seeks a match of the given {@code element} in the given list, using the equality helper to find it.

-	 * This is only used when moving or deleting proxies for now.

-	 * 

-	 * @param comparison

-	 *            The comparison which Diff we are currently merging.

-	 * @param list

-	 *            The list from which we seek a value.

-	 * @param element

-	 *            The value for which we need a match in {@code list}.

-	 * @return The match of {@code element} in {@code list}, {@code null} if none.

-	 */

-	protected EObject findMatchIn(Comparison comparison, List<EObject> list, EObject element) {

-		final IEqualityHelper helper = comparison.getEqualityHelper();

-		final Iterator<EObject> it = list.iterator();

-		while (it.hasNext()) {

-			final EObject next = it.next();

-			if (helper.matchingValues(next, element)) {

-				return next;

-			}

-		}

-		return null;

-	}

-

-	/**

-	 * This will be used by the distinct merge actions in order to find the index at which a value should be

-	 * inserted in its target list. See {@link DiffUtil#findInsertionIndex(Comparison, Diff, boolean)} for

-	 * more on this.

-	 * <p>

-	 * Sub-classes can override this if the insertion order is irrelevant. A return value of {@code -1} will

-	 * be considered as "no index" and the value will be inserted at the end of its target list.

-	 * </p>

-	 * 

-	 * @param comparison

-	 *            This will be used in order to retrieve the Match for EObjects when comparing them.

-	 * @param diff

-	 *            The diff which merging will trigger the need for an insertion index in its target list.

-	 * @param rightToLeft

-	 *            {@code true} if the merging will be done into the left list, so that we should consider the

-	 *            right model as the source and the left as the target.

-	 * @return The index at which this {@code diff}'s value should be inserted into the 'target' list, as

-	 *         inferred from {@code rightToLeft}. {@code -1} if the value should be inserted at the end of its

-	 *         target list.

-	 * @see DiffUtil#findInsertionIndex(Comparison, Diff, boolean)

-	 */

-	protected int findInsertionIndex(Comparison comparison, Diff diff, boolean rightToLeft) {

-		return DiffUtil.findInsertionIndex(comparison, diff, rightToLeft);

-	}

-}

+/*******************************************************************************
+ * Copyright (c) 2012, 2013 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.merge;
+
+import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEIsSet;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.Monitor;
+import org.eclipse.emf.compare.Comparison;
+import org.eclipse.emf.compare.Diff;
+import org.eclipse.emf.compare.DifferenceSource;
+import org.eclipse.emf.compare.DifferenceState;
+import org.eclipse.emf.compare.Match;
+import org.eclipse.emf.compare.ReferenceChange;
+import org.eclipse.emf.compare.utils.DiffUtil;
+import org.eclipse.emf.compare.utils.IEqualityHelper;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.xmi.XMIResource;
+
+/**
+ * This specific implementation of {@link AbstractMerger} will be used to merge reference changes.
+ * 
+ * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
+ */
+public class ReferenceChangeMerger extends AbstractMerger {
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
+	 */
+	public boolean isMergerFor(Diff target) {
+		return target instanceof ReferenceChange;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#copyLeftToRight(org.eclipse.emf.compare.Diff,
+	 *      org.eclipse.emf.common.util.Monitor)
+	 */
+	public void copyLeftToRight(Diff target, Monitor monitor) {
+		// Don't merge an already merged (or discarded) diff
+		if (target.getState() != DifferenceState.UNRESOLVED) {
+			return;
+		}
+		final ReferenceChange diff = (ReferenceChange)target;
+
+		// Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
+		diff.setState(DifferenceState.MERGED);
+		// Set the merge way for the diff
+		setMergeDataForDiff(diff, true);
+
+		if (diff.getEquivalence() != null) {
+			boolean continueMerge = handleEquivalences(diff, false, monitor);
+			if (!continueMerge) {
+				return;
+			}
+		}
+
+		if (diff.getSource() == DifferenceSource.LEFT) {
+			// merge all "requires" diffs
+			mergeRequires(diff, false, monitor);
+
+			switch (diff.getKind()) {
+				case ADD:
+					// Create the same element in right
+					addInTarget(diff, false);
+					break;
+				case DELETE:
+					// Delete that same element from right
+					removeFromTarget(diff, false);
+					break;
+				case MOVE:
+					moveElement(diff, false);
+					break;
+				case CHANGE:
+					// Is it an unset?
+					if (diff.getMatch().getLeft() != null) {
+						final EObject leftValue = (EObject)diff.getMatch().getLeft().eGet(
+								diff.getReference(), false);
+						if (leftValue == null) {
+							removeFromTarget(diff, false);
+						} else {
+							addInTarget(diff, false);
+						}
+					} else {
+						// we have no left, and the source is on the left. Can only be an unset
+						removeFromTarget(diff, false);
+					}
+					break;
+				default:
+					break;
+			}
+		} else {
+			// merge all "required by" diffs
+			mergeRequiredBy(diff, false, monitor);
+
+			switch (diff.getKind()) {
+				case ADD:
+					// We have a ADD on right. we need to revert this addition
+					removeFromTarget(diff, false);
+					break;
+				case DELETE:
+					// DELETE in the right. We need to re-create this element
+					addInTarget(diff, false);
+					break;
+				case MOVE:
+					moveElement(diff, false);
+					break;
+				case CHANGE:
+					// Is it an unset?
+					if (diff.getMatch().getRight() != null) {
+						final EObject rightValue = (EObject)diff.getMatch().getRight().eGet(
+								diff.getReference(), false);
+						if (rightValue == null) {
+							// Value has been unset in the right, and we are merging towards right.
+							// We need to re-add this element
+							addInTarget(diff, false);
+						} else {
+							// We'll actually need to "reset" this reference to its original value
+							resetInTarget(diff, false);
+						}
+					} else {
+						// we have no right, and the source is on the right. Can only be an unset
+						addInTarget(diff, false);
+					}
+					break;
+				default:
+					break;
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 * 
+	 * @see org.eclipse.emf.compare.merge.IMerger#copyRightToLeft(org.eclipse.emf.compare.Diff,
+	 *      org.eclipse.emf.common.util.Monitor)
+	 */
+	public void copyRightToLeft(Diff target, Monitor monitor) {
+		// Don't merge an already merged (or discarded) diff
+		if (target.getState() != DifferenceState.UNRESOLVED) {
+			return;
+		}
+		final ReferenceChange diff = (ReferenceChange)target;
+
+		// Change the diff's state before we actually merge it : this allows us to avoid requirement cycles.
+		diff.setState(DifferenceState.MERGED);
+		// Set the merge way for the diff
+		setMergeDataForDiff(diff, false);
+
+		if (diff.getEquivalence() != null) {
+			boolean continueMerge = handleEquivalences(diff, true, monitor);
+			if (!continueMerge) {
+				return;
+			}
+		}
+
+		if (diff.getSource() == DifferenceSource.LEFT) {
+			// merge all "required by" diffs
+			mergeRequiredBy(diff, true, monitor);
+
+			switch (diff.getKind()) {
+				case ADD:
+					// We have a ADD on left, thus nothing in right. We need to revert the addition
+					removeFromTarget(diff, true);
+					break;
+				case DELETE:
+					// DELETE in the left, thus an element in right. We need to re-create that element
+					addInTarget(diff, true);
+					break;
+				case MOVE:
+					moveElement(diff, true);
+					break;
+				case CHANGE:
+					// Is it an unset?
+					if (diff.getMatch().getLeft() != null) {
+						final EObject leftValue = (EObject)diff.getMatch().getLeft().eGet(
+								diff.getReference(), false);
+						if (leftValue == null) {
+							// Value has been unset in the right, and we are merging towards right.
+							// We need to re-add this element
+							addInTarget(diff, true);
+						} else {
+							// We'll actually need to "reset" this reference to its original value
+							resetInTarget(diff, true);
+						}
+					} else {
+						// we have no left, and the source is on the left. Can only be an unset
+						addInTarget(diff, true);
+					}
+					break;
+				default:
+					break;
+			}
+		} else {
+			// merge all "requires" diffs
+			mergeRequires(diff, true, monitor);
+
+			switch (diff.getKind()) {
+				case ADD:
+					addInTarget(diff, true);
+					break;
+				case DELETE:
+					removeFromTarget(diff, true);
+					break;
+				case MOVE:
+					moveElement(diff, true);
+					break;
+				case CHANGE:
+					// Is it an unset?
+					if (diff.getMatch().getRight() != null) {
+						final EObject rightValue = (EObject)diff.getMatch().getRight().eGet(
+								diff.getReference(), false);
+						if (rightValue == null) {
+							removeFromTarget(diff, true);
+						} else {
+							addInTarget(diff, true);
+						}
+					} else {
+						// we have no right, and the source is on the right. Can only be an unset
+						removeFromTarget(diff, true);
+					}
+					break;
+				default:
+					break;
+			}
+		}
+	}
+
+	/**
+	 * This will be called when trying to copy a "MOVE" diff.
+	 * 
+	 * @param diff
+	 *            The diff we are currently merging.
+	 * @param rightToLeft
+	 *            Whether we should move the value in the left or right side.
+	 */
+	protected void moveElement(ReferenceChange diff, boolean rightToLeft) {
+		final Comparison comparison = diff.getMatch().getComparison();
+		final Match valueMatch = comparison.getMatch(diff.getValue());
+		final EReference reference = diff.getReference();
+
+		final EObject expectedContainer;
+		if (reference.isContainment()) {
+			/*
+			 * We cannot "trust" the holding match (getMatch) in this case. However, "valueMatch" cannot be
+			 * null : we cannot have detected a move if the moved element is not matched on both sides. Use
+			 * that information to retrieve the proper "target" container.
+			 */
+			final Match targetContainerMatch;
+			// If it exists, use the source side's container as reference
+			if (rightToLeft && valueMatch.getRight() != null) {
+				targetContainerMatch = comparison.getMatch(valueMatch.getRight().eContainer());
+			} else if (!rightToLeft && valueMatch.getLeft() != null) {
+				targetContainerMatch = comparison.getMatch(valueMatch.getLeft().eContainer());
+			} else {
+				// Otherwise, the value we're moving on one side has been removed from its source side.
+				targetContainerMatch = comparison.getMatch(valueMatch.getOrigin().eContainer());
+			}
+			if (rightToLeft) {
+				expectedContainer = targetContainerMatch.getLeft();
+			} else {
+				expectedContainer = targetContainerMatch.getRight();
+			}
+		} else if (rightToLeft) {
+			expectedContainer = diff.getMatch().getLeft();
+		} else {
+			expectedContainer = diff.getMatch().getRight();
+		}
+		if (expectedContainer == null) {
+			// FIXME throw exception? log? re-try to merge our requirements?
+			// one of the "required" diffs should have created our container.
+			return;
+		}
+
+		final EObject expectedValue;
+		if (valueMatch == null) {
+			// The value being moved is out of the scope
+			/*
+			 * Note : there should not be a way to end up with a "move" for an out of scope value : a move can
+			 * only be detected if the object is matched on both sides, otherwise all we can see is "add" and
+			 * "delete"... Is this "fallback" code even reachable? If so, how?
+			 */
+			// We need to look it up
+			if (reference.isMany()) {
+				@SuppressWarnings("unchecked")
+				final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
+				expectedValue = findMatchIn(comparison, targetList, diff.getValue());
+			} else {
+				expectedValue = (EObject)expectedContainer.eGet(reference);
+			}
+		} else {
+			if (rightToLeft) {
+				expectedValue = valueMatch.getLeft();
+			} else {
+				expectedValue = valueMatch.getRight();
+			}
+		}
+		// We now know the target container, target reference and target value.
+		doMove(diff, comparison, expectedContainer, expectedValue, rightToLeft);
+	}
+
+	/**
+	 * This will do the actual work of moving the element into its reference. All sanity checks were made in
+	 * {@link #moveElement(boolean)} and no more verification will be made here.
+	 * 
+	 * @param diff
+	 *            The diff we are currently merging.
+	 * @param comparison
+	 *            Comparison holding this Diff.
+	 * @param expectedContainer
+	 *            The container in which we are reorganizing a reference.
+	 * @param expectedValue
+	 *            The value that is to be moved within its reference.
+	 * @param rightToLeft
+	 *            Whether we should move the value in the left or right side.
+	 */
+	@SuppressWarnings("unchecked")
+	protected void doMove(ReferenceChange diff, Comparison comparison, EObject expectedContainer,
+			EObject expectedValue, boolean rightToLeft) {
+		final EReference reference = diff.getReference();
+		if (reference.isMany()) {
+			// Element to move cannot be part of the LCS... or there would not be a MOVE diff
+			int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
+
+			/*
+			 * However, it could still have been located "before" its new index, in which case we need to take
+			 * it into account.
+			 */
+			final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
+			final int currentIndex = targetList.indexOf(expectedValue);
+			if (insertionIndex > currentIndex && currentIndex >= 0) {
+				insertionIndex--;
+			}
+
+			if (currentIndex == -1) {
+				// happens for container changes for example.
+				if (!reference.isContainment()) {
+					targetList.remove(expectedValue);
+				}
+				if (insertionIndex < 0 && insertionIndex > targetList.size()) {
+					targetList.add(expectedValue);
+				} else {
+					targetList.add(insertionIndex, expectedValue);
+				}
+			} else if (targetList instanceof EList<?>) {
+				if (insertionIndex < 0 && insertionIndex > targetList.size()) {
+					((EList<EObject>)targetList).move(targetList.size() - 1, expectedValue);
+				} else {
+					((EList<EObject>)targetList).move(insertionIndex, expectedValue);
+				}
+			} else {
+				targetList.remove(expectedValue);
+				if (insertionIndex < 0 && insertionIndex > targetList.size()) {
+					targetList.add(expectedValue);
+				} else {
+					targetList.add(insertionIndex, expectedValue);
+				}
+			}
+		} else {
+			expectedContainer.eSet(reference, expectedValue);
+		}
+	}
+
+	/**
+	 * This will be called when we need to create an element in the target side.
+	 * <p>
+	 * All necessary sanity checks have been made to ensure that the current operation is one that should
+	 * create an object in its side or add an objet to a reference. In other words, either :
+	 * <ul>
+	 * <li>We are copying from right to left and
+	 * <ul>
+	 * <li>we are copying an addition to the right side (we need to create the same object in the left), or</li>
+	 * <li>we are copying a deletion from the left side (we need to revert the deletion).</li>
+	 * </ul>
+	 * </li>
+	 * <li>We are copying from left to right and
+	 * <ul>
+	 * <li>we are copying a deletion from the right side (we need to revert the deletion), or</li>
+	 * <li>we are copying an addition to the left side (we need to create the same object in the right).</li>
+	 * </ul>
+	 * </li>
+	 * </ul>
+	 * </p>
+	 * 
+	 * @param diff
+	 *            The diff we are currently merging.
+	 * @param rightToLeft
+	 *            Tells us whether we are to add an object on the left or right side.
+	 */
+	@SuppressWarnings("unchecked")
+	protected void addInTarget(ReferenceChange diff, boolean rightToLeft) {
+		final Match match = diff.getMatch();
+		final EObject expectedContainer;
+		if (rightToLeft) {
+			expectedContainer = match.getLeft();
+		} else {
+			expectedContainer = match.getRight();
+		}
+
+		if (expectedContainer == null) {
+			// FIXME throw exception? log? re-try to merge our requirements?
+			// one of the "required" diffs should have created our container.
+			return;
+		}
+
+		final Comparison comparison = match.getComparison();
+		final EReference reference = diff.getReference();
+		final EObject expectedValue;
+		final Match valueMatch = comparison.getMatch(diff.getValue());
+		if (valueMatch == null) {
+			// This is an out of scope value.
+			if (diff.getValue().eIsProxy()) {
+				// Copy the proxy
+				expectedValue = EcoreUtil.copy(diff.getValue());
+			} else {
+				// Use the same value.
+				expectedValue = diff.getValue();
+			}
+		} else if (rightToLeft) {
+			if (reference.isContainment()) {
+				expectedValue = createCopy(diff.getValue());
+				valueMatch.setLeft(expectedValue);
+			} else {
+				expectedValue = valueMatch.getLeft();
+			}
+		} else {
+			if (reference.isContainment()) {
+				expectedValue = createCopy(diff.getValue());
+				valueMatch.setRight(expectedValue);
+			} else {
+				expectedValue = valueMatch.getRight();
+			}
+		}
+
+		// We have the container, reference and value. We need to know the insertion index.
+		if (reference.isMany()) {
+			final int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
+
+			final List<EObject> targetList = (List<EObject>)expectedContainer.eGet(reference);
+			addAt(targetList, expectedValue, insertionIndex);
+		} else {
+			expectedContainer.eSet(reference, expectedValue);
+		}
+
+		if (reference.isContainment()) {
+			// Copy XMI ID when applicable.
+			final Resource initialResource = diff.getValue().eResource();
+			final Resource targetResource = expectedValue.eResource();
+			if (initialResource instanceof XMIResource && targetResource instanceof XMIResource) {
+				((XMIResource)targetResource).setID(expectedValue, ((XMIResource)initialResource).getID(diff
+						.getValue()));
+			}
+		}
+	}
+
+	/**
+	 * This will be called when we need to remove an element from the target side.
+	 * <p>
+	 * All necessary sanity checks have been made to ensure that the current operation is one that should
+	 * delete an object. In other words, we are :
+	 * <ul>
+	 * <li>Copying from right to left and either
+	 * <ul>
+	 * <li>we are copying a deletion from the right side (we need to remove the same object in the left) or,</li>
+	 * <li>we are copying an addition to the left side (we need to revert the addition).</li>
+	 * </ul>
+	 * </li>
+	 * <li>Copying from left to right and either
+	 * <ul>
+	 * <li>we are copying an addition to the right side (we need to revert the addition), or.</li>
+	 * <li>we are copying a deletion from the left side (we need to remove the same object in the right).</li>
+	 * </ul>
+	 * </li>
+	 * </ul>
+	 * </p>
+	 * 
+	 * @param diff
+	 *            The diff we are currently merging.
+	 * @param rightToLeft
+	 *            Tells us whether we are to add an object on the left or right side.
+	 */
+	@SuppressWarnings("unchecked")
+	protected void removeFromTarget(ReferenceChange diff, boolean rightToLeft) {
+		final Match match = diff.getMatch();
+		final EReference reference = diff.getReference();
+		final EObject currentContainer;
+		if (rightToLeft) {
+			currentContainer = match.getLeft();
+		} else {
+			currentContainer = match.getRight();
+		}
+		final Comparison comparison = match.getComparison();
+		final Match valueMatch = comparison.getMatch(diff.getValue());
+
+		if (currentContainer == null) {
+			// FIXME throw exception? log? re-try to merge our requirements?
+			// one of the "required" diffs should have created our container.
+			return;
+		}
+
+		final EObject expectedValue;
+		if (valueMatch == null) {
+			// value is out of the scope... we need to look it up
+			if (reference.isMany()) {
+				final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);
+				expectedValue = findMatchIn(comparison, targetList, diff.getValue());
+			} else {
+				// the value will not be needed anyway
+				expectedValue = null;
+			}
+		} else if (rightToLeft) {
+			expectedValue = valueMatch.getLeft();
+		} else {
+			expectedValue = valueMatch.getRight();
+		}
+
+		// We have the container, reference and value to remove. Expected value can be null when the
+		// deletion was made on both side (i.e. a pseudo delete)
+		if (reference.isContainment() && expectedValue != null) {
+			EcoreUtil.remove(expectedValue);
+			if (rightToLeft && valueMatch != null) {
+				valueMatch.setLeft(null);
+			} else if (valueMatch != null) {
+				valueMatch.setRight(null);
+			}
+			// TODO remove dangling? remove empty Match?
+		} else if (reference.isMany()) {
+			/*
+			 * TODO if the same value appears twice, should we try and find the one that has actually been
+			 * deleted? Can it happen? For now, remove the first occurence we find.
+			 */
+			final List<EObject> targetList = (List<EObject>)currentContainer.eGet(reference);
+			targetList.remove(expectedValue);
+		} else {
+			currentContainer.eUnset(reference);
+		}
+	}
+
+	/**
+	 * This will be called by the merge operations in order to reset a reference to its original value, be
+	 * that the left or right side.
+	 * <p>
+	 * Should never be called on multi-valued references.
+	 * </p>
+	 * 
+	 * @param diff
+	 *            The diff we are currently merging.
+	 * @param rightToLeft
+	 *            Tells us the direction of this merge operation.
+	 */
+	protected void resetInTarget(ReferenceChange diff, boolean rightToLeft) {
+		final Match match = diff.getMatch();
+		final EReference reference = diff.getReference();
+		final EObject targetContainer;
+		if (rightToLeft) {
+			targetContainer = match.getLeft();
+		} else {
+			targetContainer = match.getRight();
+		}
+
+		final EObject originContainer;
+		if (match.getComparison().isThreeWay()) {
+			originContainer = match.getOrigin();
+		} else if (rightToLeft) {
+			originContainer = match.getRight();
+		} else {
+			originContainer = match.getLeft();
+		}
+
+		if (originContainer == null || !safeEIsSet(targetContainer, reference)
+				|| !safeEIsSet(originContainer, reference)) {
+			targetContainer.eUnset(reference);
+		} else {
+			final EObject originalValue = (EObject)originContainer.eGet(reference);
+			final Match valueMatch = match.getComparison().getMatch(originalValue);
+			final EObject expectedValue;
+			if (valueMatch == null) {
+				// Value is out of the scope, use it as-is
+				expectedValue = originalValue;
+			} else if (rightToLeft) {
+				expectedValue = valueMatch.getLeft();
+			} else {
+				expectedValue = valueMatch.getRight();
+			}
+			targetContainer.eSet(reference, expectedValue);
+		}
+	}
+
+	/**
+	 * Handles the equivalences of this difference.
+	 * <p>
+	 * Note that in certain cases, we'll merge our opposite instead of merging this diff. Specifically, we'll
+	 * do that for one-to-many eOpposites : we'll merge the 'many' side instead of the 'unique' one. This
+	 * allows us not to worry about the order of the references on that 'many' side.
+	 * </p>
+	 * <p>
+	 * This is called before the merge of <code>this</code>. In short, if this returns <code>false</code>, we
+	 * won't carry on merging <code>this</code> after returning.
+	 * </p>
+	 * 
+	 * @param diff
+	 *            The diff we are currently merging.
+	 * @param rightToLeft
+	 *            Direction of the merge.
+	 * @param monitor
+	 *            The monitor to use in order to report progress information.
+	 * @return <code>true</code> if the current difference should still be merged after handling its
+	 *         equivalences, <code>false</code> if it should be considered "already merged".
+	 */
+	protected boolean handleEquivalences(ReferenceChange diff, boolean rightToLeft, Monitor monitor) {
+		final EReference reference = diff.getReference();
+		boolean continueMerge = true;
+		for (Diff equivalent : diff.getEquivalence().getDifferences()) {
+			if (equivalent instanceof ReferenceChange
+					&& reference.getEOpposite() == ((ReferenceChange)equivalent).getReference()
+					&& equivalent.getState() == DifferenceState.UNRESOLVED) {
+				// This equivalence is on our eOpposite. Should we merge it instead of 'this'?
+				final boolean mergeEquivalence = !reference.isMany()
+						&& ((ReferenceChange)equivalent).getReference().isMany();
+				if (mergeEquivalence) {
+					mergeDiff(equivalent, rightToLeft, monitor);
+					continueMerge = false;
+				}
+			} else if (diff.getSource() == DifferenceSource.LEFT) {
+				// This can happen when merging subset/supersets... see AddInterfaceTest#testA50UseCase
+				/*
+				 * This should be removed (or we should make sure that we can never be here) when bug 398402
+				 * is fixed.
+				 */
+				if (rightToLeft && diff.getRequiredBy().contains(equivalent)) {
+					mergeDiff(equivalent, rightToLeft, monitor);
+					continueMerge = false;
+				} else if (!rightToLeft && diff.getRequires().contains(equivalent)) {
+					mergeDiff(equivalent, rightToLeft, monitor);
+					continueMerge = false;
+				}
+			} else if (diff.getSource() == DifferenceSource.RIGHT) {
+				// This can happen when merging subset/supersets... see AddInterfaceTest#testA50UseCase
+				/*
+				 * This should be removed (or we should make sure that we can never be here) when bug 398402
+				 * is fixed.
+				 */
+				if (rightToLeft && diff.getRequires().contains(equivalent)) {
+					mergeDiff(equivalent, rightToLeft, monitor);
+					continueMerge = false;
+				} else if (!rightToLeft && diff.getRequiredBy().contains(equivalent)) {
+					mergeDiff(equivalent, rightToLeft, monitor);
+					continueMerge = false;
+				}
+			}
+			equivalent.setState(DifferenceState.MERGED);
+		}
+		return continueMerge;
+	}
+
+	/**
+	 * Seeks a match of the given {@code element} in the given list, using the equality helper to find it.
+	 * This is only used when moving or deleting proxies for now.
+	 * 
+	 * @param comparison
+	 *            The comparison which Diff we are currently merging.
+	 * @param list
+	 *            The list from which we seek a value.
+	 * @param element
+	 *            The value for which we need a match in {@code list}.
+	 * @return The match of {@code element} in {@code list}, {@code null} if none.
+	 */
+	protected EObject findMatchIn(Comparison comparison, List<EObject> list, EObject element) {
+		final IEqualityHelper helper = comparison.getEqualityHelper();
+		final Iterator<EObject> it = list.iterator();
+		while (it.hasNext()) {
+			final EObject next = it.next();
+			if (helper.matchingValues(next, element)) {
+				return next;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * This will be used by the distinct merge actions in order to find the index at which a value should be
+	 * inserted in its target list. See {@link DiffUtil#findInsertionIndex(Comparison, Diff, boolean)} for
+	 * more on this.
+	 * <p>
+	 * Sub-classes can override this if the insertion order is irrelevant. A return value of {@code -1} will
+	 * be considered as "no index" and the value will be inserted at the end of its target list.
+	 * </p>
+	 * 
+	 * @param comparison
+	 *            This will be used in order to retrieve the Match for EObjects when comparing them.
+	 * @param diff
+	 *            The diff which merging will trigger the need for an insertion index in its target list.
+	 * @param rightToLeft
+	 *            {@code true} if the merging will be done into the left list, so that we should consider the
+	 *            right model as the source and the left as the target.
+	 * @return The index at which this {@code diff}'s value should be inserted into the 'target' list, as
+	 *         inferred from {@code rightToLeft}. {@code -1} if the value should be inserted at the end of its
+	 *         target list.
+	 * @see DiffUtil#findInsertionIndex(Comparison, Diff, boolean)
+	 */
+	protected int findInsertionIndex(Comparison comparison, Diff diff, boolean rightToLeft) {
+		return DiffUtil.findInsertionIndex(comparison, diff, rightToLeft);
+	}
+}