/* * 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.transformation.ui.model; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.notification.util.NotificationUtilities; import org.teiid.designer.core.transaction.SourcedNotification; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelWorkspaceException; import org.teiid.designer.diagram.ui.model.DiagramModelNode; import org.teiid.designer.diagram.ui.notation.uml.model.UmlClassifierContainerNode; import org.teiid.designer.diagram.ui.notation.uml.model.UmlClassifierNode; import org.teiid.designer.diagram.ui.util.DiagramUiUtilities; import org.teiid.designer.diagram.ui.util.RelationalUmlEObjectHelper; import org.teiid.designer.metamodels.diagram.Diagram; import org.teiid.designer.transformation.ui.PluginConstants; import org.teiid.designer.transformation.ui.UiConstants; import org.teiid.designer.transformation.ui.util.TransformationUmlEObjectHelper; import org.teiid.designer.ui.viewsupport.ModelUtilities; /** * @since 8.0 */ public class TransformationDiagramNotificationHelper { private Notification primaryNotification = null; private DiagramModelNode diagramNode = null; private boolean reconcileTargetAttributes = false; private boolean reconcileSources = false; private boolean refreshDiagram; private List reconcileTables = null; private boolean handleNotification = false; private List changedNodes = null; private List movedNodes = null; private EObject targetEObject = null; /** * @since 4.2 */ public TransformationDiagramNotificationHelper( Notification notification, DiagramModelNode transformationDiagramModelNode ) { super(); this.primaryNotification = notification; this.diagramNode = transformationDiagramModelNode; init(); } private void init() { reconcileTables = new ArrayList(); changedNodes = new ArrayList(); movedNodes = new ArrayList(); setTransformation(); setHandleNotification(); setReconcileSources(); setReconcileAttributes(); setRefreshDiagram(); } private void setTransformation() { targetEObject = ((Diagram)diagramNode.getModelObject()).getTarget(); } private void setHandleNotification() { boolean shouldHandle = false; Diagram currentDiagram = (Diagram)diagramNode.getModelObject(); ModelResource diagramMR = ModelUtilities.getModelResourceForModelObject(currentDiagram); if (diagramMR != null) { Collection dependentModels = Collections.EMPTY_LIST; try { dependentModels = ModelUtilities.getDependentResources(diagramMR); } catch (ModelWorkspaceException e) { UiConstants.Util.log(IStatus.ERROR, e, e.getMessage()); } if (primaryNotification instanceof SourcedNotification) { Object source = ((SourcedNotification)primaryNotification).getSource(); if (source == null || !source.equals(this)) { Collection notifications = ((SourcedNotification)primaryNotification).getNotifications(); Iterator iter = notifications.iterator(); Notification nextNotification = null; while (iter.hasNext() && !shouldHandle) { nextNotification = (Notification)iter.next(); Object targetObject = ModelerCore.getModelEditor().getChangedObject(nextNotification); if (targetObject != null && targetObject instanceof EObject && !DiagramUiUtilities.isNonDrawingDiagramObject((EObject)targetObject)) { // NOTE transformation diagram receives a notification from another Model, we need to look // at whether or not the target object is used in the T-Editor or not. // IF SO, then shouldHandle == true // else // If it's not, then check MR's for same model then shouldHandle == true ModelResource mr = ModelUtilities.getModelResourceForModelObject((EObject)targetObject); if (mr != null ) { if( mr.equals(diagramMR) || dependentModels.contains(mr) ) { shouldHandle = true; } } } else if (targetObject instanceof Diagram && NotificationUtilities.isRemoved(nextNotification)) { ModelResource mr = ModelUtilities.getModelResourceForModelObject((EObject)targetObject); if (mr != null && mr.equals(diagramMR)) { shouldHandle = true; } } } } } else { // SINGLE NOTIFICATION Object targetObject = ModelerCore.getModelEditor().getChangedObject(primaryNotification); if (targetObject != null && targetObject instanceof EObject && !DiagramUiUtilities.isNonDrawingDiagramObject((EObject)targetObject)) { // NOTE transformation diagram receives a notification from another Model, we need to look // at whether or not the target object is used in the T-Editor or not. // IF SO, then shouldHandle == true // else // If it's not, then check MR's for same model then shouldHandle == true ModelResource mr = ModelUtilities.getModelResourceForModelObject((EObject)targetObject); if (mr != null ) { if( mr.equals(diagramMR) || dependentModels.contains(mr) ) { shouldHandle = true; } } } else if (targetObject instanceof Diagram && NotificationUtilities.isRemoved(primaryNotification)) { ModelResource mr = ModelUtilities.getModelResourceForModelObject((EObject)targetObject); if (mr != null && mr.equals(diagramMR)) { shouldHandle = true; } } } } handleNotification = shouldHandle; } /** * @return Returns the handleNotification. * @since 4.2 */ public boolean shouldHandleNotification() { return this.handleNotification; } /** * @return Returns the reconcileSources. * @since 4.2 */ public boolean shouldReconcileSources() { return this.reconcileSources; } private void setReconcileSources() { reconcileSources = false; String diagramType = ((Diagram)diagramNode.getModelObject()).getType(); if (PluginConstants.TRANSFORMATION_DIAGRAM_TYPE_ID.equals(diagramType)) { reconcileSources = true; } else if (PluginConstants.DEPENDENCY_DIAGRAM_TYPE_ID.equals(diagramType)) { // defect 16803 - be smarter about when reconcile sources needed for // dependency diagrams Collection diagramEObjects = DiagramUiUtilities.getEObjects(diagramNode); Collection notifications = ((SourcedNotification)primaryNotification).getNotifications(); Iterator iter = notifications.iterator(); while (iter.hasNext()) { Notification nextNotification = (Notification)iter.next(); Object targetObject = ModelerCore.getModelEditor().getChangedObject(nextNotification); // check that we care about the object, and that the changed stuff // was more than just a column: if (diagramEObjects.contains(targetObject) && (isAnyContainerType(NotificationUtilities.getAddedChildren(nextNotification)) || isAnyContainerType(NotificationUtilities.getRemovedChildren(nextNotification)))) { reconcileSources = true; break; } } } } /** * Scan the EObject array to see what kind of things changed * * @param objs the array to scan * @return true if there are any BaseTables in the array. */ private static boolean isAnyContainerType( EObject[] objs ) { for (int i = 0; i < objs.length; i++) { EObject object = objs[i]; int umlType = TransformationUmlEObjectHelper.getEObjectType(object); switch (umlType) { case RelationalUmlEObjectHelper.UML_PACKAGE: case RelationalUmlEObjectHelper.UML_CLASSIFIER: case TransformationUmlEObjectHelper.MAPPING: case TransformationUmlEObjectHelper.MAPPING_CLASS: case TransformationUmlEObjectHelper.SQL_COLUMN_SET: case TransformationUmlEObjectHelper.SQL_INPUT_SET: case TransformationUmlEObjectHelper.SQL_PROCEDURE: case TransformationUmlEObjectHelper.SQL_PROCEDURE_RESULT_SET: case TransformationUmlEObjectHelper.SQL_TABLE: case TransformationUmlEObjectHelper.SQL_TRANSFORMATION: case TransformationUmlEObjectHelper.SQL_TRANSFORMATION_MAPPING_ROOT: case TransformationUmlEObjectHelper.SQL_VIRTUAL_PROCEDURE: case TransformationUmlEObjectHelper.STAGING_TABLE: case TransformationUmlEObjectHelper.TRANSFORMATION_MAPPING: case TransformationUmlEObjectHelper.TRANSFORMATION_MAPPING_ROOT: case TransformationUmlEObjectHelper.TRANSFORMATION_OBJECT: case TransformationUmlEObjectHelper.XML_DOCUMENT: // any of these types, we want to go ahead and refresh the dep. diagram return true; default: // keep scanning if not one of the above. break; } // endswitch } // endfor return false; } /** * @return Returns the reconcileSources. * @since 4.2 */ public boolean shouldRefreshDiagram() { return refreshDiagram; } private void setRefreshDiagram() { refreshDiagram = false; String diagramType = ((Diagram)diagramNode.getModelObject()).getType(); if (PluginConstants.DEPENDENCY_DIAGRAM_TYPE_ID.equals(diagramType)) { // get the eobjects in the diagram: Collection diagramEObjects = DiagramUiUtilities.getEObjects(diagramNode); // loop through all notifications: Collection notifications = ((SourcedNotification)primaryNotification).getNotifications(); Iterator iter = notifications.iterator(); while (iter.hasNext()) { Notification nextNotification = (Notification)iter.next(); // see if we care about the object affected: Object targetObject = ModelerCore.getModelEditor().getChangedObject(nextNotification); if (diagramEObjects.contains(targetObject)) { refreshDiagram = true; break; } // Check Removed if( NotificationUtilities.isRemoved(nextNotification) ) { EObject[] remChildren = NotificationUtilities.getRemovedChildren(nextNotification); for( EObject eobj : remChildren ) { if( diagramEObjects.contains(eobj) ) { refreshDiagram = true; } } } if( refreshDiagram ) { break; } } // endwhile -- all notifications } } private void setReconcileAttributes() { reconcileTargetAttributes = false; reconcileTables.clear(); // Walk through the notifications. // and determine we need to reconcile target attributes (bail on check if already set) // determine if any "source" tables need to be reconciled (don't bail on check if already set) // Add all tables to reconcileTables list Collection notifications = ((SourcedNotification)primaryNotification).getNotifications(); Iterator iter = notifications.iterator(); Notification notification = null; while (iter.hasNext()) { notification = (Notification)iter.next(); EObject targetObject = getEObjectTarget(notification); if (NotificationUtilities.isAdded(notification) || NotificationUtilities.isRemoved(notification)) { if (!reconcileTargetAttributes && targetObject == targetEObject) { reconcileTargetAttributes = true; DiagramModelNode targetNode = getPrimaryDiagramModelNode(diagramNode, targetObject); if (targetNode != null && !reconcileTables.contains(targetNode)) reconcileTables.add(targetNode); } else { // get the digram node. DiagramModelNode targetNode = getPrimaryDiagramModelNode(diagramNode, targetObject); // Check it if (canAddOrRemove(targetNode)) { if (!isNestedClassifier(targetNode)) { // We have a match, get the added children and hand them off to the generator to construct // and add to this the corresponding targetNode if (targetNode != null && !reconcileTables.contains(targetNode)) { reconcileTables.add(targetNode); } } else { // Check for nested here targetNode = getModelNode(diagramNode, targetObject); if (targetNode != null && isNestedClassifier(targetNode) && !reconcileTables.contains(targetNode)) { // We have a match, get the added children and hand them off to the generator to construct // and add to this the corresponding targetNode reconcileTables.add(targetNode); } } } } // Now we check to see if the target object is already in diagram } else if (NotificationUtilities.isChanged(notification)) { DiagramModelNode targetNode = getModelNode(diagramNode, targetObject); if (targetNode != null) { if (notification.getEventType() == Notification.MOVE) { if (!movedNodes.contains(targetNode)) movedNodes.add(targetNode); } else { if (!changedNodes.contains(targetNode)) changedNodes.add(targetNode); DiagramModelNode vgDiagramNode = getTargetDiagramNode(); if (vgDiagramNode != null && vgDiagramNode == targetNode) changedNodes.add(getTransformationDiagramNode()); } } } } } /** * @return Returns the changedNodes. * @since 4.2 */ public List getChangedNodes() { return this.changedNodes; } /** * @return Returns the changedNodes. * @since 4.2 */ public List getMovedNodes() { return this.movedNodes; } private EObject getEObjectTarget( Notification notification ) { Object targetObject = ModelerCore.getModelEditor().getChangedObject(notification); if (targetObject instanceof EObject) return (EObject)targetObject; return null; } /** * Had to create a method to perform something similar to super.getModelNode() method but insure that any selected node in a * transformation is either a classifier or a transformation. (not an extent, in other words). This was mucking up the Staging * table on detailed diagrams because it referenced the Classifier also. */ public DiagramModelNode getPrimaryDiagramModelNode( DiagramModelNode diagramModelNode, EObject someModelObject ) { if (diagramModelNode instanceof UmlClassifierNode || diagramModelNode instanceof TransformationNode) { if (diagramModelNode.getModelObject() != null && diagramModelNode.getModelObject().equals(someModelObject)) { return diagramModelNode; } } DiagramModelNode matchedNode = null; // Check the children List contents = diagramModelNode.getChildren(); if (contents != null && !contents.isEmpty()) { Iterator iter = contents.iterator(); Object nextObj = null; DiagramModelNode nextNode = null; while (iter.hasNext() && matchedNode == null) { nextObj = iter.next(); if (nextObj instanceof DiagramModelNode) { nextNode = (DiagramModelNode)nextObj; matchedNode = getPrimaryDiagramModelNode(nextNode, someModelObject); } } } return matchedNode; } public DiagramModelNode getModelNode( DiagramModelNode diagramModelNode, EObject someModelObject ) { if (diagramModelNode.getModelObject() != null && diagramModelNode.getModelObject().equals(someModelObject)) { return diagramModelNode; } DiagramModelNode matchedNode = null; // Check the children List contents = diagramModelNode.getChildren(); if (contents != null && !contents.isEmpty()) { Iterator iter = contents.iterator(); Object nextObj = null; DiagramModelNode nextNode = null; while (iter.hasNext() && matchedNode == null) { nextObj = iter.next(); if (nextObj instanceof DiagramModelNode) { nextNode = (DiagramModelNode)nextObj; matchedNode = getModelNode(nextNode, someModelObject); } } } return matchedNode; } private boolean canAddOrRemove( DiagramModelNode targetNode ) { boolean canDo = false; if (targetNode != null) { if (targetNode instanceof UmlClassifierNode) { canDo = true; } } return canDo; } private boolean isNestedClassifier( DiagramModelNode targetNode ) { if (targetNode.getParent() instanceof UmlClassifierContainerNode) return true; return false; } /** * @return Returns the reconcilable table diagram nodes (UmlClassifierNodes). * @since 4.2 */ public List getReconcileTableNodes() { return this.reconcileTables; } /** * @return Returns the diagramNode. * @since 4.2 */ public DiagramModelNode getDiagramNode() { return this.diagramNode; } public DiagramModelNode getTargetDiagramNode() { return getModelNode(diagramNode, targetEObject); } public DiagramModelNode getTransformationDiagramNode() { // walk children and look for TransformationNode type Iterator iter = diagramNode.getChildren().iterator(); DiagramModelNode nextNode = null; while (iter.hasNext()) { nextNode = (DiagramModelNode)iter.next(); if (nextNode instanceof TransformationNode) return nextNode; } return null; } }