| /******************************************************************************* |
| * 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.rcp.ui.internal.structuremergeviewer.filters.impl; |
| |
| import static org.eclipse.emf.compare.ConflictKind.REAL; |
| import static org.eclipse.emf.compare.DifferenceKind.ADD; |
| import static org.eclipse.emf.compare.DifferenceKind.DELETE; |
| import static org.eclipse.emf.compare.DifferenceKind.MOVE; |
| import static org.eclipse.emf.compare.utils.EMFComparePredicates.CONTAINMENT_REFERENCE_CHANGE; |
| import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide; |
| import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasNoDirectOrIndirectConflict; |
| import static org.eclipse.emf.compare.utils.EMFComparePredicates.ofKind; |
| |
| import com.google.common.base.Predicate; |
| import com.google.common.base.Predicates; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Iterators; |
| import com.google.common.collect.UnmodifiableIterator; |
| |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.compare.Diff; |
| import org.eclipse.emf.compare.DifferenceSource; |
| import org.eclipse.emf.compare.Match; |
| import org.eclipse.emf.compare.ResourceAttachmentChange; |
| import org.eclipse.emf.compare.match.MatchOfContainmentReferenceChangeAdapter; |
| import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.filters.AbstractDifferenceFilter; |
| import org.eclipse.emf.compare.utils.MatchUtil; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.emf.edit.tree.TreeNode; |
| |
| /** |
| * A filter used by default that filters out cascading differences (differences located under a Match that is |
| * either ADDed or DELETEd on the diff's side). The MOVE differences are not hidden by this filter. |
| * Differences hidden are all those that match the following criteria: |
| * <ul> |
| * <li>this.kind != MOVE</li> |
| * <li>this.conflict == null && this.'indirect real conflicts' is empty</li> |
| * <li>this.refines is empty</li> |
| * <li>this is located inside a TreeNode that represents a Match that is either ADDed or DELETEd, and for |
| * which the diff that represents this addition or deletion is not refined by this.</li> |
| * </ul> |
| * |
| * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> |
| * @since 4.0 |
| */ |
| public class CascadingDifferencesFilter extends AbstractDifferenceFilter { |
| |
| /** |
| * The predicate used by this filter when it is selected. |
| */ |
| private static final Predicate<? super EObject> PREDICATE_WHEN_SELECTED = new Predicate<EObject>() { |
| public boolean apply(EObject input) { |
| boolean ret = false; |
| if (input instanceof TreeNode) { |
| TreeNode treeNode = (TreeNode)input; |
| EObject data = treeNode.getData(); |
| if (data instanceof Diff && !(data instanceof ResourceAttachmentChange)) { |
| Diff diff = (Diff)data; |
| if (diff.getKind() != MOVE && hasNoDirectOrIndirectConflict(REAL).apply(diff) |
| && diff.getRefines().isEmpty()) { |
| TreeNode parent = treeNode.getParent(); |
| if (parent != null && parent.getData() instanceof Match) { |
| Match parentMatch = (Match)parent.getData(); |
| ret = isInsideAddOrDeleteTreeNode(diff, parent); |
| if (!ret && isAddOrDeleteMatch(parentMatch, diff.getSource())) { |
| ret = !Predicates |
| .and(Predicates.or(CONTAINMENT_REFERENCE_CHANGE, |
| REFINED_BY_CONTAINMENT_REF_CHANGE), ofKind(ADD, DELETE)) |
| .apply(diff); |
| } |
| } |
| } |
| } |
| } |
| return ret; |
| } |
| |
| private boolean isInsideAddOrDeleteTreeNode(Diff diff, TreeNode parent) { |
| boolean ret = false; |
| DifferenceSource side = diff.getSource(); |
| TreeNode grandParent = parent.getParent(); |
| Match grandParentMatch = null; |
| if (grandParent != null && grandParent.getData() instanceof Match) { |
| grandParentMatch = (Match)grandParent.getData(); |
| } |
| if (isAddOrDeleteMatch(grandParentMatch, side)) { |
| // The ancestor has been added/deleted, we must filter the current diff |
| // _unless_ it is refined by the diff that represents the grand-parent |
| // add/delete |
| Diff addOrDeleteDiff = findAddOrDeleteDiff(grandParentMatch, side); |
| if (addOrDeleteDiff != null) { |
| if (diff.getRefinedBy().contains(addOrDeleteDiff)) { |
| // recurse |
| ret = isInsideAddOrDeleteTreeNode(diff, grandParent); |
| } else { |
| ret = true; |
| } |
| } |
| } |
| return ret; |
| } |
| |
| private Diff findAddOrDeleteDiff(Match match, DifferenceSource side) { |
| final Iterable<Diff> addOrDeleteContainmentDiffs = MatchUtil |
| .findAddOrDeleteContainmentDiffs(match); |
| if (addOrDeleteContainmentDiffs != null) { |
| final UnmodifiableIterator<Diff> sideChanges = Iterators |
| .filter(addOrDeleteContainmentDiffs.iterator(), fromSide(side)); |
| if (sideChanges.hasNext()) { |
| return sideChanges.next(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Indicate whether a Match is that of an object that was either added or deleted on the given side. |
| * |
| * @param match |
| * The match |
| * @param side |
| * The side |
| * @return <code>true</code> if the matched object is present on side but not on origin or vice-versa. |
| * <code>false</code> if match is <code>null</code>. |
| */ |
| protected boolean isAddOrDeleteMatch(Match match, DifferenceSource side) { |
| if (match == null) { |
| return false; |
| } |
| Adapter adapter = EcoreUtil.getAdapter(match.eAdapters(), |
| MatchOfContainmentReferenceChangeAdapter.class); |
| return adapter != null; |
| } |
| }; |
| |
| private static final Predicate<Diff> REFINED_BY_CONTAINMENT_REF_CHANGE = new Predicate<Diff>() { |
| public boolean apply(Diff input) { |
| return Iterables.any(input.getRefinedBy(), CONTAINMENT_REFERENCE_CHANGE); |
| } |
| }; |
| |
| @Override |
| public Predicate<? super EObject> getPredicateWhenSelected() { |
| return PREDICATE_WHEN_SELECTED; |
| } |
| } |