diff options
61 files changed, 5605 insertions, 1918 deletions
diff --git a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java index 90a86939c..8b5f08da8 100644 --- a/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.diagram.ide.ui/src/org/eclipse/emf/compare/diagram/ide/ui/internal/contentmergeviewer/diagram/DiagramContentMergeViewer.java @@ -27,7 +27,6 @@ import com.google.common.collect.Multimap; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.EventObject; import java.util.HashMap; import java.util.Iterator; @@ -1719,34 +1718,6 @@ public class DiagramContentMergeViewer extends EMFCompareContentMergeViewer { /** * {@inheritDoc} * - * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#copyDiff(boolean) - */ - @Override - protected void copyDiff(boolean leftToRight) { - /* - * FIXME change this! For the moment we always do a new setInput() on the content viewer whenever we - * select a Diagram Difference. This is meant to change so that we use selection synchronization - * instead. This code will break whenever we implement that change. - */ - if (fCurrentSelectedDiff != null) { - final Command command = getEditingDomain().createCopyCommand( - Collections.singletonList(fCurrentSelectedDiff), leftToRight, - EMFCompareRCPPlugin.getDefault().getMergerRegistry()); - getEditingDomain().getCommandStack().execute(command); - - if (leftToRight) { - setRightDirty(true); - } else { - setLeftDirty(true); - } - // refresh(); - } - - } - - /** - * {@inheritDoc} - * * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#getContents(boolean) */ @Override diff --git a/plugins/org.eclipse.emf.compare.edit/icons/full/ovr16/merged_left_ov.gif b/plugins/org.eclipse.emf.compare.edit/icons/full/ovr16/merged_left_ov.gif Binary files differnew file mode 100644 index 000000000..117e63d56 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.edit/icons/full/ovr16/merged_left_ov.gif diff --git a/plugins/org.eclipse.emf.compare.edit/icons/full/ovr16/merged_right_ov.gif b/plugins/org.eclipse.emf.compare.edit/icons/full/ovr16/merged_right_ov.gif Binary files differnew file mode 100644 index 000000000..26ebe0fe6 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.edit/icons/full/ovr16/merged_right_ov.gif 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 365932911..a73652b16 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,217 @@ -/*******************************************************************************
- * 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.IDiffMergeData; +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, IDiffMergeData.class); + if (adapter != null) { + IDiffMergeData iMergeData = (IDiffMergeData)adapter; + if (iMergeData.isLeftEditable() && !iMergeData.isRightEditable()) { + if (iMergeData.hasBeenMergedToLeft()) { + path += "removed_ov"; + } else { + path += "merged_ov"; + } + } else if (iMergeData.isLeftEditable() && iMergeData.isRightEditable()) { + if (iMergeData.hasBeenMergedToLeft()) { + path += "merged_left_ov"; + } else { + path += "merged_right_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/icons/full/toolb16/accept_all_changes.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/accept_all_changes.gif Binary files differnew file mode 100644 index 000000000..8456759aa --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/accept_all_changes.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/accept_change.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/accept_change.gif Binary files differnew file mode 100644 index 000000000..23c97f09e --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/accept_change.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/left_to_right.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/left_to_right.gif Binary files differnew file mode 100644 index 000000000..29189bbab --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/left_to_right.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_all_to_left.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_all_to_left.gif Binary files differnew file mode 100644 index 000000000..e029948cf --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_all_to_left.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_all_to_right.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_all_to_right.gif Binary files differnew file mode 100644 index 000000000..0659813a1 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_all_to_right.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_to_left.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_to_left.gif Binary files differnew file mode 100644 index 000000000..db2b1e3d5 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_to_left.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_to_right.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_to_right.gif Binary files differnew file mode 100644 index 000000000..9ebd50a9f --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/merge_to_right.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/next_diff.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/next_diff.gif Binary files differnew file mode 100644 index 000000000..79cda1359 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/next_diff.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/prev_diff.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/prev_diff.gif Binary files differnew file mode 100644 index 000000000..d5a96ec97 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/prev_diff.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/reject_all_changes.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/reject_all_changes.gif Binary files differnew file mode 100644 index 000000000..a873b3056 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/reject_all_changes.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/reject_change.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/reject_change.gif Binary files differnew file mode 100644 index 000000000..1aca259db --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/reject_change.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/right_to_left.gif b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/right_to_left.gif Binary files differnew file mode 100644 index 000000000..ce33df2a2 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/icons/full/toolb16/right_to_left.gif diff --git a/plugins/org.eclipse.emf.compare.ide.ui/plugin.properties b/plugins/org.eclipse.emf.compare.ide.ui/plugin.properties index c87ce892d..a9c88e240 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/plugin.properties +++ b/plugins/org.eclipse.emf.compare.ide.ui/plugin.properties @@ -14,3 +14,45 @@ providerName = Eclipse Modeling Project save.model.label = Save Comparison Model save.model.tooltip = Save Comparison Model save.model.command.name = EMF Compare Save Comparison Model + +next.diff = Next Difference +next.diff.tooltip = Next Difference +next.diff.command.name = EMF Compare Next Difference + +previous.diff = Previous Difference +previous.diff.tooltip = Previous Difference +previous.diff.command.name = EMF Compare Previous Difference + +accept.change = Accept Change +accept.change.tooltip = Accept Change +accept.change.command.name = EMF Compare Accept Change + +accept.all.changes = Accept All Non-Conflicting Changes +accept.all.changes.tooltip = Accept All Non-Conflicting Changes +accept.all.changes.command.name = EMF Compare Accept All Non-Conflicting Changes + +reject.change = Reject Change +reject.change.tooltip = Reject Change +reject.change.command.name = EMF Compare Reject Change + +reject.all.changes = Reject All Non-Conflicting Changes +reject.all.changes.tooltip = Reject All Non-Conflicting Changes +reject.all.changes.command.name = EMF Compare Reject All Non-Conflicting Changes + +merged.to.left = Copy Current Change To Left +merged.to.left.tooltip = Copy Current Change From Right To Left +merged.to.left.command.name = EMF Compare Copy Current Change To Left + +merged.to.right = Copy Current Change To Right +merged.to.right.tooltip = Copy Current Change From Left To Right +merged.to.right.command.name = EMF Compare Copy Current Change To Right + +merged.all.to.left = Copy All Non-Conflicting Changes To Left +merged.all.to.left.tooltip = Copy All Non-Conflicting Changes From Right To Left +merged.all.to.left.command.name = EMF Compare Copy All Non-Conflicting Changes To Left + +merged.all.to.right = Copy All Non-Conflicting Changes To Right +merged.all.to.right.tooltip = Copy All Non-Conflicting Changes From Left To Right +merged.all.to.right.command.name = EMF Compare Copy All Non-Conflicting Changes To Right + +dropdown.tooltip = Select the way of merge diff --git a/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml b/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml index ddc8431ab..124535c5e 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml +++ b/plugins/org.eclipse.emf.compare.ide.ui/plugin.xml @@ -106,13 +106,176 @@ allPopups="false" locationURI="toolbar:org.eclipse.emf.compare.structuremergeviewer.toolbar"> <command + commandId="org.eclipse.emf.compare.ide.ui.dropdown" + icon="icons/full/toolb16/left_to_right.gif" + id="org.eclipse.emf.compare.ide.ui.setdropdown" + style="pulldown" + tooltip="%dropdown.tooltip"> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.acceptChange" + icon="icons/full/toolb16/accept_change.gif" + label="%accept.change" + style="push" + tooltip="%accept.change.tooltip"> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <test + property="emfcompare.hasReadOnlySide"> + </test> + </with> + </visibleWhen> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.acceptAllChanges" + icon="icons/full/toolb16/accept_all_changes.gif" + label="%accept.all.changes" + style="push" + tooltip="%accept.all.changes.tooltip"> + <visibleWhen + checkEnabled="true"> + </visibleWhen> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.mergedToRight" + icon="icons/full/toolb16/merge_to_right.gif" + label="%merged.to.right" + style="push" + tooltip="%merged.to.right.tooltip"> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </visibleWhen> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.mergedAllToRight" + icon="icons/full/toolb16/merge_all_to_right.gif" + label="%merged.all.to.right" + style="push" + tooltip="%merged.all.to.right.tooltip"> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </visibleWhen> + </command> + <separator + name="org.eclipse.emf.compare.ide.ui.separatorLeftRight" + visible="true"> + </separator> + <command + commandId="org.eclipse.emf.compare.ide.ui.rejectChange" + icon="icons/full/toolb16/reject_change.gif" + label="%reject.change" + style="push" + tooltip="%reject.change.tooltip"> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <test + property="emfcompare.hasReadOnlySide"> + </test> + </with> + </visibleWhen> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.rejectAllChanges" + icon="icons/full/toolb16/reject_all_changes.gif" + label="%reject.all.changes" + style="push" + tooltip="%reject.all.changes.tooltip"> + <visibleWhen + checkEnabled="true"> + </visibleWhen> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.mergedToLeft" + icon="icons/full/toolb16/merge_to_left.gif" + label="%merged.to.left" + style="push" + tooltip="%merged.to.left.tooltip"> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </visibleWhen> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.mergedAllToLeft" + icon="icons/full/toolb16/merge_all_to_left.gif" + label="%merged.all.to.left" + style="push" + tooltip="%merged.all.to.left.tooltip"> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </visibleWhen> + </command> + <separator + name="org.eclipse.emf.compare.ide.ui.separatorNextPrevDiff" + visible="true"> + </separator> + <command + commandId="org.eclipse.emf.compare.ide.ui.nextDiff" + icon="icons/full/toolb16/next_diff.gif" + label="%next.diff" + style="push" + tooltip="%next.diff.tooltip"> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.previousDiff" + icon="icons/full/toolb16/prev_diff.gif" + label="%previous.diff" + style="push" + tooltip="%previous.diff.tooltip"> + </command> + <separator + name="org.eclipse.emf.compare.ide.ui.separatorSave1" + visible="true"> + </separator> + <command commandId="org.eclipse.emf.compare.ide.ui.saveComparisonModel" icon="icons/full/toolb16/saveas_edit.gif" - label="%save.model.name" + label="%save.model.label" style="push" tooltip="%save.model.tooltip"> </command> </menuContribution> + <menuContribution + allPopups="false" + locationURI="menu:org.eclipse.emf.compare.ide.ui.setdropdown"> + <command + commandId="org.eclipse.emf.compare.ide.ui.dropdown.ltr" + icon="icons/full/toolb16/left_to_right.gif" + style="push"> + </command> + <command + commandId="org.eclipse.emf.compare.ide.ui.dropdown.rtl" + icon="icons/full/toolb16/right_to_left.gif" + style="push"> + </command> + </menuContribution> </extension> <extension point="org.eclipse.ui.commands"> @@ -131,6 +294,58 @@ name="Each Other"> </command> <command + id="org.eclipse.emf.compare.ide.ui.dropdown" + name="org.eclipse.emf.compare.ide.ui.dropdown"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.dropdown.ltr" + name="Left to Right"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.dropdown.rtl" + name="Right to Left"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.acceptChange" + name="%accept.change.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.acceptAllChanges" + name="%accept.all.changes.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.rejectChange" + name="%reject.change.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.rejectAllChanges" + name="%reject.all.changes.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.mergedToRight" + name="%merged.to.right.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.mergedAllToRight" + name="%merged.all.to.right.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.mergedToLeft" + name="%merged.to.left.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.mergedAllToLeft" + name="%merged.all.to.left.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.nextDiff" + name="%next.diff.command.name"> + </command> + <command + id="org.eclipse.emf.compare.ide.ui.previousDiff" + name="%previous.diff.command.name"> + </command> + <command id="org.eclipse.emf.compare.ide.ui.saveComparisonModel" name="%save.model.command.name"> </command> @@ -216,6 +431,161 @@ </activeWhen> </handler> <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.AcceptChange" + commandId="org.eclipse.emf.compare.ide.ui.acceptChange"> + <activeWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.hasReadOnlySide"> + </test> + </with> + </activeWhen> + <enabledWhen> + <with + variable="activeEditor"> + <and> + <test + property="emfcompare.diffSelected"> + </test> + <test + property="emfcompare.leftToRightSide"> + </test> + </and> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.AcceptAllChanges" + commandId="org.eclipse.emf.compare.ide.ui.acceptAllChanges"> + <enabledWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.hasReadOnlySide"> + </test> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.RejectChange" + commandId="org.eclipse.emf.compare.ide.ui.rejectChange"> + <activeWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.hasReadOnlySide"> + </test> + </with> + </activeWhen> + <enabledWhen> + <with + variable="activeEditor"> + <and> + <test + property="emfcompare.diffSelected"> + </test> + <test + property="emfcompare.rightToLeftSide"> + </test> + </and> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.RejectAllChanges" + commandId="org.eclipse.emf.compare.ide.ui.rejectAllChanges"> + <enabledWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.hasReadOnlySide"> + </test> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.MergedToRight" + commandId="org.eclipse.emf.compare.ide.ui.mergedToRight"> + <activeWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </activeWhen> + <enabledWhen> + <with + variable="activeEditor"> + <and> + <test + property="emfcompare.diffSelected"> + </test> + <test + property="emfcompare.leftToRightSide"> + </test> + </and> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.MergedAllToRight" + commandId="org.eclipse.emf.compare.ide.ui.mergedAllToRight"> + <activeWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </activeWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.MergedToLeft" + commandId="org.eclipse.emf.compare.ide.ui.mergedToLeft"> + <activeWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with> + </activeWhen> + <enabledWhen> + <with + variable="activeEditor"> + <and> + <test + property="emfcompare.diffSelected"> + </test> + <test + property="emfcompare.rightToLeftSide"> + </test> + </and> + </with> + </enabledWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.MergedAllToLeft" + commandId="org.eclipse.emf.compare.ide.ui.mergedAllToLeft"> + <activeWhen> + <with + variable="activeEditor"> + <test + property="emfcompare.bothSidesWriteable"> + </test> + </with></activeWhen> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.SelectNextDiff" + commandId="org.eclipse.emf.compare.ide.ui.nextDiff"> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.SelectPreviousDiff" + commandId="org.eclipse.emf.compare.ide.ui.previousDiff"> + </handler> + <handler class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.SaveComparisonModel" commandId="org.eclipse.emf.compare.ide.ui.saveComparisonModel"> <enabledWhen> @@ -228,6 +598,18 @@ </enabledWhen> </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.DropDownHandler" + commandId="org.eclipse.emf.compare.ide.ui.dropdown"> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.DropDownLeftToRight" + commandId="org.eclipse.emf.compare.ide.ui.dropdown.ltr"> + </handler> + <handler + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.DropDownRightToLeft" + commandId="org.eclipse.emf.compare.ide.ui.dropdown.rtl"> + </handler> </extension> <extension id="org.eclipse.emf.compare.model.provider" @@ -259,12 +641,47 @@ <extension point="org.eclipse.core.expressions.propertyTesters"> <propertyTester - class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.ModelSaveablePropertyTester" + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester.ModelSaveablePropertyTester" id="org.eclipse.emf.compare.ide.ui.modelSaveable" namespace="emfcompare" properties="isSaveable" type="java.lang.Object"> </propertyTester> + <propertyTester + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester.AcceptRejectChangePropertyTester" + id="org.eclipse.emf.compare.ide.ui.hasReadOnlySide" + namespace="emfcompare" + properties="hasReadOnlySide" + type="java.lang.Object"> + </propertyTester> + <propertyTester + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester.MergedToPropertyTester" + id="org.eclipse.emf.compare.ide.ui.bothSidesWriteable" + namespace="emfcompare" + properties="bothSidesWriteable" + type="java.lang.Object"> + </propertyTester> + <propertyTester + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester.DiffSelectedPropertyTester" + id="org.eclipse.emf.compare.ide.ui.diffSelected" + namespace="emfcompare" + properties="diffSelected" + type="java.lang.Object"> + </propertyTester> + <propertyTester + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester.LeftToRightSidePropertyTester" + id="org.eclipse.emf.compare.ide.ui.leftToRightSide" + namespace="emfcompare" + properties="leftToRightSide" + type="java.lang.Object"> + </propertyTester> + <propertyTester + class="org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester.RightToLeftSidePropertyTester" + id="org.eclipse.emf.compare.ide.ui.rightToLeftSide" + namespace="emfcompare" + properties="rightToLeftSide" + type="java.lang.Object"> + </propertyTester> </extension> <extension point="org.eclipse.emf.compare.ide.ui.modelResolvers"> diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java index 03c80982f..c3df55225 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/EMFCompareIDEUIPlugin.java @@ -10,6 +10,10 @@ *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; @@ -18,6 +22,8 @@ import org.eclipse.emf.compare.ide.ui.internal.logical.IModelResolverRegistry; import org.eclipse.emf.compare.ide.ui.internal.logical.ModelResolverRegistryImpl; import org.eclipse.emf.compare.ide.ui.internal.logical.ModelResolverRegistryListener; import org.eclipse.emf.compare.rcp.extension.AbstractRegistryEventListener; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; @@ -42,6 +48,9 @@ public class EMFCompareIDEUIPlugin extends AbstractUIPlugin { /** Registry of model resolvers. */ private IModelResolverRegistry modelResolverRegistry; + /** keep track of resources that should be freed when exiting. */ + private static Map<String, Image> resourcesMapper = new HashMap<String, Image>(); + /** Default constructor. */ public EMFCompareIDEUIPlugin() { // Empty constructor @@ -90,6 +99,62 @@ public class EMFCompareIDEUIPlugin extends AbstractUIPlugin { } /** + * <p> + * returns a plugin image. The returned image does not need to be explicitly disposed. + * </p> + * + * @param imagePath + * : plugin relative path to the image + * @return Image : plugin hosted image + */ + public static Image getImage(String imagePath) { + Image image = resourcesMapper.get(imagePath); + if (image == null) { + ImageDescriptor imageDescriptor = imageDescriptorFromPlugin(PLUGIN_ID, imagePath); + image = imageDescriptor.createImage(); + resourcesMapper.put(imagePath, image); + } + return image; + } + + /** + * <p> + * returns a plugin image descriptor. + * </p> + * + * @param imagePath + * : plugin relative path to the image + * @return ImageDescriptor : image descriptor. + */ + public static ImageDescriptor getImageDescriptor(String imagePath) { + return imageDescriptorFromPlugin(PLUGIN_ID, imagePath); + } + + /** + * Dispose image with the given id. + * + * @param id + * : dispose system resources associated with the image with the given id. + */ + public static void disposeImage(String id) { + Image image = resourcesMapper.remove(id); + if (image != null) { + image.dispose(); + } + } + + /** + * dispose system resources associated with cached images. + */ + public static void disposeCachedImages() { + Iterator<Image> iterator = resourcesMapper.values().iterator(); + while (iterator.hasNext()) { + iterator.next().dispose(); + } + resourcesMapper.clear(); + } + + /** * Returns the registry containing all known model resolvers. * * @return The registry containing all known model resolvers. diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/actions/expand/ExpandAllModelAction.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/actions/expand/ExpandAllModelAction.java index ea3ba9847..a36941860 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/actions/expand/ExpandAllModelAction.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/actions/expand/ExpandAllModelAction.java @@ -37,5 +37,7 @@ public class ExpandAllModelAction extends Action { @Override public void run() { treeViewer.expandToLevel(256); + // Workaround to force the redraw of the EMFCompareDiffTreeruler + treeViewer.setSelection(treeViewer.getSelection()); } } diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java index 28deda794..0ab47750e 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/EMFCompareContentMergeViewer.java @@ -23,11 +23,8 @@ import java.util.ResourceBundle; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.compare.CompareConfiguration; -import org.eclipse.compare.CompareNavigator; -import org.eclipse.compare.ICompareNavigator; import org.eclipse.compare.contentmergeviewer.ContentMergeViewer; import org.eclipse.compare.internal.CompareHandlerService; -import org.eclipse.compare.internal.Utilities; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CommandStackListener; @@ -51,8 +48,6 @@ import org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.IMergeViewerItem 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.EMFComparePredicates; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; @@ -91,10 +86,6 @@ public abstract class EMFCompareContentMergeViewer extends ContentMergeViewer im private IMergeViewer fRight; - private ActionContributionItem fCopyDiffLeftToRightItem; - - private ActionContributionItem fCopyDiffRightToLeftItem; - private final AtomicBoolean fSyncingSelections = new AtomicBoolean(false); private EMFCompareColor fColors; @@ -292,66 +283,6 @@ public abstract class EMFCompareContentMergeViewer extends ContentMergeViewer im "toolbar:org.eclipse.emf.compare.contentmergeviewer.toolbar"); } - // Copy actions - CompareConfiguration cc = getCompareConfiguration(); - - if (cc.isRightEditable()) { - Action copyLeftToRight = new Action() { - @Override - public void run() { - copyDiff(true); - // Select next diff - navigate(true); - } - }; - Utilities.initAction(copyLeftToRight, getResourceBundle(), "action.CopyDiffLeftToRight."); //$NON-NLS-1$ - copyLeftToRight.setEnabled(false); - fCopyDiffLeftToRightItem = new ActionContributionItem(copyLeftToRight); - fCopyDiffLeftToRightItem.setVisible(true); - toolBarManager.appendToGroup("merge", fCopyDiffLeftToRightItem); //$NON-NLS-1$ - getHandlerService().registerAction(copyLeftToRight, "org.eclipse.compare.copyLeftToRight"); //$NON-NLS-1$ - } - - if (cc.isLeftEditable()) { - Action copyRightToLeft = new Action() { - @Override - public void run() { - copyDiff(false); - // Select next diff - navigate(true); - } - }; - Utilities.initAction(copyRightToLeft, getResourceBundle(), "action.CopyDiffRightToLeft."); //$NON-NLS-1$ - copyRightToLeft.setEnabled(false); - fCopyDiffRightToLeftItem = new ActionContributionItem(copyRightToLeft); - fCopyDiffRightToLeftItem.setVisible(true); - toolBarManager.appendToGroup("merge", fCopyDiffRightToLeftItem); //$NON-NLS-1$ - getHandlerService().registerAction(copyRightToLeft, "org.eclipse.compare.copyRightToLeft"); //$NON-NLS-1$ - } - - // Navigation - final Action nextDiff = new Action() { - @Override - public void run() { - navigate(true); - } - }; - Utilities.initAction(nextDiff, getResourceBundle(), "action.NextDiff."); - ActionContributionItem contributionNextDiff = new ActionContributionItem(nextDiff); - contributionNextDiff.setVisible(true); - toolBarManager.appendToGroup("navigation", contributionNextDiff); - - final Action previousDiff = new Action() { - @Override - public void run() { - navigate(false); - } - }; - Utilities.initAction(previousDiff, getResourceBundle(), "action.PrevDiff."); - ActionContributionItem contributionPreviousDiff = new ActionContributionItem(previousDiff); - contributionPreviousDiff.setVisible(true); - toolBarManager.appendToGroup("navigation", contributionPreviousDiff); - undoAction = new UndoAction(getEditingDomain()); redoAction = new RedoAction(getEditingDomain()); @@ -416,29 +347,6 @@ public abstract class EMFCompareContentMergeViewer extends ContentMergeViewer im } /** - * 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 = getCompareConfiguration().getContainer().getNavigator(); - if (navigator instanceof CompareNavigator && ((CompareNavigator)navigator).hasChange(next)) { - navigator.selectChange(next); - } - } - } - - /** - * - */ - protected abstract void copyDiff(boolean leftToRight); - - /** * {@inheritDoc} * * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#handleResizeAncestor(int, int, int, int) @@ -568,36 +476,6 @@ public abstract class EMFCompareContentMergeViewer extends ContentMergeViewer im } /** - * {@inheritDoc} - * - * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#updateToolItems() - */ - @Override - protected void updateToolItems() { - super.updateToolItems(); - - manageCopyActionsActivation(); - } - - protected void manageCopyActionsActivation() { - Diff diff = getDiffFrom(getRightMergeViewer()); - if (diff == null) { - diff = getDiffFrom(getLeftMergeViewer()); - } - boolean enableCopy = false; - if (diff != null) { - enableCopy = diff.getState() == DifferenceState.UNRESOLVED; - } - - if (fCopyDiffLeftToRightItem != null) { - fCopyDiffLeftToRightItem.getAction().setEnabled(enableCopy); - } - if (fCopyDiffRightToLeftItem != null) { - fCopyDiffRightToLeftItem.getAction().setEnabled(enableCopy); - } - } - - /** * Checks the element selected in the given viewer in order to determine whether it can be adapted into a * Diff. * diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/table/TableContentMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/table/TableContentMergeViewer.java index 68a7c512a..45ef92e57 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/table/TableContentMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/table/TableContentMergeViewer.java @@ -10,14 +10,9 @@ *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.table; -import static com.google.common.collect.Iterables.addAll; - -import java.util.ArrayList; -import java.util.List; import java.util.ResourceBundle; import org.eclipse.compare.CompareConfiguration; -import org.eclipse.emf.common.command.Command; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer; import org.eclipse.emf.compare.internal.utils.ComparisonUtil; @@ -35,9 +30,7 @@ import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jface.viewers.ArrayContentProvider; -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.swt.SWT; import org.eclipse.swt.events.DisposeEvent; @@ -109,40 +102,6 @@ public class TableContentMergeViewer extends EMFCompareContentMergeViewer { /** * {@inheritDoc} * - * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#copyDiff(boolean) - */ - @Override - protected void copyDiff(boolean leftToRight) { - final Diff diffToCopy = getDiffToCopy(getRightMergeViewer()); - if (diffToCopy != null) { - List<Diff> diffsToCopy = new ArrayList<Diff>(); - diffsToCopy.add(diffToCopy); - if (isSubDiffFilterActive()) { - addAll(diffsToCopy, ComparisonUtil.getSubDiffs(leftToRight).apply(diffToCopy)); - } - Command copyCommand = getEditingDomain().createCopyCommand(diffsToCopy, leftToRight, - EMFCompareRCPPlugin.getDefault().getMergerRegistry()); - - getEditingDomain().getCommandStack().execute(copyCommand); - refresh(); - } - } - - private Diff getDiffToCopy(AbstractMergeViewer abstractMergeViewer) { - Diff diffToCopy = null; - ISelection selection = abstractMergeViewer.getSelection(); - if (selection instanceof IStructuredSelection && !selection.isEmpty()) { - Object firstElement = ((IStructuredSelection)selection).getFirstElement(); - if (firstElement instanceof IMergeViewerItem) { - diffToCopy = ((IMergeViewerItem)firstElement).getDiff(); - } - } - return diffToCopy; - } - - /** - * {@inheritDoc} - * * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#getLeftMergeViewer() */ @Override diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/tree/TreeContentMergeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/tree/TreeContentMergeViewer.java index d3f6cd792..db0483d60 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/tree/TreeContentMergeViewer.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/contentmergeviewer/tree/TreeContentMergeViewer.java @@ -11,20 +11,17 @@ package org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.tree; import static com.google.common.base.Predicates.equalTo; -import static com.google.common.collect.Iterables.addAll; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Lists.newArrayList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; -import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.compare.CompareConfiguration; -import org.eclipse.emf.common.command.Command; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.DifferenceKind; import org.eclipse.emf.compare.Match; @@ -50,9 +47,7 @@ import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.ILabelProvider; -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.swt.SWT; import org.eclipse.swt.events.DisposeEvent; @@ -167,45 +162,6 @@ public class TreeContentMergeViewer extends EMFCompareContentMergeViewer { /** * {@inheritDoc} * - * @see org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.EMFCompareContentMergeViewer#copyDiff(boolean) - */ - @Override - protected void copyDiff(boolean leftToRight) { - final Diff diffToCopy; - if (leftToRight) { - diffToCopy = getDiffToCopy(getLeftMergeViewer()); - } else { - diffToCopy = getDiffToCopy(getRightMergeViewer()); - } - if (diffToCopy != null) { - List<Diff> diffsToCopy = new ArrayList<Diff>(); - diffsToCopy.add(diffToCopy); - if (isSubDiffFilterActive()) { - addAll(diffsToCopy, ComparisonUtil.getSubDiffs(leftToRight).apply(diffToCopy)); - } - Command copyCommand = getEditingDomain().createCopyCommand(diffsToCopy, leftToRight, - EMFCompareRCPPlugin.getDefault().getMergerRegistry()); - - getEditingDomain().getCommandStack().execute(copyCommand); - refresh(); - } - } - - private Diff getDiffToCopy(AbstractMergeViewer abstractMergeViewer) { - Diff diffToCopy = null; - ISelection selection = abstractMergeViewer.getSelection(); - if (selection instanceof IStructuredSelection && !selection.isEmpty()) { - Object firstElement = ((IStructuredSelection)selection).getFirstElement(); - if (firstElement instanceof IMergeViewerItem) { - diffToCopy = ((IMergeViewerItem)firstElement).getDiff(); - } - } - return diffToCopy; - } - - /** - * {@inheritDoc} - * * @see org.eclipse.compare.contentmergeviewer.ContentMergeViewer#getContents(boolean) */ @Override diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/AbstractViewerWrapper.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/AbstractViewerWrapper.java new file mode 100644 index 000000000..41b62f00b --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/AbstractViewerWrapper.java @@ -0,0 +1,369 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer; + +import java.util.List; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.IOpenListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; + +/** + * A control that wrapped a StructuredViewer and another control. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public abstract class AbstractViewerWrapper extends StructuredViewer { + + /** The StructuredViewer associated with this wrapper. */ + private StructuredViewer fViewer; + + /** A composite control that will contains all sub-control of this wrapper. */ + private final Control fControl; + + /** The selection changed listener. */ + private ISelectionChangedListener fWrappedViewerListener; + + /** The compare configuration object. */ + private final CompareConfiguration fConfiguration; + + /** + * Constructor. + * + * @param parent + * the SWT parent control under which to create the viewer's SWT control. + * @param config + * a compare configuration the newly created viewer might want to use. + */ + public AbstractViewerWrapper(Composite parent, CompareConfiguration config) { + fConfiguration = config; + fControl = createControl(parent, config); + hookControl(fControl); + fWrappedViewerListener = new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + fireSelectionChanged(new SelectionChangedEvent(AbstractViewerWrapper.this, event + .getSelection())); + } + }; + fViewer.addSelectionChangedListener(fWrappedViewerListener); + + setLabelProvider(fViewer.getLabelProvider()); + setContentProvider(fViewer.getContentProvider()); + } + + /** + * Should call {@link #setViewer(org.eclipse.jface.viewers.Viewer)}. + * + * @param parent + * the SWT control under which to create the viewer. + * @param config + * the compare configuration object. + * @return a composite control that will contains all sub-control of this wrapper. + */ + protected abstract Control createControl(Composite parent, CompareConfiguration config); + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#addOpenListener(IOpenListener) + */ + @Override + public void addOpenListener(IOpenListener listener) { + fViewer.addOpenListener(listener); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#removeOpenListener(IOpenListener) + */ + @Override + public void removeOpenListener(IOpenListener listener) { + fViewer.removeOpenListener(listener); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#update(Object, String[]) + */ + @Override + public void update(Object element, String[] properties) { + fViewer.update(element, properties); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.Viewer#getControl() + */ + @Override + public Control getControl() { + return fControl; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ContentViewer#getInput() + */ + @Override + public Object getInput() { + return super.getInput(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#getSelection() + */ + @Override + public ISelection getSelection() { + return fViewer.getSelection(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#refresh() + */ + @Override + public void refresh() { + fViewer.refresh(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#refresh(boolean) + */ + @Override + public void refresh(boolean updateLabels) { + fViewer.refresh(updateLabels); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#setSelection(ISelection, boolean) + */ + @Override + public void setSelection(ISelection selection, boolean reveal) { + fViewer.setSelection(selection, reveal); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#reveal(Object) + */ + @Override + public void reveal(Object element) { + fViewer.reveal(element); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ContentViewer#getContentProvider() + */ + @Override + public IContentProvider getContentProvider() { + return super.getContentProvider(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ContentViewer#setContentProvider(IContentProvider) + */ + @Override + public void setContentProvider(IContentProvider provider) { + super.setContentProvider(provider); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ContentViewer#getLabelProvider() + */ + @Override + public IBaseLabelProvider getLabelProvider() { + return super.getLabelProvider(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ContentViewer#setLabelProvider(IBaseLabelProvider) + */ + @Override + public void setLabelProvider(IBaseLabelProvider labelProvider) { + super.setLabelProvider(labelProvider); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(Object) + */ + @Override + protected Widget doFindInputItem(Object element) { + /* + * Nothing to do here. The method doFindInputItem(Object element) is only called by + * StructuredViewer#update(Object element, String[] properties), which is override in this class. + */ + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(Object) + */ + @Override + protected Widget doFindItem(Object element) { + /* + * Nothing to do here. The method doFindItem(Object element) is only called by + * StructuredViewer#update(Object element, String[] properties), which is override in this class. + */ + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(Widget, Object, boolean) + */ + @Override + protected void doUpdateItem(Widget item, Object element, boolean fullMap) { + /* + * Nothing to do here. The method doUpdateItem(Widget item, Object element, boolean fullMap) is only + * called by StructuredViewer#update(Object element, String[] properties), which is override in this + * class. + */ + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget() + */ + @Override + protected List getSelectionFromWidget() { + /* + * Nothing to do here. The method getSelectionFromWidget() is only called by + * StructuredViewer#getSelection(), which is override in this class. + */ + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(Object) + */ + @Override + protected void internalRefresh(Object element) { + /* + * Nothing to do here. The method internalRefresh(Object element) is only called by + * StructuredViewer#internalRefresh(Object element, boolean updateLabels), which is override in + * AbstractTreeViewer. + */ + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(List, boolean) + */ + @Override + protected void setSelectionToWidget(List l, boolean reveal) { + /* + * Nothing to do here. The method setSelectionToWidget(List l, boolean reveal) is only called by + * StructuredViewer#setSelectionToWidget(ISelection selection, boolean reveal), which is override in + * AbstractTreeViewer. + */ + } + + /** + * Adds event listener hooks to the given control. + * <p> + * All subclasses must call this method when their control is first established. + * </p> + * <p> + * The <code>ContentViewer</code> implementation of this method hooks dispose events for the given + * control. Subclasses may override if they need to add other control hooks; however, + * <code>super.hookControl</code> must be invoked. + * </p> + * + * @param control + * the control + */ + @Override + protected void hookControl(Control control) { + control.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent event) { + handleDispose(event); + } + }); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#handleDispose(DisposeEvent) + */ + @Override + protected void handleDispose(DisposeEvent event) { + fViewer.removeSelectionChangedListener(fWrappedViewerListener); + } + + /** + * Returns the {@link StructuredViewer} associated with this wrapper. + * + * @return a StructuredViewer. + */ + protected StructuredViewer getViewer() { + return fViewer; + } + + /** + * Set the {@link StructuredViewer} of this wrapper. + * + * @param fViewer + * a StructuredViewer. + */ + protected void setViewer(StructuredViewer viewer) { + fViewer = viewer; + } + + /** + * Get the compare configuration object. + * + * @return the compare configuration object. + */ + public CompareConfiguration getCompareConfiguration() { + return fConfiguration; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java new file mode 100644 index 000000000..7cefbdb91 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeRuler.java @@ -0,0 +1,631 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.core.runtime.Assert; +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.internal.utils.DiffUtil; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.IDifferenceFilter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +/** + * A specific canvas that must be presented next to a TreeViewer. It shows consequences of a Diff (required + * and unmergeable differences), as in the TreeViewer, but in a compact format (small colored rectangles) and + * with links to respectives Treeitems. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public class EMFCompareDiffTreeRuler extends Canvas { + + /** The vertical offset for an annotation. */ + private static final int Y_OFFSET = 6; + + /** The height of an annotation. */ + private static final int ANNOTATION_HEIGHT = 5; + + /** The TreeViewer associated with this Treeruler. */ + private final TreeViewer fTreeViewer; + + /** The color a required diff. */ + private final Color requiredDiffFillColor; + + /** The color of an unmergeable diff. **/ + private final Color unmergeableDiffFillColor; + + /** The border color a required diff. */ + private final Color requiredDiffBorderColor; + + /** The border color an unmergeable diff. */ + private final Color unmergeableDiffBorderColor; + + /** The width of this tree ruler. */ + private final int fWidth; + + /** The list of required Diff that need to be shown in the TreeRuler. */ + private Set<Diff> requires; + + /** The list of unmergeables Diff that need to be shown in the TreeRuler. */ + private Set<Diff> unmergeables; + + /** A map that links a diff with tree items. */ + private Multimap<Diff, TreeItem> diffItems; + + /** A map that links a rectangle with a tree item. */ + private Map<Rectangle, TreeItem> annotationsData; + + /** The paint listener. */ + private PaintListener paintListener; + + /** The mouse click listener. */ + private MouseListener mouseClickListener; + + /** The mouse move listener. */ + private MouseMoveListener mouseMoveListener; + + /** The mouse track listener. */ + private MouseTrackListener mouseTrackListener; + + /** The last cursor used. */ + private Cursor lastCursor; + + /** The configuration for this control. */ + private CompareConfiguration fConfiguration; + + /** The selected diff in the Treeviewer associated with this Treeruler. */ + private Diff selectedDiff; + + /** + * Constructor. + * + * @param parent + * the control's parent. + * @param style + * the style of the control to construct. + * @param width + * the control's width. + * @param treeViewer + * the TreeViewer associated with this control. + * @param config + * the configuration for this control. + */ + EMFCompareDiffTreeRuler(Composite parent, int style, int width, TreeViewer treeViewer, + CompareConfiguration config) { + super(parent, style); + fWidth = width; + fTreeViewer = treeViewer; + fConfiguration = config; + + requiredDiffFillColor = JFaceResources.getColorRegistry().get( + EMFCompareDiffTreeViewer.REQUIRED_DIFF_COLOR); + requiredDiffBorderColor = JFaceResources.getColorRegistry().get( + EMFCompareDiffTreeViewer.REQUIRED_DIFF_BORDER_COLOR); + unmergeableDiffFillColor = JFaceResources.getColorRegistry().get( + EMFCompareDiffTreeViewer.UNMERGEABLE_DIFF_COLOR); + unmergeableDiffBorderColor = JFaceResources.getColorRegistry().get( + EMFCompareDiffTreeViewer.UNMERGEABLE_DIFF_BORDER_COLOR); + + requires = Sets.newHashSet(); + unmergeables = Sets.newHashSet(); + diffItems = HashMultimap.create(); + annotationsData = Maps.newHashMap(); + + paintListener = new PaintListener() { + public void paintControl(PaintEvent e) { + handlePaintEvent(e); + } + }; + addPaintListener(paintListener); + + mouseClickListener = new MouseListener() { + + public void mouseUp(MouseEvent e) { + handleMouseClickEvent(e); + } + + public void mouseDown(MouseEvent e) { + // Do nothing. + } + + public void mouseDoubleClick(MouseEvent e) { + // Do nothing. + } + }; + addMouseListener(mouseClickListener); + + mouseMoveListener = new MouseMoveListener() { + + public void mouseMove(MouseEvent e) { + handleMouveMoveEvent(e); + } + }; + addMouseMoveListener(mouseMoveListener); + + mouseTrackListener = new MouseTrackListener() { + + public void mouseHover(MouseEvent e) { + handleMouseHoverEvent(e); + } + + public void mouseExit(MouseEvent e) { + // Do nothing. + } + + public void mouseEnter(MouseEvent e) { + // Do nothing. + } + }; + addMouseTrackListener(mouseTrackListener); + } + + /** + * Compute consequences (required and unmergeable differences) when selection changed occurs. + * + * @param event + * the SelectionChangedEvent event. + */ + public void selectionChanged(SelectionChangedEvent event) { + clearAllData(); + ISelection selection = event.getSelection(); + if (selection instanceof IStructuredSelection) { + Object element = ((IStructuredSelection)selection).getFirstElement(); + if (element instanceof Adapter) { + Object target = ((Adapter)element).getTarget(); + if (target instanceof Diff) { + selectedDiff = (Diff)target; + computeConsequences(); + } + } + } + } + + /** + * Compute consequences (required and unmergeable differences). + */ + public void computeConsequences() { + clearAllData(); + if (selectedDiff != null) { + Boolean leftToRight = (Boolean)fConfiguration.getProperty(EMFCompareConstants.MERGE_WAY); + boolean ltr = false; + if (leftToRight == null || leftToRight.booleanValue()) { + ltr = true; + } + boolean leftEditable = fConfiguration.isLeftEditable(); + boolean rightEditable = fConfiguration.isRightEditable(); + boolean bothSidesEditable = leftEditable && rightEditable; + if ((ltr && (leftEditable || bothSidesEditable)) || (!ltr && (rightEditable && !leftEditable))) { + requires = DiffUtil.getRequires(selectedDiff, true, selectedDiff.getSource()); + unmergeables = DiffUtil.getUnmergeables(selectedDiff, true); + } else { + requires = DiffUtil.getRequires(selectedDiff, false, selectedDiff.getSource()); + unmergeables = DiffUtil.getUnmergeables(selectedDiff, false); + } + associateTreeItems(Lists.newLinkedList(Iterables.concat(requires, unmergeables))); + } + } + + /** + * Maps tree items with the given list of diffs. + * + * @param diffs + * the given list of diffs. + */ + private void associateTreeItems(List<Diff> diffs) { + Tree tree = fTreeViewer.getTree(); + for (TreeItem item : tree.getItems()) { + associateTreeItem(item, diffs); + } + } + + /** + * Maps, if necessary, the given tree item and all his children with the given list of diffs. + * + * @param item + * the given tree item. + * @param diffs + * the given list of diffs. + */ + private void associateTreeItem(TreeItem item, List<Diff> diffs) { + Object data = item.getData(); + if (data instanceof Adapter) { + Notifier target = ((Adapter)data).getTarget(); + if (diffs.contains(target)) { + diffItems.put((Diff)target, item); + } + } + for (TreeItem child : item.getItems()) { + associateTreeItem(child, diffs); + } + } + + /** + * Clear all data. + */ + private void clearAllData() { + requires.clear(); + unmergeables.clear(); + diffItems.clear(); + annotationsData.clear(); + } + + /** + * Handles the dispose event on this control. + */ + public void handleDispose() { + removeMouseTrackListener(mouseTrackListener); + removeMouseMoveListener(mouseMoveListener); + removeMouseListener(mouseClickListener); + removePaintListener(paintListener); + } + + /** + * Handles the paint event. + * + * @param e + * the paint event. + */ + private void handlePaintEvent(PaintEvent e) { + annotationsData.clear(); + Collection<IDifferenceFilter> filters = (Collection<IDifferenceFilter>)fConfiguration + .getProperty(EMFCompareConstants.SELECTED_FILTERS); + Collection<? extends Diff> filteredRequires = filteredDiffs(requires, filters); + Collection<? extends Diff> filteredUnmergeables = filteredDiffs(unmergeables, filters); + for (Diff diff : filteredRequires) { + for (TreeItem item : diffItems.get(diff)) { + createAnnotation(e, diff, item, requiredDiffFillColor, requiredDiffBorderColor); + } + } + for (Diff diff : filteredUnmergeables) { + for (TreeItem item : diffItems.get(diff)) { + createAnnotation(e, diff, item, unmergeableDiffFillColor, unmergeableDiffBorderColor); + } + } + } + + /** + * Handles the mouse click event. + * + * @param e + * the mouse click event. + */ + private void handleMouseClickEvent(MouseEvent e) { + for (Rectangle rect : annotationsData.keySet()) { + if (e.y >= rect.y && e.y <= rect.y + ANNOTATION_HEIGHT) { + TreeItem item = annotationsData.get(rect); + TreePath treePath = getTreePathFromItem(item); + fTreeViewer.expandToLevel(treePath, 0); + fTreeViewer.reveal(treePath); + if (isVerticalScrollBarEnabled()) { + TreeItem previousItem = getPreviousItem(item, 2); + fTreeViewer.getTree().setTopItem(previousItem); + } + redraw(); + return; + } + } + } + + /** + * Handles the mouse move event. + * + * @param e + * the mouse move event. + */ + private void handleMouveMoveEvent(MouseEvent e) { + Cursor cursor = null; + for (Rectangle rect : annotationsData.keySet()) { + if (e.y >= rect.y && e.y <= rect.y + ANNOTATION_HEIGHT) { + cursor = e.display.getSystemCursor(SWT.CURSOR_HAND); + break; + } + } + if (cursor != lastCursor) { + setCursor(cursor); + lastCursor = cursor; + } + } + + /** + * Handles the mouse hover event. + * + * @param e + * the mouve hover event. + */ + private void handleMouseHoverEvent(MouseEvent e) { + String overview = ""; //$NON-NLS-1$ + for (Rectangle rect : annotationsData.keySet()) { + if (e.y >= rect.y && e.y <= rect.y + ANNOTATION_HEIGHT) { + TreeItem item = annotationsData.get(rect); + overview = item.getText(); + break; + } + } + setToolTipText(overview); + } + + /** + * Create an annotation in the tree ruler. + * + * @param e + * the PaintEvent. + * @param diff + * the Diff for which we want to create the annotation. + * @param treeItem + * the tree item associated with the diff. + * @param fill + * the annotation's fill color. + * @param border + * the annotation's border color. + */ + private void createAnnotation(PaintEvent e, Diff diff, TreeItem treeItem, Color fill, Color border) { + TreeItem item = getDeepestVisibleTreeItem(treeItem, treeItem); + if (item != null) { + int y = item.getBounds().y; + int yRuler = getSize().y; + if (isVerticalScrollBarEnabled()) { + int yMin = Math.abs(item.getParent().getItems()[0].getBounds().y); + int yMax = getLastVisibleItem().getBounds().y; + int realYMax = yMax + yMin; + y = (y + yMin) * yRuler / realYMax; + if (y + Y_OFFSET + ANNOTATION_HEIGHT > yRuler) { + y = yRuler - Y_OFFSET - ANNOTATION_HEIGHT; + } + } + Rectangle rect = drawAnnotation(e.gc, 2, y + Y_OFFSET, fWidth - 5, ANNOTATION_HEIGHT, fill, + border); + annotationsData.put(rect, treeItem); + } + } + + /** + * Returns the full tree path of the given tree item. + * + * @param item + * the given tree item. + * @return the full tree path of the given tree item. + */ + private TreePath getTreePathFromItem(TreeItem item) { + LinkedList<Object> segments = Lists.newLinkedList(); + TreeItem parent = item; + while (parent != null) { + Object segment = parent.getData(); + Assert.isNotNull(segment); + segments.addFirst(segment); + parent = parent.getParentItem(); + } + return new TreePath(segments.toArray()); + } + + /** + * Checks if the vertical scroll bar of the tree viewer associated with this tree ruler is activated and + * enabled. + * + * @return true if the vertical scroll bar is activated and enabled, false otherwise. + */ + private boolean isVerticalScrollBarEnabled() { + ScrollBar verticalBar = fTreeViewer.getTree().getVerticalBar(); + if (verticalBar != null) { + return verticalBar.isVisible() && verticalBar.isEnabled(); + } + return false; + } + + /** + * Draw an annotation (a Rectangle) on this tree ruler. + * + * @param gc + * the swt GC. + * @param x + * the x coordinate of the origin of the annotation. + * @param y + * the y coordinate of the origin of the annotation. + * @param w + * the width of the annotation. + * @param h + * the height of the annotation. + * @param fill + * the annotation's fill color. + * @param border + * the annotation's border color. + * @return the annotation (a Rectangle). + */ + private Rectangle drawAnnotation(GC gc, int x, int y, int w, int h, Color fill, Color border) { + Rectangle rect = new Rectangle(x, y, w, h); + gc.setBackground(fill); + gc.fillRectangle(rect); + + gc.setForeground(border); + gc.drawRectangle(x, y, w, h); + return rect; + } + + /** + * Returns, for the given tree item, the deepest visible {@link TreeItem} in the Treeviewer associated + * with this TreeRuler. + * + * @param currentItem + * the given tree item. + * @param deepestVisibleItem + * the deepest visible tree item (a parent or the item itself) of the given item. + * @return the deepest visible tree item (a parent or the item itself). + */ + private TreeItem getDeepestVisibleTreeItem(final TreeItem currentItem, final TreeItem deepestVisibleItem) { + TreeItem item = null; + TreeItem parent = currentItem.getParentItem(); + if (parent == null) { + item = deepestVisibleItem; + } else if (parent.getExpanded()) { + item = getDeepestVisibleTreeItem(parent, deepestVisibleItem); + } else { + item = getDeepestVisibleTreeItem(parent, parent); + } + return item; + } + + /** + * Get the previous item of the given {@link TreeItem}. + * + * @param treeItem + * the given {@link TreeItem}. + * @param index + * the index of the previous item. + * @return the previous item of the given {@link TreeItem}. + */ + private TreeItem getPreviousItem(TreeItem treeItem, int index) { + TreeItem previousItem = treeItem; + if (index > 0) { + TreeItem parentItem = treeItem.getParentItem(); + if (parentItem != null) { + int treeItemIndex = 0; + for (TreeItem siblingItem : parentItem.getItems()) { + if (siblingItem.equals(treeItem)) { + break; + } + treeItemIndex++; + } + if (treeItemIndex == 0) { + previousItem = getPreviousItem(parentItem, index - 1); + } else if (treeItemIndex == 1) { + TreeItem firstChild = parentItem.getItem(0); + previousItem = getLastVisibleItem(firstChild); + previousItem = getPreviousItem(previousItem, index - 1); + } else { + previousItem = getPreviousItem(getLastVisibleItem(parentItem.getItem(treeItemIndex - 1)), + index - 1); + } + } else { + // It is a root item. May be there are some previous root items. + Tree tree = treeItem.getParent(); + int treeItemIndex = 0; + for (TreeItem siblingItem : tree.getItems()) { + if (siblingItem.equals(treeItem)) { + break; + } + treeItemIndex++; + } + if (treeItemIndex == 0) { + previousItem = treeItem; + } else if (treeItemIndex == 1) { + TreeItem firstRoot = tree.getItem(0); + if (firstRoot.getExpanded()) { + previousItem = getLastVisibleItem(firstRoot); + } else { + previousItem = firstRoot; + } + previousItem = getPreviousItem(previousItem, index - 1); + } else { + previousItem = tree.getItem(treeItemIndex - index); + } + } + } + return previousItem; + } + + /** + * Returns the last visible {@link TreeItem} in the Treeviewer associated with this TreeRuler. + * + * @return the last visible TreeItem in the Treeviewer associated with this TreeRuler. + */ + private TreeItem getLastVisibleItem() { + int rootChildren = fTreeViewer.getTree().getItemCount(); + return getLastVisibleItem(fTreeViewer.getTree().getItem(rootChildren - 1)); + } + + /** + * Returns the last visible child of the given {@link TreeItem} in the Treeviewer associated with this + * TreeRuler. + * + * @param item + * teh given TreeItem. + * @return the last visible child of the given TreeItem in the Treeviewer associated with this TreeRuler. + */ + private TreeItem getLastVisibleItem(TreeItem item) { + TreeItem lastVisibleItem = null; + int directChildren = item.getItemCount(); + if (directChildren == 0) { + lastVisibleItem = item; + } else { + TreeItem lastDirectChildren = item.getItem(directChildren - 1); + if (lastDirectChildren.getData() == null) { + lastVisibleItem = item; + } else if (lastDirectChildren.getExpanded()) { + lastVisibleItem = getLastVisibleItem(lastDirectChildren); + } else { + lastVisibleItem = lastDirectChildren; + } + } + return lastVisibleItem; + } + + /** + * From a list of {@link Diff}s, returns the diffs which are not filtered by a filter of the given list of + * {@link IDifferenceFilter}. + * + * @param unfilteredDiffs + * the given list of unfiltered diffs. + * @param filters + * the given list of IDifferenceFilter. + * @return A filtered list of diffs. + */ + protected Collection<? extends Diff> filteredDiffs(Collection<? extends Diff> unfilteredDiffs, + Collection<IDifferenceFilter> filters) { + if (filters != null) { + List<Diff> filteredDiffs = Lists.newArrayList(unfilteredDiffs); + for (IDifferenceFilter filter : filters) { + for (Diff unfilteredDiff : unfilteredDiffs) { + if (filter.getPredicateWhenSelected().apply(unfilteredDiff)) { + filteredDiffs.remove(unfilteredDiff); + } + } + } + return filteredDiffs; + } + return unfilteredDiffs; + + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeViewer.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeViewer.java new file mode 100644 index 000000000..d5171601d --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareDiffTreeViewer.java @@ -0,0 +1,641 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareViewerPane; +import org.eclipse.compare.CompareViewerSwitchingPane; +import org.eclipse.compare.structuremergeviewer.DiffTreeViewer; +import org.eclipse.core.runtime.Platform; +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.compare.Conflict; +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.DifferenceSource; +import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; +import org.eclipse.emf.compare.ide.ui.internal.actions.collapse.CollapseAllModelAction; +import org.eclipse.emf.compare.ide.ui.internal.actions.expand.ExpandAllModelAction; +import org.eclipse.emf.compare.internal.utils.DiffUtil; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.actions.FilterActionMenu; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.actions.GroupActionMenu; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.IDifferenceFilter; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.IDifferenceFilterSelectionChangeEvent; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.IDifferenceGroupProvider; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.StructureMergeViewerGrouper; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.DefaultGroupProvider; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IContributionItem; +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.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.swt.widgets.Widget; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.internal.actions.CommandAction; +import org.eclipse.ui.menus.IMenuService; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.ui.services.IServiceLocator; +import org.osgi.framework.Bundle; +import org.osgi.framework.Version; + +/** + * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a> + */ +public class EMFCompareDiffTreeViewer extends DiffTreeViewer { + + public static final String REQUIRED_DIFF_COLOR = "RequiredDiffColor"; //$NON-NLS-1$ + + public static final String REQUIRED_DIFF_BORDER_COLOR = "RequiredDiffBorderColor"; //$NON-NLS-1$ + + public static final String UNMERGEABLE_DIFF_COLOR = "UnmergeableDiffColor"; //$NON-NLS-1$ + + public static final String UNMERGEABLE_DIFF_BORDER_COLOR = "UnmergeableDiffBorderColor"; //$NON-NLS-1$ + + private final Color requiredDiffColor; + + private final Color unmergeableDiffColor; + + private final ISelectionChangedListener fSelectionChangedListener; + + private final CompareViewerSwitchingPane fParent; + + private ToolBarManager toolbarManager; + + private Object fRoot; + + /** + * The difference filter that will be applied to the structure viewer. Note that this will be initialized + * from {@link #createToolItems(ToolBarManager)} since that method is called from the super-constructor + * and we cannot init ourselves beforehand. + */ + private StructureMergeViewerFilter structureMergeViewerFilter; + + /** + * This will be used by our adapter factory in order to group together the differences located under the + * Comparison. Note that this will be initialized from {@link #createToolItems(ToolBarManager)} since that + * method is called from the super-constructor and we cannot init ourselves beforehand. + */ + private StructureMergeViewerGrouper structureMergeViewerGrouper; + + private MenuManager groupsMenuManager; + + private MenuManager filtersMenuManager; + + private GroupActionMenu groupActionMenu; + + private DefaultGroupProvider defaultGroupProvider; + + private FilterActionMenu filterActionMenu; + + private EventBus eventBus; + + private Listener fEraseItemListener; + + /** + * @param parent + * @param adapterFactory + * @param configuration + */ + public EMFCompareDiffTreeViewer(Composite parent, AdapterFactory adapterFactory, + CompareConfiguration configuration) { + super(parent, configuration); + + ToolBarManager tbm = CompareViewerPane.getToolBarManager(parent.getParent()); + if (tbm != null) { + tbm.removeAll(); + + tbm.add(new Separator("merge")); //$NON-NLS-1$ + tbm.add(new Separator("modes")); //$NON-NLS-1$ + tbm.add(new Separator("navigation")); //$NON-NLS-1$ + + createToolItems(tbm); + // updateActions(); + + tbm.update(true); + } + + setLabelProvider(new DelegatingStyledCellLabelProvider( + new EMFCompareStructureMergeViewerLabelProvider(adapterFactory, this))); + setContentProvider(new EMFCompareStructureMergeViewerContentProvider(adapterFactory, + getStructureMergeViewerGrouper(), getStructureMergeViewerFilter(), configuration)); + + if (parent instanceof CompareViewerSwitchingPane) { + fParent = (CompareViewerSwitchingPane)parent; + } else { + fParent = null; + } + + fSelectionChangedListener = new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + getControl().redraw(); + getCompareConfiguration() + .setProperty(EMFCompareConstants.SMV_SELECTION, event.getSelection()); + if (toolbarManager != null) { + for (IContributionItem item : toolbarManager.getItems()) { + item.update(); + } + } + } + }; + addSelectionChangedListener(fSelectionChangedListener); + + fEraseItemListener = new Listener() { + public void handleEvent(Event event) { + EMFCompareDiffTreeViewer.this.handleEraseItemEvent(event); + } + }; + getControl().addListener(SWT.EraseItem, fEraseItemListener); + + // Wrap the defined comparer in our own. + setComparer(new DiffNodeComparer(super.getComparer())); + + if (eventBus == null) { + eventBus = new EventBus(); + eventBus.register(this); + } + + JFaceResources.getColorRegistry().put(REQUIRED_DIFF_COLOR, new RGB(215, 255, 200)); + JFaceResources.getColorRegistry().put(REQUIRED_DIFF_BORDER_COLOR, new RGB(195, 235, 180)); + JFaceResources.getColorRegistry().put(UNMERGEABLE_DIFF_COLOR, new RGB(255, 205, 180)); + JFaceResources.getColorRegistry().put(UNMERGEABLE_DIFF_BORDER_COLOR, new RGB(235, 185, 160)); + + requiredDiffColor = JFaceResources.getColorRegistry().get(REQUIRED_DIFF_COLOR); + unmergeableDiffColor = JFaceResources.getColorRegistry().get(UNMERGEABLE_DIFF_COLOR); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#getComparator() + */ + @Override + public ViewerComparator getComparator() { + return null; + } + + @SuppressWarnings("unchecked") + @Subscribe + public void recordFilterSelectionChange(IDifferenceFilterSelectionChangeEvent event) { + final Object property = getCompareConfiguration().getProperty(EMFCompareConstants.SELECTED_FILTERS); + final Collection<IDifferenceFilter> selectedFilters; + if (property == null) { + selectedFilters = new HashSet<IDifferenceFilter>(); + } else { + selectedFilters = new HashSet<IDifferenceFilter>((Collection<IDifferenceFilter>)property); + } + switch (event.getAction()) { + case ACTIVATE: + selectedFilters.add(event.getFilter()); + break; + case DEACTIVATE: + selectedFilters.remove(event.getFilter()); + break; + default: + throw new IllegalStateException(); + } + getCompareConfiguration().setProperty(EMFCompareConstants.SELECTED_FILTERS, selectedFilters); + } + + @Subscribe + public void recordGroupProviderSelectionChange(IDifferenceGroupProvider differenceGroupProvider) { + getCompareConfiguration().setProperty(EMFCompareConstants.SELECTED_GROUP, differenceGroupProvider); + } + + public void configurationPropertyChanged() { + getControl().redraw(); + if (toolbarManager != null) { + for (IContributionItem item : toolbarManager.getItems()) { + item.update(); + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#getRoot() + */ + @Override + public Object getRoot() { + return fRoot; + } + + public void setRoot(Object root) { + fRoot = root; + } + + public void createChildrenSilently(Object o) { + if (o instanceof Tree) { + createChildren((Widget)o); + for (TreeItem item : ((Tree)o).getItems()) { + createChildrenSilently(item); + } + } else if (o instanceof TreeItem) { + createChildren((Widget)o); + for (TreeItem item : ((TreeItem)o).getItems()) { + createChildrenSilently(item); + } + } + } + + @Override + public void initialSelection() { + super.initialSelection(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.AbstractTreeViewer#isExpandable(java.lang.Object) + */ + @Override + public boolean isExpandable(Object parent) { + if (hasFilters()) { + // workaround for 65762 + return hasFilteredChildren(parent); + } + return super.isExpandable(parent); + } + + /** + * Public method to test if a element has any children that passed the filters + * + * @param parent + * the element to test + * @return return <code>true</code> if the element has at least a child that passed the filters + */ + public final boolean hasFilteredChildren(Object parent) { + Object[] rawChildren = getRawChildren(parent); + return containsNonFiltered(rawChildren, parent); + } + + private boolean containsNonFiltered(Object[] elements, Object parent) { + if (elements.length == 0) { + return false; + } + if (!hasFilters()) { + return true; + } + ViewerFilter[] filters = getFilters(); + for (int i = 0; i < elements.length; i++) { + Object object = elements[i]; + if (!isFiltered(object, parent, filters)) { + return true; + } + } + return false; + } + + /** + * All element filter tests must go through this method. Can be overridden by subclasses. + * + * @param object + * the object to filter + * @param parent + * the parent + * @param filters + * the filters to apply + * @return true if the element is filtered + */ + protected boolean isFiltered(Object object, Object parent, ViewerFilter[] filters) { + for (int i = 0; i < filters.length; i++) { + ViewerFilter filter = filters[i]; + if (!filter.select(this, parent, object)) { + return true; + } + } + return false; + } + + public void refreshAfterDiff(String message, Object root) { + if (getControl().isDisposed()) { + return; + } + + if (fParent != null) { + fParent.setTitleArgument(message); + } + + refresh(root); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.StructuredViewer#setComparer(org.eclipse.jface.viewers.IElementComparer) + */ + @Override + public void setComparer(IElementComparer comparer) { + // Wrap this new comparer in our own + super.setComparer(new DiffNodeComparer(comparer)); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.compare.structuremergeviewer.DiffTreeViewer#createToolItems(org.eclipse.jface.action.ToolBarManager) + */ + @Override + protected void createToolItems(ToolBarManager toolbarManager) { + + this.toolbarManager = toolbarManager; + + super.createToolItems(toolbarManager); + + // Add extension point contributions to the structure merge viewer toolbar + IServiceLocator workbench = PlatformUI.getWorkbench(); + IMenuService menuService = (IMenuService)workbench.getService(IMenuService.class); + if (menuService != null) { + menuService.populateContributionManager(toolbarManager, + "toolbar:org.eclipse.emf.compare.structuremergeviewer.toolbar"); + } + + Bundle uiWorkbenchBundle = Platform.getBundle("org.eclipse.ui.workbench"); //$NON-NLS-1$ + Version junoStart = Version.parseVersion("3.103"); //$NON-NLS-1$ + + // XXX MBA change to 3.105 once bug #366528 is fixed + Version keplerStart = Version.parseVersion("3.105"); //$NON-NLS-1$ + + if (uiWorkbenchBundle != null && uiWorkbenchBundle.getVersion().compareTo(junoStart) >= 0 + && uiWorkbenchBundle.getVersion().compareTo(keplerStart) < 0) { + IAction action = new CommandAction(PlatformUI.getWorkbench(), + "org.eclipse.emf.compare.ide.ui.saveComparisonModel"); //$NON-NLS-1$ + action.setToolTipText("Save Comparison model"); //$NON-NLS-1$ + action.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( + EMFCompareIDEUIPlugin.PLUGIN_ID, "icons/full/toolb16/saveas_edit.gif")); //$NON-NLS-1$ + toolbarManager.add(action); + } + + groupActionMenu = new GroupActionMenu(getStructureMergeViewerGrouper(), getGroupsMenuManager(), + getDefaultGroupProvider()); + filterActionMenu = new FilterActionMenu(getStructureMergeViewerFilter(), getFiltersMenuManager()); + + toolbarManager.add(new Separator()); + toolbarManager.add(new ExpandAllModelAction(this)); + toolbarManager.add(new CollapseAllModelAction(this)); + toolbarManager.add(new Separator()); + toolbarManager.add(groupActionMenu); + toolbarManager.add(filterActionMenu); + } + + /** + * Returns the viewer filter that is to be applied on the structure viewer. + * <p> + * Note that this will be called from {@link #createToolItems(ToolBarManager)}, which is called from the + * super-constructor, when we have had no time to initialize the {@link #structureMergeViewerFilter} + * field. + * </p> + * + * @return The difference filter that is to be applied on the structure viewer. + */ + protected StructureMergeViewerFilter getStructureMergeViewerFilter() { + if (structureMergeViewerFilter == null) { + if (eventBus == null) { + eventBus = new EventBus(); + eventBus.register(this); + } + structureMergeViewerFilter = new StructureMergeViewerFilter(eventBus); + structureMergeViewerFilter.install(this); + } + return structureMergeViewerFilter; + } + + /** + * Returns the viewer grouper that is to be applied on the structure viewer. + * <p> + * Note that this will be called from {@link #createToolItems(ToolBarManager)}, which is called from the + * super-constructor, when we have had no time to initialize the {@link #structureMergeViewerGrouper} + * field. + * </p> + * + * @return The viewer grouper grouper that is to be applied on the structure viewer. + */ + protected StructureMergeViewerGrouper getStructureMergeViewerGrouper() { + if (structureMergeViewerGrouper == null) { + if (eventBus == null) { + eventBus = new EventBus(); + eventBus.register(this); + } + structureMergeViewerGrouper = new StructureMergeViewerGrouper(eventBus); + structureMergeViewerGrouper.install(this); + } + return structureMergeViewerGrouper; + } + + /** + * Returns the menu manager that is to be applied to groups on the structure viewer. + * + * @return The menu manager that is to be applied to groups on the structure viewer. + */ + public MenuManager getGroupsMenuManager() { + if (groupsMenuManager == null) { + groupsMenuManager = new MenuManager(); + } + return groupsMenuManager; + } + + /** + * @return the groupActionMenu + */ + public GroupActionMenu getGroupActionMenu() { + return groupActionMenu; + } + + /** + * @param groupActionMenu + * the groupActionMenu to set + */ + public void setGroupActionMenu(GroupActionMenu groupActionMenu) { + this.groupActionMenu = groupActionMenu; + } + + /** + * Returns the menu manager that is to be applied to filters on the structure viewer. + * + * @return The menu manager that is to be applied to filters on the structure viewer. + */ + public MenuManager getFiltersMenuManager() { + if (filtersMenuManager == null) { + filtersMenuManager = new MenuManager(); + } + return filtersMenuManager; + } + + /** + * @return the filterActionMenu + */ + public FilterActionMenu getFilterActionMenu() { + return filterActionMenu; + } + + /** + * @param filterActionMenu + * the filterActionMenu to set + */ + public void setFilterActionMenu(FilterActionMenu filterActionMenu) { + this.filterActionMenu = filterActionMenu; + } + + /** + * Returns the default group provider that is to be applied on the structure viewer. + * + * @return The default group provider that is to be applied on the structure viewer. + */ + public DefaultGroupProvider getDefaultGroupProvider() { + if (defaultGroupProvider == null) { + defaultGroupProvider = new DefaultGroupProvider(); + } + return defaultGroupProvider; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.compare.structuremergeviewer.DiffTreeViewer#handleDispose(org.eclipse.swt.events.DisposeEvent) + */ + @Override + protected void handleDispose(DisposeEvent event) { + getControl().removeListener(SWT.EraseItem, fEraseItemListener); + removeSelectionChangedListener(fSelectionChangedListener); + super.handleDispose(event); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#getSortedChildren(java.lang.Object) + */ + @Override + protected Object[] getSortedChildren(Object parentElementOrTreePath) { + Object[] result = super.getSortedChildren(parentElementOrTreePath); + if (parentElementOrTreePath instanceof Adapter + && ((Adapter)parentElementOrTreePath).getTarget() instanceof Conflict) { + + Collections.sort(Arrays.asList(result), new Comparator<Object>() { + public int compare(Object o1, Object o2) { + return getValue(o1) - getValue(o2); + } + + public int getValue(Object o) { + int value = 0; + if (o instanceof Adapter && ((Adapter)o).getTarget() instanceof Diff) { + if (((Diff)((Adapter)o).getTarget()).getSource() == DifferenceSource.LEFT) { + value = 1; + } else { + value = 2; + } + } + return value; + } + }); + + } + 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 Set<Diff> unmergeables; + final Set<Diff> requires; + Boolean leftToRight = (Boolean)getCompareConfiguration().getProperty( + EMFCompareConstants.MERGE_WAY); + boolean ltr = false; + if (leftToRight == null || leftToRight.booleanValue()) { + ltr = true; + } + boolean leftEditable = getCompareConfiguration().isLeftEditable(); + boolean rightEditable = getCompareConfiguration().isRightEditable(); + boolean bothSidesEditable = leftEditable && rightEditable; + Diff diff = (Diff)target; + if ((ltr && (leftEditable || bothSidesEditable)) + || (!ltr && (rightEditable && !leftEditable))) { + requires = DiffUtil.getRequires(diff, true, diff.getSource()); + unmergeables = DiffUtil.getUnmergeables(diff, true); + } else { + requires = DiffUtil.getRequires(diff, false, diff.getSource()); + unmergeables = DiffUtil.getUnmergeables(diff, false); + } + final GC g = event.gc; + if (requires.contains(((Adapter)dataTreeItem).getTarget())) { + paintItemBackground(g, item, requiredDiffColor); + } else if (unmergeables.contains(((Adapter)dataTreeItem).getTarget())) { + paintItemBackground(g, item, unmergeableDiffColor); + } + } + } + } + } + + /** + * 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); + } +} 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 4895d4911..a9968c40e 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2013 Obeo. + * 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 @@ -12,31 +12,23 @@ package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer; import static com.google.common.collect.Iterables.getFirst; -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.EventObject; -import java.util.HashSet; import java.util.Iterator; import org.eclipse.compare.CompareConfiguration; -import org.eclipse.compare.CompareViewerSwitchingPane; +import org.eclipse.compare.INavigatable; import org.eclipse.compare.ITypedElement; -import org.eclipse.compare.structuremergeviewer.DiffTreeViewer; +import org.eclipse.compare.internal.CompareHandlerService; import org.eclipse.compare.structuremergeviewer.ICompareInput; import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; @@ -46,17 +38,14 @@ import org.eclipse.emf.common.command.CommandStackListener; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.BasicMonitor; import org.eclipse.emf.compare.Comparison; -import org.eclipse.emf.compare.Conflict; -import org.eclipse.emf.compare.Diff; -import org.eclipse.emf.compare.DifferenceSource; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.Match; import org.eclipse.emf.compare.command.ICompareCopyCommand; import org.eclipse.emf.compare.domain.ICompareEditingDomain; import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; -import org.eclipse.emf.compare.ide.ui.internal.actions.collapse.CollapseAllModelAction; -import org.eclipse.emf.compare.ide.ui.internal.actions.expand.ExpandAllModelAction; +import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.util.RedoAction; +import org.eclipse.emf.compare.ide.ui.internal.contentmergeviewer.util.UndoAction; import org.eclipse.emf.compare.ide.ui.internal.editor.ComparisonScopeInput; import org.eclipse.emf.compare.ide.ui.internal.logical.ComparisonScopeBuilder; import org.eclipse.emf.compare.ide.ui.internal.logical.IdenticalResourceMinimizer; @@ -69,14 +58,6 @@ import org.eclipse.emf.compare.ide.ui.logical.IModelResolver; import org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor; 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.actions.FilterActionMenu; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.actions.GroupActionMenu; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.IDifferenceFilter; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.IDifferenceFilterSelectionChangeEvent; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.IDifferenceGroupProvider; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.StructureMergeViewerGrouper; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.DefaultGroupProvider; import org.eclipse.emf.compare.scope.IComparisonScope; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; @@ -84,70 +65,56 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.action.Separator; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; -import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ITreeViewerListener; +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.jface.viewers.TreeExpansionEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.team.core.subscribers.Subscriber; import org.eclipse.team.core.subscribers.SubscriberMergeContext; import org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput; import org.eclipse.team.ui.synchronize.ISynchronizeParticipant; import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.internal.actions.CommandAction; -import org.eclipse.ui.menus.IMenuService; -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.eclipse.ui.services.IServiceLocator; -import org.osgi.framework.Bundle; -import org.osgi.framework.Version; +import org.eclipse.ui.actions.ActionFactory; /** - * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a> + * Implementation of {@link AbstractViewerWrapper}. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ -public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements CommandStackListener { - - private final ICompareInputChangeListener fCompareInputChangeListener; - - private final ComposedAdapterFactory fAdapterFactory; +public class EMFCompareStructureMergeViewer extends AbstractViewerWrapper implements CommandStackListener { - private final CompareViewerSwitchingPane fParent; + private static final int TREE_RULER_WIDTH = 17; - private Object fRoot; + private ComposedAdapterFactory fAdapterFactory; - /** - * The difference filter that will be applied to the structure viewer. Note that this will be initialized - * from {@link #createToolItems(ToolBarManager)} since that method is called from the super-constructor - * and we cannot init ourselves beforehand. - */ - private StructureMergeViewerFilter structureMergeViewerFilter; + /** The tree ruler associated with this viewer. */ + private EMFCompareDiffTreeRuler treeRuler; - /** - * This will be used by our adapter factory in order to group together the differences located under the - * Comparison. Note that this will be initialized from {@link #createToolItems(ToolBarManager)} since that - * method is called from the super-constructor and we cannot init ourselves beforehand. - */ - private StructureMergeViewerGrouper structureMergeViewerGrouper; + private ICompareInputChangeListener fCompareInputChangeListener; - private MenuManager groupsMenuManager; + /** The expand/collapse item listener. */ + private ITreeViewerListener fWrappedTreeListener; - private MenuManager filtersMenuManager; + /** The compare configuration property change listener. */ + private IPropertyChangeListener fCompareConfigurationPropertyChangeListener; - private GroupActionMenu groupActionMenu; + /** The tree viewer. */ + private EMFCompareDiffTreeViewer diffTreeViewer; - private DefaultGroupProvider defaultGroupProvider; + private UndoAction undoAction; - private FilterActionMenu filterActionMenu; + private RedoAction redoAction; - private EventBus eventBus; + private CompareHandlerService fHandlerService; /** * When comparing EObjects from a resource, the resource involved doesn't need to be unload by EMF @@ -156,11 +123,27 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co private boolean resourcesShouldBeUnload; /** + * Constructor. + * * @param parent - * @param configuration + * the SWT parent control under which to create the viewer's SWT control. + * @param config + * a compare configuration the newly created viewer might want to use. */ - public EMFCompareStructureMergeViewer(Composite parent, CompareConfiguration configuration) { - super(parent, configuration); + public EMFCompareStructureMergeViewer(Composite parent, CompareConfiguration config) { + super(parent, config); + } + + /** + * {@inheritDoc} + * + * @see + * org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.ViewerWrapper.createControl(Composite, + * CompareConfiguration) + */ + @Override + protected Control createControl(Composite parent, CompareConfiguration config) { + Composite control = new Composite(parent, SWT.NONE); fAdapterFactory = new ComposedAdapterFactory(EMFCompareRCPPlugin.getDefault() .getAdapterFactoryRegistry()); @@ -168,16 +151,25 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co fAdapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); fAdapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory()); - setLabelProvider(new DelegatingStyledCellLabelProvider( - new EMFCompareStructureMergeViewerLabelProvider(fAdapterFactory, this))); - setContentProvider(new EMFCompareStructureMergeViewerContentProvider(fAdapterFactory, - getStructureMergeViewerGrouper(), getStructureMergeViewerFilter(), configuration)); - - if (parent instanceof CompareViewerSwitchingPane) { - fParent = (CompareViewerSwitchingPane)parent; - } else { - fParent = null; - } + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + control.setLayout(layout); + control.setLayoutData(data); + diffTreeViewer = new EMFCompareDiffTreeViewer(control, fAdapterFactory, config); + setViewer(diffTreeViewer); + control.setData(INavigatable.NAVIGATOR_PROPERTY, diffTreeViewer.getControl().getData( + INavigatable.NAVIGATOR_PROPERTY)); + diffTreeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + GridData layoutData = new GridData(SWT.FILL, SWT.FILL, false, true); + layoutData.widthHint = TREE_RULER_WIDTH; + layoutData.minimumWidth = TREE_RULER_WIDTH; + treeRuler = new EMFCompareDiffTreeRuler(control, SWT.NONE, layoutData.widthHint, diffTreeViewer, + config); + treeRuler.setLayoutData(layoutData); fCompareInputChangeListener = new ICompareInputChangeListener() { public void compareInputChanged(ICompareInput input) { @@ -185,79 +177,145 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co } }; - // Wrap the defined comparer in our own. - setComparer(new DiffNodeComparer(super.getComparer())); + fWrappedTreeListener = new ITreeViewerListener() { - if (eventBus == null) { - eventBus = new EventBus(); - eventBus.register(this); - } + public void treeExpanded(TreeExpansionEvent event) { + treeRuler.redraw(); + } + + public void treeCollapsed(TreeExpansionEvent event) { + treeRuler.redraw(); + } + }; + diffTreeViewer.addTreeListener(fWrappedTreeListener); + + fCompareConfigurationPropertyChangeListener = new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + if (EMFCompareConstants.MERGE_WAY.equals(event.getProperty()) && event.getNewValue() != null) { + diffTreeViewer.configurationPropertyChanged(); + treeRuler.computeConsequences(); + treeRuler.redraw(); + } else if (EMFCompareConstants.SELECTED_FILTERS.equals(event.getProperty()) + && event.getNewValue() != null) { + diffTreeViewer.createChildrenSilently(diffTreeViewer.getTree()); + treeRuler.computeConsequences(); + treeRuler.redraw(); + } else if (EMFCompareConstants.SELECTED_GROUP.equals(event.getProperty()) + && event.getNewValue() != null) { + diffTreeViewer.createChildrenSilently(diffTreeViewer.getTree()); + treeRuler.computeConsequences(); + treeRuler.redraw(); + } + } + }; + getCompareConfiguration().addPropertyChangeListener(fCompareConfigurationPropertyChangeListener); + + fHandlerService = CompareHandlerService.createFor(getCompareConfiguration().getContainer(), + diffTreeViewer.getControl().getShell()); + + // inputChangedTask.setPriority(Job.LONG); - inputChangedTask.setPriority(Job.LONG); + return control; } /** * {@inheritDoc} * - * @see org.eclipse.jface.viewers.StructuredViewer#getComparator() + * @see org.eclipse.jface.viewers.Viewer#fireSelectionChanged(SelectionChangedEvent) */ @Override - public ViewerComparator getComparator() { - return null; + protected void fireSelectionChanged(SelectionChangedEvent event) { + super.fireSelectionChanged(event); + treeRuler.selectionChanged(event); + treeRuler.redraw(); } - @SuppressWarnings("unchecked") - @Subscribe - public void recordFilterSelectionChange(IDifferenceFilterSelectionChangeEvent event) { - final Object property = getCompareConfiguration().getProperty(EMFCompareConstants.SELECTED_FILTERS); - final Collection<IDifferenceFilter> selectedFilters; - if (property == null) { - selectedFilters = new HashSet<IDifferenceFilter>(); - } else { - selectedFilters = new HashSet<IDifferenceFilter>((Collection<IDifferenceFilter>)property); - } - switch (event.getAction()) { - case ACTIVATE: - selectedFilters.add(event.getFilter()); - break; - case DEACTIVATE: - selectedFilters.remove(event.getFilter()); - break; - default: - throw new IllegalStateException(); + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.Viewer#inputChanged(Object, Object) + */ + @Override + protected void inputChanged(Object input, Object oldInput) { + if (oldInput instanceof ICompareInput) { + ICompareInput old = (ICompareInput)oldInput; + old.removeCompareInputChangeListener(fCompareInputChangeListener); } - getCompareConfiguration().setProperty(EMFCompareConstants.SELECTED_FILTERS, selectedFilters); - } + if (input instanceof ICompareInput) { + ICompareInput ci = (ICompareInput)input; + ci.addCompareInputChangeListener(fCompareInputChangeListener); + + // Hack to display a message in the tree viewer while the differences are being computed. + TreeItem item = new TreeItem(diffTreeViewer.getTree(), SWT.NONE); + item.setText("Computing model differences..."); - @Subscribe - public void recordGroupProviderSelectionChange(IDifferenceGroupProvider differenceGroupProvider) { - getCompareConfiguration().setProperty(EMFCompareConstants.SELECTED_GROUP, differenceGroupProvider); + compareInputChanged(ci); + } } /** - * Triggered by fCompareInputChangeListener and {@link #inputChanged(Object, Object)}. + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.AbstractViewerWrapper#handleDispose(DisposeEvent) */ - void compareInputChanged(ICompareInput input) { - if (input == null) { - // When closing, we don't need a progress monitor to handle the input change - compareInputChanged((ICompareInput)null, new NullProgressMonitor()); - return; + @Override + protected void handleDispose(DisposeEvent event) { + if (fHandlerService != null) { + fHandlerService.dispose(); } - CompareConfiguration cc = getCompareConfiguration(); - // The compare configuration is nulled when the viewer is disposed - if (cc != null) { - inputChangedTask.schedule(); + getCompareConfiguration().removePropertyChangeListener(fCompareConfigurationPropertyChangeListener); + diffTreeViewer.removeTreeListener(fWrappedTreeListener); + Object input = getInput(); + if (input instanceof ICompareInput) { + ICompareInput ci = (ICompareInput)input; + ci.removeCompareInputChangeListener(fCompareInputChangeListener); } + compareInputChanged((ICompareInput)null); + treeRuler.handleDispose(); + fAdapterFactory.dispose(); + super.handleDispose(event); } /** * {@inheritDoc} * - * @see org.eclipse.jface.viewers.StructuredViewer#getRoot() + * @see org.eclipse.emf.common.command.CommandStackListener#commandStackChanged(java.util.EventObject) */ - @Override - protected Object getRoot() { - return fRoot; + public void commandStackChanged(EventObject event) { + if (undoAction != null) { + undoAction.update(); + } + if (redoAction != null) { + redoAction.update(); + } + + Command mostRecentCommand = ((CommandStack)event.getSource()).getMostRecentCommand(); + if (mostRecentCommand instanceof ICompareCopyCommand) { + Collection<?> affectedObjects = mostRecentCommand.getAffectedObjects(); + + SWTUtil.safeAsyncExec(new Runnable() { + public void run() { + refresh(true); + diffTreeViewer.createChildrenSilently(diffTreeViewer.getTree()); + treeRuler.computeConsequences(); + treeRuler.redraw(); + } + }); + if (!affectedObjects.isEmpty()) { + // MUST NOT call a setSelection with a list, o.e.compare does not handle it (cf + // org.eclipse.compare.CompareEditorInput#getElement(ISelection)) + final Object adaptedAffectedObject = fAdapterFactory.adapt(getFirst(affectedObjects, null), + ICompareInput.class); + SWTUtil.safeAsyncExec(new Runnable() { + public void run() { + setSelectionToWidget(new StructuredSelection(adaptedAffectedObject), true); + } + }); + } + } else { + // FIXME, should recompute the difference, something happened outside of this compare editor + } + } private Job inputChangedTask = new Job("Compute Model Differences") { @@ -270,53 +328,77 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co }; /** - * Team left us with absolutely no way to determine whether our supplied input is the result of a - * synchronization or not. - * <p> - * In order to properly resolve the logical model of the resource currently being compared we need to know - * what "other" resources were part of its logical model, and we need to know the revisions of these - * resources we are to load. All of this has already been computed by Team, but it would not let us know. - * This method uses discouraged means to get around this "black box" locking from Team. - * </p> - * <p> - * The basic need here is to retrieve the Subscriber from this point. We have a lot of accessible - * variables, the two most important being the CompareConfiguration and ICompareInput... I could find no - * way around the privileged access to the private ModelCompareEditorInput.participant field. There does - * not seem to be any adapter (or Platform.getAdapterManager().getAdapter(...)) that would allow for this, - * so I'm taking the long way 'round. - * </p> - * - * @return The subscriber used for this comparison if any could be found, <code>null</code> otherwise. + * Triggered by fCompareInputChangeListener and {@link #inputChanged(Object, Object)}. */ - @SuppressWarnings("restriction") - private Subscriber getSubscriber() { - if (getCompareConfiguration().getContainer() instanceof ModelCompareEditorInput) { - final ModelCompareEditorInput modelInput = (ModelCompareEditorInput)getCompareConfiguration() - .getContainer(); - ISynchronizeParticipant participant = null; - try { - final Field field = ModelCompareEditorInput.class.getDeclaredField("participant"); //$NON-NLS-1$ - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - field.setAccessible(true); - return null; - } - }); - participant = (ISynchronizeParticipant)field.get(modelInput); - } catch (NoSuchFieldException e) { - // Swallow this, this private field was there at least from 3.5 to 4.3 - } catch (IllegalArgumentException e) { - // Cannot happen - } catch (IllegalAccessException e) { - // "Should" not happen, but ignore it anyway - } - if (participant instanceof ModelSynchronizeParticipant - && ((ModelSynchronizeParticipant)participant).getContext() instanceof SubscriberMergeContext) { - return ((SubscriberMergeContext)((ModelSynchronizeParticipant)participant).getContext()) - .getSubscriber(); + void compareInputChanged(ICompareInput input) { + if (input == null) { + // When closing, we don't need a progress monitor to handle the input change + compareInputChanged((ICompareInput)null, new NullProgressMonitor()); + return; + } + CompareConfiguration cc = getCompareConfiguration(); + // The compare configuration is nulled when the viewer is disposed + if (cc != null) { + inputChangedTask.schedule(); + } + } + + void compareInputChanged(ComparisonNode input, IProgressMonitor monitor) { + ICompareEditingDomain editingDomain = (ICompareEditingDomain)getCompareConfiguration().getProperty( + EMFCompareConstants.EDITING_DOMAIN); + editingDomain.getCommandStack().addCommandStackListener(this); + + compareInputChanged(null, input.getTarget()); + } + + void compareInputChanged(ComparisonScopeInput input, IProgressMonitor monitor) { + ICompareEditingDomain editingDomain = (ICompareEditingDomain)getCompareConfiguration().getProperty( + EMFCompareConstants.EDITING_DOMAIN); + editingDomain.getCommandStack().addCommandStackListener(this); + + EMFCompare comparator = (EMFCompare)getCompareConfiguration().getProperty( + EMFCompareConstants.COMPARATOR); + + IComparisonScope comparisonScope = input.getComparisonScope(); + Comparison comparison = comparator.compare(comparisonScope, BasicMonitor.toMonitor(monitor)); + compareInputChanged(input.getComparisonScope(), comparison); + } + + void compareInputChanged(final IComparisonScope scope, final Comparison comparison) { + if (!getControl().isDisposed()) { // guard against disposal + diffTreeViewer.setRoot(fAdapterFactory.adapt(comparison, ICompareInput.class)); + getCompareConfiguration().setProperty(EMFCompareConstants.COMPARE_RESULT, comparison); + + String message = null; + if (comparison.getDifferences().isEmpty()) { + message = "No Differences"; } + + final String theMessage = message; + SWTUtil.safeAsyncExec(new Runnable() { + public void run() { + if (diffTreeViewer.getGroupActionMenu() != null) { + diffTreeViewer.getGroupActionMenu().createActions(scope, comparison); + } + if (diffTreeViewer.getFilterActionMenu() != null) { + diffTreeViewer.getFilterActionMenu().createActions(scope, comparison); + } + diffTreeViewer.refreshAfterDiff(theMessage, diffTreeViewer.getRoot()); + // Mandatory for the EMFCompareDiffTreeRuler, all TreeItems must have been created + diffTreeViewer.createChildrenSilently(diffTreeViewer.getTree()); + diffTreeViewer.initialSelection(); + } + }); + + ICompareEditingDomain editingDomain = (ICompareEditingDomain)getCompareConfiguration() + .getProperty(EMFCompareConstants.EDITING_DOMAIN); + + undoAction = new UndoAction(editingDomain); + redoAction = new RedoAction(editingDomain); + + fHandlerService.setGlobalActionHandler(ActionFactory.UNDO.getId(), undoAction); + fHandlerService.setGlobalActionHandler(ActionFactory.REDO.getId(), redoAction); } - return null; } void compareInputChanged(ICompareInput input, IProgressMonitor monitor) { @@ -337,7 +419,6 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co final IComparisonScope scope = buildComparisonScope(left, right, origin, subMonitor .newChild(85)); - final Comparison compareResult = EMFCompare .builder() .setMatchEngineFactoryRegistry( @@ -370,8 +451,8 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co ResourceSet rightResourceSet = null; ResourceSet originResourceSet = null; - if (fRoot != null) { - Comparison comparison = (Comparison)((Adapter)fRoot).getTarget(); + if (diffTreeViewer.getRoot() != null) { + Comparison comparison = (Comparison)((Adapter)diffTreeViewer.getRoot()).getTarget(); Iterator<Match> matchIt = comparison.getMatches().iterator(); if (comparison.isThreeWay()) { while (matchIt.hasNext() @@ -419,8 +500,10 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co getCompareConfiguration().setProperty(EMFCompareConstants.COMPARE_RESULT, null); getCompareConfiguration().setProperty(EMFCompareConstants.SELECTED_FILTERS, null); getCompareConfiguration().setProperty(EMFCompareConstants.SELECTED_GROUP, null); + getCompareConfiguration().setProperty(EMFCompareConstants.MERGE_WAY, null); + getCompareConfiguration().setProperty(EMFCompareConstants.SMV_SELECTION, null); } - fRoot = null; + diffTreeViewer.setRoot(null); } } @@ -454,25 +537,54 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co return scopeBuilder.build(left, right, origin, monitor); } - void compareInputChanged(ComparisonNode input, IProgressMonitor monitor) { - ICompareEditingDomain editingDomain = (ICompareEditingDomain)getCompareConfiguration().getProperty( - EMFCompareConstants.EDITING_DOMAIN); - editingDomain.getCommandStack().addCommandStackListener(this); - - compareInputChanged(null, input.getTarget()); - } - - void compareInputChanged(ComparisonScopeInput input, IProgressMonitor monitor) { - ICompareEditingDomain editingDomain = (ICompareEditingDomain)getCompareConfiguration().getProperty( - EMFCompareConstants.EDITING_DOMAIN); - editingDomain.getCommandStack().addCommandStackListener(this); - - EMFCompare comparator = (EMFCompare)getCompareConfiguration().getProperty( - EMFCompareConstants.COMPARATOR); - - IComparisonScope comparisonScope = input.getComparisonScope(); - Comparison comparison = comparator.compare(comparisonScope, BasicMonitor.toMonitor(monitor)); - compareInputChanged(input.getComparisonScope(), comparison); + /** + * Team left us with absolutely no way to determine whether our supplied input is the result of a + * synchronization or not. + * <p> + * In order to properly resolve the logical model of the resource currently being compared we need to know + * what "other" resources were part of its logical model, and we need to know the revisions of these + * resources we are to load. All of this has already been computed by Team, but it would not let us know. + * This method uses discouraged means to get around this "black box" locking from Team. + * </p> + * <p> + * The basic need here is to retrieve the Subscriber from this point. We have a lot of accessible + * variables, the two most important being the CompareConfiguration and ICompareInput... I could find no + * way around the privileged access to the private ModelCompareEditorInput.participant field. There does + * not seem to be any adapter (or Platform.getAdapterManager().getAdapter(...)) that would allow for this, + * so I'm taking the long way 'round. + * </p> + * + * @return The subscriber used for this comparison if any could be found, <code>null</code> otherwise. + */ + @SuppressWarnings("restriction") + private Subscriber getSubscriber() { + if (getCompareConfiguration().getContainer() instanceof ModelCompareEditorInput) { + final ModelCompareEditorInput modelInput = (ModelCompareEditorInput)getCompareConfiguration() + .getContainer(); + ISynchronizeParticipant participant = null; + try { + final Field field = ModelCompareEditorInput.class.getDeclaredField("participant"); //$NON-NLS-1$ + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + field.setAccessible(true); + return null; + } + }); + participant = (ISynchronizeParticipant)field.get(modelInput); + } catch (NoSuchFieldException e) { + // Swallow this, this private field was there at least from 3.5 to 4.3 + } catch (IllegalArgumentException e) { + // Cannot happen + } catch (IllegalAccessException e) { + // "Should" not happen, but ignore it anyway + } + if (participant instanceof ModelSynchronizeParticipant + && ((ModelSynchronizeParticipant)participant).getContext() instanceof SubscriberMergeContext) { + return ((SubscriberMergeContext)((ModelSynchronizeParticipant)participant).getContext()) + .getSubscriber(); + } + } + return null; } private static void unload(ResourceSet resourceSet) { @@ -493,344 +605,4 @@ public class EMFCompareStructureMergeViewer extends DiffTreeViewer implements Co } return null; } - - void compareInputChanged(final IComparisonScope scope, final Comparison comparison) { - if (!getControl().isDisposed()) { // guard against disposal - fRoot = fAdapterFactory.adapt(comparison, ICompareInput.class); - getCompareConfiguration().setProperty(EMFCompareConstants.COMPARE_RESULT, comparison); - - String message = null; - if (comparison.getDifferences().isEmpty()) { - message = "No Differences"; - } - - final String theMessage = message; - SWTUtil.safeAsyncExec(new Runnable() { - public void run() { - groupActionMenu.createActions(scope, comparison); - filterActionMenu.createActions(scope, comparison); - refreshAfterDiff(theMessage, fRoot); - initialSelection(); - } - }); - } - } - - /* - * (non-Javadoc) - * @see org.eclipse.jface.viewers.AbstractTreeViewer#isExpandable(java.lang.Object) - */ - @Override - public boolean isExpandable(Object parent) { - if (hasFilters()) { - // workaround for 65762 - return hasFilteredChildren(parent); - } - return super.isExpandable(parent); - } - - /** - * Public method to test if a element has any children that passed the filters - * - * @param parent - * the element to test - * @return return <code>true</code> if the element has at least a child that passed the filters - */ - public final boolean hasFilteredChildren(Object parent) { - Object[] rawChildren = getRawChildren(parent); - return containsNonFiltered(rawChildren, parent); - } - - private boolean containsNonFiltered(Object[] elements, Object parent) { - if (elements.length == 0) { - return false; - } - if (!hasFilters()) { - return true; - } - ViewerFilter[] filters = getFilters(); - for (int i = 0; i < elements.length; i++) { - Object object = elements[i]; - if (!isFiltered(object, parent, filters)) { - return true; - } - } - return false; - } - - /** - * All element filter tests must go through this method. Can be overridden by subclasses. - * - * @param object - * the object to filter - * @param parent - * the parent - * @param filters - * the filters to apply - * @return true if the element is filtered - */ - protected boolean isFiltered(Object object, Object parent, ViewerFilter[] filters) { - for (int i = 0; i < filters.length; i++) { - ViewerFilter filter = filters[i]; - if (!filter.select(this, parent, object)) { - return true; - } - } - return false; - } - - private void refreshAfterDiff(String message, Object root) { - if (getControl().isDisposed()) { - return; - } - - if (fParent != null) { - fParent.setTitleArgument(message); - } - - refresh(root); - } - - /** - * {@inheritDoc} - * - * @see org.eclipse.jface.viewers.StructuredViewer#setComparer(org.eclipse.jface.viewers.IElementComparer) - */ - @Override - public void setComparer(IElementComparer comparer) { - // Wrap this new comparer in our own - super.setComparer(new DiffNodeComparer(comparer)); - } - - /** - * {@inheritDoc} - * - * @see org.eclipse.compare.structuremergeviewer.DiffTreeViewer#createToolItems(org.eclipse.jface.action.ToolBarManager) - */ - @Override - protected void createToolItems(ToolBarManager toolbarManager) { - super.createToolItems(toolbarManager); - - // Add extension point contributions to the structure merge viewer toolbar - IServiceLocator workbench = PlatformUI.getWorkbench(); - IMenuService menuService = (IMenuService)workbench.getService(IMenuService.class); - if (menuService != null) { - menuService.populateContributionManager(toolbarManager, - "toolbar:org.eclipse.emf.compare.structuremergeviewer.toolbar"); //$NON-NLS-1$ - } - - Bundle uiWorkbenchBundle = Platform.getBundle("org.eclipse.ui.workbench"); //$NON-NLS-1$ - Version junoStart = Version.parseVersion("3.103"); //$NON-NLS-1$ - - // XXX MBA change to 3.105 once bug #366528 is fixed - Version keplerStart = Version.parseVersion("3.105"); //$NON-NLS-1$ - - if (uiWorkbenchBundle != null && uiWorkbenchBundle.getVersion().compareTo(junoStart) >= 0 - && uiWorkbenchBundle.getVersion().compareTo(keplerStart) < 0) { - IAction action = new CommandAction(PlatformUI.getWorkbench(), - "org.eclipse.emf.compare.ide.ui.saveComparisonModel"); //$NON-NLS-1$ - action.setToolTipText("Save Comparison model"); - action.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin( - EMFCompareIDEUIPlugin.PLUGIN_ID, "icons/full/toolb16/saveas_edit.gif")); //$NON-NLS-1$ - toolbarManager.add(action); - } - - groupActionMenu = new GroupActionMenu(getStructureMergeViewerGrouper(), getGroupsMenuManager(), - getDefaultGroupProvider()); - filterActionMenu = new FilterActionMenu(getStructureMergeViewerFilter(), getFiltersMenuManager()); - - toolbarManager.add(new Separator()); - toolbarManager.add(new ExpandAllModelAction(this)); - toolbarManager.add(new CollapseAllModelAction(this)); - toolbarManager.add(new Separator()); - toolbarManager.add(groupActionMenu); - toolbarManager.add(filterActionMenu); - - } - - /** - * Returns the viewer filter that is to be applied on the structure viewer. - * <p> - * Note that this will be called from {@link #createToolItems(ToolBarManager)}, which is called from the - * super-constructor, when we have had no time to initialize the {@link #structureMergeViewerFilter} - * field. - * </p> - * - * @return The difference filter that is to be applied on the structure viewer. - */ - protected StructureMergeViewerFilter getStructureMergeViewerFilter() { - if (structureMergeViewerFilter == null) { - if (eventBus == null) { - eventBus = new EventBus(); - eventBus.register(this); - } - structureMergeViewerFilter = new StructureMergeViewerFilter(eventBus); - structureMergeViewerFilter.install(this); - } - return structureMergeViewerFilter; - } - - /** - * Returns the viewer grouper that is to be applied on the structure viewer. - * <p> - * Note that this will be called from {@link #createToolItems(ToolBarManager)}, which is called from the - * super-constructor, when we have had no time to initialize the {@link #structureMergeViewerGrouper} - * field. - * </p> - * - * @return The viewer grouper grouper that is to be applied on the structure viewer. - */ - protected StructureMergeViewerGrouper getStructureMergeViewerGrouper() { - if (structureMergeViewerGrouper == null) { - if (eventBus == null) { - eventBus = new EventBus(); - eventBus.register(this); - } - structureMergeViewerGrouper = new StructureMergeViewerGrouper(eventBus); - structureMergeViewerGrouper.install(this); - } - return structureMergeViewerGrouper; - } - - /** - * Returns the menu manager that is to be applied to groups on the structure viewer. - * - * @return The menu manager that is to be applied to groups on the structure viewer. - */ - public MenuManager getGroupsMenuManager() { - if (groupsMenuManager == null) { - groupsMenuManager = new MenuManager(); - } - return groupsMenuManager; - } - - /** - * Returns the menu manager that is to be applied to filters on the structure viewer. - * - * @return The menu manager that is to be applied to filters on the structure viewer. - */ - public MenuManager getFiltersMenuManager() { - if (filtersMenuManager == null) { - filtersMenuManager = new MenuManager(); - } - return filtersMenuManager; - } - - /** - * Returns the default group provider that is to be applied on the structure viewer. - * - * @return The default group provider that is to be applied on the structure viewer. - */ - public DefaultGroupProvider getDefaultGroupProvider() { - if (defaultGroupProvider == null) { - defaultGroupProvider = new DefaultGroupProvider(); - } - return defaultGroupProvider; - } - - /** - * {@inheritDoc} - * - * @see org.eclipse.compare.structuremergeviewer.DiffTreeViewer#inputChanged(java.lang.Object, - * java.lang.Object) - */ - @Override - protected void inputChanged(Object input, Object oldInput) { - if (oldInput instanceof ICompareInput) { - ICompareInput old = (ICompareInput)oldInput; - old.removeCompareInputChangeListener(fCompareInputChangeListener); - } - if (input instanceof ICompareInput) { - ICompareInput ci = (ICompareInput)input; - ci.addCompareInputChangeListener(fCompareInputChangeListener); - - // Hack to display a message in the tree viewer while the differences are being computed. - TreeItem item = new TreeItem(getTree(), SWT.NONE); - item.setText("Computing model differences..."); - - compareInputChanged(ci); - } - } - - /** - * {@inheritDoc} - * - * @see org.eclipse.compare.structuremergeviewer.DiffTreeViewer#handleDispose(org.eclipse.swt.events.DisposeEvent) - */ - @Override - protected void handleDispose(DisposeEvent event) { - Object input = getInput(); - if (input instanceof ICompareInput) { - ICompareInput ci = (ICompareInput)input; - ci.removeCompareInputChangeListener(fCompareInputChangeListener); - } - compareInputChanged((ICompareInput)null); - fAdapterFactory.dispose(); - - super.handleDispose(event); - } - - /** - * {@inheritDoc} - * - * @see org.eclipse.emf.common.command.CommandStackListener#commandStackChanged(java.util.EventObject) - */ - public void commandStackChanged(EventObject event) { - Command mostRecentCommand = ((CommandStack)event.getSource()).getMostRecentCommand(); - if (mostRecentCommand instanceof ICompareCopyCommand) { - Collection<?> affectedObjects = mostRecentCommand.getAffectedObjects(); - - SWTUtil.safeAsyncExec(new Runnable() { - public void run() { - refresh(true); - } - }); - if (!affectedObjects.isEmpty()) { - // MUST NOT call a setSelection with a list, o.e.compare does not handle it (cf - // org.eclipse.compare.CompareEditorInput#getElement(ISelection)) - final Object adaptedAffectedObject = fAdapterFactory.adapt(getFirst(affectedObjects, null), - ICompareInput.class); - SWTUtil.safeAsyncExec(new Runnable() { - public void run() { - setSelectionToWidget(new StructuredSelection(adaptedAffectedObject), true); - } - }); - } - } else { - // FIXME, should recompute the difference, something happened outside of this compare editor - } - } - - /** - * {@inheritDoc} - * - * @see org.eclipse.jface.viewers.AbstractTreeViewer#getSortedChildren(java.lang.Object) - */ - @Override - protected Object[] getSortedChildren(Object parentElementOrTreePath) { - Object[] result = super.getSortedChildren(parentElementOrTreePath); - if (parentElementOrTreePath instanceof Adapter - && ((Adapter)parentElementOrTreePath).getTarget() instanceof Conflict) { - - Collections.sort(Arrays.asList(result), new Comparator<Object>() { - public int compare(Object o1, Object o2) { - return getValue(o1) - getValue(o2); - } - - public int getValue(Object o) { - int value = 0; - if (o instanceof Adapter && ((Adapter)o).getTarget() instanceof Diff) { - if (((Diff)((Adapter)o).getTarget()).getSource() == DifferenceSource.LEFT) { - value = 1; - } else { - value = 2; - } - } - return value; - } - }); - - } - return result; - } } diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerContentProvider.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerContentProvider.java index 492208bce..901488d5b 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerContentProvider.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerContentProvider.java @@ -23,6 +23,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; import org.eclipse.compare.CompareConfiguration; @@ -97,8 +99,19 @@ class EMFCompareStructureMergeViewerContentProvider extends AdapterFactoryConten if (element instanceof Adapter) { ret = getAdapterFactory().adapt(super.getParent(((Adapter)element).getTarget()), ICompareInput.class); + if (ret instanceof ComparisonNode) { + Comparison root = ((ComparisonNode)ret).getTarget(); + final Iterable<? extends IDifferenceGroup> groups = fViewerGrouper.getGroups(root); + Collection<IDifferenceGroup> parentGroups = new LinkedHashSet<IDifferenceGroup>(); + for (IDifferenceGroup iDifferenceGroup : groups) { + parentGroups.add(iDifferenceGroup); + } + if (!parentGroups.isEmpty()) { + ret = parentGroups; + } + } } else if (element instanceof IDifferenceGroup) { - ret = ((IDifferenceGroup)element).getComparison(); + ret = getAdapterFactory().adapt(((IDifferenceGroup)element).getComparison(), ICompareInput.class); } else { ret = null; } @@ -298,7 +311,7 @@ class EMFCompareStructureMergeViewerContentProvider extends AdapterFactoryConten * the given differences. * @return true if the object should be contained in the given differences, false otherwise. */ - private boolean isPartOfGroup(Object object, final Iterable<? extends Diff> differences) { + public static boolean isPartOfGroup(Object object, final Iterable<? extends Diff> differences) { final Predicate<? super EObject> isPartOfTree = new Predicate<EObject>() { public boolean apply(EObject input) { return Iterables.contains(differences, input); @@ -380,7 +393,7 @@ class EMFCompareStructureMergeViewerContentProvider extends AdapterFactoryConten * * @return the wrapped AbstractEDiffNode. */ - private AbstractEDiffNode delegate() { + public AbstractEDiffNode delegate() { return fDelegate; } @@ -510,5 +523,15 @@ class EMFCompareStructureMergeViewerContentProvider extends AdapterFactoryConten public boolean isAdapterForType(Object type) { return delegate().isAdapterForType(type); } + + @Override + public boolean equals(Object obj) { + return delegate().equals(obj); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } } } diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerCreator.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerCreator.java index b9b13a0de..9c8b99dfa 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerCreator.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/EMFCompareStructureMergeViewerCreator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Obeo. + * 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 diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractAcceptRejectAllChanges.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractAcceptRejectAllChanges.java new file mode 100644 index 000000000..6ad6ac943 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractAcceptRejectAllChanges.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import static com.google.common.collect.Iterables.filter; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.util.List; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +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.command.CompoundCommand; +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.command.ICompareCopyCommand; +import org.eclipse.emf.compare.domain.ICompareEditingDomain; +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; +import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.emf.compare.utils.EMFComparePredicates; +import org.eclipse.emf.ecore.change.util.ChangeRecorder; +import org.eclipse.emf.edit.command.ChangeCommand; +import org.eclipse.ui.ISources; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Abstract handler that manages the accept all and reject all actions (when one side of a diff is not + * editable). + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public abstract class AbstractAcceptRejectAllChanges extends AbstractHandler { + + /** The compare configuration object used to get the compare model. */ + protected CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + 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(); + boolean rightEditableOnly = !configuration.isLeftEditable() && configuration.isRightEditable(); + boolean leftEditableOnly = configuration.isLeftEditable() && !configuration.isRightEditable(); + if (leftEditableOnly) { + manageChanges(false); + } else if (rightEditableOnly) { + manageChanges(true); + } + } + return null; + } + + /** + * Check if the way of merge of the given diff correspond to a copy or a simple change state (unresolved + * to merged). + * + * @param diff + * the given diff. + * @param leftToRight + * the way of merge. + * @return true if the way of merge of the given diff correspond to a copy, false if it corresponds to a + * simple change state. + */ + protected abstract boolean isCopyDiffCase(Diff diff, boolean leftToRight); + + /** + * Manage changes (copy or change state) for all non-conflicting diffs. + * + * @param leftToRight + * the way of merge. + */ + private void manageChanges(final boolean leftToRight) { + final List<Diff> differences; + Comparison comparison = (Comparison)configuration.getProperty(EMFCompareConstants.COMPARE_RESULT); + if (comparison.isThreeWay()) { + differences = ImmutableList.copyOf(filter(comparison.getDifferences(), new Predicate<Diff>() { + public boolean apply(Diff diff) { + final boolean unresolved = diff.getState() == DifferenceState.UNRESOLVED; + final boolean nonConflictual = diff.getConflict() == null; + final boolean fromLeftToRight = leftToRight && diff.getSource() == DifferenceSource.LEFT; + final boolean fromRightToLeft = !leftToRight + && diff.getSource() == DifferenceSource.RIGHT; + return unresolved && nonConflictual && (fromLeftToRight || fromRightToLeft); + } + })); + } else { + differences = ImmutableList.copyOf(filter(comparison.getDifferences(), EMFComparePredicates + .hasState(DifferenceState.UNRESOLVED))); + } + + if (differences.size() > 0) { + + ICompareEditingDomain editingDomain = (ICompareEditingDomain)configuration + .getProperty(EMFCompareConstants.EDITING_DOMAIN); + + AcceptRejectAllChangesCompoundCommand compoundCommand = new AcceptRejectAllChangesCompoundCommand( + leftToRight); + + for (Diff diff : differences) { + if (DifferenceState.UNRESOLVED == diff.getState()) { + if (isCopyDiffCase(diff, leftToRight)) { + EMFCompareUIHandlerUtil.setMergeDataForDiff(diff, leftToRight, configuration + .isLeftEditable(), configuration.isRightEditable()); + compoundCommand.append(editingDomain.createCopyCommand(Lists.newArrayList(diff), + leftToRight, EMFCompareRCPPlugin.getDefault().getMergerRegistry())); + } else { + compoundCommand.append(createChangeStateFromUnresolvedToMergedCommand(diff, + !leftToRight)); + } + } + } + + editingDomain.getCommandStack().execute(compoundCommand); + } + } + + /** + * Execute a command that change the state of the given diff from {@link DifferenceState#UNRESOLVED} to + * {@link DifferenceState#MERGED}. + * + * @param diffToChangeState + * the given diff. + * @param leftToRight + * the way of merge. + * @return A command that change the state of the given diff from {@link DifferenceState#UNRESOLVED} to + * {@link DifferenceState#MERGED}. + */ + protected Command createChangeStateFromUnresolvedToMergedCommand(Diff diffToChangeState, + boolean leftToRight) { + if (diffToChangeState != null) { + ICompareEditingDomain compareEditingDomain = (ICompareEditingDomain)configuration + .getProperty(EMFCompareConstants.EDITING_DOMAIN); + Command changeStateCommand = new AcceptRejectAllChangesCommand(compareEditingDomain + .getChangeRecorder(), diffToChangeState, leftToRight, configuration); + return changeStateCommand; + } + return null; + } + + /** + * A specific {@link ChangeCommand} that change the state of the given diff from + * {@link DifferenceState#UNRESOLVED} to {@link DifferenceState#MERGED}. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ + private static class AcceptRejectAllChangesCommand extends ChangeCommand implements ICompareCopyCommand { + + /** The difference concerned by the command. */ + private Diff difference; + + /** The way of merge. */ + private boolean leftToRight; + + /** The compare configuration object. */ + private CompareConfiguration configuration; + + /** + * Constructor. + * + * @param changeRecorder + * the change recorder used by the command. + * @param difference + * the difference concerned by the command. + * @param leftToRight + * the way of merge. + * @param configuration + * the compare configuration object. + */ + public AcceptRejectAllChangesCommand(ChangeRecorder changeRecorder, Diff difference, + boolean leftToRight, CompareConfiguration configuration) { + super(changeRecorder, difference); + this.difference = difference; + this.leftToRight = leftToRight; + this.configuration = configuration; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.edit.command.ChangeCommand#doExecute() + */ + @Override + public void doExecute() { + EMFCompareUIHandlerUtil.setMergeDataForDiff(difference, leftToRight, configuration + .isLeftEditable(), configuration.isRightEditable()); + difference.setState(DifferenceState.MERGED); + + } + + /** + * Returns true if the command will be applied from left to right side, false otherwise. + * + * @return true if the command will be applied from left to right side, false otherwise. + */ + public boolean isLeftToRight() { + return leftToRight; + } + + } + + /** + * A specific {@link CompoundCommand} that implements {@link ICompareCopyCommand}. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ + private static class AcceptRejectAllChangesCompoundCommand extends CompoundCommand implements ICompareCopyCommand { + + /** The way of merge. */ + private boolean leftToRight; + + /** + * Constructor. + * + * @param leftToRight + * the way of merge. + */ + public AcceptRejectAllChangesCompoundCommand(boolean leftToRight) { + this.leftToRight = leftToRight; + } + + /** + * Returns true if the command will be applied from left to right side, false otherwise. + * + * @return true if the command will be applied from left to right side, false otherwise. + */ + public boolean isLeftToRight() { + return leftToRight; + } + + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractAcceptRejectChange.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractAcceptRejectChange.java new file mode 100644 index 000000000..317da68e6 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractAcceptRejectChange.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import com.google.common.collect.ImmutableSet; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +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.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; +import org.eclipse.emf.compare.internal.utils.DiffUtil; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.emf.ecore.change.util.ChangeRecorder; +import org.eclipse.emf.edit.command.ChangeCommand; +import org.eclipse.ui.ISources; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Abstract handler that manages the accept and reject actions (when one side of a diff is not editable). + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public abstract class AbstractAcceptRejectChange extends AbstractHandler { + + /** The compare configuration object used to get the compare model. */ + private CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + 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 rightEditableOnly = !configuration.isLeftEditable() + && configuration.isRightEditable(); + boolean leftEditableOnly = configuration.isLeftEditable() + && !configuration.isRightEditable(); + if (leftEditableOnly) { + if (isCopyDiffCase((Diff)diff, false)) { + EMFCompareUIHandlerUtil.copyDiff((Diff)diff, false, configuration); + } else { + changeStateFromUnresolvedToMerged((Diff)diff, true); + } + } else if (rightEditableOnly) { + if (isCopyDiffCase((Diff)diff, true)) { + EMFCompareUIHandlerUtil.copyDiff((Diff)diff, true, configuration); + } else { + changeStateFromUnresolvedToMerged((Diff)diff, false); + } + } + // Select next diff + EMFCompareUIHandlerUtil.navigate(true, configuration); + } + } + } + return null; + } + + /** + * Check if the way of merge of the given diff correspond to a copy or a simple change state (unresolved + * to merged). + * + * @param diff + * the given diff. + * @param leftToRight + * the way of merge. + * @return true if the way of merge of the given diff correspond to a copy, false if it corresponds to a + * simple change state. + */ + protected abstract boolean isCopyDiffCase(Diff diff, boolean leftToRight); + + /** + * Execute a command that change the state of the given diff from {@link DifferenceState#UNRESOLVED} to + * {@link DifferenceState#MERGED}. + * + * @param diffToChangeState + * the given diff. + * @param leftToRight + * the way of merge. + */ + private void changeStateFromUnresolvedToMerged(Diff diffToChangeState, boolean leftToRight) { + if (diffToChangeState != null) { + ICompareEditingDomain compareEditingDomain = (ICompareEditingDomain)configuration + .getProperty(EMFCompareConstants.EDITING_DOMAIN); + Command changeStateCommand = new AcceptRejectChangeCommand(compareEditingDomain + .getChangeRecorder(), diffToChangeState, leftToRight, configuration); + compareEditingDomain.getCommandStack().execute(changeStateCommand); + } + } + + /** + * A specific {@link ChangeCommand} that change the state of the given diff and all its required diffs + * from {@link DifferenceState#UNRESOLVED} to {@link DifferenceState#MERGED}. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ + private static class AcceptRejectChangeCommand extends ChangeCommand implements ICompareCopyCommand { + + /** The difference concerned by the command. */ + private Diff difference; + + /** The way of merge. */ + private boolean leftToRight; + + /** The compare configuration object. */ + private CompareConfiguration configuration; + + /** + * Constructor. + * + * @param changeRecorder + * the change recorder used by the command. + * @param difference + * the difference concerned by the command. + * @param leftToRight + * the way of merge. + * @param configuration + * the compare configuration object. + */ + public AcceptRejectChangeCommand(ChangeRecorder changeRecorder, Diff difference, boolean leftToRight, + CompareConfiguration configuration) { + super(changeRecorder, ImmutableSet.<Notifier> builder().add(difference).addAll( + DiffUtil.getRequires(difference, leftToRight, difference.getSource())).build()); + this.difference = difference; + this.leftToRight = leftToRight; + this.configuration = configuration; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.edit.command.ChangeCommand#doExecute() + */ + @Override + public void doExecute() { + for (Diff require : DiffUtil.getRequires(difference, leftToRight, difference.getSource())) { + EMFCompareUIHandlerUtil.setMergeDataForDiff(require, leftToRight, configuration + .isLeftEditable(), configuration.isRightEditable()); + require.setState(DifferenceState.MERGED); + } + EMFCompareUIHandlerUtil.setMergeDataForDiff(difference, leftToRight, configuration + .isLeftEditable(), configuration.isRightEditable()); + difference.setState(DifferenceState.MERGED); + + } + + /** + * Returns true if the command will be applied from left to right side, false otherwise. + * + * @return true if the command will be applied from left to right side, false otherwise. + */ + public boolean isLeftToRight() { + return leftToRight; + } + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractMergedAllTo.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractMergedAllTo.java new file mode 100644 index 000000000..0cc28123a --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractMergedAllTo.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.ISources; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Abstract Handler that manages a merge of a all non-conflicting difference in case of both sides of the + * comparison are editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public abstract class AbstractMergedAllTo extends AbstractHandler { + + /** The compare configuration object used to get the compare model. */ + private CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + Object editorInput = HandlerUtil.getVariable(event, ISources.ACTIVE_EDITOR_INPUT_NAME); + if (editorInput instanceof CompareEditorInput) { + setConfiguration(((CompareEditorInput)editorInput).getCompareConfiguration()); + copyAllDiffs(); + } + return null; + } + + /** + * Copy all non-conflicting differences. + */ + protected abstract void copyAllDiffs(); + + /** + * Get the compare configuration object. + * + * @return the configuration + */ + public CompareConfiguration getConfiguration() { + return configuration; + } + + /** + * Set the compare configuration object. + * + * @param configuration + * the configuration to set + */ + public void setConfiguration(CompareConfiguration configuration) { + this.configuration = configuration; + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractMergedTo.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractMergedTo.java new file mode 100644 index 000000000..ddb017340 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AbstractMergedTo.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +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.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; +import org.eclipse.ui.ISources; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Abstract Handler that manages a merge of a difference in case of both sides of the comparison are editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public abstract class AbstractMergedTo extends AbstractHandler { + + /** The compare configuration object used to get the compare model. */ + private CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + Object editorInput = HandlerUtil.getVariable(event, ISources.ACTIVE_EDITOR_INPUT_NAME); + if (editorInput instanceof CompareEditorInput) { + setConfiguration(((CompareEditorInput)editorInput).getCompareConfiguration()); + Object diffNode = ((CompareEditorInput)editorInput).getSelectedEdition(); + if (diffNode instanceof Adapter) { + Notifier diff = ((Adapter)diffNode).getTarget(); + if (diff instanceof Diff) { + copyDiff((Diff)diff); + // Select next diff + EMFCompareUIHandlerUtil.navigate(true, configuration); + } + } + } + return null; + } + + /** + * Copy the diff. + * + * @param diff + * the given diff. + */ + protected abstract void copyDiff(Diff diff); + + /** + * Get the compare configuration object. + * + * @return the configuration + */ + public CompareConfiguration getConfiguration() { + return configuration; + } + + /** + * Set the compare configuration object. + * + * @param configuration + * the configuration to set + */ + public void setConfiguration(CompareConfiguration configuration) { + this.configuration = configuration; + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AcceptAllChanges.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AcceptAllChanges.java new file mode 100644 index 000000000..2929d5702 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AcceptAllChanges.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.ide.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 all non-conflicting differences in case of one side of the comparison is + * not editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class AcceptAllChanges extends AbstractAcceptRejectAllChanges { + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AbstractAcceptRejectAllChanges#isCopyDiffCase(org.eclipse.emf.compare.Diff, + * boolean) + */ + @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.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AcceptChange.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AcceptChange.java new file mode 100644 index 000000000..89967207a --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/AcceptChange.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.ide.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 not editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class AcceptChange extends AbstractAcceptRejectChange { + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AbstractAcceptRejectChange#isCopyDiffCase(org.eclipse.emf.compare.Diff, + * boolean) + */ + @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.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownHandler.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownHandler.java new file mode 100644 index 000000000..3d9494106 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownHandler.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import java.util.Map; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.compare.internal.CompareEditor; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.ISources; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.commands.IElementUpdater; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.menus.UIElement; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * Handler that manages the click on the dropdown menu of the toolbar of the structure merge viewer. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public class DropDownHandler extends AbstractHandler implements IElementUpdater { + + /** The compare configuration object. */ + private CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + + Object editorInput = HandlerUtil.getVariable(event, ISources.ACTIVE_EDITOR_INPUT_NAME); + if (editorInput instanceof CompareEditorInput) { + Object trigger = event.getTrigger(); + if (trigger instanceof Event) { + Event eventWidget = (Event)event.getTrigger(); + Widget widget = eventWidget.widget; + if (widget instanceof ToolItem) { + ToolItem toolItem = (ToolItem)widget; + configuration = ((CompareEditorInput)editorInput).getCompareConfiguration(); + Boolean mergeWay = (Boolean)configuration.getProperty(EMFCompareConstants.MERGE_WAY); + if (mergeWay == null || mergeWay.booleanValue()) { + configuration.setProperty(EMFCompareConstants.MERGE_WAY, new Boolean(false)); + toolItem.setImage(EMFCompareIDEUIPlugin + .getImage("icons/full/toolb16/right_to_left.gif")); //$NON-NLS-1$ + } else { + configuration.setProperty(EMFCompareConstants.MERGE_WAY, new Boolean(true)); + toolItem.setImage(EMFCompareIDEUIPlugin + .getImage("icons/full/toolb16/left_to_right.gif")); //$NON-NLS-1$ + } + } + } + } + + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.commands.IElementUpdater#updateElement(UIElement, Map) + */ + public void updateElement(UIElement element, Map parameters) { + if (configuration == null) { + Object value = parameters.get("org.eclipse.ui.IWorkbenchWindow"); //$NON-NLS-1$ + if (value instanceof IWorkbenchWindow) { + IWorkbenchPage pa = ((IWorkbenchWindow)value).getActivePage(); + IEditorPart editor = pa.getActiveEditor(); + if (editor instanceof CompareEditor) { + IEditorInput editorInput = editor.getEditorInput(); + if (editorInput instanceof CompareEditorInput) { + configuration = ((CompareEditorInput)editorInput).getCompareConfiguration(); + } + } + } + } + if (configuration != null) { + Boolean mergeWay = (Boolean)configuration.getProperty(EMFCompareConstants.MERGE_WAY); + if (mergeWay == null || mergeWay.booleanValue()) { + element.setIcon(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID, + "icons/full/toolb16/left_to_right.gif")); //$NON-NLS-1$ + } else { + element.setIcon(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID, + "icons/full/toolb16/right_to_left.gif")); //$NON-NLS-1$ + } + } + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownLeftToRight.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownLeftToRight.java new file mode 100644 index 000000000..beeca1c86 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownLeftToRight.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.ui.ISources; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Handler that manages the click on the menu item left to right in the dropdown menu of the toolbar of the + * structure merge viewer. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public class DropDownLeftToRight extends AbstractHandler { + + /** The compare configuration object. */ + private CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + 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(); + configuration.setProperty(EMFCompareConstants.MERGE_WAY, new Boolean(true)); + } + + ICommandService commandService = (ICommandService)PlatformUI.getWorkbench().getService( + ICommandService.class); + commandService.refreshElements("org.eclipse.emf.compare.ide.ui.dropdown", null); //$NON-NLS-1$ + + return null; + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownRightToLeft.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownRightToLeft.java new file mode 100644 index 000000000..3daff914f --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/DropDownRightToLeft.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.ui.ISources; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Handler that manages the click on the menu item right to left in the dropdown menu of the toolbar of the + * structure merge viewer. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public class DropDownRightToLeft extends AbstractHandler { + + /** The compare configuration object. */ + private CompareConfiguration configuration; + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + 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(); + configuration.setProperty(EMFCompareConstants.MERGE_WAY, new Boolean(false)); + } + ICommandService commandService = (ICommandService)PlatformUI.getWorkbench().getService( + ICommandService.class); + commandService.refreshElements("org.eclipse.emf.compare.ide.ui.dropdown", null); //$NON-NLS-1$ + return null; + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedAllToLeft.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedAllToLeft.java new file mode 100644 index 000000000..543f4c945 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedAllToLeft.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; + +/** + * Handler that manages a merge from left to right of all non-conflicting differences in case of both sides of + * the comparison are editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class MergedAllToLeft extends AbstractMergedAllTo { + + @Override + protected void copyAllDiffs() { + EMFCompareUIHandlerUtil.copyAllDiffs(false, getConfiguration()); + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedAllToRight.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedAllToRight.java new file mode 100644 index 000000000..eab8d32f0 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedAllToRight.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; + +/** + * Handler that manages a merge from left to right of all non-conflicting differences in case of both sides of + * the comparison are editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class MergedAllToRight extends AbstractMergedAllTo { + + @Override + protected void copyAllDiffs() { + EMFCompareUIHandlerUtil.copyAllDiffs(true, getConfiguration()); + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedToLeft.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedToLeft.java new file mode 100644 index 000000000..dde61ec92 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedToLeft.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; + +/** + * Handler that manages a merge from right to left of a difference in case of both sides of the comparison are + * editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class MergedToLeft extends AbstractMergedTo { + + @Override + protected void copyDiff(Diff diff) { + EMFCompareUIHandlerUtil.copyDiff(diff, false, getConfiguration()); + } +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedToRight.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedToRight.java new file mode 100644 index 000000000..5e754f386 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/MergedToRight.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; + +/** + * Handler that manages a merge from left to right of a difference in case of both sides of the comparison are + * editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class MergedToRight extends AbstractMergedTo { + + @Override + protected void copyDiff(Diff diff) { + EMFCompareUIHandlerUtil.copyDiff(diff, true, getConfiguration()); + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/RejectAllChanges.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/RejectAllChanges.java new file mode 100644 index 000000000..1e9cd62a9 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/RejectAllChanges.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.ide.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 all non-conflicting differences in case of one side of the comparison is + * not editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class RejectAllChanges extends AbstractAcceptRejectAllChanges { + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AbstractAcceptRejectAllChanges#isCopyDiffCase(org.eclipse.emf.compare.Diff, + * boolean) + */ + @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.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/RejectChange.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/RejectChange.java new file mode 100644 index 000000000..996a129bd --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/RejectChange.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.ide.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 not editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class RejectChange extends AbstractAcceptRejectChange { + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AbstractAcceptRejectChange#isCopyDiffCase(org.eclipse.emf.compare.Diff, + * boolean) + */ + @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.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/SelectNextDiff.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/SelectNextDiff.java new file mode 100644 index 000000000..e2e6bb163 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/SelectNextDiff.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; +import org.eclipse.ui.ISources; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Handler that manages the select next diff button. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public class SelectNextDiff extends AbstractHandler { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + Object editorInput = HandlerUtil.getVariable(event, ISources.ACTIVE_EDITOR_INPUT_NAME); + if (editorInput instanceof CompareEditorInput) { + CompareConfiguration configuration = ((CompareEditorInput)editorInput).getCompareConfiguration(); + // Select next diff + EMFCompareUIHandlerUtil.navigate(true, configuration); + } + return null; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/SelectPreviousDiff.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/SelectPreviousDiff.java new file mode 100644 index 000000000..5ecd4e10d --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/SelectPreviousDiff.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.util.EMFCompareUIHandlerUtil; +import org.eclipse.ui.ISources; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Handler that manages the select previous diff button. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public class SelectPreviousDiff extends AbstractHandler { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + Object editorInput = HandlerUtil.getVariable(event, ISources.ACTIVE_EDITOR_INPUT_NAME); + if (editorInput instanceof CompareEditorInput) { + CompareConfiguration configuration = ((CompareEditorInput)editorInput).getCompareConfiguration(); + // Select next diff + EMFCompareUIHandlerUtil.navigate(false, configuration); + } + return null; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/AcceptRejectChangePropertyTester.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/AcceptRejectChangePropertyTester.java new file mode 100644 index 000000000..a79978c13 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/AcceptRejectChangePropertyTester.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler.propertytester; + +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; + +/** + * A property tester linked with + * {@link org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AbstractMergedTo}. It returns + * true when only one model side is editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class AcceptRejectChangePropertyTester extends PropertyTester { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.expressions.PropertyTester#test(java.lang.Object, java.lang.String, + * java.lang.Object[], java.lang.Object) + */ + 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.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/DiffSelectedPropertyTester.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/DiffSelectedPropertyTester.java new file mode 100644 index 000000000..ebad5be65 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/DiffSelectedPropertyTester.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler.propertytester; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.expressions.PropertyTester; +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.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; + +/** + * A property tester that checks if a diff is selected in the compare editor. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class DiffSelectedPropertyTester extends PropertyTester { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.expressions.PropertyTester#test(java.lang.Object, java.lang.String, + * java.lang.Object[], java.lang.Object) + */ + 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(); + ISelection selection = (ISelection)configuration + .getProperty(EMFCompareConstants.SMV_SELECTION); + if (selection instanceof IStructuredSelection) { + Object element = ((IStructuredSelection)selection).getFirstElement(); + if (element instanceof Adapter) { + Notifier diffNode = ((Adapter)element).getTarget(); + if (diffNode instanceof Diff) { + return true; + } + } + } + } + } + return false; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/LeftToRightSidePropertyTester.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/LeftToRightSidePropertyTester.java new file mode 100644 index 000000000..780860517 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/LeftToRightSidePropertyTester.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler.propertytester; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; + +/** + * A property tester that check the way of merge. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class LeftToRightSidePropertyTester extends PropertyTester { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.expressions.PropertyTester#test(java.lang.Object, java.lang.String, + * java.lang.Object[], java.lang.Object) + */ + 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(); + Boolean leftToRight = (Boolean)configuration.getProperty(EMFCompareConstants.MERGE_WAY); + if (leftToRight == null || leftToRight.booleanValue()) { + return true; + } + } + } + return false; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/MergedToPropertyTester.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/MergedToPropertyTester.java new file mode 100644 index 000000000..75447c35f --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/MergedToPropertyTester.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler.propertytester; + +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; + +/** + * A property tester linked with + * {@link org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.handler.AbstractMergedTo}. It returns + * true when both model sides are editable. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class MergedToPropertyTester extends PropertyTester { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.expressions.PropertyTester#test(java.lang.Object, java.lang.String, + * java.lang.Object[], java.lang.Object) + */ + 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; + } + } + } + return false; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/ModelSaveablePropertyTester.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/ModelSaveablePropertyTester.java index 3b409db57..ea6890f04 100644 --- a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/ModelSaveablePropertyTester.java +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/ModelSaveablePropertyTester.java @@ -8,7 +8,7 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler; +package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.propertytester; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareEditorInput; @@ -17,8 +17,9 @@ import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; /** - * A property tester linked with {@link SaveComparisonModel}. It tests the editable property of both model - * sides. + * A property tester linked with + * {@link org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.handler.SaveComparisonModel}. It tests + * the editable property of both model sides. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> * @since 3.0 diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/RightToLeftSidePropertyTester.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/RightToLeftSidePropertyTester.java new file mode 100644 index 000000000..92310abc7 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/propertytester/RightToLeftSidePropertyTester.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler.propertytester; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareConstants; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; + +/** + * A property tester that check the way of merge. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class RightToLeftSidePropertyTester extends PropertyTester { + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.expressions.PropertyTester#test(java.lang.Object, java.lang.String, + * java.lang.Object[], java.lang.Object) + */ + 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(); + Boolean leftToRight = (Boolean)configuration.getProperty(EMFCompareConstants.MERGE_WAY); + if (leftToRight != null && !leftToRight.booleanValue()) { + return true; + } + } + } + return false; + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/util/EMFCompareUIHandlerUtil.java b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/util/EMFCompareUIHandlerUtil.java new file mode 100644 index 000000000..59e1b6bb9 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.ui/src/org/eclipse/emf/compare/ide/ui/internal/structuremergeviewer/handler/util/EMFCompareUIHandlerUtil.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * 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.ide.ui.internal.structuremergeviewer.handler.util; + +import static com.google.common.collect.Iterables.addAll; +import static com.google.common.collect.Iterables.filter; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareNavigator; +import org.eclipse.compare.ICompareNavigator; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.notify.Adapter; +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.domain.ICompareEditingDomain; +import org.eclipse.emf.compare.internal.merge.DiffMergeDataAdapter; +import org.eclipse.emf.compare.internal.merge.IDiffMergeData; +import org.eclipse.emf.compare.internal.utils.DiffUtil; +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.EMFComparePredicates; +import org.eclipse.emf.ecore.util.EcoreUtil; + +/** + * Util class that provides utilities methods for RCP UI handlers. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public final class EMFCompareUIHandlerUtil { + + /** + * Utility classes don't need a default constructor. + */ + private EMFCompareUIHandlerUtil() { + // Hides default constructor. + } + + /** + * Checks the state of the cascading differences filter. + * + * @param configuration + * the compare configuration object. + * @return true, if the cascading differences filter is active, false otherwise. + */ + public static boolean isCascadingDifferencesFilterActive(CompareConfiguration configuration) { + Object property = configuration.getProperty(EMFCompareConstants.SELECTED_FILTERS); + final Collection<IDifferenceFilter> selectedFilters; + if (property != null) { + selectedFilters = (Collection<IDifferenceFilter>)property; + for (IDifferenceFilter iDifferenceFilter : selectedFilters) { + if (iDifferenceFilter instanceof CascadingDifferencesFilter) { + return true; + } + } + } + return false; + } + + /** + * Copy the given diff. + * + * @param diffToCopy + * the given diff to copy. + * @param leftToRight + * the way of merge. + * @param configuration + * the compare configuration object. + */ + public static void copyDiff(Diff diffToCopy, boolean leftToRight, CompareConfiguration configuration) { + if (diffToCopy != null) { + List<Diff> diffsToCopy = new ArrayList<Diff>(); + diffsToCopy.add(diffToCopy); + for (Diff require : DiffUtil.getRequires(diffToCopy, leftToRight, diffToCopy.getSource())) { + EMFCompareUIHandlerUtil.setMergeDataForDiff(require, leftToRight, configuration + .isLeftEditable(), configuration.isRightEditable()); + } + if (EMFCompareUIHandlerUtil.isCascadingDifferencesFilterActive(configuration)) { + addAll(diffsToCopy, org.eclipse.emf.compare.utils.DiffUtil.getSubDiffs(leftToRight).apply( + diffToCopy)); + } + for (Diff diff : diffsToCopy) { + EMFCompareUIHandlerUtil.setMergeDataForDiff(diff, leftToRight, + configuration.isLeftEditable(), configuration.isRightEditable()); + } + ICompareEditingDomain editingDomain = (ICompareEditingDomain)configuration + .getProperty(EMFCompareConstants.EDITING_DOMAIN); + Command copyCommand = editingDomain.createCopyCommand(diffsToCopy, leftToRight, + EMFCompareRCPPlugin.getDefault().getMergerRegistry()); + editingDomain.getCommandStack().execute(copyCommand); + } + } + + /** + * Copy all non-conflicting changes. + * + * @param leftToRight + * the way of merge. + * @param configuration + * the compare configuration object. + */ + public static void copyAllDiffs(final boolean leftToRight, CompareConfiguration configuration) { + final List<Diff> differences; + Comparison comparison = (Comparison)configuration.getProperty(EMFCompareConstants.COMPARE_RESULT); + if (comparison.isThreeWay()) { + differences = ImmutableList.copyOf(filter(comparison.getDifferences(), new Predicate<Diff>() { + public boolean apply(Diff diff) { + final boolean unresolved = diff.getState() == DifferenceState.UNRESOLVED; + final boolean nonConflictual = diff.getConflict() == null; + final boolean fromLeftToRight = leftToRight && diff.getSource() == DifferenceSource.LEFT; + final boolean fromRightToLeft = !leftToRight + && diff.getSource() == DifferenceSource.RIGHT; + return unresolved && nonConflictual && (fromLeftToRight || fromRightToLeft); + } + })); + } else { + differences = ImmutableList.copyOf(filter(comparison.getDifferences(), EMFComparePredicates + .hasState(DifferenceState.UNRESOLVED))); + } + + if (differences.size() > 0) { + for (Diff diff : differences) { + EMFCompareUIHandlerUtil.setMergeDataForDiff(diff, leftToRight, + configuration.isLeftEditable(), configuration.isRightEditable()); + } + ICompareEditingDomain editingDomain = (ICompareEditingDomain)configuration + .getProperty(EMFCompareConstants.EDITING_DOMAIN); + final Command copyCommand = editingDomain.createCopyCommand(differences, leftToRight, + EMFCompareRCPPlugin.getDefault().getMergerRegistry()); + + editingDomain.getCommandStack().execute(copyCommand); + } + } + + /** + * 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. + * @param configuration + * the compare configuration object. + */ + public static void navigate(boolean next, CompareConfiguration configuration) { + final ICompareNavigator navigator = configuration.getContainer().getNavigator(); + if (navigator instanceof CompareNavigator && ((CompareNavigator)navigator).hasChange(next)) { + navigator.selectChange(next); + } + } + + /** + * 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. + * @param leftEditable + * the left side of the difference is editable. + * @param rightEditable + * the right side of the difference is editable. + */ + public static void setMergeDataForDiff(Diff diff, boolean leftToRight, boolean leftEditable, + boolean rightEditable) { + Adapter adapter = EcoreUtil.getExistingAdapter(diff, IDiffMergeData.class); + if (adapter != null) { + ((IDiffMergeData)adapter).setMergedTo(leftToRight); + ((IDiffMergeData)adapter).setLeftEditable(leftEditable); + ((IDiffMergeData)adapter).setRightEditable(rightEditable); + } else { + diff.eAdapters().add(new DiffMergeDataAdapter(leftToRight, leftEditable, rightEditable)); + } + } +} diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/EMFCompareConstants.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/EMFCompareConstants.java index aa7c8f985..4d1b07392 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/EMFCompareConstants.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/EMFCompareConstants.java @@ -43,6 +43,10 @@ public final class EMFCompareConstants { public static final String SELECTED_GROUP = EMFCompareRCPUIPlugin.PLUGIN_ID + ".SELECTED_GROUP"; //$NON-NLS-1$ + public static final String SMV_SELECTION = EMFCompareRCPUIPlugin.PLUGIN_ID + ".SMV_SELECTION"; //$NON-NLS-1$ + + public static final String MERGE_WAY = EMFCompareRCPUIPlugin.PLUGIN_ID + ".MERGE_WAY"; //$NON-NLS-1$ + // ITypedElement#getType() public static final String NODE_TYPE__EMF_RESOURCESET = "NODE_TYPE__EMF_RESOURCESET"; //$NON-NLS-1$ diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/StructureMergeViewerGrouper.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/StructureMergeViewerGrouper.java index e2ed6a97e..29970dfa2 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/StructureMergeViewerGrouper.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/StructureMergeViewerGrouper.java @@ -91,8 +91,8 @@ public final class StructureMergeViewerGrouper { if (this.provider != provider) { this.provider = provider; filteredGroups = null; - eventBus.post(provider); refreshViewers(); + eventBus.post(provider); } } diff --git a/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.compare/META-INF/MANIFEST.MF index 6f1c8b284..10caf73a8 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 @@ Export-Package: org.eclipse.emf.compare, 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.ide.ui", 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.internal.utils; diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/DiffMergeDataAdapter.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/DiffMergeDataAdapter.java new file mode 100644 index 000000000..513e76b47 --- /dev/null +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/DiffMergeDataAdapter.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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; + +/** + * Adapter that help to know the way of merge and the editable sides of a difference. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + * @since 3.0 + */ +public class DiffMergeDataAdapter extends AdapterImpl implements IDiffMergeData { + + /** The merge way. */ + boolean leftToRight; + + /** Left side is editable. */ + boolean leftEditable; + + /** Right side is editable. */ + boolean rightEditable; + + /** + * Constructor. + * + * @param leftToRight + * The merge way. + * @param leftEditable + * Left side editable. + * @param rightEditable + * Right side editable. + */ + public DiffMergeDataAdapter(boolean leftToRight, boolean leftEditable, boolean rightEditable) { + this.leftToRight = leftToRight; + this.leftEditable = leftEditable; + this.rightEditable = rightEditable; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#hasBeenMergedToLeft() + */ + public boolean hasBeenMergedToLeft() { + return !leftToRight; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#hasBeenMergedToRight() + */ + public boolean hasBeenMergedToRight() { + return leftToRight; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#setMergedTo(boolean) + */ + public void setMergedTo(boolean lToR) { + this.leftToRight = lToR; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#isLeftEditable() + */ + public boolean isLeftEditable() { + return leftEditable; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#isRightEditable() + */ + public boolean isRightEditable() { + return rightEditable; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#setLeftEditable(boolean) + */ + public void setLeftEditable(boolean leftEditable) { + this.leftEditable = leftEditable; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.compare.internal.merge.IDiffMergeData#setRightEditable(boolean) + */ + public void setRightEditable(boolean rightEditable) { + this.rightEditable = rightEditable; + } + + @Override + public boolean isAdapterForType(Object type) { + return type == IDiffMergeData.class; + } + +} diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/IDiffMergeData.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/IDiffMergeData.java new file mode 100644 index 000000000..7531b546d --- /dev/null +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/merge/IDiffMergeData.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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; + +/** + * Interface implemented by {@link org.eclipse.emf.compare.internal.merge.DiffMergeDataAdapter}. It helps to + * know the way of merge and the editable sides of a difference. + * + * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> + */ +public interface IDiffMergeData { + + /** + * Check if the difference has been merged from right to left. + * + * @return true if the difference has been merged from right to left, false otherwise. + */ + boolean hasBeenMergedToLeft(); + + /** + * Check if the difference has been merged from left to right. + * + * @return true if the difference has been merged from left to right, false otherwise. + */ + boolean hasBeenMergedToRight(); + + /** + * Set the way of merge. + * + * @param leftToRight + * true if the difference has been merge from left to right, false otehrwise. + */ + void setMergedTo(boolean leftToRight); + + /** + * Check if the left side of the difference is editable. + * + * @return true if the left side of the difference is editable, false otherwise. + */ + boolean isLeftEditable(); + + /** + * Check if the right side of the difference is editable. + * + * @return true if the right side of the difference is editable, false otherwise. + */ + boolean isRightEditable(); + + /** + * Set that the left side of the difference is editable or not. + * + * @param leftEditable + * true if the left side of the difference is editable, false otherwise. + */ + void setLeftEditable(boolean leftEditable); + + /** + * Set that the right side of the difference is editable or not. + * + * @param rightEditable + * true if the right side of the difference is editable, false otherwise. + */ + void setRightEditable(boolean rightEditable); + +} diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java index dfa567f9a..8dedf68c9 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java @@ -10,6 +10,11 @@ *******************************************************************************/ package org.eclipse.emf.compare.internal.utils; +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.or; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; + import com.google.common.base.Predicate; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; @@ -27,6 +32,8 @@ import java.util.Set; import org.eclipse.emf.compare.AttributeChange; 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; @@ -866,6 +873,127 @@ public final class DiffUtil { } /** + * Get the list of all required differences for merge of the given difference (required, required of + * required...). + * + * @param diff + * the given difference. + * @param leftToRight + * the way of merge. + * @param originalDiffSource + * the source of the given diff. + * @return the list of all required differences. + * @since 3.0 + */ + public static Set<Diff> getRequires(Diff diff, boolean leftToRight, DifferenceSource originalDiffSource) { + return getRequires(diff, diff, leftToRight, originalDiffSource, Sets.newHashSet()); + } + + /** + * Get the list of all required differences for merge of the given original difference (required, required + * of required...). + * + * @param currentDiff + * the current difference being processed. + * @param originalDiff + * the original given difference. + * @param leftToRight + * the way of merge. + * @param originalDiffSource + * the source of the given diff. + * @param processedDiffs + * the list of already processed diffs. + * @return the list of all required differences. + * @since 3.0 + */ + private static Set<Diff> getRequires(Diff currentDiff, Diff originalDiff, boolean leftToRight, + DifferenceSource originalDiffSource, Set<Object> processedDiffs) { + Set<Diff> requires = Sets.newHashSet(); + final List<Diff> diffRequires; + if (leftToRight) { + if (DifferenceSource.LEFT == originalDiffSource) { + diffRequires = currentDiff.getRequires(); + } else if (DifferenceSource.RIGHT == originalDiffSource) { + diffRequires = currentDiff.getRequiredBy(); + } else { + diffRequires = Collections.emptyList(); + } + } else { + if (DifferenceSource.RIGHT == originalDiffSource) { + diffRequires = currentDiff.getRequires(); + } else if (DifferenceSource.LEFT == originalDiffSource) { + diffRequires = currentDiff.getRequiredBy(); + } else { + diffRequires = Collections.emptyList(); + } + } + diffRequires.addAll(currentDiff.getRefinedBy()); + for (Diff require : diffRequires) { + if (!originalDiff.equals(require) && !processedDiffs.contains(require)) { + processedDiffs.add(require); + requires.add(require); + requires.addAll(getRequires(require, originalDiff, leftToRight, originalDiffSource, + processedDiffs)); + } + } + return requires; + } + + /** + * Get the list of unmergeable differences after the merge of the given difference. + * + * @param diff + * the given difference. + * @param leftToRight + * the way of merge. + * @return the list of unmergeable differences. + * @since 3.0 + */ + public static Set<Diff> getUnmergeables(Diff diff, boolean leftToRight) { + Set<Diff> unmergeables = Sets.newHashSet(); + Conflict conflict = diff.getConflict(); + if (conflict != null && conflict.getKind() == ConflictKind.REAL) { + for (Diff diffConflict : conflict.getDifferences()) { + if (leftToRight + && 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 (leftToRight + && 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); + } + } else if (!leftToRight + && and(fromSide(DifferenceSource.RIGHT), + or(ofKind(DifferenceKind.DELETE), ofKind(DifferenceKind.CHANGE))).apply(diff)) { + if (and(fromSide(DifferenceSource.LEFT), + or(ofKind(DifferenceKind.ADD), ofKind(DifferenceKind.CHANGE))) + .apply(diffConflict)) { + unmergeables.add(diffConflict); + } + } else if (!leftToRight + && and(fromSide(DifferenceSource.RIGHT), + or(ofKind(DifferenceKind.ADD), ofKind(DifferenceKind.CHANGE))).apply(diff)) { + if (and(fromSide(DifferenceSource.LEFT), + or(ofKind(DifferenceKind.DELETE), ofKind(DifferenceKind.CHANGE))).apply( + diffConflict)) { + unmergeables.add(diffConflict); + } + } + + } + } + return unmergeables; + } + + /** * Retrieves the "source" list of the given {@code diff}. This will be different according to the kind of * change and the direction of the merging. * 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 8d852314c..93460f792 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,190 @@ -/*******************************************************************************
- * 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.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); + } + } + } +} 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 f77eef08e..ce9d162b4 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,753 +1,753 @@ -/*******************************************************************************
- * 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 com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-
-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.internal.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.getSource() == DifferenceSource.LEFT) {
- // merge all "requires" diffs
- mergeRequires(diff, false, monitor);
- handleImplies(diff, false, monitor);
- } else {
- // merge all "required by" diffs
- mergeRequiredBy(diff, false, monitor);
- handleImpliedBy(diff, false, monitor);
- }
-
- boolean hasToBeMerged = true;
- if (diff.getEquivalence() != null) {
- hasToBeMerged = handleEquivalences(diff, false, monitor);
- }
-
- if (hasToBeMerged) {
- if (diff.getSource() == DifferenceSource.LEFT) {
- accept(diff, false);
- } else {
- reject(diff, false);
- }
- }
- }
-
- /**
- * {@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.getSource() == DifferenceSource.LEFT) {
- // merge all "required by" diffs
- mergeRequiredBy(diff, true, monitor);
- handleImpliedBy(diff, true, monitor);
- } else {
- // merge all "requires" diffs
- mergeRequires(diff, true, monitor);
- handleImplies(diff, true, monitor);
- }
-
- boolean hasToBeMerged = true;
- if (diff.getEquivalence() != null) {
- hasToBeMerged = handleEquivalences(diff, true, monitor);
- }
-
- if (hasToBeMerged) {
- if (diff.getSource() == DifferenceSource.LEFT) {
- reject(diff, true);
- } else {
- accept(diff, true);
- }
- }
- }
-
- /**
- * Merge the given difference rejecting it.
- *
- * @param diff
- * The difference to merge.
- * @param rightToLeft
- * The direction of the merge.
- */
- private void reject(final ReferenceChange diff, boolean rightToLeft) {
- DifferenceSource source = diff.getSource();
- switch (diff.getKind()) {
- case ADD:
- // We have a ADD on left, thus nothing in right. We need to revert the addition
- removeFromTarget(diff, rightToLeft);
- break;
- case DELETE:
- // DELETE in the left, thus an element in right. We need to re-create that element
- addInTarget(diff, rightToLeft);
- break;
- case MOVE:
- moveElement(diff, rightToLeft);
- break;
- case CHANGE:
- EObject container = null;
- if (source == DifferenceSource.LEFT) {
- container = diff.getMatch().getLeft();
-
- } else {
- container = diff.getMatch().getRight();
- }
- // Is it an unset?
- if (container != null) {
- final EObject leftValue = (EObject)container.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, rightToLeft);
- } else {
- // We'll actually need to "reset" this reference to its original value
- resetInTarget(diff, rightToLeft);
- }
- } else {
- // we have no left, and the source is on the left. Can only be an unset
- addInTarget(diff, rightToLeft);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Merge the given difference accepting it.
- *
- * @param diff
- * The difference to merge.
- * @param rightToLeft
- * The direction of the merge.
- */
- private void accept(final ReferenceChange diff, boolean rightToLeft) {
- DifferenceSource source = diff.getSource();
- switch (diff.getKind()) {
- case ADD:
- // Create the same element in right
- addInTarget(diff, rightToLeft);
- break;
- case DELETE:
- // Delete that same element from right
- removeFromTarget(diff, rightToLeft);
- break;
- case MOVE:
- moveElement(diff, rightToLeft);
- break;
- case CHANGE:
- EObject container = null;
- if (source == DifferenceSource.LEFT) {
- container = diff.getMatch().getLeft();
- } else {
- container = diff.getMatch().getRight();
- }
- // Is it an unset?
- if (container != null) {
- final EObject leftValue = (EObject)container.eGet(diff.getReference(), false);
- if (leftValue == null) {
- removeFromTarget(diff, rightToLeft);
- } else {
- addInTarget(diff, rightToLeft);
- }
- } else {
- // we have no left, and the source is on the left. Can only be an unset
- removeFromTarget(diff, rightToLeft);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Mark as MERGED all the implied differences recursively from the given one.
- *
- * @param diff
- * The difference from which the implications have to be marked.
- * @param rightToLeft
- * The direction of the merge.
- * @param monitor
- * Monitor.
- */
- private void handleImplies(Diff diff, boolean rightToLeft, Monitor monitor) {
- for (Diff implied : diff.getImplies()) {
- implied.setState(DifferenceState.MERGED);
- handleImplies(implied, rightToLeft, monitor);
- }
- }
-
- /**
- * Mark as MERGED all the implying differences recursively from the given one.
- *
- * @param diff
- * The difference from which the implications have to be marked.
- * @param rightToLeft
- * The direction of the merge.
- * @param monitor
- * Monitor.
- */
- private void handleImpliedBy(Diff diff, boolean rightToLeft, Monitor monitor) {
- for (Diff impliedBy : diff.getImpliedBy()) {
- impliedBy.setState(DifferenceState.MERGED);
- handleImpliedBy(impliedBy, rightToLeft, monitor);
- }
- }
-
- /**
- * 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()) {
- // For 1..*, merge diff on many-valued to preserve ordering
- 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;
- }
- }
-
- /*
- * If one of the equivalent differences is implied or implying (depending on the merge direction)
- * a merged diff, then we have a dependency loop : the "current" difference has already been
- * merged because of this implication. This will allow us to break out of that loop.
- */
- if (rightToLeft) {
- if (diff.getSource() == DifferenceSource.LEFT) {
- continueMerge = continueMerge
- && !containsAny(diff.getRequiredBy(), equivalent.getImplies());
- } else {
- continueMerge = continueMerge
- && !containsAny(diff.getRequires(), equivalent.getImpliedBy());
- }
- } else {
- if (diff.getSource() == DifferenceSource.LEFT) {
- continueMerge = continueMerge
- && !containsAny(diff.getRequires(), equivalent.getImpliedBy());
- } else {
- continueMerge = continueMerge
- && !containsAny(diff.getRequiredBy(), equivalent.getImplies());
- }
- }
-
- equivalent.setState(DifferenceState.MERGED);
- }
- return continueMerge;
- }
-
- /**
- * Utility method to check that the first sequence contains one of the elements of the second sequence at
- * least.
- *
- * @param sequence1
- * The first sequence.
- * @param sequence2
- * The second sequence.
- * @return True if the given first sequence contains one of the elements of the second sequence at least.
- * false otherwise.
- */
- private boolean containsAny(List<? extends EObject> sequence1, List<? extends EObject> sequence2) {
- return Iterables.any(sequence2, Predicates.in(sequence1));
- }
-
- /**
- * 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 com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +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.internal.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.getSource() == DifferenceSource.LEFT) { + // merge all "requires" diffs + mergeRequires(diff, false, monitor); + handleImplies(diff, false, monitor); + } else { + // merge all "required by" diffs + mergeRequiredBy(diff, false, monitor); + handleImpliedBy(diff, false, monitor); + } + + boolean hasToBeMerged = true; + if (diff.getEquivalence() != null) { + hasToBeMerged = handleEquivalences(diff, false, monitor); + } + + if (hasToBeMerged) { + if (diff.getSource() == DifferenceSource.LEFT) { + accept(diff, false); + } else { + reject(diff, false); + } + } + } + + /** + * {@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.getSource() == DifferenceSource.LEFT) { + // merge all "required by" diffs + mergeRequiredBy(diff, true, monitor); + handleImpliedBy(diff, true, monitor); + } else { + // merge all "requires" diffs + mergeRequires(diff, true, monitor); + handleImplies(diff, true, monitor); + } + + boolean hasToBeMerged = true; + if (diff.getEquivalence() != null) { + hasToBeMerged = handleEquivalences(diff, true, monitor); + } + + if (hasToBeMerged) { + if (diff.getSource() == DifferenceSource.LEFT) { + reject(diff, true); + } else { + accept(diff, true); + } + } + } + + /** + * Merge the given difference rejecting it. + * + * @param diff + * The difference to merge. + * @param rightToLeft + * The direction of the merge. + */ + private void reject(final ReferenceChange diff, boolean rightToLeft) { + DifferenceSource source = diff.getSource(); + switch (diff.getKind()) { + case ADD: + // We have a ADD on left, thus nothing in right. We need to revert the addition + removeFromTarget(diff, rightToLeft); + break; + case DELETE: + // DELETE in the left, thus an element in right. We need to re-create that element + addInTarget(diff, rightToLeft); + break; + case MOVE: + moveElement(diff, rightToLeft); + break; + case CHANGE: + EObject container = null; + if (source == DifferenceSource.LEFT) { + container = diff.getMatch().getLeft(); + + } else { + container = diff.getMatch().getRight(); + } + // Is it an unset? + if (container != null) { + final EObject leftValue = (EObject)container.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, rightToLeft); + } else { + // We'll actually need to "reset" this reference to its original value + resetInTarget(diff, rightToLeft); + } + } else { + // we have no left, and the source is on the left. Can only be an unset + addInTarget(diff, rightToLeft); + } + break; + default: + break; + } + } + + /** + * Merge the given difference accepting it. + * + * @param diff + * The difference to merge. + * @param rightToLeft + * The direction of the merge. + */ + private void accept(final ReferenceChange diff, boolean rightToLeft) { + DifferenceSource source = diff.getSource(); + switch (diff.getKind()) { + case ADD: + // Create the same element in right + addInTarget(diff, rightToLeft); + break; + case DELETE: + // Delete that same element from right + removeFromTarget(diff, rightToLeft); + break; + case MOVE: + moveElement(diff, rightToLeft); + break; + case CHANGE: + EObject container = null; + if (source == DifferenceSource.LEFT) { + container = diff.getMatch().getLeft(); + } else { + container = diff.getMatch().getRight(); + } + // Is it an unset? + if (container != null) { + final EObject leftValue = (EObject)container.eGet(diff.getReference(), false); + if (leftValue == null) { + removeFromTarget(diff, rightToLeft); + } else { + addInTarget(diff, rightToLeft); + } + } else { + // we have no left, and the source is on the left. Can only be an unset + removeFromTarget(diff, rightToLeft); + } + break; + default: + break; + } + } + + /** + * Mark as MERGED all the implied differences recursively from the given one. + * + * @param diff + * The difference from which the implications have to be marked. + * @param rightToLeft + * The direction of the merge. + * @param monitor + * Monitor. + */ + private void handleImplies(Diff diff, boolean rightToLeft, Monitor monitor) { + for (Diff implied : diff.getImplies()) { + implied.setState(DifferenceState.MERGED); + handleImplies(implied, rightToLeft, monitor); + } + } + + /** + * Mark as MERGED all the implying differences recursively from the given one. + * + * @param diff + * The difference from which the implications have to be marked. + * @param rightToLeft + * The direction of the merge. + * @param monitor + * Monitor. + */ + private void handleImpliedBy(Diff diff, boolean rightToLeft, Monitor monitor) { + for (Diff impliedBy : diff.getImpliedBy()) { + impliedBy.setState(DifferenceState.MERGED); + handleImpliedBy(impliedBy, rightToLeft, monitor); + } + } + + /** + * 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()) { + // For 1..*, merge diff on many-valued to preserve ordering + 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; + } + } + + /* + * If one of the equivalent differences is implied or implying (depending on the merge direction) + * a merged diff, then we have a dependency loop : the "current" difference has already been + * merged because of this implication. This will allow us to break out of that loop. + */ + if (rightToLeft) { + if (diff.getSource() == DifferenceSource.LEFT) { + continueMerge = continueMerge + && !containsAny(diff.getRequiredBy(), equivalent.getImplies()); + } else { + continueMerge = continueMerge + && !containsAny(diff.getRequires(), equivalent.getImpliedBy()); + } + } else { + if (diff.getSource() == DifferenceSource.LEFT) { + continueMerge = continueMerge + && !containsAny(diff.getRequires(), equivalent.getImpliedBy()); + } else { + continueMerge = continueMerge + && !containsAny(diff.getRequiredBy(), equivalent.getImplies()); + } + } + + equivalent.setState(DifferenceState.MERGED); + } + return continueMerge; + } + + /** + * Utility method to check that the first sequence contains one of the elements of the second sequence at + * least. + * + * @param sequence1 + * The first sequence. + * @param sequence2 + * The second sequence. + * @return True if the given first sequence contains one of the elements of the second sequence at least. + * false otherwise. + */ + private boolean containsAny(List<? extends EObject> sequence1, List<? extends EObject> sequence2) { + return Iterables.any(sequence2, Predicates.in(sequence1)); + } + + /** + * 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); + } +} |