| /******************************************************************************* |
| * 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: |
| * chodnick |
| ******************************************************************************/ |
| package org.eclipse.emf.emfstore.internal.client.model.changeTracking; |
| |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.emfstore.internal.client.model.util.WorkspaceUtil; |
| import org.eclipse.emf.emfstore.internal.common.model.ModelElementId; |
| import org.eclipse.emf.emfstore.internal.common.model.impl.IdEObjectCollectionImpl; |
| import org.eclipse.emf.emfstore.internal.common.model.impl.ProjectImpl; |
| import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil; |
| import org.eclipse.emf.emfstore.internal.common.model.util.NotificationInfo; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AttributeOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.ContainmentType; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.FeatureOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiAttributeMoveOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiAttributeOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiAttributeSetOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiReferenceMoveOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiReferenceOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.MultiReferenceSetOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.OperationsFactory; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.ReferenceOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.SingleReferenceOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.UnsetType; |
| |
| /** |
| * Converts an EMF notification to an Operation. |
| * |
| * @author chodnick |
| */ |
| public final class NotificationToOperationConverter { |
| |
| private final IdEObjectCollectionImpl project; |
| |
| /** |
| * Default constructor. |
| * |
| * @param project |
| * project |
| */ |
| public NotificationToOperationConverter(IdEObjectCollectionImpl project) { |
| this.project = project; |
| } |
| |
| /** |
| * Converts given notification to an operation. May return null if the |
| * notification signifies a no-op. |
| * |
| * @param n |
| * the notification to convert |
| * @return the operation or null |
| */ |
| // BEGIN COMPLEX CODE |
| public AbstractOperation convert(NotificationInfo n) { |
| |
| if (n.isTouch() || n.isTransient() || !n.isValid()) { |
| return null; |
| } |
| |
| switch (n.getEventType()) { |
| |
| case Notification.SET: |
| if (n.isAttributeNotification()) { |
| return handleSetAttribute(n); |
| } |
| return handleSetReference(n); |
| |
| case Notification.UNSET: |
| if (n.isAttributeNotification()) { |
| return handleUnsetAttribute(n); |
| } |
| return handleUnsetReference(n); |
| |
| case Notification.ADD: |
| if (n.isAttributeNotification()) { |
| return handleMultiAttribute(n); |
| } |
| return handleMultiReference(n); |
| |
| case Notification.ADD_MANY: |
| if (n.isAttributeNotification()) { |
| return handleMultiAttribute(n); |
| } |
| return handleMultiReference(n); |
| |
| case Notification.REMOVE: |
| if (n.isAttributeNotification()) { |
| return handleMultiAttribute(n); |
| } |
| return handleMultiReference(n); |
| |
| case Notification.REMOVE_MANY: |
| if (n.isAttributeNotification()) { |
| return handleMultiAttribute(n); |
| } |
| return handleMultiReference(n); |
| |
| case Notification.MOVE: |
| if (n.isAttributeNotification()) { |
| return handleAttributeMove(n); |
| } |
| return handleReferenceMove(n); |
| |
| default: |
| return null; |
| } |
| } |
| |
| // END COMPLEX CODE |
| |
| @SuppressWarnings("unchecked") |
| private AbstractOperation handleMultiAttribute(NotificationInfo n) { |
| final MultiAttributeOperation operation = OperationsFactory.eINSTANCE.createMultiAttributeOperation(); |
| setCommonValues(project, operation, n.getNotifierModelElement()); |
| operation.setFeatureName(n.getAttribute().getName()); |
| operation.setAdd(n.isAddEvent() || n.isAddManyEvent()); |
| // operation.setIndex(n.getPosition()); |
| |
| List<Object> list = null; |
| |
| switch (n.getEventType()) { |
| |
| case Notification.ADD: |
| list = new ArrayList<Object>(); |
| operation.getIndexes().add(n.getPosition()); |
| list.add(n.getNewValue()); |
| break; |
| case Notification.ADD_MANY: |
| list = (List<Object>) n.getNewValue(); |
| for (int i = 0; i < list.size(); i++) { |
| operation.getIndexes().add(n.getPosition() + i); |
| } |
| break; |
| case Notification.REMOVE: |
| list = new ArrayList<Object>(); |
| operation.getIndexes().add(n.getPosition()); |
| list.add(n.getOldValue()); |
| break; |
| case Notification.REMOVE_MANY: |
| list = (List<Object>) n.getOldValue(); |
| if (n.getNewValue() == null) { |
| for (int i = 0; i < list.size(); i++) { |
| operation.getIndexes().add(i); |
| } |
| } else { |
| for (final int value : (int[]) n.getNewValue()) { |
| operation.getIndexes().add(value); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (list != null) { |
| for (final Object valueElement : list) { |
| operation.getReferencedValues().add(valueElement); |
| } |
| } |
| |
| if (n.wasUnset()) { |
| operation.setUnset(UnsetType.WAS_UNSET); |
| } |
| |
| return operation; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private AbstractOperation handleMultiReference(NotificationInfo n) { |
| |
| List<EObject> list = new ArrayList<EObject>(); |
| |
| switch (n.getEventType()) { |
| case Notification.ADD: |
| list.add(n.getNewModelElementValue()); |
| break; |
| case Notification.ADD_MANY: |
| list = (List<EObject>) n.getNewValue(); |
| break; |
| case Notification.REMOVE: |
| list.add(n.getOldModelElementValue()); |
| break; |
| case Notification.REMOVE_MANY: |
| list = (List<EObject>) n.getOldValue(); |
| break; |
| default: |
| break; |
| } |
| |
| final boolean isAdd = n.isAddEvent() || n.isAddManyEvent(); |
| final MultiReferenceOperation multiRefOp = createMultiReferenceOperation(project, n.getNotifierModelElement(), |
| n.getReference(), list, isAdd, |
| n.getPosition()); |
| |
| if (n.wasUnset()) { |
| multiRefOp.setUnset(UnsetType.WAS_UNSET); |
| } |
| |
| return multiRefOp; |
| } |
| |
| /** |
| * Creates a multi reference operation based on the given information. |
| * |
| * @param collection |
| * the collection the <code>modelElement</code> is contained in |
| * @param modelElement |
| * the model element holding the reference |
| * @param reference |
| * the actual reference |
| * @param referencedElements |
| * the elements referenced by the reference |
| * @param isAdd |
| * whether any referenced model elements were added to the <code>collection</code> |
| * @param position |
| * the index of the model element within the <code>referenceElements</code> affected by |
| * the generated operation |
| * @return a multi reference operation |
| */ |
| public static MultiReferenceOperation createMultiReferenceOperation(IdEObjectCollectionImpl collection, |
| EObject modelElement, EReference reference, List<EObject> referencedElements, boolean isAdd, int position) { |
| final MultiReferenceOperation op = OperationsFactory.eINSTANCE.createMultiReferenceOperation(); |
| setCommonValues(collection, op, modelElement); |
| setBidirectionalAndContainmentInfo(op, reference); |
| op.setFeatureName(reference.getName()); |
| op.setAdd(isAdd); |
| op.setIndex(position); |
| final List<ModelElementId> referencedModelElements = op.getReferencedModelElements(); |
| |
| for (final EObject valueElement : referencedElements) { |
| ModelElementId id = collection.getModelElementId(valueElement); |
| if (id == null) { |
| id = collection.getDeletedModelElementId(valueElement); |
| } |
| if (id != null) { |
| referencedModelElements.add(id); |
| } else if (ModelUtil.getProject(valueElement) == collection) { |
| throw new IllegalStateException( |
| Messages.NotificationToOperationConverter_Element_Has_No_ID + valueElement); |
| } |
| // ignore value elements outside of the current project, they are |
| // not tracked |
| } |
| return op; |
| |
| } |
| |
| private AbstractOperation handleReferenceMove(NotificationInfo n) { |
| |
| final MultiReferenceMoveOperation op = OperationsFactory.eINSTANCE.createMultiReferenceMoveOperation(); |
| setCommonValues(project, op, n.getNotifierModelElement()); |
| op.setFeatureName(n.getReference().getName()); |
| op.setReferencedModelElementId(project.getModelElementId(n.getNewModelElementValue())); |
| op.setNewIndex(n.getPosition()); |
| op.setOldIndex((Integer) n.getOldValue()); |
| |
| return op; |
| } |
| |
| private AbstractOperation handleAttributeMove(NotificationInfo n) { |
| final MultiAttributeMoveOperation operation = OperationsFactory.eINSTANCE.createMultiAttributeMoveOperation(); |
| setCommonValues(project, operation, n.getNotifierModelElement()); |
| operation.setFeatureName(n.getAttribute().getName()); |
| operation.setNewIndex(n.getPosition()); |
| operation.setOldIndex((Integer) n.getOldValue()); |
| operation.setReferencedValue(n.getNewValue()); |
| return operation; |
| } |
| |
| private AbstractOperation handleSetAttribute(NotificationInfo n) { |
| |
| if (!n.getAttribute().isMany()) { |
| AttributeOperation op = null; |
| // special handling for diagram layout changes |
| op = OperationsFactory.eINSTANCE.createAttributeOperation(); |
| |
| setCommonValues(project, op, n.getNotifierModelElement()); |
| op.setFeatureName(n.getAttribute().getName()); |
| op.setNewValue(n.getNewValue()); |
| op.setOldValue(n.getOldValue()); |
| |
| if (n.wasUnset()) { |
| op.setUnset(UnsetType.WAS_UNSET); |
| } |
| return op; |
| } |
| final MultiAttributeSetOperation setOperation = OperationsFactory.eINSTANCE |
| .createMultiAttributeSetOperation(); |
| setCommonValues(project, setOperation, n.getNotifierModelElement()); |
| setOperation.setFeatureName(n.getAttribute().getName()); |
| setOperation.setNewValue(n.getNewValue()); |
| setOperation.setOldValue(n.getOldValue()); |
| setOperation.setIndex(n.getPosition()); |
| |
| if (n.wasUnset()) { |
| setOperation.setUnset(UnsetType.WAS_UNSET); |
| } |
| |
| return setOperation; |
| } |
| |
| /** |
| * Creates a single reference operation based on the given information. |
| * |
| * @param collection |
| * the collection the <code>modelElement</code> is contained in |
| * @param oldReference |
| * the {@link ModelElementId} of the model element the reference was pointing to |
| * @param newReference |
| * the {@link ModelElementId} of the model element the reference is now pointing to |
| * @param reference |
| * the actual reference |
| * @param modelElement |
| * the model element holding the reference |
| * @return a single reference operation |
| */ |
| public static SingleReferenceOperation createSingleReferenceOperation(IdEObjectCollectionImpl collection, |
| ModelElementId oldReference, ModelElementId newReference, EReference reference, EObject modelElement) { |
| |
| final SingleReferenceOperation op = OperationsFactory.eINSTANCE.createSingleReferenceOperation(); |
| setCommonValues(collection, op, modelElement); |
| op.setFeatureName(reference.getName()); |
| setBidirectionalAndContainmentInfo(op, reference); |
| |
| op.setOldValue(oldReference); |
| op.setNewValue(newReference); |
| |
| return op; |
| } |
| |
| private AbstractOperation handleSetReference(NotificationInfo n) { |
| |
| ModelElementId oldModelElementId = project.getModelElementId(n.getOldModelElementValue()); |
| ModelElementId newModelElementId = project.getModelElementId(n.getNewModelElementValue()); |
| |
| if (oldModelElementId == null) { |
| oldModelElementId = ((ProjectImpl) project).getDeletedModelElementId(n.getOldModelElementValue()); |
| } |
| |
| if (newModelElementId == null) { |
| newModelElementId = ((ProjectImpl) project).getDeletedModelElementId(n.getNewModelElementValue()); |
| } |
| |
| if (!n.getReference().isMany()) { |
| final SingleReferenceOperation singleRefOperation = createSingleReferenceOperation(project, |
| oldModelElementId, |
| newModelElementId, n.getReference(), |
| n.getNotifierModelElement()); |
| |
| if (n.wasUnset()) { |
| singleRefOperation.setUnset(UnsetType.WAS_UNSET); |
| } |
| |
| return singleRefOperation; |
| |
| } |
| final MultiReferenceSetOperation setOperation = OperationsFactory.eINSTANCE |
| .createMultiReferenceSetOperation(); |
| setCommonValues(project, setOperation, (EObject) n.getNotifier()); |
| setOperation.setFeatureName(n.getReference().getName()); |
| setBidirectionalAndContainmentInfo(setOperation, n.getReference()); |
| |
| setOperation.setIndex(n.getPosition()); |
| |
| if (n.getOldValue() != null) { |
| setOperation.setOldValue(oldModelElementId); |
| } |
| |
| if (n.getNewValue() != null) { |
| setOperation.setNewValue(newModelElementId); |
| } |
| |
| if (n.wasUnset()) { |
| setOperation.setUnset(UnsetType.WAS_UNSET); |
| } |
| |
| return setOperation; |
| } |
| |
| // utility methods |
| private static void setCommonValues(IdEObjectCollectionImpl collection, AbstractOperation operation, |
| EObject modelElement) { |
| operation.setClientDate(new Date()); |
| ModelElementId id = collection.getModelElementId(modelElement); |
| if (id == null) { |
| id = collection.getDeletedModelElementId(modelElement); |
| } |
| if (id == null) { |
| WorkspaceUtil.handleException(new IllegalStateException( |
| Messages.NotificationToOperationConverter_Element_Has_No_ID |
| + modelElement)); |
| } |
| operation.setModelElementId(id); |
| } |
| |
| private static void setBidirectionalAndContainmentInfo(ReferenceOperation referenceOperation, |
| EReference reference) { |
| if (reference.getEOpposite() != null) { |
| referenceOperation.setBidirectional(true); |
| referenceOperation.setOppositeFeatureName(reference.getEOpposite().getName()); |
| } else { |
| referenceOperation.setBidirectional(false); |
| } |
| if (reference.isContainer()) { |
| referenceOperation.setContainmentType(ContainmentType.CONTAINER); |
| } |
| if (reference.isContainment()) { |
| referenceOperation.setContainmentType(ContainmentType.CONTAINMENT); |
| } |
| } |
| |
| private AbstractOperation handleUnsetAttribute(NotificationInfo n) { |
| final FeatureOperation op = (FeatureOperation) handleSetAttribute(n); |
| op.setUnset(UnsetType.IS_UNSET); |
| return op; |
| } |
| |
| private AbstractOperation handleUnsetReference(NotificationInfo n) { |
| FeatureOperation op; |
| if (!n.getReference().isMany()) { |
| op = (FeatureOperation) handleSetReference(n); |
| } else { |
| op = (FeatureOperation) handleMultiReference(n); |
| } |
| op.setUnset(UnsetType.IS_UNSET); |
| return op; |
| } |
| |
| } |