/*******************************************************************************
* 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.merge;
import static org.eclipse.emf.compare.DifferenceKind.DELETE;
import static org.eclipse.emf.compare.DifferenceSource.LEFT;
import static org.eclipse.emf.compare.DifferenceSource.RIGHT;
import static org.eclipse.emf.compare.DifferenceState.MERGED;
import java.util.Set;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceKind;
import org.eclipse.emf.compare.ReferenceChange;
import org.eclipse.emf.compare.utils.ReferenceUtil;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
/**
* This specific implementation of {@link AbstractMerger} will be used to merge reference changes in an
* additive merge context.
*
* @author <a href="mailto:mathieu.cartaud@obeo.fr">Mathieu Cartaud</a>
* @since 3.4
*/
public class AdditiveReferenceChangeMerger extends ReferenceChangeMerger {
/**
* The constructor specify the context where this merger can be used.
*/
public AdditiveReferenceChangeMerger() {
super();
mergeOptions.put(IMergeCriterion.OPTION_MERGE_CRITERION, AdditiveMergeCriterion.INSTANCE);
}
@Override
public boolean apply(IMergeCriterion criterion) {
return criterion == AdditiveMergeCriterion.INSTANCE;
}
@Override
public void copyRightToLeft(Diff target, Monitor monitor) {
if (isInTerminalState(target)) {
return;
}
ReferenceChange rc = (ReferenceChange)target;
if (rc.getReference().isContainment()) {
if (rc.getSource() == RIGHT) {
if (rc.getKind() == DELETE) {
markAsMerged(rc);
} else {
super.copyRightToLeft(rc, monitor);
}
} else {
if (rc.getKind() == DELETE) {
super.copyRightToLeft(rc, monitor);
} else {
markAsMerged(rc);
}
}
} else {
if (rc.getSource() == RIGHT) {
if (rc.getKind() == DELETE || isUnsetRelatedToDeletion(rc)) {
markAsMerged(rc);
} else {
super.copyRightToLeft(rc, monitor);
}
} else {
if (rc.getKind() == DELETE || isUnsetRelatedToDeletion(rc)) {
super.copyRightToLeft(rc, monitor);
} else {
markAsMerged(rc);
}
}
}
}
/**
* Determine if the given diff is an unset of a value which is related to the deletion of an object.
*
* @param rc
* The given ReferenceChange
* @return <code>true</code> if the diff is an unset related to a deleted object
*/
private boolean isUnsetRelatedToDeletion(ReferenceChange rc) {
if (rc.getKind() == DifferenceKind.CHANGE && isUnsetOfValue(rc)) {
for (Diff diff : rc.getRequiredBy()) {
if (diff.getKind() == DELETE && diff instanceof ReferenceChange
&& ((ReferenceChange)diff).getReference().isContainment()) {
return true;
}
}
}
return false;
}
/**
* Determine if the diff is an unset.
*
* @param difference
* The given difference
* @return <code>true</code> if the given diff is an unset
*/
private boolean isUnsetOfValue(ReferenceChange difference) {
boolean isUnset = false;
final EReference reference = difference.getReference();
// FIXME bad smell -> what about multiple references?
if (!reference.isMany()) {
EObject referrer = null;
if (difference.getSource() == LEFT) {
referrer = difference.getMatch().getLeft();
} else {
referrer = difference.getMatch().getRight();
}
isUnset = referrer == null || !ReferenceUtil.safeEIsSet(referrer, reference);
}
return isUnset;
}
/**
* Mark a diff and its refining diffs as merged.
*
* @param diff
* The diff to merge
*/
private void markAsMerged(Diff diff) {
diff.setState(MERGED);
for (Diff refiningDiff : diff.getRefinedBy()) {
refiningDiff.setState(MERGED);
}
}
// CHECKSTYLE:OFF
@Override
public Set<Diff> getDirectMergeDependencies(Diff diff, boolean mergeRightToLeft) {
ReferenceChange rc = (ReferenceChange)diff;
if (rc.getReference().isContainment()) {
if (rc.getSource() == RIGHT) {
if (rc.getKind() == DELETE) {
return super.getDirectMergeDependencies(diff, !mergeRightToLeft);
} else {
return super.getDirectMergeDependencies(diff, mergeRightToLeft);
}
} else {
if (rc.getKind() == DELETE) {
return super.getDirectMergeDependencies(diff, mergeRightToLeft);
} else {
return super.getDirectMergeDependencies(diff, !mergeRightToLeft);
}
}
} else {
if (rc.getSource() == RIGHT) {
if (rc.getKind() == DELETE || isUnsetRelatedToDeletion(rc)) {
return super.getDirectMergeDependencies(diff, !mergeRightToLeft);
} else {
return super.getDirectMergeDependencies(diff, mergeRightToLeft);
}
} else {
if (rc.getKind() == DELETE || isUnsetRelatedToDeletion(rc)) {
return super.getDirectMergeDependencies(diff, mergeRightToLeft);
} else {
return super.getDirectMergeDependencies(diff, !mergeRightToLeft);
}
}
}
}
@Override
public Set<Diff> getDirectResultingMerges(Diff diff, boolean mergeRightToLeft) {
ReferenceChange rc = (ReferenceChange)diff;
if (rc.getReference().isContainment()) {
if (rc.getSource() == RIGHT) {
if (rc.getKind() == DELETE) {
return super.getDirectResultingMerges(diff, !mergeRightToLeft);
} else {
return super.getDirectResultingMerges(diff, mergeRightToLeft);
}
} else {
if (rc.getKind() == DELETE) {
return super.getDirectResultingMerges(diff, mergeRightToLeft);
} else {
return super.getDirectResultingMerges(diff, !mergeRightToLeft);
}
}
} else {
if (rc.getSource() == RIGHT) {
if (rc.getKind() == DELETE || isUnsetRelatedToDeletion(rc)) {
return super.getDirectResultingMerges(diff, !mergeRightToLeft);
} else {
return super.getDirectResultingMerges(diff, mergeRightToLeft);
}
} else {
if (rc.getKind() == DELETE || isUnsetRelatedToDeletion(rc)) {
return super.getDirectResultingMerges(diff, mergeRightToLeft);
} else {
return super.getDirectResultingMerges(diff, !mergeRightToLeft);
}
}
}
}
// CHECKSTYLE:ON
}