/* * 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.ui.table; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.impl.EStringToStringMapEntryImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.Resource.Diagnostic; import org.eclipse.emf.edit.provider.INotifyChangedListener; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.xsd.XSDFacet; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.metamodel.MetamodelDescriptor; import org.teiid.designer.core.notification.util.NotificationUtilities; import org.teiid.designer.core.transaction.SourcedNotification; import org.teiid.designer.metamodels.core.Annotation; import org.teiid.designer.metamodels.core.AnnotationContainer; import org.teiid.designer.ui.UiConstants; import org.teiid.designer.ui.UiPlugin; import org.teiid.designer.ui.viewsupport.ModelUtilities; import org.teiid.designer.ui.wizards.NewModelWizard; /** * TableNotificationHandler * * @since 8.0 */ public class TableNotificationHandler implements INotifyChangedListener, UiConstants { /** the ModelTableEditor that created this handler */ private ModelTableEditor tableEditor; /** the EMF Resource that this handler's TreeView is displaying */ private Resource emfResource; /** key = EClass, value = TableViewer */ private HashMap viewersByClass = new HashMap(); /** key = ModelObjectTableModel, value = TableViewer */ private HashMap modelsByClass = new HashMap(); /** Collection of <code>Class</code>es that should not be added as a tabe to the table editor. */ private List excludedTypes = new ArrayList(); private static final String EXTENDED_METACLASS_PREFIX = "extendedMetaclass:"; //$NON-NLS-1$ TableNotificationHandler( ModelTableEditor editor ) { this.tableEditor = editor; this.emfResource = editor.getEmfResource(); } public void addTable( EClass eClass, TableViewer tableViewer, ModelObjectTableModel model ) { viewersByClass.put(eClass, tableViewer); modelsByClass.put(eClass, model); } /* (non-Javadoc) * @see org.eclipse.emf.edit.provider.INotifyChangedListener#notifyChanged(org.eclipse.emf.common.notify.Notification) */ @Override public void notifyChanged( final Notification notification ) { // determine if the notification is within the scope of the table // jh 10/24: to handle SourcedNotifications correctly, I am moving calls to // checkNotification to the handleNotification method. handleNotification(notification); } /** * Determine if the specified notification is within the scope of this table and should therefore be handled. * * @param notification * @return */ private boolean checkNotification( Notification notification ) { // get the target of the notification EObject target = NotificationUtilities.getEObject(notification); if (target == null) { // might have been an add or remove beneath the root if (NotificationUtilities.isAdded(notification)) { EObject[] children = NotificationUtilities.getAddedChildren(notification); if (children != null && children.length > 0) { target = children[0]; } } else if (NotificationUtilities.isRemoved(notification)) { EObject[] children = NotificationUtilities.getRemovedChildren(notification); if (children != null && children.length > 0) { target = children[0]; } } } if (target != null) { // get target resource in order to compare with this editor's resource Resource resource = target.eResource(); if (resource == null && NotificationUtilities.isRemoved(notification)) { final Object notifier = notification.getNotifier(); if (notifier instanceof Resource) { resource = (Resource)notifier; } else if (notifier instanceof EObject) { if (notifier instanceof Annotation) { resource = ((Annotation)notifier).getAnnotatedObject().eResource(); } else { resource = ((EObject)notifier).eResource(); } } } return ((resource != null) && resource.equals(this.emfResource)); } return false; } /** * Refresh this object's ModelTableEditor as necessary to reflect the specified notification. * * @param notification */ private void handleNotification( final Notification notification ) { // Defect 14521: To avoid duplicate entries when responding to Notifications from a newly // created model, bail out if the source is a NewModelWizard. if (notification instanceof SourcedNotification && ((SourcedNotification)notification).getSource() instanceof NewModelWizard) { return; } if (notification instanceof SourcedNotification) { Collection notifications = ((SourcedNotification)notification).getNotifications(); Iterator iter = notifications.iterator(); List deleteNotificationsToProcess = new ArrayList(); List nonDeleteNotificationsToProcess = new ArrayList(); while (iter.hasNext()) { Notification ntfTemp = (Notification)iter.next(); if (checkNotification(ntfTemp)) { if (NotificationUtilities.isRemoved(ntfTemp)) { deleteNotificationsToProcess.add(ntfTemp); } else { nonDeleteNotificationsToProcess.add(ntfTemp); } } } if (nonDeleteNotificationsToProcess.size() > 0 || deleteNotificationsToProcess.size() > 0) { // System.out.println("------------Start Notifications-------"); // System.out.println("Non Delete Notifier Count = " + nonDeleteNotificationsToProcess.size()); // System.out.println("Delete Notifier Count = " + deleteNotificationsToProcess.size()); // long start = System.currentTimeMillis(); if (nonDeleteNotificationsToProcess.size() > 0) { for (int i = nonDeleteNotificationsToProcess.size() - 1; i >= 0; i--) { handleSingleNotification((Notification)nonDeleteNotificationsToProcess.get(i)); } } if (deleteNotificationsToProcess.size() > 0) { for (int i = 0; i < deleteNotificationsToProcess.size(); i++) { handleSingleNotification((Notification)deleteNotificationsToProcess.get(i)); } } // long elapsed = System.currentTimeMillis() - start; // System.out.println("Elapsed Time = " + elapsed); // System.out.println("------------End Notifications-------"); } } else { if (checkNotification(notification)) { // System.out.println("------------Start Notifications-------"); // long start = System.currentTimeMillis(); handleSingleNotification(notification); // long elapsed = System.currentTimeMillis() - start; // System.out.println("Elapsed Time = " + elapsed); // System.out.println("------------End Notifications-------"); } } } /** * Refresh this object's ModelTableEditor as necessary to reflect the specified notification. * * @param notification */ private void handleSingleNotification( final Notification notification ) { if (NotificationUtilities.isEObjectNotifier(notification)) { handleSingleEObjectNotification(notification); } else { handleSingleResourceNotification(notification); } } /** * Refresh this object's ModelTableEditor as necessary to reflect the specified notification. * * @param notification */ private void handleSingleEObjectNotification( final Notification notification ) { EObject target = NotificationUtilities.getEObject(notification); if (target instanceof EStringToStringMapEntryImpl) { target = target.eContainer(); } if (target instanceof AnnotationContainer) { refreshAnnotations((AnnotationContainer)target, notification); } else if (target instanceof Annotation) { refreshAnnotation((Annotation)target, notification); } else { if (NotificationUtilities.isAdded(notification)) { EObject[] addedChildren = NotificationUtilities.getAddedChildren(notification); if (addedChildren != null && addedChildren.length > 0) { HashMap typeMap = sortObjectsByType(addedChildren); // walk through the EClass types and distribute the new objects to the correct tables for (Iterator iter = typeMap.keySet().iterator(); iter.hasNext();) { EClass eClass = (EClass)iter.next(); ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(eClass); if (model != null) { // found the table for this type. add the rows. model.addRows((ArrayList)typeMap.get(eClass)); } else { // no table was found for this type. // check to see if the type is of the PrimaryMetamodel EObject anObject = (EObject)((ArrayList)typeMap.get(eClass)).get(0); MetamodelDescriptor descriptor = ModelerCore.getModelEditor().getMetamodelDescriptor(anObject); if (descriptor != null && descriptor.isPrimary()) { // there is not a tab for this type: add it to the table if (anObject instanceof Diagnostic) { // screen these out - they are just "noteworthy issues" from EMF } else if (isObjectTypeAddable(anObject) && tableEditor.canAddTable(anObject)) { tableEditor.addTable(eClass, (ArrayList)typeMap.get(eClass)); } } } } } } else if (NotificationUtilities.isRemoved(notification)) { EObject[] removedChildren = NotificationUtilities.getRemovedChildren(notification); for (int i = 0; i < removedChildren.length; ++i) { HashMap typeMap = sortObjectsByType(removedChildren); for (Iterator iter = typeMap.keySet().iterator(); iter.hasNext();) { EClass eClass = (EClass)iter.next(); ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(eClass); if (model != null) { model.removeRows((ArrayList)typeMap.get(eClass)); } } } } else if (NotificationUtilities.isChanged(notification)) { // refresh target TableViewer viewer = (TableViewer)viewersByClass.get(target.eClass()); if (viewer != null && !viewer.getTable().isDisposed()) { ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(target.eClass()); Object row = model.getRowElementForInstance(target); if (row != null) { viewer.refresh(row); } } // refresh target's children. if the target's name changed the location property of // the children needs to be updated Object[] kids = ModelUtilities.getModelContentProvider().getChildren(target); if ((kids != null) && (kids.length > 0)) { for (int i = 0; i < kids.length; i++) { if (kids[i] instanceof EObject) { EClass childClass = ((EObject)kids[i]).eClass(); viewer = (TableViewer)viewersByClass.get(childClass); if ((viewer != null) && !viewer.getTable().isDisposed()) { ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(childClass); Object row = model.getRowElementForInstance((EObject)kids[i]); if (row != null) { viewer.refresh(row); } } } } } } } } /** * Indicates if the type of the specified object can be added to the table. * * @param theObject the object whose type is being checked * @return <code>true</code>if the type can be added; <code>false</code> otherwise. * @since 4.2 */ private boolean isObjectTypeAddable( Object theObject ) { boolean result = false; Class type = theObject.getClass(); if (!excludedTypes.contains(type)) { if (theObject instanceof XSDFacet) { this.excludedTypes.add(type); } else { result = true; } } return result; } /** * When the first <code>Annotation</code> is added the notification received is for the adding of the * <code>AnnotationContainer</code>. You will not receive notifications for the new <code>Annotation</code>. So here we must * check to see if the container has any. */ private void refreshAnnotations( AnnotationContainer theAnnotationContainer, Notification theNotification ) { Annotation annotation; EObject annotatedEObject = null; if (NotificationUtilities.isRemoved(theNotification)) { annotation = (Annotation)theNotification.getOldValue(); } else { annotation = (Annotation)theNotification.getNewValue(); } annotatedEObject = annotation.getAnnotatedObject(); if (annotatedEObject == null) { return; } // New Metaclass Extension annotation scheme makes it difficult to determine EClass (upon which table maps are based). // - We will look for change change involving EXTENDED_METACLASS_PREFIX. In this case, do a forced Refresh to // pick up the properties change. boolean forceRefresh = false; if (annotatedEObject instanceof EStringToStringMapEntryImpl) { String key = ((EStringToStringMapEntryImpl)annotatedEObject).getKey(); if (key.startsWith(EXTENDED_METACLASS_PREFIX)) { forceRefresh = true; } } // if (NotificationUtilities.isRemoved(theNotification)) { TableViewer viewer = (TableViewer)viewersByClass.get(annotatedEObject.eClass()); if (viewer != null) { if (!viewer.getTable().isDisposed()) { ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(annotatedEObject.eClass()); Object row = model.getRowElementForInstance(annotatedEObject); if (row != null) { viewer.refresh(row); } } } // In case of extended metaclass updates, refresh all the viewers. if (forceRefresh) { Collection<EClass> eClasses = viewersByClass.keySet(); for (EClass eClass : eClasses) { TableViewer tViewer = (TableViewer)viewersByClass.get(eClass); if (!tViewer.getTable().isDisposed()) { ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(eClass); model.refreshProperties(); EObjectPropertiesOrderPreferences modelTableColumnUtils = UiPlugin.getDefault().getEObjectPropertiesOrderPreferences(); if (modelTableColumnUtils != null) { modelTableColumnUtils.firePropertiesChanged(null); } tViewer.refresh(); } } } } /** * When the first <code>Annotation</code> is added the notification received is for the adding of the * <code>AnnotationContainer</code>. You will not receive notifications for the new <code>Annotation</code>. So here we must * check to see if the container has any. */ private void refreshAnnotations( AnnotationContainer theAnnotationContainer ) { EObject annotatedEObject = null; // We are assuming here that we won't get a REMOVE notification for the AnnotationContainer // We also assume that if we get an ADD for the container, then we just need to get it's contents // and find each annotated object. List annotations = theAnnotationContainer.getAnnotations(); for (Iterator iter = annotations.iterator(); iter.hasNext();) { Object next = iter.next(); if( next == null ) { break; } annotatedEObject = ((Annotation)next).getAnnotatedObject(); // if (NotificationUtilities.isRemoved(theNotification)) { TableViewer viewer = (TableViewer)viewersByClass.get(annotatedEObject.eClass()); if (viewer != null) { if (!viewer.getTable().isDisposed()) { ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(annotatedEObject.eClass()); Object row = model.getRowElementForInstance(annotatedEObject); if (row != null) { viewer.refresh(row); } } } } } private void refreshAnnotation( Annotation annotation, Notification theNotification ) { EObject annotatedEObject = annotation.getAnnotatedObject(); if (annotatedEObject == null) { return; } // if (NotificationUtilities.isRemoved(theNotification)) { TableViewer viewer = (TableViewer)viewersByClass.get(annotatedEObject.eClass()); if (viewer != null) { if (!viewer.getTable().isDisposed()) { ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(annotatedEObject.eClass()); model.refreshProperties(); EObjectPropertiesOrderPreferences modelTableColumnUtils = UiPlugin.getDefault().getEObjectPropertiesOrderPreferences(); if (modelTableColumnUtils != null) { modelTableColumnUtils.firePropertiesChanged(Collections.singletonList(annotatedEObject.eClass().getName())); } Object row = model.getRowElementForInstance(annotatedEObject); if (row != null) { viewer.refresh(row); } } } } /** * Refresh this object's ModelTableEditor as necessary to reflect the specified notification. * * @param notification */ private void handleSingleResourceNotification( final Notification notification ) { if (NotificationUtilities.isAdded(notification)) { EObject[] addedChildren = NotificationUtilities.getAddedChildren(notification); if (addedChildren != null && addedChildren.length > 0) { if (addedChildren[0] instanceof AnnotationContainer) { refreshAnnotations((AnnotationContainer)addedChildren[0]); } else { // changes for defect 13339 (the for loop is not required since the typeMap below // contains all the objects in the addedChildren collection // for (int i = 0; i < addedChildren.length; ++i) { HashMap typeMap = sortObjectsByType(addedChildren); // walk through the EClass types and distribute the new objects to the correct tables for (Iterator iter = typeMap.keySet().iterator(); iter.hasNext();) { EClass eClass = (EClass)iter.next(); ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(eClass); if (model != null) { // found the table for this type. add the rows. model.addRows((ArrayList)typeMap.get(eClass)); } else { // no table was found for this type. // check to see if the type is of the PrimaryMetamodel EObject anObject = (EObject)((ArrayList)typeMap.get(eClass)).get(0); MetamodelDescriptor descriptor = ModelerCore.getModelEditor().getMetamodelDescriptor(anObject); if (descriptor != null && descriptor.isPrimary()) { // there is not a tab for this type: add it to the table if (tableEditor.canAddTable(anObject)) { tableEditor.addTable(eClass, (ArrayList)typeMap.get(eClass)); } } } } // } changes for defect 13339 } } } else if (NotificationUtilities.isRemoved(notification)) { EObject[] removedChildren = NotificationUtilities.getRemovedChildren(notification); if (removedChildren != null && removedChildren.length > 0) { for (int i = 0; i < removedChildren.length; ++i) { HashMap typeMap = sortObjectsByType(removedChildren); for (Iterator iter = typeMap.keySet().iterator(); iter.hasNext();) { EClass eClass = (EClass)iter.next(); ModelObjectTableModel model = (ModelObjectTableModel)modelsByClass.get(eClass); if (model != null) { model.removeRows((ArrayList)typeMap.get(eClass)); } } } } } // in this method, we do not have a target, so this code will now work. // //// if ( NotificationUtilities.isChanged(notification) ) { // //// // //// TableViewer viewer = (TableViewer)viewersByClass.get(target.eClass()); // //// if (viewer != null && !viewer.getTable().isDisposed()) { // //// ModelObjectTableModel model = (ModelObjectTableModel) modelsByClass.get(target.eClass()); // //// Object row = model.getRowElementForInstance(target); // //// if ( row != null ) { // //// viewer.refresh(row); // //// } else { // //// viewer.refresh(); // //// } // //// } // //// // //// } } /** * Build a HashMap of the content of the specified Collection of EObjects recursively. * * @param a Collection of EObject instances of various EClass types. * @return a HashMap of key=EClass types and value=ArrayList of EObjects of the key type. */ private HashMap sortObjectsByType( EObject[] objectArray ) { HashMap result = new HashMap(); for (int i = 0; i < objectArray.length; ++i) { loadObjectTypeMap(objectArray[i], result); } return result; } /** * Recursively fill the specified object HashMap beginning with the specified object. */ private void loadObjectTypeMap( EObject o, HashMap objectTypeMap ) { if (o != null) { ArrayList list = (ArrayList)objectTypeMap.get(o.eClass()); if (list == null) { list = new ArrayList(); objectTypeMap.put(o.eClass(), list); } list.add(o); for (Iterator iter = o.eContents().iterator(); iter.hasNext();) { loadObjectTypeMap((EObject)iter.next(), objectTypeMap); } } } }