blob: 349093f29369fc1911e0ae3449a91b96b2a78ddc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2015 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.impl;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getFirst;
import static com.google.common.collect.Lists.newArrayList;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import java.util.List;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.ConflictKind;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.DifferenceSource;
import org.eclipse.emf.compare.DifferenceState;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.ResourceAttachmentChange;
import org.eclipse.emf.compare.internal.utils.DiffUtil;
import org.eclipse.emf.compare.rcp.ui.internal.util.MergeViewerUtil;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.item.IMergeViewerItem;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroupProvider;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
/**
* A specific {@link MergeViewerItem} for {@link ResourceAttachmentChange}.
*
* @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
*/
public class ResourceAttachmentChangeMergeViewerItem extends MergeViewerItem.Container {
/**
* @see org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.impl.MergeViewerItem.Container#Container(Comparison
* comparison, Diff diff, Object left, Object right, Object ancestor, MergeViewerSide side,
* AdapterFactory adapterFactory)
*/
public ResourceAttachmentChangeMergeViewerItem(Comparison comparison, Diff diff, Resource left,
Resource right, Resource ancestor, IMergeViewer.MergeViewerSide side,
AdapterFactory adapterFactory) {
super(comparison, diff, left, right, ancestor, side, adapterFactory);
}
/**
* @see org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.impl.MergeViewerItem.Container#Container(Comparison,
* Diff, Match, MergeViewerSide, AdapterFactory)
*/
public ResourceAttachmentChangeMergeViewerItem(Comparison comparison, Diff diff, Match match,
IMergeViewer.MergeViewerSide side, AdapterFactory adapterFactory) {
super(comparison, diff, match, side, adapterFactory);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.impl.MergeViewerItem.Container#hasChildren(IDifferenceGroupProvider,
* Predicate)
*/
@Override
public boolean hasChildren(IDifferenceGroupProvider groupProvider, Predicate<? super EObject> predicate) {
return getChildren(groupProvider, predicate).length > 0;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.impl.MergeViewerItem.Container#getChildren(IDifferenceGroupProvider,
* Predicate)
*/
@SuppressWarnings("unchecked")
@Override
public IMergeViewerItem[] getChildren(IDifferenceGroupProvider group, Predicate<? super EObject> filters) {
Object sideValue = getSideValue(getSide());
Object bestSideValue = getBestSideValue();
List<IMergeViewerItem> ret = newArrayList();
if (bestSideValue instanceof Resource) {
List<IMergeViewerItem> mergeViewerItems = newArrayList();
if (sideValue instanceof Resource) {
mergeViewerItems = createMergeViewerItemFrom(((Resource)sideValue).getContents());
}
if (getSide() != IMergeViewer.MergeViewerSide.ANCESTOR) {
EList<Diff> differences = getComparison().getDifferences();
Iterable<ResourceAttachmentChange> racs = filter(differences, ResourceAttachmentChange.class);
List<ResourceAttachmentChange> resourceAttachmentChanges = Lists.newArrayList(racs);
Object left = getLeft();
Object right = getRight();
Object ancestor = getAncestor();
for (ResourceAttachmentChange resourceAttachmentChange : racs) {
// filter out merged reference changes
if (resourceAttachmentChange.getState() == DifferenceState.MERGED) {
// Remove resource attachment changes that are not linked with the current resources.
resourceAttachmentChanges.remove(resourceAttachmentChange);
} else if (isUnrelated(resourceAttachmentChange, left)
&& isUnrelated(resourceAttachmentChange, right)
&& isUnrelated(resourceAttachmentChange, ancestor)) {
resourceAttachmentChanges.remove(resourceAttachmentChange);
}
}
ret.addAll(createInsertionPoints(mergeViewerItems,
(List<ResourceAttachmentChange>)filteredDiffs(resourceAttachmentChanges, filters,
group)));
} else {
ret.addAll(mergeViewerItems);
}
}
return ret.toArray(getNoItemsArr());
}
private boolean isUnrelated(ResourceAttachmentChange change, Object resource) {
return resource == null
|| (resource instanceof Resource && !change.getResourceURI().equals(
((Resource)resource).getURI().toString()));
}
/**
* Creates an IMergeViewerItem from an EObject.
*
* @param eObject
* the given eObject.
* @return an IMergeViewerItem.
*/
@Override
protected IMergeViewerItem createMergeViewerItemFrom(EObject eObject) {
Match match = getComparison().getMatch(eObject);
if (match != null) {
ResourceAttachmentChange rac = getFirst(filter(match.getDifferences(),
ResourceAttachmentChange.class), null);
if (rac != null) {
Object left = match.getLeft();
Object right = match.getRight();
Object ancestor = match.getOrigin();
// Manage case where the resource attachment change is between an existing resource and an
// unknown resource
if (MergeViewerUtil.getResource(getComparison(), MergeViewerSide.LEFT, rac) == null) {
left = null;
}
if (MergeViewerUtil.getResource(getComparison(), MergeViewerSide.RIGHT, rac) == null) {
right = null;
}
if (MergeViewerUtil.getResource(getComparison(), MergeViewerSide.ANCESTOR, rac) == null) {
ancestor = null;
}
return new MergeViewerItem.Container(getComparison(), rac, left, right, ancestor, getSide(),
getAdapterFactory());
}
}
return null;
}
/**
* Creates insertion points for the given list of IMergeViewerItem corresponding to the given list of
* ResourceAttachmentChange.
*
* @param values
* the given list of IMergeViewerItem.
* @param racs
* the givel list of ResourceAttachmentChange
* @return the given list of IMergeViewerItem with additional insertion points.
*/
private List<? extends IMergeViewerItem> createInsertionPoints(
final List<? extends IMergeViewerItem> values, final List<ResourceAttachmentChange> racs) {
List<IMergeViewerItem> ret = newArrayList(values);
for (ResourceAttachmentChange diff : Lists.reverse(racs)) {
boolean rightToLeft = (getSide() == IMergeViewer.MergeViewerSide.LEFT);
Comparison comparison = getComparison();
Object left = MergeViewerUtil.getValueFromResourceAttachmentChange(diff, comparison,
IMergeViewer.MergeViewerSide.LEFT);
Object right = MergeViewerUtil.getValueFromResourceAttachmentChange(diff, comparison,
IMergeViewer.MergeViewerSide.RIGHT);
DifferenceSource source = diff.getSource();
DifferenceKind kind = diff.getKind();
DifferenceState state = diff.getState();
boolean b1 = source == DifferenceSource.LEFT && kind == DifferenceKind.DELETE
&& getSide() == MergeViewerSide.LEFT && DifferenceState.MERGED != state;
boolean b2 = source == DifferenceSource.LEFT && kind == DifferenceKind.ADD
&& getSide() == MergeViewerSide.RIGHT && DifferenceState.MERGED != state;
boolean b3 = source == DifferenceSource.RIGHT && kind == DifferenceKind.ADD
&& getSide() == MergeViewerSide.LEFT && DifferenceState.MERGED != state;
boolean b4 = source == DifferenceSource.RIGHT && kind == DifferenceKind.DELETE
&& getSide() == MergeViewerSide.RIGHT && DifferenceState.MERGED != state;
boolean b5 = DifferenceState.MERGED == state && source == DifferenceSource.LEFT
&& kind == DifferenceKind.ADD && right == null;
boolean b6 = DifferenceState.MERGED == state && source == DifferenceSource.LEFT
&& kind == DifferenceKind.DELETE && left == null;
boolean b7 = DifferenceState.MERGED == state && source == DifferenceSource.RIGHT
&& kind == DifferenceKind.ADD && left == null;
boolean b8 = DifferenceState.MERGED == state && source == DifferenceSource.RIGHT
&& kind == DifferenceKind.DELETE && right == null;
// do not duplicate insertion point for pseudo add conflict
// so we must only create one for pseudo delete conflict
boolean b9 = diff.getConflict() == null
|| (diff.getConflict().getKind() != ConflictKind.PSEUDO || kind == DifferenceKind.DELETE);
if ((b1 || b2 || b3 || b4 || b5 || b6 || b7 || b8) && b9) {
Object ancestor = MergeViewerUtil.getValueFromResourceAttachmentChange(diff, comparison,
IMergeViewer.MergeViewerSide.ANCESTOR);
if (left != null
&& MergeViewerUtil.getResource(comparison, IMergeViewer.MergeViewerSide.LEFT, diff) == null) {
left = null;
}
if (right != null
&& MergeViewerUtil.getResource(comparison, IMergeViewer.MergeViewerSide.RIGHT, diff) == null) {
right = null;
}
if (ancestor != null
&& MergeViewerUtil.getResource(comparison, IMergeViewer.MergeViewerSide.ANCESTOR,
diff) == null) {
ancestor = null;
}
if (b5 || b8) {
left = null;
}
if (b6 || b7) {
right = null;
}
IMergeViewerItem insertionPoint = new MergeViewerItem.Container(comparison, diff, left,
right, ancestor, getSide(), getAdapterFactory());
final int insertionIndex;
if (left == null && right == null && ancestor != null) {
Resource resource = MergeViewerUtil.getResource(comparison,
IMergeViewer.MergeViewerSide.ANCESTOR, diff);
List<EObject> contents = resource.getContents();
insertionIndex = contents.indexOf(ancestor);
} else {
insertionIndex = Math.min(findInsertionIndex(diff, rightToLeft), ret.size());
}
// offset the insertion by the number of previous insertion points in the list
// Can not b improved by keeping the number of created insertion points because the given
// "values" parameter may already contains some insertion points.
int realIndex = 0;
for (int index = 0; index < insertionIndex && realIndex < ret.size(); realIndex++) {
if (!ret.get(realIndex).isInsertionPoint()) {
index++;
}
}
ret.add(realIndex, insertionPoint);
}
}
return ret;
}
/**
* Find an insertion index for the given diff.
*
* @param diff
* the given diff.
* @param rightToLeft
* the way of merge.
* @return an insertion index for the given diff.
*/
private int findInsertionIndex(Diff diff, boolean rightToLeft) {
final Match valueMatch = diff.getMatch();
final Comparison comparison = getComparison();
final EObject expectedValue;
if (valueMatch.getLeft() != null) {
expectedValue = valueMatch.getLeft();
} else {
expectedValue = valueMatch.getRight();
}
final Resource initialResource;
final Resource expectedResource;
if (rightToLeft) {
initialResource = MergeViewerUtil.getResource(comparison, IMergeViewer.MergeViewerSide.RIGHT,
diff);
expectedResource = MergeViewerUtil.getResource(comparison, IMergeViewer.MergeViewerSide.LEFT,
diff);
} else {
initialResource = MergeViewerUtil
.getResource(comparison, IMergeViewer.MergeViewerSide.LEFT, diff);
expectedResource = MergeViewerUtil.getResource(comparison, IMergeViewer.MergeViewerSide.RIGHT,
diff);
}
if (expectedResource != null) {
final List<EObject> sourceList = initialResource.getContents();
final List<EObject> targetList = expectedResource.getContents();
return DiffUtil.findInsertionIndex(comparison, sourceList, targetList, expectedValue);
} else {
return 0;
}
}
}