/******************************************************************************* * 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.notification; import java.util.List; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; /** * Validates an EMF notification. Optionally generates a status message, describing potential problems. * * @author chodnick */ final class NotificationValidator { private static NotificationValidator instance; /** * Singleton access. * * @return the validator instance */ public static NotificationValidator getInstance() { if (instance == null) { instance = new NotificationValidator(); } return instance; } private NotificationValidator() { } /** * Validates a notification and sets its valid flag and validationmessage string. * * @param n the notification to validate * @return */ protected void validate(NotificationInfo n) { if (n == null) { throw new IllegalArgumentException("NotificationInfo argument cannot be null"); } // assume notification is valid, handlers will reset state and message, if something is wrong n.setValid(true); n.setValidationMessage("OK"); // consider touch and transient notification to always be valid if (n.isTouch() || n.isTransient()) { return; } switch (n.getEventType()) { case Notification.SET: handleSetNotification(n); break; case Notification.ADD: handleAddNotification(n); break; case Notification.REMOVE: handleRemoveNotification(n); break; case Notification.ADD_MANY: handleAddManyNotification(n); break; case Notification.REMOVE_MANY: handleRemoveManyNotification(n); break; case Notification.UNSET: handleUnsetNotification(n); break; case Notification.MOVE: handleMoveNotification(n); break; default: handleUnknownNotification(n); break; } } private void handleUnknownNotification(NotificationInfo n) { n.setValid(false); n.setValidationMessage("Error: unknown notification event type. " + n.toString()); } private void handleMoveNotification(NotificationInfo n) { // if (n.isAttributeNotification()) { // n.setValid(false); // n.setValidationMessage("MOVE notification on attribute feature with multiplicty" // + "greater 1 not supported yet!"); // return; // } if (n.isReferenceNotification()) { // sanity checks if (n.getNewValue() == null || n.getOldValue() == null) { n.setValid(false); n.setValidationMessage("Null detected in oldValue or NewValue of move notification about: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } if (!(n.getNewValue() instanceof EObject)) { // non model element references must be marked transient n.setValid(false); n.setValidationMessage("Non-transient non-EObject reference feature detected: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } if (!(n.getOldValue() instanceof Integer)) { n.setValid(false); n.setValidationMessage("Error with old position in move: not an Integer"); return; } } } private void handleUnsetNotification(NotificationInfo n) { n.setValid(false); n.setValidationMessage("Cannot handle UNSET notifications"); } private void handleRemoveManyNotification(NotificationInfo n) { // // attributes not allowed for REMOVE_MANY (yet) // if (n.isAttributeNotification()) { // n.setValid(false); // n.setValidationMessage("REMOVE_MANY on attribute feature with multiplicity greater" // + "than 1 not yet supported."); // return; // } // reference validation if (n.isReferenceNotification()) { // the new guys must come in a list if (!(n.getOldValue() instanceof List<?>)) { n.setValid(false); n.setValidationMessage("Non-List oldValue argument for REMOVE_MANY notification on: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } // checking up on EMF, the reference must have max multiplicity greater than 1... if (!n.getReference().isMany()) { n.setValid(false); n.setValidationMessage("Unkown notification state: REMOVE_MANY notification on reference " + "feature with isMany==false"); return; } } } private void handleAddManyNotification(NotificationInfo n) { // TODO add validation // // attributes not allowed for ADD_MANY (yet) // if (n.isAttributeNotification()) { // n.setValid(false); // n.setValidationMessage("ADD_MANY on attribute feature with multiplicity greater than 1 not yet supported."); // return; // } // reference validation if (n.isReferenceNotification()) { // the new guys must come in a list if (!(n.getNewValue() instanceof List<?>)) { n.setValid(false); n.setValidationMessage("Non-List newValue argument for ADD_MANY notification on: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } // checking up on EMF, the reference must have max multiplicity greater than 1... if (!n.getReference().isMany()) { n.setValid(false); n.setValidationMessage("Unkown notification state: ADD_MANY notification on reference " + "feature with isMany==false"); return; } } } private void handleRemoveNotification(NotificationInfo n) { // validation for REMOVE reference if (n.isReferenceNotification()) { // non model element references must be marked transient if (!(n.getOldValue() instanceof EObject)) { n.setValid(false); n.setValidationMessage("Non-transient non-EObject reference feature detected: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } // checking up on EMF... if (!n.getReference().isMany()) { n.setValid(false); n.setValidationMessage("Unkown notification state: REMOVE notification on reference " + "feature with isMany==false"); } } // no validation for REMOVE attribute } private void handleAddNotification(NotificationInfo n) { // validation for ADD reference if (n.isReferenceNotification()) { // non model element references must be marked transient if (!(n.getNewValue() instanceof EObject)) { n.setValid(false); n.setValidationMessage("Non-transient non-EObject reference feature detected: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } // checking up on EMF... if (!n.getReference().isMany()) { n.setValid(false); n.setValidationMessage("Unkown notification state: ADD notification on reference " + "feature with isMany==false"); } } // no validation for ADD attribute } private void handleSetNotification(NotificationInfo n) { // validation for SET reference if (n.isReferenceNotification()) { // sanity check newValue and oldValue must be ModelElements or null Object newValueObject = n.getNewValue(); Object oldValueObject = n.getOldValue(); boolean newValIsNoME = newValueObject != null && !(newValueObject instanceof EObject); boolean oldValIsNoME = oldValueObject != null && !(oldValueObject instanceof EObject); if (newValIsNoME || oldValIsNoME) { // non model element references must be marked transient n.setValid(false); n.setValidationMessage("Non-transient non-EObject reference feature detected: " + n.getNotifier().getClass().getCanonicalName() + "-" + n.getReference().getName()); return; } } // no validation for SET attribute } }