/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.notification.util; import java.util.ArrayList; import java.util.Iterator; 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.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xsd.XSDAttributeGroupDefinition; import org.teiid.designer.metamodels.core.Annotation; /** * The <code>NotificationUtilities</code> class contains utility methods for use with * {@link org.eclipse.emf.common.notify.Notification} objects. * * @since 8.0 */ public class NotificationUtilities { private static INotificationHelper notificationHelper = new DefaultNotificationHelper(); /////////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS /////////////////////////////////////////////////////////////////////////////////////////////// /** No arg construction not allowed. */ private NotificationUtilities() { } /////////////////////////////////////////////////////////////////////////////////////////////// // METHODS /////////////////////////////////////////////////////////////////////////////////////////////// /** * Gets the <code>EObject</code>s added to a list-based property of the event notifier. * @param theNotification the event being processed * @return a collection of added values or <code>null</code> if the event was not an addition * @see #isAdded(Notification) */ public static EObject[] getAddedChildren(Notification theNotification) { EObject[] result = new EObject[0]; if (isAdded(theNotification)) { //Backed this change out to fix defect 13609. Coupled with changes to UoW 13339 AND 13609 as well //as the test cases all pass. //changes for defect 13339 to handle add_many logic (wrapped in try catch for extra security // if(theNotification.getNotifier() instanceof Resource) { // try { // final Resource rsrc = (Resource)theNotification.getNotifier(); // final int position = theNotification.getPosition(); // if(position > -1) { // int size = 1; // if(theNotification.getNewValue() instanceof Collection) { // size = ((Collection)theNotification.getNewValue()).size(); // } // // result = new EObject[size]; // int tmp = 0; // while(tmp < size) { // result[tmp] = (EObject)rsrc.getContents().get(position + tmp); // tmp++; // } // // return result; // } // } catch (Exception err) { // //Added try catch for extra security for new code implementation. // //If we get an exception, let the code fall through to the old implementation. // } // } Object added = theNotification.getNewValue(); List temp = new ArrayList(); if (added instanceof List) { List newKids = (List)added; for (int size = newKids.size(), i = 0; i < size; i++) { Object kid = newKids.get(i); if (kid instanceof EObject) { temp.add(kid); } } } else { if (added instanceof EObject) { temp.add(added); } } // if found EObjects put them in the result if (!temp.isEmpty()) { int size = temp.size(); result = new EObject[size]; for (int i = 0; i < size; i++) { result[i] = (EObject)temp.get(i); } } } return result; } /** * Gets the <code>EObject</code>s remove from a list-based property of the event notifier. * @param theNotification the event being processed * @return a collection of removed values or <code>null</code> if the event was not a removal * @see #isRemoved(Notification) */ public static EObject[] getRemovedChildren(Notification theNotification) { EObject[] result = new EObject[0]; if (isRemoved(theNotification)) { //If this is an up notification... just return the notifier Object removed = theNotification.getOldValue(); if (removed instanceof List) { List oldKids = (List)removed; int size = oldKids.size(); result = new EObject[size]; for (int i = 0; i < size; i++) { result[i] = (EObject)oldKids.get(i); } } else if( removed instanceof EObject ) { result = new EObject[1]; result[0] = (EObject)removed; } } return result; } /** * Gets the <code>EObject</code> whose state has changed. * @param theNotification the notification event * @return the target <code>EObject</code> or <code>null</code> if not an <code>EObject</code> */ public static EObject getEObject(Notification theNotification) { Object obj = getNotificationHelper().getNotifier(theNotification); return (obj instanceof EObject) ? (EObject)obj : null; } /** * Gets the <code>Resource</code> whose state has changed. * @param theNotification the notification event * @return the target <code>Resource</code> or <code>null</code> if not an <code>Resource</code> */ public static Resource getResource(Notification theNotification) { Object obj = getNotificationHelper().getNotifier(theNotification); return (obj instanceof Resource) ? (Resource)obj : null; } /** * Gets the model object identifier associated with the notification feature. * @param theNotification the notification event * @return the feature model object identifier or -1 if feature doesn't exist */ public static int getFeatureChanged(Notification theNotification) { int result = -1; Object obj = theNotification.getFeature(); if (obj instanceof EStructuralFeature) { EStructuralFeature feature = (EStructuralFeature)obj; result = feature.getFeatureID(); } return result; } /** * Indicates if a list-based feature of the notifier has been inserted into. Also handles * Notifications where type==SET and the set feature is an EReference, which we also treat * as an add. * @param theNotification the notification event * @return <code>true</code> if an insertion has occurred; <code>false</code> otherwise. */ public static boolean isAdded(Notification notification) { final int eventType = notification.getEventType(); final boolean isValidSet = eventType == Notification.SET && isSetAdd(notification); final Object feature = notification.getFeature(); if (feature instanceof EReference) { final EReference ref = (EReference)feature; if (!ref.isContainment()){ return false; } // Ignore new attribute group references final Object newVal = notification.getNewValue(); if (newVal instanceof XSDAttributeGroupDefinition) { final XSDAttributeGroupDefinition attrGrp = (XSDAttributeGroupDefinition)newVal; if (attrGrp != attrGrp.getResolvedAttributeGroupDefinition()) { return false; } } } return ( (eventType == Notification.ADD) || (eventType == Notification.ADD_MANY) || isValidSet); } /** * Rules for Set as an add... * 1) oldValue != null && newValue == null * 2) the sf is a containment EReference * 4) if feature is a list only rule 1 applies */ private static boolean isSetAdd(final Notification notification){ final Object feature = notification.getFeature(); final Object oldVal = notification.getOldValue(); final Object newVal = notification.getNewValue(); if(feature instanceof EReference){ final EReference ref = (EReference)feature; //If this is a down notification it is an add if the sf is containment //and the newVal != null and the oldVal == null return (ref.isContainment() && newVal != null && oldVal == null); }else if(feature instanceof List){ //is and add if feature is a list and newVal != null && oldVal == null return (newVal != null && oldVal == null); } return false; } /** * Indicates if the notifier state has changed. State will not have changed when setting a feature to * it's present value. * @param theNotification the notification event * @return <code>true</code> if the notifier state has changed; <code>false</code> otherwise. */ public static boolean isChanged(Notification theNotification) { return !theNotification.isTouch(); } /** * Indicates if the event notifier is an {@link org.eclipse.emf.ecore.EObject}. * @param theNotification the event being processed * @return <code>true</code> if notifier is an <code>EObject</code>; <code>false</code> otherwise. */ public static boolean isEObjectNotifier(Notification theNotification) { return (getEObject(theNotification) != null); } /** * Indicates if the event notifier is an {@link org.eclipse.emf.ecore.resource.Resource}. * @param theNotification the event being processed * @return <code>true</code> if notifier is an <code>Resource</code>; <code>false</code> otherwise. */ public static boolean isResourceNotifier(Notification theNotification) { Object obj = getNotificationHelper().getNotifier(theNotification); if( obj instanceof Resource ) return true; return false; } /** * Indicates if the given feature was changed by this notification. * @param theNotification the notification event * @param theFeatureId the feature model identifier being checked * @return <code>true</code> if the given feature has changed; <code>false</code> otherwise. */ public static boolean isFeatureChanged(Notification theNotification, int theFeatureId) { return isFeatureChanged(theNotification, new int[] { theFeatureId }); } /** * Indicates if at least on of the given features were changed by this notification. * @param theNotification the notification event * @param theFeatureIds the feature model identifiers being checked * @return <code>true</code> if at least one feature has changed; <code>false</code> otherwise. */ public static boolean isFeatureChanged(Notification theNotification, int[] theFeatureIds) { boolean result = false; Object obj = theNotification.getFeature(); if (obj instanceof EStructuralFeature) { EStructuralFeature feature = (EStructuralFeature)obj; for (int i = 0; i < theFeatureIds.length; i++) { if ((feature.eClass().getClassifierID() == theFeatureIds[i]) && isChanged(theNotification)) { result = true; break; } } } return result; } /** * Indicates if a value of a list-based feature of the notifier has been moved. * @param theNotification the notification event * @return <code>true</code> if a value has been moved; <code>false</code> otherwise. */ public static boolean isMoved(Notification theNotification) { return (theNotification.getEventType() == Notification.MOVE); } /** * Indicates if muliple values of a list-based feature of the notifier have either been added or * removed. * @param theNotification the notification event * @return <code>true</code> if multiple values have changed; <code>false</code> otherwise. */ public static boolean isMultipleChanges(Notification theNotification) { return ( (theNotification.getEventType() == Notification.ADD_MANY) || (theNotification.getEventType() == Notification.REMOVE_MANY)); } /** * Indicates if a list-based feature of the notifier has been removed from. * @param theNotification the notification event * @return <code>true</code> if a removal has occurred; <code>false</code> otherwise. */ public static boolean isRemoved(Notification theNotification) { final int eventType = theNotification.getEventType(); if ( (eventType == Notification.REMOVE) || (eventType == Notification.REMOVE_MANY) || isSetRemove(theNotification) ){ final Object feature = theNotification.getFeature(); if(feature instanceof EReference && !((EReference)feature).isContainment() ){ return false; } //Feature must be containment eReference final Object removed = theNotification.getOldValue(); final Object parent = theNotification.getNotifier(); //If the parent is no longer in a resource (it has also been removed) return false //This replaces the notifier not null parent check logic that was incorrect if(parent instanceof EObject){ Resource resource = null; if( parent instanceof Annotation ) { resource = ((Annotation)parent).getAnnotatedObject().eResource(); } else { resource = ((EObject)parent).eResource(); } if(resource == null) { return false; } } boolean isRemoved = true; if (removed instanceof EObject) { isRemoved = isRemoved(parent,(EObject)removed); } else if (removed instanceof List) { //Iterate over each removed object and ensure it has really been removed from it's parent final Iterator removedObjs = ((List)removed).iterator(); while(removedObjs.hasNext() ){ final Object removedObj = removedObjs.next(); // If any are not removed, returns false if(removedObj instanceof EObject){ if( !isRemoved(parent,(EObject)removedObj)) { isRemoved = false; break; } } else { isRemoved = false; break; } } } else { isRemoved = false; } return isRemoved; } return false; } /** * Private method for isRemoved check * @param parent the parent object * @param removedObj the removed Object * @return <code>true</code> if a removal has occurred; <code>false</code> otherwise. */ private static boolean isRemoved(Object parent, EObject removedObj) { boolean isRemoved = true; if( parent instanceof EObject ) { //If the eObject has not yet been removed from parent return false isRemoved = !((EObject)parent).eContents().contains(removedObj); } else if(parent instanceof Resource){ //If the eObject is a root, but has not been removed from the resource, return false isRemoved = !((Resource)parent).getContents().contains(removedObj); } return isRemoved; } /** * Rules for Set as an remove... * 1) oldValue == null && newValue != null * 2) the sf is a containment EReference * 4) if feature is a list only rule 1 applies */ private static boolean isSetRemove(final Notification notification){ final Object feature = notification.getFeature(); final Object oldVal = notification.getOldValue(); final Object newVal = notification.getNewValue(); if(feature instanceof EReference){ final EReference ref = (EReference)feature; //it is an remove if the sf is containment //and the newVal == null and the oldVal != null return (ref.isContainment() && newVal == null && oldVal != null); }else if(feature instanceof List){ //is a remove if feature is a list and newVal == null && oldVal != null return (newVal == null && oldVal != null); } return false; } /** * Gets a string representation of the properties of the given <code>Notification</code>. * @param theNotification the notification being processed * @return the string representation */ public static String paramString(Notification theNotification) { String notifierClass = (getNotificationHelper().getNotifier(theNotification) == null) ? "null" //$NON-NLS-1$ : getNotificationHelper().getNotifier(theNotification).getClass().getName(); return new StringBuffer().append("Notification:\n event type=").append(theNotification.getEventType()) //$NON-NLS-1$ .append("\n feature=").append(theNotification.getFeature()) //$NON-NLS-1$ .append("\n notifier=").append(getNotificationHelper().getNotifier(theNotification)) //$NON-NLS-1$ .append("\n notifier class=").append(notifierClass) //$NON-NLS-1$ .append("\n position=").append(theNotification.getPosition()) //$NON-NLS-1$ .append("\n reset=").append(theNotification.isReset()) //$NON-NLS-1$ .append("\n touch=").append(theNotification.isTouch()) //$NON-NLS-1$ .append("\n isAdded=" + isAdded(theNotification)) //$NON-NLS-1$ .append("\n isChanged=").append(isChanged(theNotification)) //$NON-NLS-1$ .append("\n isEObjectNotifier=").append(isEObjectNotifier(theNotification)) //$NON-NLS-1$ .append("\n isMoved=").append(isMoved(theNotification)) //$NON-NLS-1$ .append("\n isMultipleChanges=").append(isMultipleChanges(theNotification)) //$NON-NLS-1$ .append("\n isRemoved=").append(isRemoved(theNotification)) //$NON-NLS-1$ .append("\n getAddedChildren=").append(getAddedChildrenPrintString(theNotification)) //$NON-NLS-1$ .append("\n getRemovedChildren=").append(getRemovedChildrenPrintString(theNotification)) //$NON-NLS-1$ .toString(); } private static String getAddedChildrenPrintString(Notification notification) { if( getAddedChildren(notification) == null || getAddedChildren(notification).length == 0 ) return "EMPTY"; //$NON-NLS-1$ StringBuffer returnString = new StringBuffer().append(" "); //$NON-NLS-1$ for( int i=0; i<getAddedChildren(notification).length; i++ ) { returnString.append("\n child =").append(getAddedChildren(notification)[i]); //$NON-NLS-1$ } return returnString.toString(); } private static String getRemovedChildrenPrintString(Notification notification) { if( getRemovedChildren(notification) == null || getRemovedChildren(notification).length == 0 ) return "EMPTY"; //$NON-NLS-1$ StringBuffer returnString = new StringBuffer().append(" "); //$NON-NLS-1$ for( int i=0; i<getRemovedChildren(notification).length; i++ ) { returnString.append("\n child =").append(getRemovedChildren(notification)[i]); //$NON-NLS-1$ } return returnString.toString(); } public static boolean addedChildrenParentIsNotifier(Notification notification) { EObject[] addedChildren = getAddedChildren(notification); if( addedChildren == null || addedChildren.length == 0 ) return false; //process the new children against the notifier boolean notifierIsParent = true; Object notifier = getNotificationHelper().getNotifier(notification); int nObjects = addedChildren.length; for(int i=0; i<nObjects; i++) { EObject childsParent = addedChildren[i].eContainer(); if(childsParent!=null && childsParent.equals(notifier)) { //allOK } else { notifierIsParent = false; break; } } return notifierIsParent; } /** * @return */ public static INotificationHelper getNotificationHelper() { return notificationHelper; } /** * @param helper */ public static void setNotificationHelper(INotificationHelper helper) { notificationHelper = helper; } // /** // * When dealing with a set notification it is possible for the notifier to be the child and // * the new / old value to be the parent. This method will return true if the notification's // * notifier is the parent of the relationship. // * // * Rules for isDown.. // * if notifier is EObject // * 1) Feature is eRef and isContainment == true // * 2) Feature is a list // * if notifier is Resource // * 1) the newValue or oldValue is an instanceof EObject // * if notifier is a resource set // * 1) the newValue or oldValue is an instanceof Resource // */ // private static boolean isDown(final Notification notification){ // final Object potentialParent = notification.getNotifier(); // final Object feature = notification.getFeature(); // if(potentialParent instanceof EObject){ // if(feature instanceof EReference){ // return ((EReference)feature).isContainment(); // }else if(feature instanceof List){ // return true; // } // // return false; // }else if(potentialParent instanceof Resource){ // return (notification.getOldValue() instanceof EObject || notification.getNewValue() instanceof EObject); // }else if(potentialParent instanceof ResourceSet){ // return (notification.getOldValue() instanceof Resource || notification.getNewValue() instanceof Resource); // } // // return true; // } // // }