/******************************************************************************* * Copyright (c) 2012, 2016 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 * Philip Langer (EclipseSource) - bug 488618 *******************************************************************************/ package org.eclipse.emf.compare.provider.spec; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.Iterables.any; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.util.URI; 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.ReferenceChange; import org.eclipse.emf.compare.internal.EMFCompareEditMessages; import org.eclipse.emf.compare.provider.IItemDescriptionProvider; import org.eclipse.emf.compare.provider.IItemStyledLabelProvider; import org.eclipse.emf.compare.provider.ISemanticObjectLabelProvider; import org.eclipse.emf.compare.provider.ReferenceChangeItemProvider; import org.eclipse.emf.compare.provider.SafeAdapterFactoryItemDelegator; import org.eclipse.emf.compare.provider.utils.ComposedStyledString; import org.eclipse.emf.compare.provider.utils.IStyledString; import org.eclipse.emf.compare.provider.utils.IStyledString.Style; import org.eclipse.emf.compare.utils.EMFComparePredicates; import org.eclipse.emf.compare.utils.ReferenceUtil; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.edit.provider.AdapterFactoryItemDelegator; import org.eclipse.emf.edit.provider.IItemFontProvider; /** * Specialized {@link ReferenceChangeItemProvider} returning nice output for {@link #getText(Object)} and * {@link #getImage(Object)}. * * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a> */ public class ReferenceChangeItemProviderSpec extends ReferenceChangeItemProvider implements IItemStyledLabelProvider, IItemDescriptionProvider, ISemanticObjectLabelProvider { /** The elide length. */ private static final int ELIDE_LENGTH = 50; /** The image provider used with this item provider. */ private final OverlayImageProvider overlayProvider; /** The item delegator for reference change values. */ private final AdapterFactoryItemDelegator itemDelegator; /** * Constructor calling super {@link #ReferenceChangeItemProvider(AdapterFactory)}. * * @param adapterFactory * the adapter factory */ public ReferenceChangeItemProviderSpec(AdapterFactory adapterFactory) { super(adapterFactory); itemDelegator = new SafeAdapterFactoryItemDelegator(getRootAdapterFactory()); overlayProvider = new OverlayImageProvider(getResourceLocator()); } /** * {@inheritDoc} * * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#isAdapterForType(Object) */ @Override public boolean isAdapterForType(Object type) { if (type == ReferenceChangeItemProviderSpec.class) { return true; } return super.isAdapterForType(type); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.provider.ReferenceChangeItemProvider#getText(java.lang.Object) */ @Override public String getText(Object object) { return getStyledText(object).getString(); } /** * Returns the change text for the given diff on the given feature. * * @param diff * the diff representing the change. * @param feature * the feature that changed. * @return the change text for the given diff on the given feature. */ static String changeText(final Diff diff, EStructuralFeature feature) { DifferenceSource source = diff.getSource(); Match matchOfInterrest = diff.getMatch(); final EObject sourceSide; final EObject otherSide; if (source == DifferenceSource.LEFT) { sourceSide = matchOfInterrest.getLeft(); otherSide = matchOfInterrest.getRight(); } else { // source == DifferenceSource.RIGHT sourceSide = matchOfInterrest.getRight(); otherSide = matchOfInterrest.getLeft(); } String changeText = changeText(feature, sourceSide, otherSide); return changeText; } /** * Returns the type of change linked to the given {@link EStructuralFeature} ("unset", "set" or * "changed"), according the the given sides. * * @param eStructuralFeature * the given {@link EStructuralFeature}. * @param sourceSide * the source side as an {@link EObject}. * @param otherSide * the other side as an {@link EObject}. * @return a String ("unset", "set" or "changed") containing the type of change linked to the given * {@link EStructuralFeature}, according the the given sides. */ private static String changeText(final EStructuralFeature eStructuralFeature, EObject sourceSide, EObject otherSide) { String changeText; if (sourceSide != null) { Object leftValue = ReferenceUtil.safeEGet(sourceSide, eStructuralFeature); if (leftValue == null || isStringAndNullOrEmpty(leftValue)) { changeText = EMFCompareEditMessages.getString("ReferenceChangeItemProviderSpec.unset"); //$NON-NLS-1$ } else if (otherSide != null) { Object otherValue = ReferenceUtil.safeEGet(otherSide, eStructuralFeature); if (otherValue == null || isStringAndNullOrEmpty(otherValue)) { changeText = EMFCompareEditMessages.getString("ReferenceChangeItemProviderSpec.set"); //$NON-NLS-1$ } else { changeText = EMFCompareEditMessages.getString("ReferenceChangeItemProviderSpec.changed"); //$NON-NLS-1$ } } else { changeText = EMFCompareEditMessages.getString("ReferenceChangeItemProviderSpec.set"); //$NON-NLS-1$ } } else { changeText = EMFCompareEditMessages.getString("ReferenceChangeItemProviderSpec.unset"); //$NON-NLS-1$ } return changeText; } /** * Checks if the given Object is a null or empty String. * * @param s * the given Object. * @return true if the Object is a null or empty String, false otherwise. */ private static boolean isStringAndNullOrEmpty(Object s) { if (s instanceof String) { return isNullOrEmpty((String)s); } else { return false; } } /** * Returns the name of the reference linked to the given {@link ReferenceChange}. * * @param refChange * the given {@link ReferenceChange}. * @return the name of the reference linked to the given {@link ReferenceChange}. */ protected String getReferenceText(final ReferenceChange refChange) { return refChange.getReference().getName(); } /** * Converts to text the given {@link ReferenceChange}. * * @param refChange * the given {@link ReferenceChange}. * @return a nice text from the the given {@link ReferenceChange}. */ protected String getValueText(final ReferenceChange refChange) { EObject refChangeValue = refChange.getValue(); String value = itemDelegator.getText(refChangeValue); if (isNullOrEmpty(value)) { if (refChangeValue.eIsProxy()) { value = "proxy : " + ((InternalEObject)refChangeValue).eProxyURI().toString(); //$NON-NLS-1$ } else { value = "<null>"; //$NON-NLS-1$ } } else { value = org.eclipse.emf.compare.provider.spec.Strings.elide(value, ELIDE_LENGTH, "..."); //$NON-NLS-1$ } return value; } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.provider.ReferenceChangeItemProvider#getImage(java.lang.Object) */ @Override public Object getImage(Object object) { ReferenceChange refChange = (ReferenceChange)object; Object refChangeValueImage = itemDelegator.getImage(refChange.getValue()); Object diffImage = overlayProvider.getComposedImage(refChange, refChangeValueImage); Object ret = overlayImage(object, diffImage); return ret; } /** * {@inheritDoc} * * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getForeground(java.lang.Object) */ @Override public Object getForeground(Object object) { ReferenceChange referenceChange = (ReferenceChange)object; switch (referenceChange.getState()) { case MERGED: case DISCARDED: return URI.createURI("color://rgb/156/156/156"); //$NON-NLS-1$ default: return super.getForeground(object); } } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.provider.IItemStyledLabelProvider#getStyledText(java.lang.Object) */ @Override public IStyledString.IComposedStyledString getStyledText(Object object) { final ReferenceChange refChange = (ReferenceChange)object; final String valueText = getValueText(refChange); final String referenceText = getReferenceText(refChange); ComposedStyledString ret = new ComposedStyledString(); if (refChange.getReference().isContainment()) { EObject value = refChange.getValue(); Match match = refChange.getMatch().getComparison().getMatch(value); if (match != null) { Iterable<Diff> subDifferences = match.getAllDifferences(); if (refChange.getState() != DifferenceState.UNRESOLVED && any(subDifferences, EMFComparePredicates.hasState(DifferenceState.UNRESOLVED))) { ret.append("> ", Style.DECORATIONS_STYLER); //$NON-NLS-1$ } } } final EObject refChangeValue = refChange.getValue(); if (refChangeValue.eIsProxy()) { Style italic = Style.builder().setFont(IItemFontProvider.ITALIC_FONT).build(); ret.append(valueText, italic); } else { ret.append(valueText); } ret.append(" [" + referenceText, Style.DECORATIONS_STYLER); //$NON-NLS-1$ switch (refChange.getKind()) { case ADD: ret.append( ' ' + EMFCompareEditMessages .getString("ReferenceChangeItemProviderSpec.decoration.add"), //$NON-NLS-1$ Style.DECORATIONS_STYLER); break; case DELETE: ret.append( ' ' + EMFCompareEditMessages .getString("ReferenceChangeItemProviderSpec.decoration.delete"), //$NON-NLS-1$ Style.DECORATIONS_STYLER); break; case CHANGE: ret.append(' ' + changeText(refChange, refChange.getReference()), Style.DECORATIONS_STYLER); break; case MOVE: ret.append( ' ' + EMFCompareEditMessages .getString("ReferenceChangeItemProviderSpec.decoration.move"), //$NON-NLS-1$ Style.DECORATIONS_STYLER); break; default: throw new IllegalStateException("Unsupported " + DifferenceKind.class.getSimpleName() //$NON-NLS-1$ + " value: " + refChange.getKind()); //$NON-NLS-1$ } ret.append("]", Style.DECORATIONS_STYLER); //$NON-NLS-1$ return ret; } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.provider.ISemanticObjectLabelProvider#getSemanticObjectLabel(java.lang.Object) * @since 4.2 */ public String getSemanticObjectLabel(Object object) { final ReferenceChange refChange = (ReferenceChange)object; final String valueText = getValueText(refChange); StringBuilder ret = new StringBuilder(); if (refChange.getReference().isContainment()) { EObject value = refChange.getValue(); Match match = refChange.getMatch().getComparison().getMatch(value); if (match != null) { Iterable<Diff> subDifferences = match.getAllDifferences(); if (refChange.getState() != DifferenceState.UNRESOLVED && any(subDifferences, EMFComparePredicates.hasState(DifferenceState.UNRESOLVED))) { ret.append("> "); //$NON-NLS-1$ } } } ret.append(valueText); return ret.toString(); } /** * {@inheritDoc} * * @see org.eclipse.emf.compare.provider.IItemDescriptionProvider#getDescription(java.lang.Object) */ public String getDescription(Object object) { final ReferenceChange refChange = (ReferenceChange)object; final String valueText = getValueText(refChange); final String referenceText = getReferenceText(refChange); String remotely = ""; //$NON-NLS-1$ if (refChange.getSource() == DifferenceSource.RIGHT) { remotely = "remotely "; //$NON-NLS-1$ } String ret = ""; //$NON-NLS-1$ final String hasBeen = " has been "; //$NON-NLS-1$ switch (refChange.getKind()) { case ADD: ret = valueText + hasBeen + remotely + "added to " + referenceText; //$NON-NLS-1$ break; case DELETE: ret = valueText + hasBeen + remotely + "deleted from " + referenceText; //$NON-NLS-1$ break; case CHANGE: String changeText = changeText(refChange, refChange.getReference()); ret = referenceText + " " + valueText + hasBeen + remotely + changeText; //$NON-NLS-1$ break; case MOVE: ret = valueText + hasBeen + remotely + "moved in " + referenceText; //$NON-NLS-1$ break; default: throw new IllegalStateException("Unsupported " + DifferenceKind.class.getSimpleName() //$NON-NLS-1$ + " value: " + refChange.getKind()); //$NON-NLS-1$ } return ret; } }