/** * <copyright> * * Copyright (c) 2010-2016 Thales Global Services S.A.S 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: * Thales Global Services S.A.S. - initial API and implementation * Stephane Bouchet (Intel Corporation) - Bug #442492 : hide number of differences in the UI * * </copyright> */ package org.eclipse.emf.diffmerge.ui.viewers; import java.util.HashMap; import java.util.Map; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.compare.structuremergeviewer.Differencer; import org.eclipse.emf.diffmerge.api.IComparison; import org.eclipse.emf.diffmerge.api.Role; import org.eclipse.emf.diffmerge.diffdata.EComparison; import org.eclipse.emf.diffmerge.diffdata.EMatch; import org.eclipse.emf.diffmerge.ui.EMFDiffMergeUIPlugin; import org.eclipse.emf.diffmerge.ui.EMFDiffMergeUIPlugin.DifferenceColorKind; import org.eclipse.emf.diffmerge.ui.diffuidata.UIComparison; import org.eclipse.emf.diffmerge.ui.diffuidata.impl.UIComparisonImpl; import org.eclipse.emf.diffmerge.ui.setup.ModelScopeTypedElement; import org.eclipse.emf.diffmerge.ui.util.UIUtil; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.edit.domain.IEditingDomainProvider; import org.eclipse.emf.edit.provider.IDisposable; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; /** * An ICompareInput that wraps a model comparison. * @author Olivier Constant */ public class EMFDiffNode extends DiffNode implements IDisposable, IEditingDomainProvider { /** The resource manager */ private final ComparisonResourceManager _resourceManager; /** The non-null comparison-related contents */ private final UIComparison _contents; /** The optional editing domain */ private final EditingDomain _editingDomain; /** The role that drives the representation of the comparison */ private Role _drivingRole; /** The non-null role on the left-hand side */ private Role _leftRole; /** The potentially null role to use as a reference in a two-way comparison */ private Role _twoWayReferenceRole; /** The non-null difference category manager */ private final CategoryManager _categoryManager; /** Whether to use custom icons for differences */ private boolean _useCustomIcons; /** Whether to use custom labels for differences */ private boolean _useCustomLabels; /** Whether the left model is editable */ private boolean _isTargetEditable; /** Whether the right model is editable */ private boolean _isReferenceEditable; /** Whether editing the target scope is possible at all */ private final boolean _isTargetEditionPossible; /** Whether editing the reference scope is possible at all */ private final boolean _isReferenceEditionPossible; /** Whether the left model has been modified */ private boolean _isTargetModified; /** Whether the right model has been modified */ private boolean _isReferenceModified; /** Whether differences number must be hidden */ private boolean _isHideDifferenceNumbers; /** Whether an impact dialog must be shown at merge time */ private boolean _isShowMergeImpact; /** Whether to support undo/redo (cost in memory usage and response time) */ private boolean _isUndoRedoSupported; /** Whether events must be logged */ private boolean _isLogEvents; /** The default value for the "cover children" property as proposed to the user when merging */ private boolean _defaultCoverChildren; /** The default value for the "incremental mode" property as proposed to the user when merging */ private boolean _defaultIncrementalMode; /** The default value for "show merge impact" property as proposed to the user when merging */ private boolean _defaultShowMergeImpact; /** A map from color kind to SWT color code from the SWT class */ private final Map<DifferenceColorKind, Integer> _differenceColors; /** * Constructor * @param comparison_p a non-null comparison */ public EMFDiffNode(EComparison comparison_p) { this(comparison_p, null); } /** * Constructor * @param comparison_p a non-null comparison * @param domain_p the optional editing domain for undo/redo */ public EMFDiffNode(EComparison comparison_p, EditingDomain domain_p) { this(comparison_p, domain_p, true, true); } /** * Constructor * @param comparison_p a non-null comparison * @param domain_p the optional editing domain for undo/redo * @param isLeftEditionPossible_p whether edition on the left is possible at all * @param isRightEditionPossible_p whether edition on the right is possible at all */ public EMFDiffNode(EComparison comparison_p, EditingDomain domain_p, boolean isLeftEditionPossible_p, boolean isRightEditionPossible_p) { super( Differencer.CHANGE, comparison_p.isThreeWay()? new ModelScopeTypedElement( comparison_p.getScope(Role.ANCESTOR)): null, new ModelScopeTypedElement(comparison_p.getScope(Role.TARGET)), new ModelScopeTypedElement(comparison_p.getScope(Role.REFERENCE))); _resourceManager = new ComparisonResourceManager(); _contents = new UIComparisonImpl(comparison_p); _editingDomain = domain_p; _leftRole = EMFDiffMergeUIPlugin.getDefault().getDefaultLeftRole(); _drivingRole = _leftRole; _twoWayReferenceRole = null; _categoryManager = new CategoryManager(this); _differenceColors = new HashMap<EMFDiffMergeUIPlugin.DifferenceColorKind, Integer>(); initializeDifferenceColors(_differenceColors); _useCustomIcons = true; _useCustomLabels = false; _isTargetEditionPossible = isLeftEditionPossible_p; _isReferenceEditionPossible = isRightEditionPossible_p; _isTargetEditable = _isTargetEditionPossible; _isReferenceEditable = _isReferenceEditionPossible; _isTargetModified = false; _isReferenceModified = false; _isHideDifferenceNumbers = false; _isShowMergeImpact = true; _isUndoRedoSupported = _editingDomain != null; _isLogEvents = false; _defaultShowMergeImpact = _isShowMergeImpact; _defaultCoverChildren = true; _defaultIncrementalMode = false; } /** * Initialize the given map from color kinds to SWT color codes * @param differenceColorsMap_p a non-null, modifiable map */ protected void initializeDifferenceColors( Map<DifferenceColorKind, Integer> differenceColorsMap_p) { differenceColorsMap_p.put(DifferenceColorKind.LEFT, Integer.valueOf(SWT.COLOR_DARK_RED)); differenceColorsMap_p.put(DifferenceColorKind.RIGHT, Integer.valueOf(SWT.COLOR_BLUE)); differenceColorsMap_p.put(DifferenceColorKind.BOTH, Integer.valueOf(SWT.COLOR_DARK_MAGENTA)); differenceColorsMap_p.put(DifferenceColorKind.NONE, Integer.valueOf(SWT.COLOR_GRAY)); differenceColorsMap_p.put(DifferenceColorKind.CONFLICT, Integer.valueOf(SWT.COLOR_RED)); differenceColorsMap_p.put(DifferenceColorKind.DEFAULT, Integer.valueOf(SWT.COLOR_BLACK)); } /** * Return whether edition of the given side is enabled * @param left_p whether the side is left or right */ public boolean isEditable(boolean left_p) { return getRoleForSide(left_p) == Role.TARGET? _isTargetEditable: _isReferenceEditable; } /** * Return whether editing the scope of the given side is possible at all * @param left_p whether the side is left or right */ public boolean isEditionPossible(boolean left_p) { return getRoleForSide(left_p) == Role.TARGET? _isTargetEditionPossible: _isReferenceEditionPossible; } /** * Return whether the given side has been modified * @param left_p whether the side is left or right */ public boolean isModified(boolean left_p) { return getRoleForSide(left_p) == Role.TARGET? _isTargetModified: _isReferenceModified; } /** * Return whether an impact dialog must be shown at merge time */ public boolean isShowMergeImpact() { return _isShowMergeImpact; } /** * Return whether to support undo/redo (cost in memory usage and response time) */ public boolean isUndoRedoSupported() { return _isUndoRedoSupported; } /** * Return whether events must be logged */ public boolean isLogEvents() { return _isLogEvents; } /** * Return the default value for the "cover children" property as proposed to the user when merging */ public boolean isDefaultCoverChildren() { return _defaultCoverChildren; } /** * Return the default value for the "incremental mode" property as proposed to the user when merging */ public boolean isDefaultIncrementalMode() { return _defaultIncrementalMode; } /** * Return the default value for the "show merge impact" property as proposed to the user when merging */ public boolean isDefaultShowImpact() { return _defaultShowMergeImpact; } /** * @see org.eclipse.emf.edit.provider.IDisposable#dispose() */ public void dispose() { _resourceManager.dispose(); } /** * Return the model comparison of this node * @return a non-null comparison, unless the UI comparison has been disposed */ public EComparison getActualComparison() { return getUIComparison().getActualComparison(); } /** * Return the category manager for this diff node * @return a non-null object */ public CategoryManager getCategoryManager() { return _categoryManager; } /** * Return the container of the given match from a UI perspective * @param match_p a non-null match * @return a potentially null match */ protected EMatch getContainerOf(EMatch match_p) { EMatch result = null; IComparison comparison = getActualComparison(); if (comparison != null) { Role containerSide; Role drivingRole = getDrivingRole(); if (match_p.getUncoveredRole() == drivingRole) containerSide = drivingRole.opposite(); else containerSide = drivingRole; result = (EMatch)comparison.getContainerOf(match_p, containerSide); } return result; } /** * Return the color that corresponds to the given color kind * @param colorKind_p a non-null color kind * @return a non-null color */ public Color getDifferenceColor(DifferenceColorKind colorKind_p) { int colorCode = SWT.COLOR_BLACK; Integer colorCodeI = _differenceColors.get(colorKind_p); if (colorCodeI != null) colorCode = colorCodeI.intValue(); return UIUtil.getColor(colorCode); } /** * Return the role that drives the representation of the model comparison * @return a non-null role which is TARGET or REFERENCE */ public Role getDrivingRole() { return _drivingRole; } /** * Return the editing domain for merge operations, if any * @return a potentially null editing domain */ public EditingDomain getEditingDomain() { return _editingDomain; } /** * Return the role which is used as the reference role, if any. * The reference role determines that all differences should be represented * in a way which is relative to it. In a three-way comparison, it is always * ANCESTOR. In a two-way comparison, it can naturally be REFERENCE but it * does not have to. If null, then both sides in the two-way comparison * are represented in a symmetric way. * @return ANCESTOR, TARGET, REFERENCE, or null */ public Role getReferenceRole() { return isThreeWay()? Role.ANCESTOR: _twoWayReferenceRole; } /** * Return the resource manager for this node * @return a non-null resource manager */ public ComparisonResourceManager getResourceManager() { return _resourceManager; } /** * Return the role that corresponds to the given side * @param left_p whether the side to consider is left or right * @return a non-null role */ public Role getRoleForSide(boolean left_p) { return left_p? _leftRole: _leftRole.opposite(); } /** * Return the UI comparison of this node * @return a non-null UI comparison */ public UIComparison getUIComparison() { return _contents; } /** * @see org.eclipse.compare.structuremergeviewer.DiffContainer#hasChildren() */ @Override public boolean hasChildren() { // Is there content? IComparison comparison = getActualComparison(); return comparison != null? comparison.hasRemainingDifferences(): false; } /** * Return whether the are still differences that the user has to handle */ public boolean isEmpty() { return getCategoryManager().isEmpty(); } /** * Return whether this viewer displays difference numbers */ public boolean isHideDifferenceNumbers() { return _isHideDifferenceNumbers; } /** * Return whether this comparison is 3-way */ public boolean isThreeWay() { IComparison comparison = getActualComparison(); return comparison != null? comparison.isThreeWay(): false; } /** * Set the default value for the "cover children" property as proposed to the user when merging */ public void setDefaultCoverChildren(boolean coverChildren_p) { _defaultCoverChildren = coverChildren_p; } /** * Set the default value for the "incremental mode" property as proposed to the user when merging */ public void setDefaultIncrementalMode(boolean isIncrementalMode_p) { _defaultIncrementalMode = isIncrementalMode_p; } /** * Set the default value for the "show merge impact" property as proposed to the user when merging */ public void setDefaultShowImpact(boolean showImpact_p) { _defaultShowMergeImpact = showImpact_p; } /** * Set the color that corresponds to the given color kind * @param colorKind_p a non-null color kind * @param swtColor_p an identifier of an SWT color from class SWT */ public void setDifferenceColor(DifferenceColorKind colorKind_p, int swtColor_p) { _differenceColors.put(colorKind_p, new Integer(swtColor_p)); } /** * Set the role that drives the representation of the model comparison * @param drivingRole_p a non-null role which is TARGET or REFERENCE */ public void setDrivingRole(Role drivingRole_p) { if (Role.TARGET == drivingRole_p || Role.REFERENCE == drivingRole_p) _drivingRole = drivingRole_p; } /** * Set whether the given side is editable * @param isEditable_p whether it is editable * @param left_p whether the side is left or right */ public void setEditable(boolean isEditable_p, boolean left_p) { if (isEditionPossible(left_p)) { if (getRoleForSide(left_p) == Role.TARGET) _isTargetEditable = isEditable_p; else _isReferenceEditable = isEditable_p; } } /** * Set whether this viewer should display differences numbers */ public void setHideDifferenceNumbers(boolean hideDifferenceNumbers_p) { _isHideDifferenceNumbers = hideDifferenceNumbers_p; } /** * Set the role on the left-hand side * @param leftRole_p a non-null role which is TARGET or REFERENCE */ public void setLeftRole(Role leftRole_p) { if (Role.TARGET == leftRole_p || Role.REFERENCE == leftRole_p) _leftRole = leftRole_p; } /** * Set whether events must be logged */ public void setLogEvents(boolean logEvents_p) { _isLogEvents = logEvents_p; } /** * Set whether the given side has been modified * @param isModified_p whether it has been modified * @param left_p whether the side is left or right */ public void setModified(boolean isModified_p, boolean left_p) { if (getRoleForSide(left_p) == Role.TARGET) _isTargetModified = isModified_p; else _isReferenceModified = isModified_p; } /** * Set the role which is used as the reference role. * In a three-way comparison, this operation has no effect. * @see EMFDiffNode#getReferenceRole() * @param role_p TARGET, REFERENCE, or null */ public void setReferenceRole(Role role_p) { if (!isThreeWay() && (Role.TARGET == role_p || Role.REFERENCE == role_p)) _twoWayReferenceRole = role_p; } /** * Set whether an impact dialog must be shown at merge time */ public void setShowMergeImpact(boolean showMergeImpact_p) { _isShowMergeImpact = showMergeImpact_p; } /** * Set whether to support undo/redo (cost in memory usage and response time). * Undo/redo may only be supported if the editing domain is known (see getEditingDomain()). */ public void setUndoRedoSupported(boolean supportUndoRedo_p) { _isUndoRedoSupported = getEditingDomain() != null && supportUndoRedo_p; } /** * Set whether this viewer should use custom icons to represent differences */ public void setUseCustomIcons(boolean useCustom_p) { _useCustomIcons = useCustom_p; } /** * Set whether this viewer should use custom labels to represent differences */ public void setUseCustomLabels(boolean useCustom_p) { _useCustomLabels = useCustom_p; } /** * Return whether this viewer uses custom icons to represent differences */ public boolean usesCustomIcons() { return _useCustomIcons; } /** * Return whether this viewer uses custom labels to represent differences */ public boolean usesCustomLabels() { return _useCustomLabels; } /** * Re-compute filtering and differences numbers */ public void updateDifferenceNumbers() { getCategoryManager().update(); fireChange(); } }