blob: 4ca6646498135dfe22b29217d53085dc32d3edc9 [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.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
}
}