/*******************************************************************************
* 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.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.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.emfstore.client.model.changeTracking.notification.NotificationInfo;
import org.eclipse.emf.emfstore.client.model.util.WorkspaceUtil;
import org.eclipse.emf.emfstore.common.model.IdEObjectCollection;
import org.eclipse.emf.emfstore.common.model.ModelElementId;
import org.eclipse.emf.emfstore.common.model.impl.ProjectImpl;
import org.eclipse.emf.emfstore.common.model.util.ModelUtil;
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.ContainmentType;
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.OperationsFactory;
import org.eclipse.emf.emfstore.server.model.versioning.operations.ReferenceOperation;
import org.eclipse.emf.emfstore.server.model.versioning.operations.SingleReferenceOperation;
/**
* Converts an EMF notification to an Operation.
*
* @author chodnick
*/
public final class NotificationToOperationConverter {
private IdEObjectCollection project;
/**
* Default constructor.
*
* @param project
* project
*/
public NotificationToOperationConverter(IdEObjectCollection 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);
} else {
return handleSetReference(n);
}
case Notification.ADD:
if (n.isAttributeNotification()) {
return handleMultiAttribute(n);
} else {
return handleMultiReference(n);
}
case Notification.ADD_MANY:
if (n.isAttributeNotification()) {
return handleMultiAttribute(n);
} else {
return handleMultiReference(n);
}
case Notification.REMOVE:
if (n.isAttributeNotification()) {
return handleMultiAttribute(n);
} else {
return handleMultiReference(n);
}
case Notification.REMOVE_MANY:
if (n.isAttributeNotification()) {
return handleMultiAttribute(n);
} else {
return handleMultiReference(n);
}
case Notification.MOVE:
if (n.isAttributeNotification()) {
return handleAttributeMove(n);
} else {
return handleReferenceMove(n);
}
default:
return null;
}
}
// END COMPLEX CODE
@SuppressWarnings("unchecked")
private AbstractOperation handleMultiAttribute(NotificationInfo n) {
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 (int value : ((int[]) n.getNewValue())) {
operation.getIndexes().add(value);
}
}
break;
default:
break;
}
for (Object valueElement : list) {
operation.getReferencedValues().add(valueElement);
}
return operation;
}
@SuppressWarnings("unchecked")
private AbstractOperation handleMultiReference(NotificationInfo n) {
List<EObject> list = null;
switch (n.getEventType()) {
case Notification.ADD:
list = new ArrayList<EObject>();
list.add(n.getNewModelElementValue());
break;
case Notification.ADD_MANY:
list = (List<EObject>) n.getNewValue();
break;
case Notification.REMOVE:
list = new ArrayList<EObject>();
list.add(n.getOldModelElementValue());
break;
case Notification.REMOVE_MANY:
list = (List<EObject>) n.getOldValue();
break;
default:
break;
}
boolean isAdd = n.isAddEvent() || n.isAddManyEvent();
return createMultiReferenceOperation(project, n.getNotifierModelElement(), n.getReference(), list, isAdd,
n.getPosition());
}
public static MultiReferenceOperation createMultiReferenceOperation(IdEObjectCollection collection,
EObject modelElement, EReference reference, List<EObject> referencedElements, boolean isAdd, int position) {
MultiReferenceOperation op = OperationsFactory.eINSTANCE.createMultiReferenceOperation();
setCommonValues(collection, op, modelElement);
setBidirectionalAndContainmentInfo(op, reference);
op.setFeatureName(reference.getName());
op.setAdd(isAdd);
op.setIndex(position);
List<ModelElementId> referencedModelElements = op.getReferencedModelElements();
for (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("Element in Project does not have an ID: " + valueElement);
}
// ignore value elements outside of the current project, they are
// not tracked
}
return op;
}
private AbstractOperation handleReferenceMove(NotificationInfo n) {
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) {
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
if (isDiagramLayoutAttribute(n.getAttribute(), n.getNotifierModelElement())) {
op = OperationsFactory.eINSTANCE.createDiagramLayoutOperation();
} else {
op = OperationsFactory.eINSTANCE.createAttributeOperation();
}
setCommonValues(project, op, n.getNotifierModelElement());
op.setFeatureName(n.getAttribute().getName());
op.setNewValue(n.getNewValue());
op.setOldValue(n.getOldValue());
return op;
} else {
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());
return setOperation;
}
}
public static SingleReferenceOperation createSingleReferenceOperation(IdEObjectCollection collection,
ModelElementId oldReference, ModelElementId newReference, EReference reference, EObject modelElement) {
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()) {
return createSingleReferenceOperation(project, oldModelElementId, newModelElementId, n.getReference(),
n.getNotifierModelElement());
} else {
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);
}
return setOperation;
}
}
// utility methods
private static void setCommonValues(IdEObjectCollection 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("Model Element does not have an 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 boolean isDiagramLayoutAttribute(EAttribute attribute, EObject modelElement) {
// FIXME: to check if attribute is the layout of a diagram
boolean isLayoutAttribute = attribute.getName().equals("diagramLayout");
return isLayoutAttribute;
}
}