blob: ae1314e3605fbadfada61576bcc026f7cdce142f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 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.utils;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isDeleteOrUnsetDiff;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Predicate;
import org.eclipse.emf.compare.AttributeChange;
import org.eclipse.emf.compare.Conflict;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.FeatureMapChange;
import org.eclipse.emf.compare.ReferenceChange;
import org.eclipse.emf.compare.ResourceLocationChange;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.FeatureMap;
/**
* This class will provide a number of Predicates that can be used to retrieve particular {@link Diff}s from
* an iterable.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
public final class EMFCompareJavaPredicates {
/**
* Predicate builder for diffs that can conflict with the given diff.
*
* @param diff
* The diff
* @return A predicate that accepts diffs that might conflict with the given diff.
*/
public static Predicate<Diff> possiblyConflictingWith(Diff diff) {
return new ConflictCandidateFilter(diff);
}
/**
* This predicate can be used to check whether a particular diff is of the given {@code kind}. This is
* mainly used to differentiate additions from deletions.
*
* @param kind
* The kind we expect this diff to have.
* @return The created predicate.
*/
public static Predicate<? super Diff> ofKind(DifferenceKind... kind) {
return new Predicate<Diff>() {
public boolean test(Diff input) {
if (input != null) {
return Arrays.asList(kind).contains(input.getKind());
}
return false;
}
};
}
/**
* Accept only diffs that inherit either AttributeChange, ReferenceChange, or FeatureMapChange that
* concern the given feature.
*
* @param feature
* Feature to deal with
* @return a new predicate that accepts diffs that concern the given feature.
*/
public static Predicate<Diff> onFeature(EStructuralFeature feature) {
return new OnFeature(feature);
}
/**
* Accept only diffs whose value matches the given value.
*
* @param helper
* The helper to match values
* @param value
* The value to match
* @return The created predicate.
*/
public static Predicate<Diff> valueMatches(final IEqualityHelper helper, final Object value) {
return new Predicate<Diff>() {
public boolean test(Diff input) {
if (input instanceof ReferenceChange) {
return helper.matchingValues(value, ((ReferenceChange)input).getValue());
} else if (input instanceof AttributeChange) {
return helper.matchingValues(value, ((AttributeChange)input).getValue());
} else if (input instanceof FeatureMapChange) {
return helper.matchingValues(value,
((FeatureMap.Entry)((FeatureMapChange)input).getValue()).getValue());
}
return false;
}
};
}
/**
* This will be used to filter out the list of potential candidates for conflict with a given Diff.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
private static final class ConflictCandidateFilter implements Predicate<Diff> {
/** The Diff for which we seek conflict candidates. */
private final Diff diff;
/**
* Instantiates our filtering Predicate given the reference Diff for which to seek potential
* conflicts.
*
* @param diff
* The Diff for which we seek conflict candidates, must not be null.
*/
ConflictCandidateFilter(Diff diff) {
this.diff = Objects.requireNonNull(diff);
}
/**
* {@inheritDoc}
*
* @see com.google.common.base.Predicate#apply(java.lang.Object)
*/
public boolean test(Diff input) {
return !(input instanceof ResourceLocationChange) && canConflictWith(input);
}
/**
* Checks if the given {@link Diff diff1} can be in conflict with the given {@link Diff diff2}.
* <p>
* Notably, we don't need to try and detect a conflict between two diffs if they're one and the same
* or if they have already been detected as a conflicting couple. Likewise, there can be no conflict
* if the two diffs originate from the same side.
* </p>
* <p>
* bug 381143 : we'll also remove any containment deletion diff on other Matches from here.
* </p>
*
* @param other
* candidate difference to consider for conflict detection.
* @return {@code true} if the two given diffs can conflict, {@code false} otherwise.
*/
private boolean canConflictWith(Diff other) {
if (diff == other || diff.getSource() == other.getSource()) {
return false;
}
Conflict conflict = diff.getConflict();
boolean canConflict = false;
if (conflict == null || !conflict.getDifferences().contains(other)) {
if (diff.getMatch() != other.getMatch() && other instanceof ReferenceChange
&& ((ReferenceChange)other).getReference().isContainment()) {
canConflict = !isDeleteOrUnsetDiff(other);
} else {
canConflict = true;
}
}
return canConflict;
}
}
/**
* Predicate for diffs taht concern a given feature.
*
* @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
*/
private static class OnFeature implements Predicate<Diff> {
/** The feature. */
private final EStructuralFeature feature;
/**
* Constructor.
*
* @param feature
* the feature
*/
OnFeature(EStructuralFeature feature) {
this.feature = checkNotNull(feature);
}
/**
* Apply the predicate.
*
* @param input
* The diff to filter.
* @return true if and only if input concerns the given feature.
*/
public boolean test(Diff input) {
if (input == null) {
return false;
}
boolean apply = false;
if (input instanceof ReferenceChange) {
apply = ((ReferenceChange)input).getReference() == feature;
} else if (input instanceof AttributeChange) {
apply = ((AttributeChange)input).getAttribute() == feature;
} else if (input instanceof FeatureMapChange) {
apply = ((FeatureMapChange)input).getAttribute() == feature;
}
return apply;
}
}
}