| /******************************************************************************* |
| * Copyright (c) 2013, 2015 Obeo and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Obeo - initial API and implementation |
| * Michael Borkowski - bug 462237 |
| * Martin Fleck - bug 483798 |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions; |
| |
| import static com.google.common.collect.Iterables.addAll; |
| import static com.google.common.collect.Iterables.filter; |
| import static com.google.common.collect.Iterables.transform; |
| import static com.google.common.collect.Lists.newArrayList; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Preconditions; |
| |
| import java.util.List; |
| |
| import org.eclipse.compare.INavigatable; |
| 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.Diff; |
| import org.eclipse.emf.compare.command.ICompareCopyCommand; |
| import org.eclipse.emf.compare.domain.ICompareEditingDomain; |
| import org.eclipse.emf.compare.domain.IMergeRunnable; |
| import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; |
| import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; |
| import org.eclipse.emf.compare.ide.ui.internal.preferences.EMFCompareUIPreferences; |
| import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.Navigatable; |
| import org.eclipse.emf.compare.internal.merge.MergeMode; |
| import org.eclipse.emf.compare.merge.IDiffRelationshipComputer; |
| import org.eclipse.emf.compare.merge.IMerger; |
| import org.eclipse.emf.compare.merge.IMerger.Registry; |
| import org.eclipse.emf.compare.provider.ITooltipLabelProvider; |
| import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; |
| import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.edit.tree.TreeNode; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.ui.actions.BaseSelectionListenerAction; |
| import org.eclipse.ui.plugin.AbstractUIPlugin; |
| |
| /** |
| * Abstract Action 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 class MergeAction extends BaseSelectionListenerAction { |
| |
| protected static final Function<? super Adapter, ? extends Notifier> ADAPTER__TARGET = new Function<Adapter, Notifier>() { |
| public Notifier apply(Adapter adapter) { |
| return adapter.getTarget(); |
| } |
| }; |
| |
| protected final Registry mergerRegistry; |
| |
| protected ICompareEditingDomain editingDomain; |
| |
| private boolean leftToRight; |
| |
| protected IMergeRunnable mergeRunnable; |
| |
| private final List<Diff> selectedDifferences; |
| |
| private final INavigatable navigatable; |
| |
| /** |
| * The merge mode used for the comparison. |
| */ |
| private final MergeMode selectedMode; |
| |
| /** |
| * The adapter factory for the comparison. |
| */ |
| private AdapterFactory adapterFactory; |
| |
| private IDiffRelationshipComputer diffRelationshipComputer; |
| |
| private boolean isMirrored; |
| |
| private final boolean isLeftEditable; |
| |
| private final boolean isRightEditable; |
| |
| /** |
| * Constructor. |
| * |
| * @param configuration |
| * The compare configuration object. |
| */ |
| public MergeAction(IEMFCompareConfiguration compareConfiguration, IMerger.Registry mergerRegistry, |
| MergeMode mode, INavigatable navigatable) { |
| super(""); //$NON-NLS-1$ |
| |
| adapterFactory = compareConfiguration.getAdapterFactory(); |
| diffRelationshipComputer = compareConfiguration.getDiffRelationshipComputer(); |
| isLeftEditable = compareConfiguration.isLeftEditable(); |
| isRightEditable = compareConfiguration.isRightEditable(); |
| |
| this.navigatable = navigatable; |
| Preconditions.checkNotNull(mode); |
| // at least should be editable |
| Preconditions.checkState(isLeftEditable || isRightEditable); |
| // if left and right editable, the only accepted mode are LtR or RtL |
| if (isLeftEditable && isRightEditable) { |
| Preconditions.checkState(mode == MergeMode.LEFT_TO_RIGHT || mode == MergeMode.RIGHT_TO_LEFT); |
| } |
| // if mode is accept or reject, left and right can't be both read only (no action should be created in |
| // this case) and can't be both editable. |
| if (isLeftEditable != isRightEditable) { |
| Preconditions.checkState(mode == MergeMode.ACCEPT || mode == MergeMode.REJECT); |
| } |
| |
| this.editingDomain = compareConfiguration.getEditingDomain(); |
| this.mergerRegistry = mergerRegistry; |
| this.leftToRight = mode.isLeftToRight(isLeftEditable, isRightEditable); |
| this.mergeRunnable = createMergeRunnable(mode, isLeftEditable, isRightEditable, |
| diffRelationshipComputer); |
| this.selectedDifferences = newArrayList(); |
| this.selectedMode = mode; |
| |
| initToolTipAndImage(mode); |
| } |
| |
| public MergeAction(IEMFCompareConfiguration compareConfiguration, IMerger.Registry mergerRegistry, |
| MergeMode mode, INavigatable navigatable, IStructuredSelection selection) { |
| this(compareConfiguration, mergerRegistry, mode, navigatable); |
| updateSelection(selection); |
| } |
| |
| protected IMergeRunnable createMergeRunnable(MergeMode mode, boolean leftEditable, boolean rightEditable, |
| IDiffRelationshipComputer relationshipComputer) { |
| return new MergeRunnableImpl(leftEditable, rightEditable, mode, relationshipComputer); |
| } |
| |
| protected void initToolTipAndImage(MergeMode mode) { |
| switch (mode) { |
| case LEFT_TO_RIGHT: |
| setText(EMFCompareIDEUIMessages.getString("merged.to.right.tooltip")); //$NON-NLS-1$ |
| setToolTipText(EMFCompareIDEUIMessages.getString("merged.to.right.tooltip")); //$NON-NLS-1$ |
| setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID, |
| "icons/full/toolb16/merge_to_right.gif")); //$NON-NLS-1$ |
| break; |
| case RIGHT_TO_LEFT: |
| setText(EMFCompareIDEUIMessages.getString("merged.to.left.tooltip")); //$NON-NLS-1$ |
| setToolTipText(EMFCompareIDEUIMessages.getString("merged.to.left.tooltip")); //$NON-NLS-1$ |
| setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID, |
| "icons/full/toolb16/merge_to_left.gif")); //$NON-NLS-1$ |
| break; |
| case ACCEPT: |
| setText(EMFCompareIDEUIMessages.getString("accept.change.tooltip")); //$NON-NLS-1$ |
| setToolTipText(EMFCompareIDEUIMessages.getString("accept.change.tooltip")); //$NON-NLS-1$ |
| setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID, |
| "icons/full/toolb16/accept_change.gif")); //$NON-NLS-1$ |
| break; |
| case REJECT: |
| setText(EMFCompareIDEUIMessages.getString("reject.change.tooltip")); //$NON-NLS-1$ |
| setToolTipText(EMFCompareIDEUIMessages.getString("reject.change.tooltip")); //$NON-NLS-1$ |
| setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EMFCompareIDEUIPlugin.PLUGIN_ID, |
| "icons/full/toolb16/reject_change.gif")); //$NON-NLS-1$ |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| } |
| |
| /** |
| * This method is used to created contextual tooltips. |
| */ |
| protected void contextualizeTooltip() { |
| if (this.selectedDifferences.size() > 1) { |
| // multiple selection |
| setMultipleTooltip(this.selectedMode); |
| } else if (this.selectedDifferences.isEmpty()) { |
| // no selection |
| initToolTipAndImage(this.selectedMode); |
| } else { |
| Diff diff = this.selectedDifferences.get(0); |
| Object adapter = adapterFactory.adapt(diff, ITooltipLabelProvider.class); |
| if (adapter instanceof ITooltipLabelProvider) { |
| String tooltip = ((ITooltipLabelProvider)adapter).getTooltip(this.selectedMode); |
| setToolTipText(tooltip); |
| } else { |
| initToolTipAndImage(this.selectedMode); |
| } |
| } |
| } |
| |
| /** |
| * Set the tooltips for multiple selection. |
| * |
| * @param mode |
| * The comparison mode |
| */ |
| private void setMultipleTooltip(MergeMode mode) { |
| switch (mode) { |
| case LEFT_TO_RIGHT: |
| setToolTipText(EMFCompareIDEUIMessages.getString("merged.multiple.to.right.tooltip")); //$NON-NLS-1$ |
| break; |
| case RIGHT_TO_LEFT: |
| setToolTipText(EMFCompareIDEUIMessages.getString("merged.multiple.to.left.tooltip")); //$NON-NLS-1$ |
| break; |
| case ACCEPT: |
| setToolTipText(EMFCompareIDEUIMessages.getString("accept.multiple.changes.tooltip")); //$NON-NLS-1$ |
| break; |
| case REJECT: |
| setToolTipText(EMFCompareIDEUIMessages.getString("reject.multiple.changes.tooltip")); //$NON-NLS-1$ |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| ICompareCopyCommand mergeCommand = editingDomain.createCopyCommand(selectedDifferences, leftToRight, |
| mergerRegistry, mergeRunnable); |
| editingDomain.getCommandStack().execute(mergeCommand); |
| |
| if (navigatable != null && EMFCompareIDEUIPlugin.getDefault().getPreferenceStore() |
| .getBoolean(EMFCompareUIPreferences.SELECT_NEXT_UNRESOLVED_DIFF)) { |
| // navigator is null in MergeAllNonConflictingAction |
| navigatable.selectChange(Navigatable.NEXT_UNRESOLVED_CHANGE); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection) |
| */ |
| @Override |
| protected boolean updateSelection(IStructuredSelection selection) { |
| addAll(selectedDifferences, getSelectedDifferences(selection)); |
| if (this.adapterFactory != null) { |
| contextualizeTooltip(); |
| } |
| return selection.toList().size() == selectedDifferences.size(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.ui.actions.BaseSelectionListenerAction#clearCache() |
| */ |
| @Override |
| protected void clearCache() { |
| selectedDifferences.clear(); |
| } |
| |
| protected Iterable<Diff> getSelectedDifferences(IStructuredSelection selection) { |
| List<?> selectedObjects = selection.toList(); |
| Iterable<Adapter> selectedAdapters = filter(selectedObjects, Adapter.class); |
| Iterable<Notifier> selectedNotifiers = transform(selectedAdapters, ADAPTER__TARGET); |
| Iterable<TreeNode> selectedTreeNode = filter(selectedNotifiers, TreeNode.class); |
| Iterable<EObject> selectedEObjects = transform(selectedTreeNode, IDifferenceGroup.TREE_NODE_DATA); |
| return filter(selectedEObjects, Diff.class); |
| } |
| |
| /** |
| * @param newValue |
| */ |
| public final void setEditingDomain(ICompareEditingDomain editingDomain) { |
| this.editingDomain = editingDomain; |
| clearCache(); |
| setEnabled(editingDomain != null && updateSelection(getStructuredSelection())); |
| } |
| |
| /** |
| * Set the adapter factory used by this action. |
| * |
| * @param adapterFactory |
| * adapter factory |
| */ |
| public final void setAdapterFactory(AdapterFactory adapterFactory) { |
| this.adapterFactory = adapterFactory; |
| if (adapterFactory != null) { |
| contextualizeTooltip(); |
| } |
| } |
| |
| /** |
| * Refreshes the merge action by re-creating the necessary elements based on the current compare |
| * configuration. |
| */ |
| public void setMirrored(boolean mirrored) { |
| if (selectedMode == MergeMode.ACCEPT || selectedMode == MergeMode.REJECT) { |
| return; |
| } |
| |
| if (this.isMirrored != mirrored) { |
| this.isMirrored = mirrored; |
| if (mirrored) { |
| MergeMode mirroredMode = selectedMode.inverse(); |
| leftToRight = mirroredMode.isLeftToRight(isRightEditable, isLeftEditable); |
| mergeRunnable = createMergeRunnable(mirroredMode, isRightEditable, isLeftEditable, |
| diffRelationshipComputer); |
| } else { |
| leftToRight = selectedMode.isLeftToRight(isLeftEditable, isRightEditable); |
| mergeRunnable = createMergeRunnable(selectedMode, isLeftEditable, isRightEditable, |
| diffRelationshipComputer); |
| } |
| } |
| } |
| |
| /** |
| * @return the leftToRight |
| */ |
| protected final boolean isLeftToRight() { |
| return leftToRight; |
| } |
| |
| } |