[572074] Constraint gmf.runtime range for Sirius integration
[EMFCompare2.git] / plugins / org.eclipse.emf.compare / src / org / eclipse / emf / compare / merge / FeatureMapChangeMerger.java
blob42b13537c4502fcaf7ed9983874a5e7cfe46df48
1 /*******************************************************************************
2 * Copyright (c) 2014, 2017 Obeo and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Obeo - initial API and implementation
10 * Alexandra Buzila - Fixes for bug 446252
11 * Stefan Dirix - Fixes for Bugs 453218 and 453749
12 *******************************************************************************/
13 package org.eclipse.emf.compare.merge;
15 import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isFeatureMapContainment;
16 import static org.eclipse.emf.compare.utils.ReferenceUtil.safeEGet;
17 import static org.eclipse.emf.compare.utils.ReferenceUtil.safeESet;
19 import com.google.common.collect.Iterables;
21 import java.util.List;
23 import org.eclipse.emf.compare.Comparison;
24 import org.eclipse.emf.compare.Diff;
25 import org.eclipse.emf.compare.DifferenceSource;
26 import org.eclipse.emf.compare.Equivalence;
27 import org.eclipse.emf.compare.FeatureMapChange;
28 import org.eclipse.emf.compare.Match;
29 import org.eclipse.emf.compare.ReferenceChange;
30 import org.eclipse.emf.compare.internal.utils.ComparisonUtil;
31 import org.eclipse.emf.compare.internal.utils.DiffUtil;
32 import org.eclipse.emf.compare.utils.IEqualityHelper;
33 import org.eclipse.emf.ecore.EObject;
34 import org.eclipse.emf.ecore.EReference;
35 import org.eclipse.emf.ecore.EStructuralFeature;
36 import org.eclipse.emf.ecore.resource.Resource;
37 import org.eclipse.emf.ecore.util.BasicFeatureMap;
38 import org.eclipse.emf.ecore.util.FeatureMap;
39 import org.eclipse.emf.ecore.util.FeatureMapUtil;
40 import org.eclipse.emf.ecore.xmi.XMIResource;
42 /**
43 * This specific implementation of {@link AbstractMerger} will be used to merge attribute changes.
45 * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
46 * @since 3.2
48 public class FeatureMapChangeMerger extends AbstractMerger {
49 /**
50 * {@inheritDoc}
52 * @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
54 public boolean isMergerFor(Diff target) {
55 return target instanceof FeatureMapChange;
58 /**
59 * {@inheritDoc}
61 * @see org.eclipse.emf.compare.merge.AbstractMerger#accept(org.eclipse.emf.compare.Diff, boolean)
63 @Override
64 protected void accept(final Diff diff, boolean rightToLeft) {
65 FeatureMapChange featureMapChange = (FeatureMapChange)diff;
66 switch (diff.getKind()) {
67 case ADD:
68 // Create the same element in right
69 addInTarget(featureMapChange, rightToLeft);
70 break;
71 case DELETE:
72 // Delete that same element from right
73 removeFromTarget(featureMapChange, rightToLeft);
74 break;
75 case MOVE:
76 moveElement(featureMapChange, rightToLeft);
77 break;
78 case CHANGE:
79 changeValue(featureMapChange, rightToLeft);
80 break;
81 default:
82 break;
86 /**
87 * {@inheritDoc}
89 * @see org.eclipse.emf.compare.merge.AbstractMerger#reject(org.eclipse.emf.compare.Diff, boolean)
91 @Override
92 protected void reject(Diff diff, boolean rightToLeft) {
93 FeatureMapChange featureMapChange = (FeatureMapChange)diff;
94 switch (diff.getKind()) {
95 case ADD:
96 // We have a ADD on right. we need to revert this addition
97 removeFromTarget(featureMapChange, rightToLeft);
98 break;
99 case DELETE:
100 // DELETE in the right. We need to re-create this element
101 addInTarget(featureMapChange, rightToLeft);
102 break;
103 case MOVE:
104 moveElement(featureMapChange, rightToLeft);
105 break;
106 case CHANGE:
107 changeValue(featureMapChange, rightToLeft);
108 break;
109 default:
110 break;
115 * This will be called when we need to create an element in the target side.
116 * <p>
117 * All necessary sanity checks have been made to ensure that the current operation is one that should
118 * create an object in its side or add an object to an attribute. In other words, either :
119 * <ul>
120 * <li>We are copying from right to left and
121 * <ul>
122 * <li>we are copying an addition to the right side (we need to create the same object in the left), or
123 * </li>
124 * <li>we are copying a deletion from the left side (we need to revert the deletion).</li>
125 * </ul>
126 * </li>
127 * <li>We are copying from left to right and
128 * <ul>
129 * <li>we are copying a deletion from the right side (we need to revert the deletion), or</li>
130 * <li>we are copying an addition to the left side (we need to create the same object in the right).</li>
131 * </ul>
132 * </li>
133 * </ul>
134 * </p>
136 * @param diff
137 * The diff we are currently merging.
138 * @param rightToLeft
139 * Tells us whether we are to add an object on the left or right side.
141 @SuppressWarnings("unchecked")
142 protected void addInTarget(FeatureMapChange diff, boolean rightToLeft) {
143 final Match match = diff.getMatch();
144 final EObject expectedContainer;
145 if (rightToLeft) {
146 expectedContainer = match.getLeft();
147 } else {
148 expectedContainer = match.getRight();
150 if (expectedContainer == null) {
151 throw new IllegalStateException(
152 "Couldn't add in target because its parent hasn't been merged yet: " + diff); //$NON-NLS-1$
154 final Comparison comparison = match.getComparison();
155 final EStructuralFeature attribute = diff.getAttribute();
156 final FeatureMap.Entry expectedValue = (FeatureMap.Entry)diff.getValue();
157 // We have the container, attribute and value. We need to know the insertion index.
158 final int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
160 final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, attribute);
161 addFeatureMapValueInTarget(comparison, rightToLeft, targetList, insertionIndex, expectedValue);
165 * Add the FeatueMapEntry value at the insertionIndex position in the list.
167 * @param comparison
168 * The comparison object.
169 * @param rightToLeft
170 * The way of merge.
171 * @param list
172 * The list into which {@code value} should be added.
173 * @param insertionIndex
174 * The index at which {@code value} should be inserted into {@code list}. {@code -1} if it
175 * should be appended at the end of the list.
176 * @param entry
177 * The value we need to add to {@code list}.
179 private void addFeatureMapValueInTarget(final Comparison comparison, final boolean rightToLeft,
180 final List<Object> list, final int insertionIndex, final FeatureMap.Entry entry) {
181 final Object value = entry.getValue();
182 final EStructuralFeature key = entry.getEStructuralFeature();
183 if (value instanceof EObject) {
184 final EObject copy;
185 // The value has its equivalent on the opposite side, or not
186 final Match match = comparison.getMatch((EObject)value);
187 final EObject left = match.getLeft();
188 final EObject right = match.getRight();
189 if (rightToLeft && left != null) {
190 copy = left;
191 } else if (!rightToLeft && right != null) {
192 copy = right;
193 } else {
194 copy = createCopy((EObject)value);
197 ((BasicFeatureMap)(Object)list).addUnique(insertionIndex, FeatureMapUtil.createEntry(key, copy));
199 if (DiffUtil.isContainmentReference(key)) {
200 if (rightToLeft) {
201 match.setLeft(copy);
202 } else {
203 match.setRight(copy);
205 // Copy XMI ID when applicable.
206 final Resource initialResource = ((EObject)value).eResource();
207 final Resource targetResource = copy.eResource();
208 if (initialResource instanceof XMIResource && targetResource instanceof XMIResource) {
209 ((XMIResource)targetResource).setID(copy,
210 ((XMIResource)initialResource).getID((EObject)value));
213 } else {
214 ((BasicFeatureMap)(Object)list).add(insertionIndex, FeatureMapUtil.createEntry(key, value));
219 * This will be called when we need to remove an element from the target side.
220 * <p>
221 * All necessary sanity checks have been made to ensure that the current operation is one that should
222 * delete an object. In other words, we are :
223 * <ul>
224 * <li>Copying from right to left and either
225 * <ul>
226 * <li>we are copying a deletion from the right side (we need to remove the same object in the left) or,
227 * </li>
228 * <li>we are copying an addition to the left side (we need to revert the addition).</li>
229 * </ul>
230 * </li>
231 * <li>Copying from left to right and either
232 * <ul>
233 * <li>we are copying an addition to the right side (we need to revert the addition), or.</li>
234 * <li>we are copying a deletion from the left side (we need to remove the same object in the right).</li>
235 * </ul>
236 * </li>
237 * </ul>
238 * </p>
240 * @param diff
241 * The diff we are currently merging.
242 * @param rightToLeft
243 * Tells us whether we are to add an object on the left or right side.
245 @SuppressWarnings("unchecked")
246 protected void removeFromTarget(FeatureMapChange diff, boolean rightToLeft) {
247 final EObject currentContainer;
248 if (rightToLeft) {
249 currentContainer = diff.getMatch().getLeft();
250 } else {
251 currentContainer = diff.getMatch().getRight();
254 if (currentContainer != null) {
255 FeatureMap.Entry expectedValue = (FeatureMap.Entry)diff.getValue();
257 if (!isDiffSourceIsMergeTarget(diff, rightToLeft)) {
258 final List<Object> targetList = (List<Object>)safeEGet(currentContainer, diff.getAttribute());
259 for (Object object : targetList) {
260 if (diff.getMatch().getComparison().getEqualityHelper().matchingValues(expectedValue,
261 object)) {
262 expectedValue = (FeatureMap.Entry)object;
263 break;
268 final EStructuralFeature attribute = diff.getAttribute();
269 // We have the container, attribute and value to remove.
271 * TODO if the same value appears twice, should we try and find the one that has actually been
272 * deleted? Will it happen that often? For now, remove the first occurence we find.
274 final List<Object> targetList = (List<Object>)safeEGet(currentContainer, attribute);
275 final Comparison comparison = diff.getMatch().getComparison();
276 removeFeatureMapValueFromTarget(comparison, rightToLeft, targetList, expectedValue);
281 * Remove the FeatueMapEntry value from the list.
283 * @param comparison
284 * The comparison object.
285 * @param rightToLeft
286 * The way of merge.
287 * @param list
288 * The list from which {@code value} should be removed.
289 * @param entry
290 * The value we need to remove from {@code list}.
292 private void removeFeatureMapValueFromTarget(final Comparison comparison, final boolean rightToLeft,
293 final List<Object> list, final FeatureMap.Entry entry) {
294 final Object value = entry.getValue();
295 final EStructuralFeature key = entry.getEStructuralFeature();
297 if (((EReference)key).isContainment()) {
298 final Match expectedContainerMatch = comparison.getMatch((EObject)value);
299 if (rightToLeft) {
300 expectedContainerMatch.setLeft(null);
301 } else {
302 expectedContainerMatch.setRight(null);
305 ((BasicFeatureMap)(Object)list).remove(key, value);
309 * This will be called when trying to copy a "MOVE" diff.
311 * @param diff
312 * The diff we are currently merging.
313 * @param rightToLeft
314 * Whether we should move the value in the left or right side.
316 protected void moveElement(final FeatureMapChange diff, final boolean rightToLeft) {
317 final Match match = diff.getMatch();
318 final Comparison comparison = match.getComparison();
319 final EObject expectedContainer = ComparisonUtil.moveElementGetExpectedContainer(comparison, diff,
320 rightToLeft);
321 if (expectedContainer == null) {
322 throw new IllegalStateException(
323 "Couldn't move element because its parent hasn't been merged yet: " + diff); //$NON-NLS-1$
325 final FeatureMap.Entry expectedEntry = moveElementGetExpectedEntry(comparison, diff,
326 expectedContainer, rightToLeft);
327 // We now know the target container, target attribute and target entry.
328 doMove(diff, comparison, expectedContainer, expectedEntry, rightToLeft);
332 * Get the expected FeatureMap.Entry to move.
334 * @param comparison
335 * The comparison object.
336 * @param diff
337 * The diff we are currently merging.
338 * @param expectedContainer
339 * The expected container that will contain the expected entry to move.
340 * @param rightToLeft
341 * Whether we should move the value in the left or right side.
342 * @return The expected entry if found, <code>null</code> otherwise.
344 private FeatureMap.Entry moveElementGetExpectedEntry(final Comparison comparison,
345 final FeatureMapChange diff, final EObject expectedContainer, final boolean rightToLeft) {
346 final FeatureMap.Entry expectedEntry;
347 if (isDiffSourceIsMergeTarget(diff, rightToLeft)) {
348 expectedEntry = getExpectedEntryWhenDiffSourceIsMergeTarget(comparison, diff);
350 } else {
351 expectedEntry = getExpectedEntryWhenDiffSourceIsNotMergeTarget(comparison, diff,
352 expectedContainer, rightToLeft);
354 return expectedEntry;
358 * Get the expected FeatureMap.Entry to move.
360 * @param comparison
361 * The comparison object.
362 * @param diff
363 * The diff we are currently merging.
364 * @param expectedContainer
365 * The expected container that will contain the expected entry to move.
366 * @param rightToLeft
367 * Whether we should move the value in the left or right side.
368 * @return The expected entry if found, <code>null</code> otherwise.
370 @SuppressWarnings("unchecked")
371 private FeatureMap.Entry getExpectedEntryWhenDiffSourceIsNotMergeTarget(final Comparison comparison,
372 final FeatureMapChange diff, final EObject expectedContainer, final boolean rightToLeft) {
373 FeatureMap.Entry expectedEntry = null;
374 final IEqualityHelper equalityHelper = comparison.getEqualityHelper();
375 final FeatureMap.Entry diffEntry = (FeatureMap.Entry)diff.getValue();
376 // It is a Move on containment entry and the diff has an equivalence.
377 Equivalence equ = diff.getEquivalence();
378 if (isFeatureMapContainment(diff) && equ != null) {
379 // There is a ReferenceChange associated with the FeatureMapChange. This ReferenceChange
380 // contains the expected value to move.
381 for (ReferenceChange equivalence : Iterables.filter(equ.getDifferences(),
382 ReferenceChange.class)) {
383 final Match equivalenceMatchValue = comparison.getMatch(equivalence.getValue());
384 final Object expectedEntryValue;
385 if (rightToLeft) {
386 expectedEntryValue = equivalenceMatchValue.getLeft();
387 } else {
388 expectedEntryValue = equivalenceMatchValue.getRight();
390 expectedEntry = FeatureMapUtil.createEntry(diffEntry.getEStructuralFeature(),
391 expectedEntryValue);
392 break;
394 } else {
395 final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, diff.getAttribute());
396 for (Object object : targetList) {
397 if (equalityHelper.matchingValues(diffEntry, object)) {
398 expectedEntry = (FeatureMap.Entry)object;
399 break;
403 return expectedEntry;
407 * Get the expected FeatureMap.Entry to move.
409 * @param comparison
410 * The comparison object.
411 * @param diff
412 * The diff we are currently merging.
413 * @return The expected entry if found, <code>null</code> otherwise.
415 @SuppressWarnings("unchecked")
416 private FeatureMap.Entry getExpectedEntryWhenDiffSourceIsMergeTarget(final Comparison comparison,
417 final FeatureMapChange diff) {
418 final FeatureMap.Entry expectedEntry;
419 final IEqualityHelper equalityHelper = comparison.getEqualityHelper();
420 final FeatureMap.Entry diffEntry = (FeatureMap.Entry)diff.getValue();
421 final Match matchValue = comparison.getMatch((EObject)diffEntry.getValue());
422 final EObject value;
423 if (diff.getSource() == DifferenceSource.RIGHT) {
424 value = matchValue.getRight();
425 } else {
426 value = matchValue.getLeft();
428 if (comparison.isThreeWay() && isFeatureMapContainment(diff)) {
429 // search the origin key associated to the value
430 EStructuralFeature originKey = null;
431 final List<Object> originList = (List<Object>)safeEGet(matchValue.getOrigin().eContainer(),
432 diff.getAttribute());
433 for (Object object : originList) {
434 if (object instanceof FeatureMap.Entry
435 && equalityHelper.matchingValues(value, ((FeatureMap.Entry)object).getValue())) {
436 // same value, get the key
437 originKey = ((FeatureMap.Entry)object).getEStructuralFeature();
438 break;
441 expectedEntry = FeatureMapUtil.createEntry(originKey, value);
442 } else if (((EReference)diffEntry.getEStructuralFeature()).isContainment()) {
443 EStructuralFeature targetReference = getTargetReference(comparison, diff);
444 expectedEntry = FeatureMapUtil.createEntry(targetReference, value);
445 } else {
446 expectedEntry = FeatureMapUtil.createEntry(diffEntry.getEStructuralFeature(), value);
448 return expectedEntry;
452 * Get the target EStructuralFeature when moving a FeatureMap.Entry.
454 * @param comparison
455 * The comparison object.
456 * @param diff
457 * The diff we are currently merging.
458 * @return The target reference, i.e. the reference into which the value will be moved.
460 private EStructuralFeature getTargetReference(final Comparison comparison, final FeatureMapChange diff) {
461 final FeatureMap.Entry diffEntry = (FeatureMap.Entry)diff.getValue();
462 final Match equivalenceMatchValue = comparison.getMatch((EObject)diffEntry.getValue());
463 final EObject targetValue;
464 if (diff.getSource() == DifferenceSource.LEFT) {
465 targetValue = equivalenceMatchValue.getRight();
466 } else {
467 targetValue = equivalenceMatchValue.getLeft();
469 return targetValue.eContainingFeature();
473 * Checks if the source of the given diff is the same as the target of the merge.
475 * @param diff
476 * The diff we are currently merging.
477 * @param rightToLeft
478 * Whether we should merge the diff in the left or right side.
479 * @return true if the source of the given diff is the same as the target of the merge, false otherwise.
481 private boolean isDiffSourceIsMergeTarget(final Diff diff, final boolean rightToLeft) {
482 DifferenceSource source = diff.getSource();
483 return source == DifferenceSource.LEFT && rightToLeft
484 || source == DifferenceSource.RIGHT && !rightToLeft;
488 * This will do the actual work of moving the element into its attribute. All sanity checks were made in
489 * {@link #moveElement(boolean)} and no more verification will be made here.
491 * @param diff
492 * The diff we are currently merging.
493 * @param comparison
494 * Comparison holding this Diff.
495 * @param expectedContainer
496 * The container in which we are reorganizing an attribute.
497 * @param expectedValue
498 * The value that is to be moved within its attribute.
499 * @param rightToLeft
500 * Whether we should move the value in the left or right side.
502 @SuppressWarnings("unchecked")
503 protected void doMove(FeatureMapChange diff, Comparison comparison, EObject expectedContainer,
504 FeatureMap.Entry expectedValue, boolean rightToLeft) {
505 final EStructuralFeature attribute = diff.getAttribute();
506 if (attribute.isMany()) {
507 // Element to move cannot be part of the LCS... or there would not be a MOVE diff
508 int insertionIndex = findInsertionIndex(comparison, diff, rightToLeft);
510 * However, it could still have been located "before" its new index, in which case we need to take
511 * it into account.
513 final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, attribute);
514 final int currentIndex = targetList.indexOf(expectedValue);
515 if (insertionIndex > currentIndex && currentIndex >= 0) {
516 insertionIndex--;
519 if (currentIndex == -1) {
520 if (insertionIndex < 0 || insertionIndex >= targetList.size()) {
521 ((BasicFeatureMap)(Object)targetList).addUnique(expectedValue);
522 } else {
523 ((BasicFeatureMap)(Object)targetList).addUnique(insertionIndex, expectedValue);
525 } else {
526 if (insertionIndex < 0 || insertionIndex >= targetList.size()) {
527 ((BasicFeatureMap)(Object)targetList).move(targetList.size() - 1, expectedValue);
528 } else {
529 ((BasicFeatureMap)(Object)targetList).move(insertionIndex, expectedValue);
532 } else {
533 // This will never happen with the default diff engine, but may still be done from extenders
534 safeESet(expectedContainer, attribute, expectedValue);
539 * This will be called by the merge operations in order to change a key.
541 * @param diff
542 * The diff we are currently merging.
543 * @param rightToLeft
544 * Direction of the merge.
546 @SuppressWarnings("unchecked")
547 protected void changeValue(FeatureMapChange diff, boolean rightToLeft) {
548 final Match match = diff.getMatch();
549 final IEqualityHelper equalityHelper = match.getComparison().getEqualityHelper();
550 final EStructuralFeature attribute = diff.getAttribute();
551 final FeatureMap.Entry entry = (FeatureMap.Entry)diff.getValue();
552 // the value we're looking for in expected and origin container.
553 final Object entryValue = entry.getValue();
555 // Get the XMI ID
556 final String originValueId;
557 if (entryValue instanceof EObject) {
558 final Resource initialResource = ((EObject)entryValue).eResource();
559 if (initialResource instanceof XMIResource) {
560 originValueId = ((XMIResource)initialResource).getID((EObject)entryValue);
561 } else {
562 originValueId = null;
564 } else {
565 originValueId = null;
568 final EObject expectedContainer;
569 if (rightToLeft) {
570 expectedContainer = match.getLeft();
571 } else {
572 expectedContainer = match.getRight();
575 final EObject originContainer;
576 final boolean resetToOrigin = diff.getSource() == DifferenceSource.LEFT && rightToLeft
577 || diff.getSource() == DifferenceSource.RIGHT && !rightToLeft;
578 if (resetToOrigin && match.getComparison().isThreeWay()) {
579 originContainer = match.getOrigin();
580 } else if (rightToLeft) {
581 originContainer = match.getRight();
582 } else {
583 originContainer = match.getLeft();
586 // search the origin key associated to the value
587 EStructuralFeature originKey = null;
588 final List<Object> originList = (List<Object>)safeEGet(originContainer, attribute);
589 for (Object object : originList) {
590 if (object instanceof FeatureMap.Entry) {
591 // same value, get the key
592 if (equalityHelper.matchingValues(entryValue, ((FeatureMap.Entry)object).getValue())) {
593 originKey = ((FeatureMap.Entry)object).getEStructuralFeature();
594 break;
599 if (originKey == null) {
600 throw new RuntimeException("FeatureMapChangeMerger: Cannot find the key to change."); //$NON-NLS-1$
603 // search the value in expected container to change his key.
604 final List<Object> targetList = (List<Object>)safeEGet(expectedContainer, attribute);
605 int index = 0;
606 for (Object object : targetList) {
607 if (object instanceof FeatureMap.Entry) {
608 // same value, now change the key
609 Object targetValue = ((FeatureMap.Entry)object).getValue();
610 if (equalityHelper.matchingValues(entryValue, targetValue)) {
611 // forced to use setUnique(int, Entry) because if the originKey is not present in the
612 // target map, the setUnique(EStructuralFeature, int, Object) will not validate the key
613 ((BasicFeatureMap)(Object)targetList).setUnique(index,
614 FeatureMapUtil.createEntry(originKey, targetValue));
616 // setUnique(int, Entry) doesn't keep ID, so copy XMI ID when applicable.
617 final Resource targetResource;
618 if (DiffUtil.isContainmentReference(originKey) && targetValue instanceof EObject
619 && originValueId != null) {
620 targetResource = ((EObject)targetValue).eResource();
621 } else {
622 targetResource = null;
624 if (targetResource instanceof XMIResource) {
625 ((XMIResource)targetResource).setID((EObject)targetValue, originValueId);
627 break;
630 index++;
635 * This will be used by the distinct merge actions in order to find the index at which a value should be
636 * inserted in its target list. See {@link DiffUtil#findInsertionIndex(Comparison, Diff, boolean)} for
637 * more on this.
638 * <p>
639 * Sub-classes can override this if the insertion order is irrelevant. A return value of {@code -1} will
640 * be considered as "no index" and the value will be inserted at the end of its target list.
641 * </p>
643 * @param comparison
644 * This will be used in order to retrieve the Match for EObjects when comparing them.
645 * @param diff
646 * The diff which merging will trigger the need for an insertion index in its target list.
647 * @param rightToLeft
648 * {@code true} if the merging will be done into the left list, so that we should consider the
649 * right model as the source and the left as the target.
650 * @return The index at which this {@code diff}'s value should be inserted into the 'target' list, as
651 * inferred from {@code rightToLeft}. {@code -1} if the value should be inserted at the end of its
652 * target list.
653 * @see DiffUtil#findInsertionIndex(Comparison, Diff, boolean)
655 protected int findInsertionIndex(Comparison comparison, Diff diff, boolean rightToLeft) {
656 return DiffUtil.findInsertionIndex(comparison, diff, rightToLeft);