/******************************************************************************* * Copyright (c) 2015 EclipseSource Muenchen GmbH 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: * Philip Langer - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.rcp.ui.internal.contentmergeviewer; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.compare.AttributeChange; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.internal.utils.ComparisonUtil; import org.eclipse.emf.compare.rcp.ui.internal.util.MergeViewerUtil; import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide; import org.eclipse.emf.compare.utils.IEqualityHelper; import org.eclipse.emf.compare.utils.ReferenceUtil; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.command.ChangeCommand; /** * A {@link IModelUpdateStrategy} for single-valued {@link EAttribute EAttributes}. * <p> * This strategy is tolerant in the sense that it will not throw exceptions if the input is not a supported * {@link AttributeChange}. In this case, this strategy will return <code>false</code> on * {@link #canUpdate(Diff, MergeViewerSide)} and return a command on * {@link #getModelUpdateCommand(Diff, Object, MergeViewerSide)} that specifies false on * {@link Command#canExecute()}. Therefore, it can be used as default strategy. * </p> * * @author Philip Langer <planger@eclipsesource.com> */ public class SingleValuedAttributeModelUpdateStrategy implements IModelUpdateStrategy { /** * {@inheritDoc} * * @see IModelUpdateStrategy#canUpdate(Diff, MergeViewerSide) */ public boolean canUpdate(Diff diff, MergeViewerSide side) { return isSingleValuedAttributeChange(diff) && haveTargetObject(diff, side); } /** * Specifies whether the given {@code diff} is an {@link AttributeChange} of a single-valued attribute. * * @param diff * The diff to check. * @return <code>true</code> if it is a change of a single-valued attribute, <code>false</code> otherwise. */ private boolean isSingleValuedAttributeChange(Diff diff) { return diff instanceof AttributeChange && !((AttributeChange)diff).getAttribute().isMany(); } /** * Specifies whether we have a target object based on the {@link Diff#getMatch() match} of the given * {@code diff} on the given {@code side}. * * @param diff * The diff to check. * @param side * The side to check on. * @return <code>true</code> if we have a target object, <code>false</code> otherwise. */ private boolean haveTargetObject(Diff diff, MergeViewerSide side) { return getTargetObject(diff, side) != null; } /** * Returns the target object for the {@link Diff#getMatch() match} of the given {@code diff} on the given * {@code side}. * * @param diff * The diff to get the target object for. * @param side * The side to get the target object from. * @return The target object. */ private EObject getTargetObject(Diff diff, MergeViewerSide side) { return MergeViewerUtil.getEObject(diff.getMatch(), side); } /** * {@inheritDoc} * * @see IModelUpdateStrategy#getModelUpdateCommand(Diff, Object, MergeViewerSide) */ public Command getModelUpdateCommand(final Diff diff, final Object newValue, final MergeViewerSide side) { final EObject targetObject = getTargetObject(diff, side); return new ChangeCommand(targetObject) { @Override public boolean canExecute() { return canUpdate(diff, side) && needsUpdate(diff, newValue, side) && super.canExecute(); } @Override protected void doExecute() { final EAttribute eAttribute = ((AttributeChange)diff).getAttribute(); targetObject.eSet(eAttribute, newValue); } }; } /** * Specifies whether the value in the model needs to be updated with the given {@code newValue} on the * given {@code side}. * * @param diff * The diff acting as context of the potential model update. * @param newValue * The potentially changed new value to be checked against. * @param side * The side to check. * @return <code>true</code> if an update is necessary, <code>false</code> otherwise. */ private boolean needsUpdate(Diff diff, Object newValue, MergeViewerSide side) { final IEqualityHelper equalityHelper = ComparisonUtil.getComparison(diff).getEqualityHelper(); final EObject eObject = getTargetObject(diff, side); final EAttribute eAttribute = ((AttributeChange)diff).getAttribute(); final Object oldValue = getStringValue(eObject, eAttribute); return !equalityHelper.matchingAttributeValues(newValue, oldValue); } /** * Returns the value as String of the given {@code eAttribute} in the given {@code eObject}. * * @param eObject * The EObject containing the attribute value. * @param eAttribute * The EAttribute to get its value. * @return The String representation of the value. */ private String getStringValue(final EObject eObject, final EAttribute eAttribute) { final EDataType eAttributeType = eAttribute.getEAttributeType(); final Object value; if (eObject == null) { value = null; } else { value = ReferenceUtil.safeEGet(eObject, eAttribute); } return EcoreUtil.convertToString(eAttributeType, value); } }