blob: 1abc5205f3fb1ea905bf17e03ea3a3a3fec0ead2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* 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:
******************************************************************************/
package org.eclipse.emf.emfstore.server.conflictDetection;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isAttribute;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isCreateDelete;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isMultiAtt;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isMultiAttMove;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isMultiAttSet;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isMultiRef;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isMultiRefSet;
import static org.eclipse.emf.emfstore.server.model.versioning.operations.util.OperationUtil.isSingleRef;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.emfstore.common.model.ModelElementId;
import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.AttributeOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.CompositeOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.ContainmentType;
import org.eclipse.emf.emfstore.server.model.versioning.operations.CreateDeleteOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.FeatureOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiAttributeMoveOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiAttributeOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiAttributeSetOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiReferenceMoveOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiReferenceOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.MultiReferenceSetOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.ReferenceOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.SingleReferenceOperation;
/**
* A conflict detection strategy that will operate on a per attribute and feature level.
*
* @author chodnick
* @author wesendon
*/
// BEGIN COMPLEX CODE
public class IndexSensitiveConflictDetectionStrategy implements ConflictDetectionStrategy {
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.server.conflictDetection.ConflictDetectionStrategy#doConflict(org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation,
* org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation)
*/
public boolean doConflict(AbstractOperation operationA, AbstractOperation operationB) {
if (operationA instanceof CompositeOperation) {
CompositeOperation compA = (CompositeOperation) operationA;
for (AbstractOperation op : compA.getSubOperations()) {
if (doConflict(op, operationB)) {
return true;
}
}
return false;
} else if (operationB instanceof CompositeOperation) {
CompositeOperation compB = (CompositeOperation) operationB;
for (AbstractOperation op : compB.getSubOperations()) {
if (doConflict(operationA, op)) {
return true;
}
}
return false;
}
return doConflictHard(operationA, operationB);
}
/**
* Hard conflicts change the toplogogy of the model element graph. This method returns, given the operations in
* question, whether such a conflict exists.
*
* @param operationA any operation
* @param operationB any operation
* @return true if topology of the resulting model element graph depends on serialization of operationA and
* operationB false otherwise
*/
private boolean doConflictHard(AbstractOperation operationA, AbstractOperation operationB) {
if (isCreateDelete(operationA)) {
return doConflictHardCreateDelete((CreateDeleteOperation) operationA, operationB);
}
if (isCreateDelete(operationB)) {
return doConflictHardCreateDelete((CreateDeleteOperation) operationB, operationA);
}
if (isAttribute(operationA) && isAttribute(operationB)) {
return doConflictHardAttributes((AttributeOperation) operationA, (AttributeOperation) operationB);
}
if (isMultiAtt(operationA) && isMultiAtt(operationB)) {
return doConflictHardMultiAttributes((MultiAttributeOperation) operationA,
(MultiAttributeOperation) operationB);
}
if (isMultiAttSet(operationA) && isMultiAttSet(operationB)) {
return doConflictHardMultiAttributeSets((MultiAttributeSetOperation) operationA,
(MultiAttributeSetOperation) operationB);
}
if (isMultiAttSet(operationA) && isMultiAtt(operationB)) {
return doConflictHardMultiAttAndSet((MultiAttributeOperation) operationB,
(MultiAttributeSetOperation) operationA);
}
if (isMultiAtt(operationA) && isMultiAttSet(operationB)) {
return doConflictHardMultiAttAndSet((MultiAttributeOperation) operationA,
(MultiAttributeSetOperation) operationB);
}
if (isMultiAttMove(operationA) && isMultiAttMove(operationB)) {
return doConflictHardMultiAttributeMoves((MultiAttributeMoveOperation) operationA,
(MultiAttributeMoveOperation) operationB);
}
if (isMultiAttMove(operationA) && isMultiAtt(operationB)) {
return doConflictHardMultiAttAndMove((MultiAttributeMoveOperation) operationA,
(MultiAttributeOperation) operationB);
}
if (isMultiAtt(operationA) && isMultiAttMove(operationB)) {
return doConflictHardMultiAttAndMove((MultiAttributeMoveOperation) operationB,
(MultiAttributeOperation) operationA);
}
if (isMultiAttSet(operationA) && isMultiAttMove(operationB)) {
return doConflictHardMultiAttMoveAndSet((MultiAttributeMoveOperation) operationB,
(MultiAttributeSetOperation) operationA);
}
if (isMultiAttMove(operationA) && isMultiAttSet(operationB)) {
return doConflictHardMultiAttMoveAndSet((MultiAttributeMoveOperation) operationA,
(MultiAttributeSetOperation) operationB);
}
if (isSingleRef(operationA) && isSingleRef(operationB)) {
return doConflictHardSingleReferences((SingleReferenceOperation) operationA,
(SingleReferenceOperation) operationB);
}
if (isSingleRef(operationA) && isMultiRef(operationB)) {
return doConflictHardSingleMultiReferences((SingleReferenceOperation) operationA,
(MultiReferenceOperation) operationB);
}
if (isMultiRef(operationA) && isSingleRef(operationB)) {
return doConflictHardSingleMultiReferences((SingleReferenceOperation) operationB,
(MultiReferenceOperation) operationA);
}
if (isSingleRef(operationA) && isMultiRefSet(operationB)) {
return doConflictHardSingeMultiSet((SingleReferenceOperation) operationA,
(MultiReferenceSetOperation) operationB);
}
if (isMultiRefSet(operationA) && isSingleRef(operationB)) {
return doConflictHardSingeMultiSet((SingleReferenceOperation) operationB,
(MultiReferenceSetOperation) operationA);
}
if (isMultiRef(operationA) && isMultiRefSet(operationB)) {
return doConflictHardMultiReferenceAndSet((MultiReferenceOperation) operationA,
(MultiReferenceSetOperation) operationB);
}
if (isMultiRefSet(operationA) && isMultiRef(operationB)) {
return doConflictHardMultiReferenceAndSet((MultiReferenceOperation) operationB,
(MultiReferenceSetOperation) operationA);
}
if (isMultiRefSet(operationA) && isMultiRefSet(operationB)) {
return doConflictHardMultiReferenceSet((MultiReferenceSetOperation) operationA,
(MultiReferenceSetOperation) operationB);
}
if (isMultiRef(operationA) && isMultiRef(operationB)) {
return doConflictHardMultiReferences((MultiReferenceOperation) operationA,
(MultiReferenceOperation) operationB);
}
return false;
}
private boolean doConflictHardSingeMultiSet(SingleReferenceOperation opA, MultiReferenceSetOperation opB) {
if (!bothContaining(opA, opB)) {
return false;
}
return (opA.getNewValue() != null && isSame(opA.getNewValue(), opB.getNewValue()));
}
private boolean doConflictHardMultiAttributeMoves(MultiAttributeMoveOperation operationA,
MultiAttributeMoveOperation operationB) {
// this is a soft conflict
return false;
}
private boolean doConflictHardMultiAttributeSets(MultiAttributeSetOperation operationA,
MultiAttributeSetOperation operationB) {
if (!sameFeatureAndId(operationA, operationB)) {
return false;
}
if (operationA.getIndex() == operationB.getIndex()
&& isDifferent(operationA.getNewValue(), operationB.getNewValue())) {
return true;
}
return false;
}
private boolean doConflictHardMultiReferenceSet(MultiReferenceSetOperation operationA,
MultiReferenceSetOperation operationB) {
if (sameFeatureAndId(operationA, operationB)) {
if (operationA.getIndex() == operationB.getIndex()
&& isDifferent(operationA.getNewValue(), operationB.getNewValue())) {
return true;
}
} else {
if (!bothContaining(operationA, operationB)) {
return false;
}
return isSame(operationA.getNewValue(), operationB.getNewValue());
}
return false;
}
private boolean doConflictHardMultiReferenceAndSet(MultiReferenceOperation operationA,
MultiReferenceSetOperation operationB) {
if (sameFeatureAndId(operationA, operationB)) {
if (!operationA.isAdd()) {
for (ModelElementId id : operationA.getReferencedModelElements()) {
if (id.equals(operationB.getOldValue())) {
return true;
}
}
}
} else {
if (!bothContaining(operationA, operationB)) {
return false;
}
if (!operationA.isAdd()) {
return false;
}
return containsId(operationA.getReferencedModelElements(), operationB.getNewValue());
}
return false;
}
private boolean doConflictHardMultiAttAndMove(MultiAttributeMoveOperation operationB,
MultiAttributeOperation operationA) {
if (!sameFeatureAndId(operationA, operationB)) {
return false;
}
int index = getLowestIndex(operationA.getIndexes());
if (index == -1) {
return false;
}
return (index <= operationB.getOldIndex() || index <= operationB.getNewIndex());
}
private boolean doConflictHardMultiAttMoveAndSet(MultiAttributeMoveOperation operationA,
MultiAttributeSetOperation operationB) {
if (!sameFeatureAndId(operationA, operationB)) {
return false;
}
if (between(operationB.getIndex(), operationA.getOldIndex(), operationA.getNewIndex())) {
return true;
}
return false;
}
private boolean doConflictHardMultiAttAndSet(MultiAttributeOperation operationB,
MultiAttributeSetOperation operationA) {
if (!sameFeatureAndId(operationA, operationB)) {
return false;
}
int index = getLowestIndex(operationB.getIndexes());
if (index == -1) {
return false;
}
return (index <= operationA.getIndex());
}
private boolean doConflictHardMultiAttributes(MultiAttributeOperation operationA, MultiAttributeOperation operationB) {
if (!sameFeatureAndId(operationA, operationB)) {
return false;
}
return true;
}
private boolean doConflictHardCreateDelete(CreateDeleteOperation opA, AbstractOperation opB) {
// creates do not conflict with anything, by definition
if (isCreateOperation(opA)) {
return false;
}
if (isCreateOperation(opB)) {
return false;
}
// we have a delete here
// we need to check whether the other operation changes any of the deleted elements
if (opB instanceof CreateDeleteOperation) {
Set<ModelElementId> allDeletedElementsA = getAllDeletedElements(opA);
Set<ModelElementId> allDeletedElementsB = getAllDeletedElements((CreateDeleteOperation) opB);
boolean intersecting = allDeletedElementsA.removeAll(allDeletedElementsB);
if (intersecting && !allDeletedElementsA.isEmpty()) {
return true;
}
return false;
} else {
for (ModelElementId m : getAllDeletedElements(opA)) {
if (changesModelElement(opB, m)) {
return true;
}
}
}
// we need to check whether the cross reference change do conflict with the other op
for (ReferenceOperation referenceOperation : opA.getSubOperations()) {
if (doConflictHard(referenceOperation, opB)) {
return true;
}
}
return false;
}
private Set<ModelElementId> getAllDeletedElements(CreateDeleteOperation op) {
// EObject element = op.getModelElement();
return new HashSet<ModelElementId>(op.getEObjectToIdMap().values());
// Set<EObject> allDeleteTreeElements = ModelUtil.getAllContainedModelElements(element, false);
// allDeleteTreeElements.add(op.getModelElement());
// Set<ModelElementId> result = new HashSet<ModelElementId>(allDeleteTreeElements.size());
// for (EObject modelElement : allDeleteTreeElements) {
// result.add(ModelUtil.getProject(modelElement).getModelElementId(modelElement));
// }
// return result;
}
private boolean doConflictHardMultiReferences(MultiReferenceOperation opA, MultiReferenceOperation opB) {
if (sameFeatureAndId(opA, opB)) {
// if add and remove meet, there might be a hard conflict
if (opA.isAdd() != opB.isAdd()) {
for (ModelElementId mA : opA.getOtherInvolvedModelElements()) {
for (ModelElementId mB : opB.getOtherInvolvedModelElements()) {
if (mA.equals(mB)) {
return true;
}
}
}
}
return false;
} else {
if (!bothContaining(opA, opB)) {
return false;
}
if (!(opA.isAdd() == opB.isAdd())) {
return false;
}
for (ModelElementId mA : opA.getOtherInvolvedModelElements()) {
for (ModelElementId mB : opB.getOtherInvolvedModelElements()) {
if (mA.equals(mB)) {
return true;
}
}
}
}
return false;
}
private boolean doConflictHardSingleMultiReferences(SingleReferenceOperation opA, MultiReferenceOperation opB) {
if (!bothContaining(opA, opB)) {
return false;
}
if (!opB.isAdd()) {
return false;
}
for (ModelElementId id : opB.getOtherInvolvedModelElements()) {
if (id.equals(opA.getNewValue())) {
return true;
}
}
return false;
}
private boolean doConflictHardSingleReferences(SingleReferenceOperation opA, SingleReferenceOperation opB) {
if (sameFeatureAndId(opA, opB)) {
// unless both operations set the same new value
if (isDifferent(opA.getNewValue(), opB.getNewValue())) {
return true;
}
return false;
} else {
if (!bothContaining(opA, opB)) {
return false;
}
return (opA.getNewValue() != null && isSame(opA.getNewValue(), opB.getNewValue()));
}
}
private boolean doConflictHardAttributes(AttributeOperation opA, AttributeOperation opB) {
// if same object's same feature is set, there's a potential conflict
if (sameFeatureAndId(opA, opB)) {
// unless both operations set the same new value
if (isSame(opA.getNewValue(), opB.getNewValue())) {
return false;
}
return true;
}
return false;
}
/**
* This method tests whether the both give operations change the same multifeature on the same object. If so, and
* additionally the order of items in the multifeature depends on the serialization of the operations, there is an
* index integrity conflict, and the method returns true. TODO: MultiAttribute and MultiReferenceSet operations only
* implemented for hard conflict.
*
* @param operationA any operation
* @param operationB any operation
* @return true if an index integrity conflict exists, false otherwise
*/
public boolean doConflictIndexIntegrity(AbstractOperation operationA, AbstractOperation operationB) {
if (operationA instanceof CompositeOperation) {
CompositeOperation compA = (CompositeOperation) operationA;
for (AbstractOperation op : compA.getSubOperations()) {
if (doConflictIndexIntegrity(op, operationB)) {
return true;
}
}
return false;
} else if (operationB instanceof CompositeOperation) {
CompositeOperation compB = (CompositeOperation) operationB;
for (AbstractOperation op : compB.getSubOperations()) {
if (doConflictIndexIntegrity(operationA, op)) {
return true;
}
}
return false;
}
if (operationA instanceof MultiReferenceOperation && operationB instanceof MultiReferenceOperation) {
return doConflictIndexIntegrityMultiReferences((MultiReferenceOperation) operationA,
(MultiReferenceOperation) operationB);
}
if (operationA instanceof SingleReferenceOperation && operationB instanceof MultiReferenceOperation) {
return doConflictIndexIntegritySingleMultiReferences((SingleReferenceOperation) operationA,
(MultiReferenceOperation) operationB);
}
if (operationB instanceof SingleReferenceOperation && operationA instanceof MultiReferenceOperation) {
return doConflictIndexIntegritySingleMultiReferences((SingleReferenceOperation) operationB,
(MultiReferenceOperation) operationA);
}
if (operationA instanceof MultiReferenceMoveOperation && operationB instanceof MultiReferenceMoveOperation) {
return doConflictIndexIntegrityMultiMoveReferences((MultiReferenceMoveOperation) operationA,
(MultiReferenceMoveOperation) operationB);
}
if (operationA instanceof MultiReferenceMoveOperation && operationB instanceof MultiReferenceOperation) {
return doConflictIndexIntegrityMultiMoveMultiReferences((MultiReferenceMoveOperation) operationA,
(MultiReferenceOperation) operationB);
}
if (operationB instanceof MultiReferenceMoveOperation && operationA instanceof MultiReferenceOperation) {
return doConflictIndexIntegrityMultiMoveMultiReferences((MultiReferenceMoveOperation) operationB,
(MultiReferenceOperation) operationA);
}
if (operationA instanceof MultiReferenceMoveOperation && operationB instanceof SingleReferenceOperation) {
return doConflictIndexIntegrityMultiMoveSingleReferences((MultiReferenceMoveOperation) operationA,
(SingleReferenceOperation) operationB);
}
if (operationB instanceof MultiReferenceMoveOperation && operationA instanceof SingleReferenceOperation) {
return doConflictIndexIntegrityMultiMoveSingleReferences((MultiReferenceMoveOperation) operationB,
(SingleReferenceOperation) operationA);
}
if (operationA instanceof SingleReferenceOperation && operationB instanceof SingleReferenceOperation) {
return doConflictIndexIntegritySingleReferences((SingleReferenceOperation) operationA,
(SingleReferenceOperation) operationB);
}
return false;
}
private boolean doConflictIndexIntegritySingleReferences(SingleReferenceOperation opA, SingleReferenceOperation opB) {
// Due to refactoring: The opposite case is covered by the second operation created by bidirectional links.
return false;
}
private boolean doConflictIndexIntegrityMultiMoveSingleReferences(MultiReferenceMoveOperation opA,
SingleReferenceOperation opB) {
// Due to refactoring: MultiMove only occurs on multifeatures, so it can't conflict with a single operation. The
// opposite case is covered by the second operation created by bidirectional links.
return false;
}
private boolean doConflictIndexIntegrityMultiMoveMultiReferences(MultiReferenceMoveOperation opA,
MultiReferenceOperation opB) {
// 2 cases
// regular vs. regular
// regular vs. opposite
// regular vs. regular
if (opA.getModelElementId().equals(opB.getModelElementId())
&& opA.getFeatureName().equals(opB.getFeatureName())) {
if (containsId(opB.getReferencedModelElements(), opA.getReferencedModelElementId())) {
if (opB.isAdd()) {
return true;
}
return false;
} else {
// so there is an add or remove and a move going on on different objects but on the same feature
if (opB.isAdd() && opB.getIndex() >= opA.getNewIndex()) {
return true;
} else if (!opB.isAdd()
&& (opB.getIndex() + opB.getReferencedModelElements().size() - 1) < opA.getNewIndex()) {
return true;
}
return false;
}
}
return false;
}
private boolean doConflictIndexIntegrityMultiMoveReferences(MultiReferenceMoveOperation opA,
MultiReferenceMoveOperation opB) {
if (opA.getModelElementId().equals(opB.getModelElementId())
&& opA.getFeatureName().equals(opB.getFeatureName())) {
if (opA.getReferencedModelElementId().equals(opB.getReferencedModelElementId())) {
return opA.getNewIndex() != opB.getNewIndex();
} else {
return opA.getNewIndex() == opB.getNewIndex();
}
}
return false;
}
private boolean doConflictIndexIntegritySingleMultiReferences(SingleReferenceOperation opA,
MultiReferenceOperation opB) {
return false;
}
private boolean doConflictIndexIntegrityMultiReferences(MultiReferenceOperation opA, MultiReferenceOperation opB) {
// 3 cases to look for
// regular vs. regular
// regular vs. opposite
// opposite vs. regular
// case 1: regular vs. regular
if (opA.getModelElementId().equals(opB.getModelElementId())
&& opA.getFeatureName().equals(opB.getFeatureName())) {
if (opA.isAdd() && opB.isAdd()) {
// make sure that some of the added things are different
if (opA.getIndex() == opB.getIndex()) {
for (ModelElementId mA : opA.getOtherInvolvedModelElements()) {
if (!containsId(opB.getOtherInvolvedModelElements(), mA)) {
return true;
}
}
for (ModelElementId mB : opB.getOtherInvolvedModelElements()) {
// if all were the same, this would be identical operations, thus no index conflict
if (!containsId(opA.getOtherInvolvedModelElements(), mB)) {
return true;
}
}
} else {
return true;
}
}
if (opA.isAdd() != opB.isAdd()) {
for (ModelElementId mA : opA.getOtherInvolvedModelElements()) {
if (!containsId(opB.getOtherInvolvedModelElements(), mA)) {
if (opB.getReferencedModelElements().size() == 1
&& opA.getReferencedModelElements().size() == 1) {
if (opA.isAdd()) {
return opA.getIndex() > opB.getIndex() && opA.getIndex() != 0 && opB.getIndex() != 0;
} else {
return opA.getIndex() < opB.getIndex() && opA.getIndex() != 0 && opB.getIndex() != 0;
}
} else {
return true;
}
}
}
for (ModelElementId mB : opB.getOtherInvolvedModelElements()) {
if (!containsId(opA.getOtherInvolvedModelElements(), mB)) {
if (opB.getReferencedModelElements().size() == 1
&& opA.getReferencedModelElements().size() == 1) {
if (opA.isAdd()) {
return opA.getIndex() > opB.getIndex() && opA.getIndex() != 0 && opB.getIndex() != 0;
} else {
return opA.getIndex() < opB.getIndex() && opA.getIndex() != 0 && opB.getIndex() != 0;
}
} else {
return true;
}
}
}
}
}
return false;
}
/**
* {@inheritDoc} TODO multiattribute and mulitreferenceset operations are not yet considerered
*
* @see org.eclipse.emf.emfstore.server.conflictDetection.ConflictDetectionStrategy#isRequired(org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation,
* org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation)
*/
public boolean isRequired(AbstractOperation requiredOperation, AbstractOperation operation) {
// composite operation decomposition section
if (requiredOperation instanceof CompositeOperation) {
CompositeOperation compA = (CompositeOperation) requiredOperation;
for (AbstractOperation op : compA.getSubOperations()) {
if (isRequired(op, operation)) {
return true;
}
}
return false;
} else if (operation instanceof CompositeOperation) {
CompositeOperation compB = (CompositeOperation) operation;
for (AbstractOperation op : compB.getSubOperations()) {
if (isRequired(requiredOperation, op)) {
return true;
}
}
return false;
}
// non-composite op handling
if (requiredOperation instanceof CreateDeleteOperation) {
return isRequiredCreate((CreateDeleteOperation) requiredOperation, operation);
} else if (requiredOperation instanceof MultiReferenceOperation
&& operation instanceof MultiReferenceMoveOperation) {
return isRequiredMultiByMoveReference((MultiReferenceOperation) requiredOperation,
(MultiReferenceMoveOperation) operation);
} else if (requiredOperation instanceof MultiReferenceOperation && operation instanceof MultiReferenceOperation) {
return isRequiredMutiByMultiReference((MultiReferenceOperation) requiredOperation,
(MultiReferenceOperation) operation);
} else if (requiredOperation instanceof SingleReferenceOperation
&& operation instanceof MultiReferenceMoveOperation) {
return isRequiredSingleByMoveReference((SingleReferenceOperation) requiredOperation,
(MultiReferenceMoveOperation) operation);
} else if (requiredOperation instanceof SingleReferenceOperation
&& operation instanceof MultiReferenceOperation) {
return isRequiredSingleByMultiReference((SingleReferenceOperation) requiredOperation,
(MultiReferenceOperation) operation);
}
return false;
}
private boolean isRequiredSingleByMultiReference(SingleReferenceOperation requiredOperation,
MultiReferenceOperation operation) {
return false;
}
private boolean isRequiredSingleByMoveReference(SingleReferenceOperation requiredOperation,
MultiReferenceMoveOperation operation) {
if (!requiredOperation.isBidirectional()) {
return false;
}
boolean sameFeature = requiredOperation.getOppositeFeatureName().equals(operation.getFeatureName());
boolean sameMovedElement = requiredOperation.getModelElementId()
.equals(operation.getReferencedModelElementId());
boolean sameElement = isSame(requiredOperation.getNewValue(), operation.getModelElementId());
return (sameElement && sameFeature && sameMovedElement);
}
private boolean isRequiredMutiByMultiReference(MultiReferenceOperation requiredOperation,
MultiReferenceOperation operation) {
return false;
}
private boolean isRequiredMultiByMoveReference(MultiReferenceOperation requiredOperation,
MultiReferenceMoveOperation operation) {
boolean sameElement = requiredOperation.getModelElementId().equals(operation.getModelElementId());
boolean sameFeature = requiredOperation.getFeatureName().equals(operation.getFeatureName());
// require the add of the element, that is moved around
if (sameElement && sameFeature) {
for (ModelElementId modelElementId : requiredOperation.getReferencedModelElements()) {
if (modelElementId.equals(operation.getReferencedModelElementId())) {
return true;
}
}
}
return false;
}
private boolean isRequiredCreate(CreateDeleteOperation requiredOperation, AbstractOperation operation) {
if (!requiredOperation.isDelete()) {
// in case of a delete, it only requires the create if the deleted element is created,
// creates of subelements in the deltree are not required
if (operation instanceof CreateDeleteOperation) {
CreateDeleteOperation op = (CreateDeleteOperation) operation;
if (op.isDelete() && op.getModelElementId().equals(requiredOperation.getModelElementId())) {
return true;
}
return false;
}
// in case of a single ref op, we don't need to require it if the create references the old value
if (operation instanceof SingleReferenceOperation) {
SingleReferenceOperation op = (SingleReferenceOperation) operation;
if (isSame(op.getOldValue(), requiredOperation.getModelElementId())) {
return false;
}
}
if (changesModelElement(operation, requiredOperation.getModelElementId())) {
return true;
}
}
return false;
}
// helper methods
private static boolean isDifferent(Object a, Object b) {
// if both are null, they are not different
if (a == null && b == null) {
return false;
}
// if either is null now, the other is not, so the objects ARE different
if (a == null || b == null) {
return true;
}
// if neither is null, let the objects sort out equality
return !a.equals(b);
}
private static boolean isSame(Object a, Object b) {
// if both are null, they are the same
if (a == null && b == null) {
return true;
}
// if either is null now, the other is not, so the objects ARE different
if (a == null || b == null) {
return false;
}
// if neither is null, let the objects sort out equality
return a.equals(b);
}
private boolean bothContaining(ReferenceOperation opA, ReferenceOperation opB) {
return ContainmentType.CONTAINMENT.equals(opA.getContainmentType())
&& ContainmentType.CONTAINMENT.equals(opB.getContainmentType());
}
private boolean sameFeatureAndId(FeatureOperation operationA, FeatureOperation operationB) {
return (isSame(operationA.getModelElementId(), operationB.getModelElementId()) && isSame(
operationA.getFeatureName(), operationB.getFeatureName()));
}
private boolean between(int index, int lower, int upper) {
if (lower > upper) {
return between(index, upper, lower);
}
return (lower <= index && index <= upper);
}
private int getLowestIndex(EList<Integer> indexes) {
int result = -1;
for (Integer tmp : indexes) {
if (result >= tmp || result == -1) {
result = tmp;
}
}
return result;
}
private static boolean isCreateOperation(AbstractOperation op) {
if (op instanceof CreateDeleteOperation) {
CreateDeleteOperation cdop = (CreateDeleteOperation) op;
return !cdop.isDelete();
}
return false;
}
private boolean containsId(Iterable<ModelElementId> list, ModelElementId id) {
for (ModelElementId m : list) {
if (m.equals(id)) {
return true;
}
}
return false;
}
/**
* Tests whether an operation changes a given model element.
*
* @param op the op to examine
* @param id the modelelement to check
* @return if the operation changes the given model element
*/
public static boolean changesModelElement(AbstractOperation op, ModelElementId id) {
for (ModelElementId m : op.getAllInvolvedModelElements()) {
if (m.equals(id)) {
return true;
}
}
return false;
}
// END COMPLEX CODE
}