blob: bbfa3a5ad5dcd577093dee1f9dde9287fb32ef2a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 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
*******************************************************************************/
package org.eclipse.emf.compare.merge;
import static org.eclipse.emf.compare.ConflictKind.REAL;
import static org.eclipse.emf.compare.DifferenceKind.DELETE;
import static org.eclipse.emf.compare.DifferenceKind.MOVE;
import static org.eclipse.emf.compare.DifferenceSource.LEFT;
import static org.eclipse.emf.compare.DifferenceSource.RIGHT;
import static org.eclipse.emf.compare.DifferenceState.DISCARDED;
import static org.eclipse.emf.compare.merge.IMergeCriterion.NONE;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.Conflict;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.DifferenceSource;
import org.eclipse.emf.compare.ReferenceChange;
import org.eclipse.emf.compare.utils.IEqualityHelper;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.InternalEList;
/**
* This specific implementation of {@link AbstractMerger} will be used to merge real conflicts.
*
* @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
* @since 3.1
*/
public class ConflictMerger extends AbstractMerger {
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger#isMergerFor(org.eclipse.emf.compare.Diff)
*/
public boolean isMergerFor(Diff target) {
final Conflict conflict = target.getConflict();
return conflict != null && conflict.getKind() == REAL;
}
@Override
public boolean apply(IMergeCriterion criterion) {
return criterion == null || criterion == NONE;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger#copyLeftToRight(org.eclipse.emf.compare.Diff,
* org.eclipse.emf.common.util.Monitor)
*/
@Override
public void copyLeftToRight(Diff target, Monitor monitor) {
if (isInTerminalState(target)) {
return;
}
if (target.getSource() == LEFT) {
// Call the appropriate merger for each conflicted diff
Conflict conflict = target.getConflict();
Predicate<Diff> conflictVsMoveAndDelete = isConflictVsMoveAndDelete(target, MOVE, DELETE);
for (Diff conflictedDiff : getDifferences(conflict)) {
if (conflictedDiff.getSource() == RIGHT) {
if (conflictVsMoveAndDelete.apply(conflictedDiff)) {
conflictedDiff.setState(DISCARDED);
} else {
mergeConflictedDiff(conflictedDiff, true, monitor);
}
}
}
}
// Call the appropriate merger for the current diff
getMergerDelegate(target).copyLeftToRight(target, monitor);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.merge.IMerger#copyRightToLeft(org.eclipse.emf.compare.Diff,
* org.eclipse.emf.common.util.Monitor)
*/
@Override
public void copyRightToLeft(Diff target, Monitor monitor) {
if (isInTerminalState(target)) {
return;
}
if (target.getSource() == RIGHT) {
// Call the appropriate merger for each conflicted diff
Conflict conflict = target.getConflict();
Predicate<Diff> conflictVsMoveAndDelete = isConflictVsMoveAndDelete(target, MOVE, DELETE);
for (Diff conflictedDiff : getDifferences(conflict)) {
if (conflictedDiff.getSource() == LEFT) {
if (conflictVsMoveAndDelete.apply(conflictedDiff)) {
conflictedDiff.setState(DISCARDED);
} else {
mergeConflictedDiff(conflictedDiff, false, monitor);
}
}
}
}
// Call the appropriate merger for the current diff
getMergerDelegate(target).copyRightToLeft(target, monitor);
}
@Override
public Set<Diff> getDirectMergeDependencies(Diff diff, boolean rightToLeft) {
Set<Diff> result = super.getDirectMergeDependencies(diff, rightToLeft);
if (AbstractMerger.isAccepting(diff, rightToLeft)) {
Conflict conflict = diff.getConflict();
DifferenceSource source = diff.getSource();
Predicate<Diff> conflictVsMoveAndDelete = isConflictVsMoveAndDelete(diff, DELETE, MOVE);
// Add each conflicting diff from the other side
for (Diff conflictingDiff : getDifferences(conflict)) {
if (conflictingDiff.getSource() != source && conflictingDiff.getKind() != MOVE
&& !conflictVsMoveAndDelete.apply(conflictingDiff)) {
result.add(conflictingDiff);
}
}
}
return result;
}
@Override
public Set<Diff> getDirectResultingMerges(Diff diff, boolean rightToLeft) {
Set<Diff> result = super.getDirectResultingMerges(diff, rightToLeft);
if (AbstractMerger.isAccepting(diff, rightToLeft)) {
Conflict conflict = diff.getConflict();
DifferenceSource source = diff.getSource();
Predicate<Diff> conflictVsMoveAndDelete = isConflictVsMoveAndDelete(diff, DELETE, MOVE);
for (Diff conflictingDiff : getDifferences(conflict)) {
if (conflictingDiff.getSource() != source && (conflictingDiff.getKind() != MOVE
|| conflictVsMoveAndDelete.apply(conflictingDiff))) {
result.add(conflictingDiff);
}
}
}
return result;
}
/**
* Returns the unresolving basic list for the {@link Conflict#getDifferences() conflict's differences}.
*
* @param conflict
* the conflict.
* @return the unresolving basic list for the conflict's differences.
*/
private List<Diff> getDifferences(Conflict conflict) {
return ((InternalEList<Diff>)conflict.getDifferences()).basicList();
}
/**
* A predicate to test if the given diff and the predicate's diff are diffs on the same object with one
* move and one delete. The move diff must be the one selected by the user for merging.
*
* @param diff
* the given diff.
* @param diffKind
* the kind of the given diff.
* @param otherDiffKind
* the kind of the predicate's diff.
* @return A predicate to test if the given diff and the predicate's diff are diffs on the same object
* with one move and one delete.
*/
private Predicate<Diff> isConflictVsMoveAndDelete(final Diff diff, DifferenceKind diffKind,
final DifferenceKind otherDiffKind) {
if (diff.getKind() == diffKind && diff instanceof ReferenceChange) {
return new Predicate<Diff>() {
private EObject targetValue;
private IEqualityHelper equalityHelper;
public boolean apply(Diff input) {
if (input.getKind() == otherDiffKind && input instanceof ReferenceChange) {
if (equalityHelper == null) {
equalityHelper = diff.getMatch().getComparison().getEqualityHelper();
targetValue = ((ReferenceChange)diff).getValue();
}
return equalityHelper.matchingValues(targetValue,
((ReferenceChange)input).getValue());
} else {
return false;
}
}
};
} else {
return Predicates.alwaysFalse();
}
}
/**
* Manages the merge of the given conflicted diff.
*
* @param conflictedDiff
* The given diff.
* @param leftToRight
* The way of merge.
* @param monitor
* Monitor.
*/
private void mergeConflictedDiff(Diff conflictedDiff, boolean leftToRight, Monitor monitor) {
if (conflictedDiff.getKind() != MOVE) {
DelegatingMerger delegate = getMergerDelegate(conflictedDiff);
if (leftToRight) {
delegate.copyLeftToRight(conflictedDiff, monitor);
} else {
delegate.copyRightToLeft(conflictedDiff, monitor);
}
} else {
conflictedDiff.setState(DISCARDED);
}
}
@Override
protected DelegatingMerger getMergerDelegate(Diff diff) {
IMergeCriterion criterion = (IMergeCriterion)getMergeOptions()
.get(IMergeCriterion.OPTION_MERGE_CRITERION);
Iterator<IMerger> it = ((Registry2)getRegistry()).getMergersByRankDescending(diff, criterion);
IMerger merger = this;
while (it.hasNext() && merger == this) {
merger = it.next();
}
if (merger == null) {
throw new IllegalStateException("No merger found for diff " + diff.getClass().getSimpleName()); //$NON-NLS-1$
}
return new DelegatingMerger(merger, criterion);
}
}