diff options
author | Laurent Delaigue | 2016-10-13 07:54:27 +0000 |
---|---|---|
committer | Laurent Delaigue | 2016-10-13 10:37:31 +0000 |
commit | 3c7d3d74422aa061acf2db09586db94088136e61 (patch) | |
tree | 7a468249e80b8f0b9eb6cc5d5bdd53a8c3d610c3 | |
parent | 78e37cb0bbd9d806af97dfdae8f48db30a5d1cf7 (diff) | |
download | org.eclipse.emf.compare-3c7d3d74422aa061acf2db09586db94088136e61.tar.gz org.eclipse.emf.compare-3c7d3d74422aa061acf2db09586db94088136e61.tar.xz org.eclipse.emf.compare-3c7d3d74422aa061acf2db09586db94088136e61.zip |
[501864] Refactoring of group providers & filters
Diffs that have no real conflict (direct or indirect) and only some
pseudo-conflicts (but also non-conflicting refining diffs) only appear
in the side group.
Also add javadoc to describe the expected behaviour of the group
provider 'by side' and some unit tests for predicates.
Change-Id: I3d02172b1c77199ea0904459b196289b19db35d6
Signed-off-by: Laurent Delaigue <laurent.delaigue@obeo.fr>
10 files changed, 506 insertions, 192 deletions
diff --git a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java index 9e064ff72..b3947caf2 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupTest.java @@ -32,8 +32,8 @@ import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.Stru import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.impl.TechnicalitiesFilter; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.BasicDifferenceGroupImpl; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.CompositeConflict; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl.CompositeConflict; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.ConflictNode; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.DiffNode; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.MatchNode; @@ -355,7 +355,7 @@ public class ConflictsGroupTest extends AbstractTestTreeNodeItemProviderAdapter * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=501864"</a> */ @Test - public void testRefinedDiffsWithOnlyOnePseudoConflictAreInConflictGroupAndDiffGroup() { + public void testRefinedDiffsWithOnlyOnePseudoConflictAreInDiffGroupOnly() { ConflictsGroupWithRefinedDiffTestScenario scenario = new ConflictsGroupWithRefinedDiffTestScenario(); // Create pseudo conflict @@ -403,7 +403,7 @@ public class ConflictsGroupTest extends AbstractTestTreeNodeItemProviderAdapter assertEquals(1, diffNodesLeftFiltered.size()); final List<? extends TreeNode> subDiffNodesLeftFiltered = applyTechnicalitiesFilter( diffNodesLeftFiltered.get(0).getChildren()); - assertEquals(1, subDiffNodesLeftFiltered.size()); + assertEquals(2, subDiffNodesLeftFiltered.size()); // Build right side final BasicDifferenceGroupImpl rightSide = buildDifferenceGroup(scenario.comparison, @@ -422,7 +422,7 @@ public class ConflictsGroupTest extends AbstractTestTreeNodeItemProviderAdapter assertEquals(1, diffNodesRightFiltered.size()); final List<? extends TreeNode> subDiffNodesRightFiltered = applyTechnicalitiesFilter( diffNodesRightFiltered.get(0).getChildren()); - assertEquals(1, subDiffNodesRightFiltered.size()); + assertEquals(2, subDiffNodesRightFiltered.size()); } /** diff --git a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupWithRefinedDiffTestScenario.java b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupWithRefinedDiffTestScenario.java index e271b0288..95f973a3c 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupWithRefinedDiffTestScenario.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui.tests/src/org/eclipse/emf/compare/rcp/ui/tests/structuremergeviewer/groups/ConflictsGroupWithRefinedDiffTestScenario.java @@ -19,6 +19,23 @@ import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.DifferenceSource; import org.eclipse.emf.compare.Match; +/** + * This test scenario creates a comparison like this: + * <ul> + * <li>diff1 (LEFT), refined by + * <ul> + * <li>diff1a (LEFT)</li> + * <li>diff1b (RIGHT)</li> + * </ul> + * </li> + * <li>diff2 (RIGHT), refined by + * <ul> + * <li>diff2a (LEFT)</li> + * <li>diff2b (RIGHT)</li> + * </ul> + * </li> + * </ul> + */ public class ConflictsGroupWithRefinedDiffTestScenario { private static final CompareFactory FACTORY = CompareFactory.eINSTANCE; @@ -73,6 +90,14 @@ public class ConflictsGroupWithRefinedDiffTestScenario { comparison.getMatches().add(match1); } + /** + * Add a conflict between 2 existing differences of this scenario's comparison. + * + * @param conflictingDiff1 + * @param conflictingDiff2 + * @param kind + * @return The created conflict. + */ Conflict addConflict(Diff conflictingDiff1, Diff conflictingDiff2, ConflictKind kind) { final Conflict conflict = FACTORY.createConflict(); conflict.getDifferences().add(conflictingDiff1); diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/filters/impl/TechnicalitiesFilter.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/filters/impl/TechnicalitiesFilter.java index 3f6ca3871..2fcaa5c5a 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/filters/impl/TechnicalitiesFilter.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/filters/impl/TechnicalitiesFilter.java @@ -12,14 +12,16 @@ *******************************************************************************/ package org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.impl; +import static com.google.common.base.Predicates.or; import static com.google.common.collect.Iterators.any; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefiningDiffs; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasDirectOrIndirectConflict; +import static org.eclipse.emf.compare.ConflictKind.PSEUDO; +import static org.eclipse.emf.compare.ConflictKind.REAL; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.allAtomicRefining; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict; import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasNoDirectOrIndirectConflict; import com.google.common.base.Predicate; -import org.eclipse.emf.compare.ConflictKind; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.FeatureMapChange; import org.eclipse.emf.compare.Match; @@ -42,16 +44,6 @@ import org.eclipse.emf.edit.tree.TreeNode; public class TechnicalitiesFilter extends AbstractDifferenceFilter { /** - * The predicate use by this filter when it is selected. - */ - private static final Predicate<? super EObject> PREDICATE_WHEN_SELECTED = new Predicate<EObject>() { - public boolean apply(EObject input) { - return PREDICATE_EMPTY_MATCH_RESOURCES.apply(input) || PREDICATE_FEATURE_MAP.apply(input) - || PREDICATE_IDENTICAL_ELEMENTS.apply(input) || PREDICATE_PSEUDO_CONFLICT.apply(input); - } - }; - - /** * The predicate use to filter empty match resources. */ private static final Predicate<? super EObject> PREDICATE_EMPTY_MATCH_RESOURCES = new Predicate<EObject>() { @@ -95,7 +87,7 @@ public class TechnicalitiesFilter extends AbstractDifferenceFilter { if (data instanceof Diff) { Diff diff = (Diff)data; if (diff.getMatch().getComparison().isThreeWay()) { - ret = hasDirectOrIndirectPseudoConflictOnly(diff); + ret = isConsideredAsPseudoConflicting(diff); } } } @@ -104,19 +96,17 @@ public class TechnicalitiesFilter extends AbstractDifferenceFilter { }; /** - * Specifies whether the given diff has a direct or indirect pseudo conflict, but not a direct or indirect - * real conflict. + * Specifies whether the given diff can be considered as part of a pseudo conflict, but not of a real + * conflict. * * @param diff * The diff to check. - * @return <code>true</code> if it only has a direct or indirect pseudo conflict, <code>false</code> - * otherwise. + * @return <code>true</code> if it has a direct pseudo-conflict or of all its atomic refining diffs are in + * pseudo-conflict. */ - private static boolean hasDirectOrIndirectPseudoConflictOnly(Diff diff) { - return hasDirectOrIndirectConflict(ConflictKind.PSEUDO).apply(diff) - && !hasDirectOrIndirectConflict(ConflictKind.REAL).apply(diff) - && !anyRefiningDiffs(hasNoDirectOrIndirectConflict(ConflictKind.REAL, ConflictKind.PSEUDO)) - .apply(diff); + private static boolean isConsideredAsPseudoConflicting(Diff diff) { + return allAtomicRefining(hasConflict(PSEUDO)).apply(diff) + && hasNoDirectOrIndirectConflict(REAL).apply(diff); } /** @@ -136,6 +126,14 @@ public class TechnicalitiesFilter extends AbstractDifferenceFilter { }; /** + * The predicate use by this filter when it is selected. + */ + @SuppressWarnings("unchecked") + private static final Predicate<? super EObject> PREDICATE_WHEN_SELECTED = or( + PREDICATE_EMPTY_MATCH_RESOURCES, PREDICATE_FEATURE_MAP, PREDICATE_IDENTICAL_ELEMENTS, + PREDICATE_PSEUDO_CONFLICT); + + /** * Predicate to know if the given TreeNode is a diff. */ private static final Predicate<EObject> DATA_IS_DIFF = new Predicate<EObject>() { diff --git a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java index 33d722705..60664fa94 100644 --- a/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java +++ b/plugins/org.eclipse.emf.compare.rcp.ui/src/org/eclipse/emf/compare/rcp/ui/internal/structuremergeviewer/groups/impl/ThreeWayComparisonGroupProvider.java @@ -16,23 +16,28 @@ package org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.and; import static com.google.common.base.Predicates.not; +import static com.google.common.base.Predicates.or; import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterators.concat; import static com.google.common.collect.Iterators.filter; import static com.google.common.collect.Iterators.transform; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefiningDiffs; +import static java.util.Collections.unmodifiableSet; +import static org.eclipse.emf.compare.ConflictKind.PSEUDO; +import static org.eclipse.emf.compare.ConflictKind.REAL; +import static org.eclipse.emf.compare.internal.utils.DiffUtil.getRootRefinedDiffs; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.allAtomicRefining; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefining; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.containsConflictOfTypes; import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide; import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasConflict; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasDirectOrIndirectConflict; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasNoDirectOrIndirectConflict; import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasState; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterators; import com.google.common.collect.Sets; +import com.google.common.collect.Sets.SetView; import com.google.common.collect.UnmodifiableIterator; import java.util.Collection; @@ -63,7 +68,6 @@ import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.Confli import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.AbstractDifferenceGroupProvider; import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup; import org.eclipse.emf.compare.scope.IComparisonScope; -import org.eclipse.emf.compare.utils.EMFComparePredicates; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; import org.eclipse.emf.ecore.util.EcoreUtil; @@ -72,6 +76,45 @@ import org.eclipse.emf.ecore.util.EcoreUtil; * This implementation of a * {@link org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroupProvider} will be used to * group the differences by their {@link DifferenceSource side} : left, right and conflicts. + * <p> + * The table below describes the location of a diff depending on its status and that of its refining diffs + * (whether all or some of them are in a real/pseudo conflict). <br/> + * <br/> + * <table style="border-collapse:collapse;"> + * <thead> + * <tr> + * <th rowspan="2"></th> + * <th colspan="2" style="border:1px solid;">Real Conflicts</th> + * <th colspan="2" style="border:1px solid;">Pseudo-Conflicts</th> + * <th style="border:1px solid;">No Conflict</th> + * </tr> + * <tr> + * <th style="border:1px solid;">All</th> + * <th style="border:1px solid;">Some</th> + * <th style="border:1px solid;">All</th> + * <th style="border:1px solid;">Some</th> + * <th style="border:1px solid;">All</th> + * </tr> + * </thead> <tbody> + * <tr> + * <td style="border:1px solid; padding:0 2px;">Tech. filter ON</td> + * <td style="border:1px solid; padding:0 2px;">Conflict</td> + * <td style="border:1px solid; padding:0 2px;">Conflict</td> + * <td style="border:1px solid; padding:0 2px;">Conflict (hidden)</td> + * <td style="border:1px solid; padding:0 2px;">Side</td> + * <td style="border:1px solid; padding:0 2px;">Side</td> + * </tr> + * <tr> + * <td style="border:1px solid; padding:0 2px;">Tech. filter OFF</td> + * <td style="border:1px solid; padding:0 2px;">Conflict</td> + * <td style="border:1px solid; padding:0 2px;">Conflict</td> + * <td style="border:1px solid; padding:0 2px;">Conflict</td> + * <td style="border:1px solid; padding:0 2px;">Side</td> + * <td style="border:1px solid; padding:0 2px;">Side</td> + * </tr> + * </tbody> + * </table> + * </p> * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> * @since 4.0 @@ -83,10 +126,9 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv * diffs that do not have a direct or indirect real conflict and that do not have only pseudo conflicts. */ public static final Predicate<? super Diff> DEFAULT_DIFF_GROUP_FILTER_PREDICATE = and( - not(hasDirectOrIndirectConflict(ConflictKind.REAL)), - not(and(anyRefiningDiffs(hasConflict(ConflictKind.PSEUDO)), not(anyRefiningDiffs( - hasNoDirectOrIndirectConflict(ConflictKind.PSEUDO, ConflictKind.REAL)))))); - + not(hasConflict(REAL, PSEUDO)), + not(or(anyRefining(hasConflict(REAL)), allAtomicRefining(hasConflict(PSEUDO))))); + /** * {@inheritDoc} * @@ -113,7 +155,7 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv */ private static final Predicate<? super Diff> DEFAULT_CONFLICT_GROUP_FILTER_PREDICATE = hasConflict( ConflictKind.REAL, ConflictKind.PSEUDO); - + /** * Conflict groups to show in SMV. */ @@ -150,7 +192,7 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv ECrossReferenceAdapter crossReferenceAdapter) { super(comparison, DEFAULT_CONFLICT_GROUP_FILTER_PREDICATE, name, crossReferenceAdapter); } - + /** * In conflicts, a special case must be handled for refining diffs: If they are not part of the same * conflict then they should not be in the same group as the refined diff. @@ -216,121 +258,6 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv } /** - * This extension of {@link Conflict} is used to handle {@link Diff#getRefinedBy() refined} diffs and - * to join conflicts for the SMV. If refining diffs are part of a conflict, we show their refined - * diffs instead. As we show refined diffs instead of the refining diffs, multiple conflicts may - * consequently include the same refined diffs. To avoid that, this extension of a conflict also joins - * such overlapping conflicts. - * - * @author <a href="mailto:tmayerhofer@eclipsesource.com">Tanja Mayerhofer</a> - */ - public static class CompositeConflict extends ConflictImpl { - - /** The joined conflicts. */ - private Set<Conflict> conflicts = new LinkedHashSet<Conflict>(); - - /** The diffs of all composed conflicts. */ - private EList<Diff> diffs = new BasicEList<Diff>(); - - /** The conflict kind of this composite conflict. */ - private ConflictKind conflictKind = ConflictKind.REAL; - - /** - * Creates a new composite conflict for the given conflict. - * - * @param conflict - * The conflict to create a composite conflict for. - */ - public CompositeConflict(Conflict conflict) { - this.conflicts.add(checkNotNull(conflict)); - this.conflictKind = conflict.getKind(); - this.diffs.addAll(computeRefinedDiffs(conflict)); - } - - /** - * Computes the refined diffs of the conflict. In particular, refining diffs are replaced by - * refined diffs. - * - * @param conflict - * The conflict to compute its refined diffs for. - * @return The set of refined diffs of the conflict - */ - private Set<Diff> computeRefinedDiffs(Conflict conflict) { - final LinkedHashSet<Diff> computedDiffs = new LinkedHashSet<Diff>(); - for (Diff diff : conflict.getDifferences()) { - if (diff.getRefines().isEmpty()) { - computedDiffs.add(diff); - } else { - computedDiffs.addAll(getRootRefinedDiffs(diff)); - } - } - return computedDiffs; - } - - /** - * Determines the leaf refined diff of a refining diff, i.e., a refined diff that is not refining - * another diff. - * - * @param diff - * The diff for which the leaf refined diff is to be determined - * @return The leaf refined diff of the provided (refining diff) - */ - private List<Diff> getRootRefinedDiffs(Diff diff) { - final List<Diff> rootRefinedDiffs = newArrayList(); - for (Diff refinedDiff : diff.getRefines()) { - if (refinedDiff.getRefines().isEmpty()) { - rootRefinedDiffs.add(refinedDiff); - } else { - rootRefinedDiffs.addAll(getRootRefinedDiffs(refinedDiff)); - } - } - return rootRefinedDiffs; - } - - @Override - public ConflictKind getKind() { - return this.conflictKind; - } - - @Override - public EList<Diff> getDifferences() { - return this.diffs; - } - - /** - * Returns the joined conflicts. - * - * @return The joined conflicts - */ - public Set<Conflict> getConflicts() { - return conflicts; - } - - /** - * Joins the provided composite conflict with this composite conflict. - * - * @param conflict - * The conflict to be joined with this composite conflict - */ - public void join(CompositeConflict conflict) { - final LinkedHashSet<Diff> joinedDiffs = new LinkedHashSet<Diff>( - Sets.union(new LinkedHashSet<Diff>(this.diffs), - new LinkedHashSet<Diff>(conflict.getDifferences()))); - this.diffs.clear(); - this.diffs.addAll(joinedDiffs); - this.conflicts.addAll(conflict.getConflicts()); - - final Conflict realConflict = Iterators.find(this.conflicts.iterator(), - EMFComparePredicates.containsConflictOfTypes(ConflictKind.REAL), null); - if (realConflict != null) { - this.conflictKind = ConflictKind.REAL; - } else { - this.conflictKind = ConflictKind.PSEUDO; - } - } - } - - /** * {@inheritDoc} * * @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.BasicDifferenceGroupImpl#getStyledName() @@ -353,10 +280,105 @@ public class ThreeWayComparisonGroupProvider extends AbstractDifferenceGroupProv } /** - * {@inheritDoc} + * This extension of {@link Conflict} is used to handle {@link Diff#getRefinedBy() refined} diffs and to + * join conflicts for the SMV. If refining diffs are part of a conflict, we show their refined diffs + * instead. As we show refined diffs instead of the refining diffs, multiple conflicts may consequently + * include the same refined diffs. To avoid that, this extension of a conflict also joins such overlapping + * conflicts. * - * @see org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.AbstractBuildingDifferenceGroupProvider#buildGroups(org.eclipse.emf.compare.Comparison) + * @author <a href="mailto:tmayerhofer@eclipsesource.com">Tanja Mayerhofer</a> */ + public static class CompositeConflict extends ConflictImpl { + + /** The joined conflicts. */ + private Set<Conflict> conflicts = new LinkedHashSet<Conflict>(); + + /** The diffs of all composed conflicts. */ + private EList<Diff> diffs = new BasicEList<Diff>(); + + /** The conflict kind of this composite conflict. */ + private ConflictKind conflictKind = ConflictKind.REAL; + + /** + * Creates a new composite conflict for the given conflict. + * + * @param conflict + * The conflict to create a composite conflict for, must not be <code>null</code> and must + * have a non-<code>null</code> {@link Conflict#getKind() kind}. + */ + public CompositeConflict(Conflict conflict) { + conflicts.add(checkNotNull(conflict)); + conflictKind = checkNotNull(conflict.getKind()); + diffs.addAll(computeRefinedDiffs(conflict)); + } + + /** + * Computes the refined diffs of the conflict. In particular, refining diffs are replaced by refined + * diffs. + * + * @param conflict + * The conflict to compute its refined diffs for. + * @return The set of refined diffs of the conflict + */ + private Set<Diff> computeRefinedDiffs(Conflict conflict) { + Set<Diff> computedDiffs = new LinkedHashSet<Diff>(); + for (Diff diff : conflict.getDifferences()) { + if (diff.getRefines().isEmpty()) { + computedDiffs.add(diff); + } else { + computedDiffs.addAll(getRootRefinedDiffs(diff)); + } + } + return computedDiffs; + } + + @Override + public ConflictKind getKind() { + return conflictKind; + } + + /** + * Returns an EList built by aggregating the diffs of all the aggregated conflicts. + * <p> + * <b>This list is not supposed to be used for update, since modifying this list will not modify the + * underlying conflicts.</b> + * </p> + */ + @Override + public EList<Diff> getDifferences() { + return diffs; + } + + /** + * Returns an unmodifiable view of the joined conflicts. + * + * @return An unmodifiable view of the joined conflicts, never <code>null</code> nor empty. + */ + public Set<Conflict> getConflicts() { + return unmodifiableSet(conflicts); + } + + /** + * Joins the provided composite conflict with this composite conflict. + * + * @param conflict + * The conflict to be joined with this composite conflict + */ + public void join(CompositeConflict conflict) { + Set<Diff> currentDiffSet = new LinkedHashSet<Diff>(diffs); + Set<Diff> otherDiffSet = new LinkedHashSet<Diff>(conflict.getDifferences()); + SetView<Diff> newDiffs = Sets.difference(otherDiffSet, currentDiffSet); + diffs.addAll(newDiffs); + if (conflicts.addAll(conflict.getConflicts()) && conflictKind != REAL) { + if (any(conflicts, containsConflictOfTypes(REAL))) { + conflictKind = REAL; + } else { + conflictKind = PSEUDO; + } + } + } + } + @Override protected Collection<? extends IDifferenceGroup> buildGroups(Comparison comparison2) { Adapter adapter = EcoreUtil.getAdapter(getComparison().eAdapters(), SideLabelProvider.class); diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java index 38afa7a79..4a1003a90 100644 --- a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java +++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/suite/AllTests.java @@ -13,10 +13,6 @@ *******************************************************************************/ package org.eclipse.emf.compare.tests.suite; -import junit.framework.JUnit4TestAdapter; -import junit.framework.Test; -import junit.textui.TestRunner; - import org.eclipse.emf.compare.ComparePackage; import org.eclipse.emf.compare.tests.command.CommandStackTestSuite; import org.eclipse.emf.compare.tests.conflict.ConflictDetectionTest; @@ -65,6 +61,7 @@ import org.eclipse.emf.compare.tests.postprocess.PostProcessorTest; import org.eclipse.emf.compare.tests.req.ReqComputingTest; import org.eclipse.emf.compare.tests.scope.ComparisonScopeAdapterTest; import org.eclipse.emf.compare.tests.scope.DefaultComparisonScopeTest; +import org.eclipse.emf.compare.tests.utils.EMFComparePredicatesTest; import org.eclipse.emf.compare.tests.utils.EqualityHelperTest; import org.eclipse.emf.compare.tests.utils.MatchUtilFeatureContainsTest; import org.eclipse.emf.ecore.EPackage; @@ -74,6 +71,10 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import junit.framework.JUnit4TestAdapter; +import junit.framework.Test; +import junit.textui.TestRunner; + /** * This test suite allows us to launch all tests for EMF Compare at once. * @@ -96,7 +97,7 @@ import org.junit.runners.Suite.SuiteClasses; MultiLineAttributeMergeTest.class, MonitorCancelTest.class, IdentifierEObjectMatcherTest.class, MatchUtilFeatureContainsTest.class, RefineMergeTest.class, Bug484557ConflictTest.class, Bug485266_MoveDeleteConflict_Test.class, ResourceAttachmentChangeBug492261.class, - ComparisonScopeAdapterTest.class, }) + ComparisonScopeAdapterTest.class, EMFComparePredicatesTest.class, }) public class AllTests { /** * Standalone launcher for all of compare's tests. diff --git a/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/EMFComparePredicatesTest.java b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/EMFComparePredicatesTest.java new file mode 100644 index 000000000..679ba6db1 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.tests/src/org/eclipse/emf/compare/tests/utils/EMFComparePredicatesTest.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 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.tests.utils; + +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.instanceOf; +import static java.util.Arrays.asList; +import static org.eclipse.emf.compare.ConflictKind.PSEUDO; +import static org.eclipse.emf.compare.ConflictKind.REAL; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.allAtomicRefining; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefining; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasDirectOrIndirectConflict; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasNoDirectOrIndirectConflict; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.compare.AttributeChange; +import org.eclipse.emf.compare.CompareFactory; +import org.eclipse.emf.compare.Comparison; +import org.eclipse.emf.compare.Conflict; +import org.eclipse.emf.compare.Diff; +import org.eclipse.emf.compare.Match; +import org.eclipse.emf.compare.ReferenceChange; +import org.junit.Test; + +public class EMFComparePredicatesTest { + + private CompareFactory factory = CompareFactory.eINSTANCE; + + @Test + public void testAnyRefiningWithoutRecursion() { + Comparison comp = factory.createComparison(); + Match rootMatch = factory.createMatch(); + comp.getMatches().add(rootMatch); + + AttributeChange ac = factory.createAttributeChange(); + ReferenceChange rc1 = factory.createReferenceChange(); + ReferenceChange rc2 = factory.createReferenceChange(); + ReferenceChange rc3 = factory.createReferenceChange(); + EList<Diff> diffs = rootMatch.getDifferences(); + diffs.add(ac); + diffs.add(rc1); + diffs.add(rc2); + diffs.add(rc3); + ac.getRefinedBy().addAll(asList(rc1, rc2, rc3)); + + assertFalse(anyRefining(instanceOf(AttributeChange.class)).apply(ac)); + assertTrue(anyRefining(instanceOf(ReferenceChange.class)).apply(ac)); + assertTrue(anyRefining(equalTo((Diff)rc3)).apply(ac)); + } + + @Test + public void testAnyRefiningWithRecursion() { + Comparison comp = factory.createComparison(); + Match rootMatch = factory.createMatch(); + comp.getMatches().add(rootMatch); + + AttributeChange ac = factory.createAttributeChange(); + AttributeChange ac1 = factory.createAttributeChange(); + ReferenceChange rc11 = factory.createReferenceChange(); + ReferenceChange rc12 = factory.createReferenceChange(); + ReferenceChange rc2 = factory.createReferenceChange(); + ReferenceChange rc3 = factory.createReferenceChange(); + EList<Diff> diffs = rootMatch.getDifferences(); + diffs.add(ac); + diffs.add(ac1); + diffs.add(rc11); + diffs.add(rc12); + diffs.add(rc2); + diffs.add(rc3); + ac.getRefinedBy().addAll(asList(ac1, rc2, rc3)); + ac1.getRefinedBy().addAll(asList(rc11, rc12)); + + assertTrue(anyRefining(instanceOf(AttributeChange.class)).apply(ac)); + assertTrue(anyRefining(instanceOf(ReferenceChange.class)).apply(ac)); + assertTrue(anyRefining(equalTo((Diff)rc12)).apply(ac)); + assertFalse(anyRefining(equalTo((Diff)ac)).apply(ac)); + } + + @Test + public void testAllAtomicRefiningWithoutRecursion() { + Comparison comp = factory.createComparison(); + Match rootMatch = factory.createMatch(); + comp.getMatches().add(rootMatch); + + AttributeChange ac = factory.createAttributeChange(); + ReferenceChange rc1 = factory.createReferenceChange(); + ReferenceChange rc2 = factory.createReferenceChange(); + ReferenceChange rc3 = factory.createReferenceChange(); + EList<Diff> diffs = rootMatch.getDifferences(); + diffs.add(ac); + diffs.add(rc1); + diffs.add(rc2); + diffs.add(rc3); + ac.getRefinedBy().addAll(asList(rc1, rc2, rc3)); + + assertFalse(allAtomicRefining(instanceOf(AttributeChange.class)).apply(ac)); + assertTrue(allAtomicRefining(instanceOf(ReferenceChange.class)).apply(ac)); + assertFalse(allAtomicRefining(equalTo((Diff)rc3)).apply(ac)); + } + + @Test + public void testAllAtomicRefiningWithRecursion() { + Comparison comp = factory.createComparison(); + Match rootMatch = factory.createMatch(); + comp.getMatches().add(rootMatch); + + AttributeChange ac = factory.createAttributeChange(); + AttributeChange ac1 = factory.createAttributeChange(); + ReferenceChange rc11 = factory.createReferenceChange(); + ReferenceChange rc12 = factory.createReferenceChange(); + ReferenceChange rc2 = factory.createReferenceChange(); + ReferenceChange rc3 = factory.createReferenceChange(); + EList<Diff> diffs = rootMatch.getDifferences(); + diffs.add(ac); + diffs.add(ac1); + diffs.add(rc11); + diffs.add(rc12); + diffs.add(rc2); + diffs.add(rc3); + ac.getRefinedBy().addAll(asList(ac1, rc2, rc3)); + ac1.getRefinedBy().addAll(asList(rc11, rc12)); + + assertFalse(allAtomicRefining(instanceOf(AttributeChange.class)).apply(ac)); + assertTrue(allAtomicRefining(instanceOf(ReferenceChange.class)).apply(ac)); + assertFalse(allAtomicRefining(equalTo((Diff)rc12)).apply(ac)); + } + + @Test + public void testHasDirectOrIndirectConflictForDirectConflict() { + Comparison comp = factory.createComparison(); + Match rootMatch = factory.createMatch(); + comp.getMatches().add(rootMatch); + AttributeChange acl = factory.createAttributeChange(); + AttributeChange acr = factory.createAttributeChange(); + rootMatch.getDifferences().addAll(asList(acl, acr)); + Conflict conflict = factory.createConflict(); + conflict.setKind(REAL); + conflict.getDifferences().addAll(asList(acl, acr)); + comp.getConflicts().add(conflict); + + assertTrue(hasDirectOrIndirectConflict(REAL).apply(acl)); + assertTrue(hasDirectOrIndirectConflict(REAL).apply(acr)); + assertFalse(hasDirectOrIndirectConflict(PSEUDO).apply(acl)); + assertFalse(hasDirectOrIndirectConflict(PSEUDO).apply(acr)); + + assertFalse(hasNoDirectOrIndirectConflict(REAL).apply(acl)); + assertFalse(hasNoDirectOrIndirectConflict(REAL).apply(acr)); + assertTrue(hasNoDirectOrIndirectConflict(PSEUDO).apply(acl)); + assertTrue(hasNoDirectOrIndirectConflict(PSEUDO).apply(acr)); + } + + @Test + public void testHasDirectOrIndirectConflictForIndirectConflict() { + Comparison comp = factory.createComparison(); + Match rootMatch = factory.createMatch(); + comp.getMatches().add(rootMatch); + ReferenceChange rc = factory.createReferenceChange(); + AttributeChange acl = factory.createAttributeChange(); + AttributeChange acr = factory.createAttributeChange(); + rootMatch.getDifferences().addAll(asList(rc, acl, acr)); + rc.getRefinedBy().addAll(asList(acl)); + Conflict conflict = factory.createConflict(); + conflict.setKind(REAL); + conflict.getDifferences().addAll(asList(acl, acr)); + comp.getConflicts().add(conflict); + + assertTrue(hasDirectOrIndirectConflict(REAL).apply(rc)); + assertFalse(hasDirectOrIndirectConflict(PSEUDO).apply(rc)); + assertFalse(hasNoDirectOrIndirectConflict(REAL).apply(rc)); + assertTrue(hasNoDirectOrIndirectConflict(PSEUDO).apply(rc)); + } +} diff --git a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java index fc072b076..bea16fbe8 100644 --- a/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java +++ b/plugins/org.eclipse.emf.compare.uml2.rcp.ui.tests/src/org/eclipse/emf/compare/uml2/rcp/ui/tests/groups/ConflictsGroupTest.java @@ -27,8 +27,8 @@ import org.eclipse.emf.compare.Conflict; import org.eclipse.emf.compare.ConflictKind; import org.eclipse.emf.compare.provider.spec.CompareItemProviderAdapterFactorySpec; import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareRCPUIMessages; +import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.CompositeConflict; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl; -import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.ThreeWayComparisonGroupProvider.ConflictsGroupImpl.CompositeConflict; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.ConflictNode; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.DiffNode; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.nodes.MatchNode; diff --git a/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/MultiplicityElementChangePostProcessor.java b/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/MultiplicityElementChangePostProcessor.java index b9341c0c5..fc0317205 100644 --- a/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/MultiplicityElementChangePostProcessor.java +++ b/plugins/org.eclipse.emf.compare.uml2/src/org/eclipse/emf/compare/uml2/internal/postprocessor/MultiplicityElementChangePostProcessor.java @@ -14,7 +14,7 @@ package org.eclipse.emf.compare.uml2.internal.postprocessor; import static com.google.common.base.Predicates.instanceOf; import static com.google.common.collect.Iterables.all; import static com.google.common.collect.Iterables.filter; -import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefinedDiffs; +import static org.eclipse.emf.compare.utils.EMFComparePredicates.anyRefined; import com.google.common.base.Optional; import com.google.common.base.Predicate; @@ -122,8 +122,7 @@ public class MultiplicityElementChangePostProcessor implements IPostProcessor { */ private void verifyConflicts(Comparison comparison) { for (Conflict conflict : comparison.getConflicts()) { - if (all(conflict.getDifferences(), - anyRefinedDiffs(instanceOf(MultiplicityElementChange.class)))) { + if (all(conflict.getDifferences(), anyRefined(instanceOf(MultiplicityElementChange.class)))) { final Iterable<Diff> leftDiffs = collectRefinedDiffs(conflict.getLeftDifferences(), instanceOf(MultiplicityElementChange.class)); for (Diff leftDiff : leftDiffs) { diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java index 7bb3f646f..89f2e2501 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/internal/utils/DiffUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2014 Obeo and others. + * Copyright (c) 2012, 2016 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 @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.emf.compare.internal.utils; +import static com.google.common.collect.Lists.newArrayList; + import com.google.common.base.Predicate; import com.google.common.collect.HashMultiset; import com.google.common.collect.Iterables; @@ -24,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; import java.util.Set; @@ -67,6 +70,70 @@ public final class DiffUtil { } /** + * The set of all the diffs that refine the given diff or one of its refining diffs, recursively. + * + * @param diff + * The diff for which all the refining diffs are seeked + * @return A set of all the refining diffs (as opposed to getting only the 1st level of refining diffs + * that would be obtained by calling getRefinedBy() on diff. + */ + public static Set<Diff> getAllRefiningDiffs(Diff diff) { + Set<Diff> result = new LinkedHashSet<Diff>(); + if (diff != null) { + for (Diff refiningDiff : diff.getRefinedBy()) { + result.add(refiningDiff); + if (!refiningDiff.getRefinedBy().isEmpty()) { + result.addAll(getAllRefiningDiffs(refiningDiff)); + } + } + } + return result; + } + + /** + * Determines the root refined diff of a refining diff, i.e. a refined diff that is not refining another + * diff. + * + * @param diff + * The diff for which the root refined diffs are to be determined + * @return The root refined diffs of the provided (refining) diff, as a list. Never <code>null</code>. + * Empty if the provided diff does not refine any diff. + */ + public static List<Diff> getRootRefinedDiffs(Diff diff) { + List<Diff> rootRefinedDiffs = newArrayList(); + for (Diff refinedDiff : diff.getRefines()) { + if (refinedDiff.getRefines().isEmpty()) { + rootRefinedDiffs.add(refinedDiff); + } else { + rootRefinedDiffs.addAll(getRootRefinedDiffs(refinedDiff)); + } + } + return rootRefinedDiffs; + } + + /** + * The set of all the diffs that refine the given diff or one of its refining diffs, recursively. + * + * @param diff + * The diff for which all the refining diffs are seeked + * @return A set of all the refining diffs (as opposed to getting only the 1st level of refining diffs + * that would be obtained by calling getRefinedBy() on diff. + */ + public static Set<Diff> getAllAtomicRefiningDiffs(Diff diff) { + Set<Diff> result = new LinkedHashSet<Diff>(); + if (diff != null) { + for (Diff refiningDiff : diff.getRefinedBy()) { + if (refiningDiff.getRefinedBy().isEmpty()) { + result.add(refiningDiff); + } else { + result.addAll(getAllAtomicRefiningDiffs(refiningDiff)); + } + } + } + return result; + } + + /** * Computes the dice coefficient between the two given String's bigrams. * <p> * This implementation is case sensitive. @@ -77,10 +144,10 @@ public final class DiffUtil { * want the similarity between <code>"v1"</code> and <code>"v2"</code> to be <code>0.5</code> and not * <code>0</code>. However, we also want <code>"v1"</code> and <code>"v2"</code> to be "more similar" to * each other than <code>"v"</code> and <code>"v2"</code> and <code>"v1"</code> and <code>"v11"</code> to - * be "more similar" than <code>"v"</code> and <code>"v11"</code> while this latter also needs to be - * "less similar" than <code>"v1"</code> and <code>"v2"</code>. This requires a slightly different - * handling for comparisons with a "single character"-long string than for "two character"-long ones. A - * set of invariants we wish to meet can be found in the unit tests. + * be "more similar" than <code>"v"</code> and <code>"v11"</code> while this latter also needs to be "less + * similar" than <code>"v1"</code> and <code>"v2"</code>. This requires a slightly different handling for + * comparisons with a "single character"-long string than for "two character"-long ones. A set of + * invariants we wish to meet can be found in the unit tests. * </p> * * @param first diff --git a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java index 97d88cf8d..d005ebfb6 100644 --- a/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java +++ b/plugins/org.eclipse.emf.compare/src/org/eclipse/emf/compare/utils/EMFComparePredicates.java @@ -16,13 +16,18 @@ package org.eclipse.emf.compare.utils; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.and; import static com.google.common.base.Predicates.not; -import static com.google.common.collect.Iterators.any; +import static com.google.common.base.Predicates.or; +import static com.google.common.collect.Iterables.all; +import static com.google.common.collect.Iterables.any; import static org.eclipse.emf.compare.internal.utils.ComparisonUtil.isDeleteOrUnsetDiff; +import static org.eclipse.emf.compare.internal.utils.DiffUtil.getAllAtomicRefiningDiffs; +import static org.eclipse.emf.compare.internal.utils.DiffUtil.getAllRefiningDiffs; import com.google.common.base.Predicate; import java.util.Arrays; import java.util.Iterator; +import java.util.Set; import org.eclipse.emf.compare.AttributeChange; import org.eclipse.emf.compare.Conflict; @@ -1111,33 +1116,59 @@ public final class EMFComparePredicates { } /** - * This predicate can be used to check whether any refining diffs of a given diff fulfill the given + * This predicate can be used to check whether any refining diff of a given diff fulfills the given * predicate. * * @param predicate * The predicate to check. * @return The predicate. + * @since 3.4 */ - public static Predicate<? super Diff> anyRefiningDiffs(final Predicate<? super Diff> predicate) { + public static Predicate<Diff> anyRefining(final Predicate<? super Diff> predicate) { return new Predicate<Diff>() { - public boolean apply(Diff input) { - return input != null && any(input.getRefinedBy().iterator(), predicate); + public boolean apply(Diff diff) { + return diff != null && any(getAllRefiningDiffs(diff), predicate); } }; } /** - * This predicate can be used to check whether any refined diffs of a given diff fulfill the given + * This predicate can be used to check whether any refined diff of a given diff fulfills the given * predicate. * * @param predicate * The predicate to check. * @return The predicate. + * @since 3.4 */ - public static Predicate<? super Diff> anyRefinedDiffs(final Predicate<? super Diff> predicate) { + public static Predicate<Diff> anyRefined(final Predicate<? super Diff> predicate) { return new Predicate<Diff>() { public boolean apply(Diff input) { - return input != null && any(input.getRefines().iterator(), predicate); + return input != null && any(input.getRefines(), predicate); + } + }; + } + + /** + * This predicate can be used to check whether a diff has refiningDiffs AND all these refining diffs + * fulfill the given predicate. + * <p> + * <b>BEWARE: If the given diff has no refining diff, the predicate returns <code>false</code>.</b> + * </p> + * + * @param predicate + * The predicate to check on each 'atomic' (i.e. not refined) refining diff. + * @return The predicate. + * @since 3.4 + */ + public static Predicate<Diff> allAtomicRefining(final Predicate<? super Diff> predicate) { + return new Predicate<Diff>() { + public boolean apply(Diff diff) { + Set<Diff> atomicRefiningDiffs = getAllAtomicRefiningDiffs(diff); + if (atomicRefiningDiffs.isEmpty()) { + return false; + } + return all(atomicRefiningDiffs, predicate); } }; } @@ -1152,22 +1183,10 @@ public final class EMFComparePredicates { * @param kinds * Type(s) of the conflict(s) we seek. * @return The created predicate. + * @since 3.4 */ public static Predicate<? super Diff> hasDirectOrIndirectConflict(final ConflictKind... kinds) { - return new Predicate<Diff>() { - public boolean apply(Diff diff) { - if (hasConflict(kinds).apply(diff)) { - return true; - } else { - for (ConflictKind kind : kinds) { - if (anyRefiningDiffs(hasConflict(kind)).apply(diff)) { - return true; - } - } - } - return false; - } - }; + return or(hasConflict(kinds), anyRefining(hasConflict(kinds))); } /** @@ -1180,9 +1199,10 @@ public final class EMFComparePredicates { * @param kinds * Type(s) of the conflict(s) we seek. * @return The created predicate. + * @since 3.4 */ public static Predicate<? super Diff> hasNoDirectOrIndirectConflict(final ConflictKind... kinds) { - return and(not(hasConflict(kinds)), not(anyRefiningDiffs(hasConflict(kinds)))); + return not(hasDirectOrIndirectConflict(kinds)); } /** |