/******************************************************************************* * 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 }