| /******************************************************************************* |
| * Copyright (c) 2014, 2017 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 |
| * Alexandra Buzila - Fixes for bug 446252 |
| * Stefan Dirix - Fixes for Bugs 453218 and 453749 |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.merge; |
| |
| import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isFeatureMapContainment; |
| import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEGet; |
| import static org.eclipse.emf.compare.utils.ReferenceUtil.safeESet; |
| |
| import com.google.common.collect.Iterables; |
| |
| import java.util.List; |
| |
| import org.eclipse.emf.compare.Comparison; |
| import org.eclipse.emf.compare.Diff; |
| import org.eclipse.emf.compare.DifferenceSource; |
| import org.eclipse.emf.compare.Equivalence; |
| import org.eclipse.emf.compare.FeatureMapChange; |
| import org.eclipse.emf.compare.Match; |
| import org.eclipse.emf.compare.ReferenceChange; |
| import org.eclipse.emf.compare.internal.utils.ComparisonUtil; |
| 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.EStructuralFeature; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.BasicFeatureMap; |
| import org.eclipse.emf.ecore.util.FeatureMap; |
| import org.eclipse.emf.ecore.util.FeatureMapUtil; |
| import org.eclipse.emf.ecore.xmi.XMIResource; |
| |
| /** |
| * This specific implementation of {@link AbstractMerger} will be used to merge attribute changes. |
| * |
| * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a> |
| * @since 3.2 |
| */ |
| public class FeatureMapChangeMerger extends AbstractMerger { |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff) |
| */ |
| public boolean isMergerFor(Diff target) { |
| return target instanceof FeatureMapChange; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.compare.merge.AbstractMerger#accept(org.eclipse.emf.compare.Diff, boolean) |
| */ |
| @Override |
| protected void accept(final Diff diff, boolean rightToLeft) { |
| FeatureMapChange featureMapChange = (FeatureMapChange)diff; |
| switch (diff.getKind()) { |
| case ADD: |
| // Create the same element in right |
| addInTarget(featureMapChange, rightToLeft); |
| break; |
| case DELETE: |
| // Delete that same element from right |
| removeFromTarget(featureMapChange, rightToLeft); |
| break; |
| case MOVE: |
| moveElement(featureMapChange, rightToLeft); |
| break; |
| case CHANGE: |
| changeValue(featureMapChange, rightToLeft); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.compare.merge.AbstractMerger#reject(org.eclipse.emf.compare.Diff, boolean) |
| */ |
| @Override |
| protected void reject(Diff diff, boolean rightToLeft) { |
| FeatureMapChange featureMapChange = (FeatureMapChange)diff; |
| switch (diff.getKind()) { |
| case ADD: |
| // We have a ADD on right. we need to revert this addition |
| removeFromTarget(featureMapChange, rightToLeft); |
| break; |
| case DELETE: |
| // DELETE in the right. We need to re-create this element |
| addInTarget(featureMapChange, rightToLeft); |
| break; |
| case MOVE: |
| moveElement(featureMapChange, rightToLeft); |
| break; |
| case CHANGE: |
| changeValue(featureMapChange, rightToLeft); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * 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 object to an attribute. 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(FeatureMapChange diff, boolean rightToLeft) { |
| final Match match = diff.getMatch(); |
| final EObject expectedContainer; |
| if (rightToLeft) { |
| expectedContainer = match.getLeft(); |
| } else { |
| expectedContainer = match.getRight(); |
| } |
| if (expectedContainer == null) { |
| throw new IllegalStateException( |
| "Couldn't add in target because its parent hasn't been merged yet: " + diff); //$NON-NLS-1$ |
| } |
| final Comparison comparison = match.getComparison(); |
| final EStructuralFeature attribute = diff.getAttribute(); |
| final FeatureMap.Entry expectedValue = (FeatureMap.Entry)diff.getValue(); |
| // We have the container, attribute and value. We need to know the insertion index. |
| final int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft); |
| |
| final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, attribute); |
| addFeatureMapValueInTarget(comparison, rightToLeft, targetList, insertionIndex, expectedValue); |
| } |
| |
| /** |
| * Add the FeatueMapEntry value at the insertionIndex position in the list. |
| * |
| * @param comparison |
| * The comparison object. |
| * @param rightToLeft |
| * The way of merge. |
| * @param list |
| * The list into which {@code value} should be added. |
| * @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. |
| * @param entry |
| * The value we need to add to {@code list}. |
| */ |
| private void addFeatureMapValueInTarget(final Comparison comparison, final boolean rightToLeft, |
| final List<Object> list, final int insertionIndex, final FeatureMap.Entry entry) { |
| final Object value = entry.getValue(); |
| final EStructuralFeature key = entry.getEStructuralFeature(); |
| if (value instanceof EObject) { |
| final EObject copy; |
| // The value has its equivalent on the opposite side, or not |
| final Match match = comparison.getMatch((EObject)value); |
| final EObject left = match.getLeft(); |
| final EObject right = match.getRight(); |
| if (rightToLeft && left != null) { |
| copy = left; |
| } else if (!rightToLeft && right != null) { |
| copy = right; |
| } else { |
| copy = createCopy((EObject)value); |
| } |
| |
| ((BasicFeatureMap)(Object)list).addUnique(insertionIndex, FeatureMapUtil.createEntry(key, copy)); |
| |
| if (DiffUtil.isContainmentReference(key)) { |
| if (rightToLeft) { |
| match.setLeft(copy); |
| } else { |
| match.setRight(copy); |
| } |
| // Copy XMI ID when applicable. |
| final Resource initialResource = ((EObject)value).eResource(); |
| final Resource targetResource = copy.eResource(); |
| if (initialResource instanceof XMIResource && targetResource instanceof XMIResource) { |
| ((XMIResource)targetResource).setID(copy, |
| ((XMIResource)initialResource).getID((EObject)value)); |
| } |
| } |
| } else { |
| ((BasicFeatureMap)(Object)list).add(insertionIndex, FeatureMapUtil.createEntry(key, value)); |
| } |
| } |
| |
| /** |
| * 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(FeatureMapChange diff, boolean rightToLeft) { |
| final EObject currentContainer; |
| if (rightToLeft) { |
| currentContainer = diff.getMatch().getLeft(); |
| } else { |
| currentContainer = diff.getMatch().getRight(); |
| } |
| |
| if (currentContainer != null) { |
| FeatureMap.Entry expectedValue = (FeatureMap.Entry)diff.getValue(); |
| |
| if (!isDiffSourceIsMergeTarget(diff, rightToLeft)) { |
| final List<Object> targetList = (List<Object>)safeEGet(currentContainer, diff.getAttribute()); |
| for (Object object : targetList) { |
| if (diff.getMatch().getComparison().getEqualityHelper().matchingValues(expectedValue, |
| object)) { |
| expectedValue = (FeatureMap.Entry)object; |
| break; |
| } |
| } |
| } |
| |
| final EStructuralFeature attribute = diff.getAttribute(); |
| // We have the container, attribute and value to remove. |
| /* |
| * TODO if the same value appears twice, should we try and find the one that has actually been |
| * deleted? Will it happen that often? For now, remove the first occurence we find. |
| */ |
| final List<Object> targetList = (List<Object>)safeEGet(currentContainer, attribute); |
| final Comparison comparison = diff.getMatch().getComparison(); |
| removeFeatureMapValueFromTarget(comparison, rightToLeft, targetList, expectedValue); |
| } |
| } |
| |
| /** |
| * Remove the FeatueMapEntry value from the list. |
| * |
| * @param comparison |
| * The comparison object. |
| * @param rightToLeft |
| * The way of merge. |
| * @param list |
| * The list from which {@code value} should be removed. |
| * @param entry |
| * The value we need to remove from {@code list}. |
| */ |
| private void removeFeatureMapValueFromTarget(final Comparison comparison, final boolean rightToLeft, |
| final List<Object> list, final FeatureMap.Entry entry) { |
| final Object value = entry.getValue(); |
| final EStructuralFeature key = entry.getEStructuralFeature(); |
| |
| if (((EReference)key).isContainment()) { |
| final Match expectedContainerMatch = comparison.getMatch((EObject)value); |
| if (rightToLeft) { |
| expectedContainerMatch.setLeft(null); |
| } else { |
| expectedContainerMatch.setRight(null); |
| } |
| } |
| ((BasicFeatureMap)(Object)list).remove(key, value); |
| } |
| |
| /** |
| * 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(final FeatureMapChange diff, final boolean rightToLeft) { |
| final Match match = diff.getMatch(); |
| final Comparison comparison = match.getComparison(); |
| final EObject expectedContainer = ComparisonUtil.moveElementGetExpectedContainer(comparison, diff, |
| rightToLeft); |
| if (expectedContainer == null) { |
| throw new IllegalStateException( |
| "Couldn't move element because its parent hasn't been merged yet: " + diff); //$NON-NLS-1$ |
| } |
| final FeatureMap.Entry expectedEntry = moveElementGetExpectedEntry(comparison, diff, |
| expectedContainer, rightToLeft); |
| // We now know the target container, target attribute and target entry. |
| doMove(diff, comparison, expectedContainer, expectedEntry, rightToLeft); |
| } |
| |
| /** |
| * Get the expected FeatureMap.Entry to move. |
| * |
| * @param comparison |
| * The comparison object. |
| * @param diff |
| * The diff we are currently merging. |
| * @param expectedContainer |
| * The expected container that will contain the expected entry to move. |
| * @param rightToLeft |
| * Whether we should move the value in the left or right side. |
| * @return The expected entry if found, <code>null</code> otherwise. |
| */ |
| private FeatureMap.Entry moveElementGetExpectedEntry(final Comparison comparison, |
| final FeatureMapChange diff, final EObject expectedContainer, final boolean rightToLeft) { |
| final FeatureMap.Entry expectedEntry; |
| if (isDiffSourceIsMergeTarget(diff, rightToLeft)) { |
| expectedEntry = getExpectedEntryWhenDiffSourceIsMergeTarget(comparison, diff); |
| |
| } else { |
| expectedEntry = getExpectedEntryWhenDiffSourceIsNotMergeTarget(comparison, diff, |
| expectedContainer, rightToLeft); |
| } |
| return expectedEntry; |
| } |
| |
| /** |
| * Get the expected FeatureMap.Entry to move. |
| * |
| * @param comparison |
| * The comparison object. |
| * @param diff |
| * The diff we are currently merging. |
| * @param expectedContainer |
| * The expected container that will contain the expected entry to move. |
| * @param rightToLeft |
| * Whether we should move the value in the left or right side. |
| * @return The expected entry if found, <code>null</code> otherwise. |
| */ |
| @SuppressWarnings("unchecked") |
| private FeatureMap.Entry getExpectedEntryWhenDiffSourceIsNotMergeTarget(final Comparison comparison, |
| final FeatureMapChange diff, final EObject expectedContainer, final boolean rightToLeft) { |
| FeatureMap.Entry expectedEntry = null; |
| final IEqualityHelper equalityHelper = comparison.getEqualityHelper(); |
| final FeatureMap.Entry diffEntry = (FeatureMap.Entry)diff.getValue(); |
| // It is a Move on containment entry and the diff has an equivalence. |
| Equivalence equ = diff.getEquivalence(); |
| if (isFeatureMapContainment(diff) && equ != null) { |
| // There is a ReferenceChange associated with the FeatureMapChange. This ReferenceChange |
| // contains the expected value to move. |
| for (ReferenceChange equivalence : Iterables.filter(equ.getDifferences(), |
| ReferenceChange.class)) { |
| final Match equivalenceMatchValue = comparison.getMatch(equivalence.getValue()); |
| final Object expectedEntryValue; |
| if (rightToLeft) { |
| expectedEntryValue = equivalenceMatchValue.getLeft(); |
| } else { |
| expectedEntryValue = equivalenceMatchValue.getRight(); |
| } |
| expectedEntry = FeatureMapUtil.createEntry(diffEntry.getEStructuralFeature(), |
| expectedEntryValue); |
| break; |
| } |
| } else { |
| final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, diff.getAttribute()); |
| for (Object object : targetList) { |
| if (equalityHelper.matchingValues(diffEntry, object)) { |
| expectedEntry = (FeatureMap.Entry)object; |
| break; |
| } |
| } |
| } |
| return expectedEntry; |
| } |
| |
| /** |
| * Get the expected FeatureMap.Entry to move. |
| * |
| * @param comparison |
| * The comparison object. |
| * @param diff |
| * The diff we are currently merging. |
| * @return The expected entry if found, <code>null</code> otherwise. |
| */ |
| @SuppressWarnings("unchecked") |
| private FeatureMap.Entry getExpectedEntryWhenDiffSourceIsMergeTarget(final Comparison comparison, |
| final FeatureMapChange diff) { |
| final FeatureMap.Entry expectedEntry; |
| final IEqualityHelper equalityHelper = comparison.getEqualityHelper(); |
| final FeatureMap.Entry diffEntry = (FeatureMap.Entry)diff.getValue(); |
| final Match matchValue = comparison.getMatch((EObject)diffEntry.getValue()); |
| final EObject value; |
| if (diff.getSource() == DifferenceSource.RIGHT) { |
| value = matchValue.getRight(); |
| } else { |
| value = matchValue.getLeft(); |
| } |
| if (comparison.isThreeWay() && isFeatureMapContainment(diff)) { |
| // search the origin key associated to the value |
| EStructuralFeature originKey = null; |
| final List<Object> originList = (List<Object>)safeEGet(matchValue.getOrigin().eContainer(), |
| diff.getAttribute()); |
| for (Object object : originList) { |
| if (object instanceof FeatureMap.Entry |
| && equalityHelper.matchingValues(value, ((FeatureMap.Entry)object).getValue())) { |
| // same value, get the key |
| originKey = ((FeatureMap.Entry)object).getEStructuralFeature(); |
| break; |
| } |
| } |
| expectedEntry = FeatureMapUtil.createEntry(originKey, value); |
| } else if (((EReference)diffEntry.getEStructuralFeature()).isContainment()) { |
| EStructuralFeature targetReference = getTargetReference(comparison, diff); |
| expectedEntry = FeatureMapUtil.createEntry(targetReference, value); |
| } else { |
| expectedEntry = FeatureMapUtil.createEntry(diffEntry.getEStructuralFeature(), value); |
| } |
| return expectedEntry; |
| } |
| |
| /** |
| * Get the target EStructuralFeature when moving a FeatureMap.Entry. |
| * |
| * @param comparison |
| * The comparison object. |
| * @param diff |
| * The diff we are currently merging. |
| * @return The target reference, i.e. the reference into which the value will be moved. |
| */ |
| private EStructuralFeature getTargetReference(final Comparison comparison, final FeatureMapChange diff) { |
| final FeatureMap.Entry diffEntry = (FeatureMap.Entry)diff.getValue(); |
| final Match equivalenceMatchValue = comparison.getMatch((EObject)diffEntry.getValue()); |
| final EObject targetValue; |
| if (diff.getSource() == DifferenceSource.LEFT) { |
| targetValue = equivalenceMatchValue.getRight(); |
| } else { |
| targetValue = equivalenceMatchValue.getLeft(); |
| } |
| return targetValue.eContainingFeature(); |
| } |
| |
| /** |
| * Checks if the source of the given diff is the same as the target of the merge. |
| * |
| * @param diff |
| * The diff we are currently merging. |
| * @param rightToLeft |
| * Whether we should merge the diff in the left or right side. |
| * @return true if the source of the given diff is the same as the target of the merge, false otherwise. |
| */ |
| private boolean isDiffSourceIsMergeTarget(final Diff diff, final boolean rightToLeft) { |
| DifferenceSource source = diff.getSource(); |
| return source == DifferenceSource.LEFT && rightToLeft |
| || source == DifferenceSource.RIGHT && !rightToLeft; |
| } |
| |
| /** |
| * This will do the actual work of moving the element into its attribute. 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 an attribute. |
| * @param expectedValue |
| * The value that is to be moved within its attribute. |
| * @param rightToLeft |
| * Whether we should move the value in the left or right side. |
| */ |
| @SuppressWarnings("unchecked") |
| protected void doMove(FeatureMapChange diff, Comparison comparison, EObject expectedContainer, |
| FeatureMap.Entry expectedValue, boolean rightToLeft) { |
| final EStructuralFeature attribute = diff.getAttribute(); |
| if (attribute.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<Object> targetList = (List<Object>)safeEGet(expectedContainer, attribute); |
| final int currentIndex = targetList.indexOf(expectedValue); |
| if (insertionIndex > currentIndex && currentIndex >= 0) { |
| insertionIndex--; |
| } |
| |
| if (currentIndex == -1) { |
| if (insertionIndex < 0 || insertionIndex >= targetList.size()) { |
| ((BasicFeatureMap)(Object)targetList).addUnique(expectedValue); |
| } else { |
| ((BasicFeatureMap)(Object)targetList).addUnique(insertionIndex, expectedValue); |
| } |
| } else { |
| if (insertionIndex < 0 || insertionIndex >= targetList.size()) { |
| ((BasicFeatureMap)(Object)targetList).move(targetList.size() - 1, expectedValue); |
| } else { |
| ((BasicFeatureMap)(Object)targetList).move(insertionIndex, expectedValue); |
| } |
| } |
| } else { |
| // This will never happen with the default diff engine, but may still be done from extenders |
| safeESet(expectedContainer, attribute, expectedValue); |
| } |
| } |
| |
| /** |
| * This will be called by the merge operations in order to change a key. |
| * |
| * @param diff |
| * The diff we are currently merging. |
| * @param rightToLeft |
| * Direction of the merge. |
| */ |
| @SuppressWarnings("unchecked") |
| protected void changeValue(FeatureMapChange diff, boolean rightToLeft) { |
| final Match match = diff.getMatch(); |
| final IEqualityHelper equalityHelper = match.getComparison().getEqualityHelper(); |
| final EStructuralFeature attribute = diff.getAttribute(); |
| final FeatureMap.Entry entry = (FeatureMap.Entry)diff.getValue(); |
| // the value we're looking for in expected and origin container. |
| final Object entryValue = entry.getValue(); |
| |
| // Get the XMI ID |
| final String originValueId; |
| if (entryValue instanceof EObject) { |
| final Resource initialResource = ((EObject)entryValue).eResource(); |
| if (initialResource instanceof XMIResource) { |
| originValueId = ((XMIResource)initialResource).getID((EObject)entryValue); |
| } else { |
| originValueId = null; |
| } |
| } else { |
| originValueId = null; |
| } |
| |
| final EObject expectedContainer; |
| if (rightToLeft) { |
| expectedContainer = match.getLeft(); |
| } else { |
| expectedContainer = match.getRight(); |
| } |
| |
| final EObject originContainer; |
| final boolean resetToOrigin = diff.getSource() == DifferenceSource.LEFT && rightToLeft |
| || diff.getSource() == DifferenceSource.RIGHT && !rightToLeft; |
| if (resetToOrigin && match.getComparison().isThreeWay()) { |
| originContainer = match.getOrigin(); |
| } else if (rightToLeft) { |
| originContainer = match.getRight(); |
| } else { |
| originContainer = match.getLeft(); |
| } |
| |
| // search the origin key associated to the value |
| EStructuralFeature originKey = null; |
| final List<Object> originList = (List<Object>)safeEGet(originContainer, attribute); |
| for (Object object : originList) { |
| if (object instanceof FeatureMap.Entry) { |
| // same value, get the key |
| if (equalityHelper.matchingValues(entryValue, ((FeatureMap.Entry)object).getValue())) { |
| originKey = ((FeatureMap.Entry)object).getEStructuralFeature(); |
| break; |
| } |
| } |
| } |
| |
| if (originKey == null) { |
| throw new RuntimeException("FeatureMapChangeMerger: Cannot find the key to change."); //$NON-NLS-1$ |
| } |
| |
| // search the value in expected container to change his key. |
| final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, attribute); |
| int index = 0; |
| for (Object object : targetList) { |
| if (object instanceof FeatureMap.Entry) { |
| // same value, now change the key |
| Object targetValue = ((FeatureMap.Entry)object).getValue(); |
| if (equalityHelper.matchingValues(entryValue, targetValue)) { |
| // forced to use setUnique(int, Entry) because if the originKey is not present in the |
| // target map, the setUnique(EStructuralFeature, int, Object) will not validate the key |
| ((BasicFeatureMap)(Object)targetList).setUnique(index, |
| FeatureMapUtil.createEntry(originKey, targetValue)); |
| |
| // setUnique(int, Entry) doesn't keep ID, so copy XMI ID when applicable. |
| final Resource targetResource; |
| if (DiffUtil.isContainmentReference(originKey) && targetValue instanceof EObject |
| && originValueId != null) { |
| targetResource = ((EObject)targetValue).eResource(); |
| } else { |
| targetResource = null; |
| } |
| if (targetResource instanceof XMIResource) { |
| ((XMIResource)targetResource).setID((EObject)targetValue, originValueId); |
| } |
| break; |
| } |
| } |
| index++; |
| } |
| } |
| |
| /** |
| * 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); |
| } |
| } |