| //------------------------------------------------------------------------------ |
| // Copyright (c) 2005, 2007 IBM Corporation and others. |
| // 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: |
| // IBM Corporation - initial implementation |
| //------------------------------------------------------------------------------ |
| //------------------------------------------------------------------------------ |
| // Copyright (c) 2005, 2007 IBM Corporation and others. |
| // 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: |
| // IBM Corporation - initial implementation |
| //------------------------------------------------------------------------------ |
| package org.eclipse.epf.diagram.core.bridge; |
| |
| 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 java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.impl.ENotificationImpl; |
| import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain; |
| import org.eclipse.epf.common.utils.StrUtil; |
| import org.eclipse.epf.diagram.model.util.TxUtil; |
| import org.eclipse.epf.library.edit.command.IActionManager; |
| import org.eclipse.epf.library.edit.process.BreakdownElementWrapperItemProvider; |
| import org.eclipse.epf.library.edit.util.TngUtil; |
| import org.eclipse.epf.uma.BreakdownElement; |
| import org.eclipse.epf.uma.DescribableElement; |
| import org.eclipse.epf.uma.MethodElement; |
| import org.eclipse.epf.uma.UmaPackage; |
| import org.eclipse.epf.uma.WorkBreakdownElement; |
| import org.eclipse.epf.uma.WorkOrder; |
| import org.eclipse.epf.uma.util.UmaUtil; |
| import org.eclipse.gmf.runtime.notation.FontStyle; |
| import org.eclipse.gmf.runtime.notation.NotationPackage; |
| import org.eclipse.gmf.runtime.notation.View; |
| import org.eclipse.uml2.uml.Activity; |
| import org.eclipse.uml2.uml.ActivityEdge; |
| import org.eclipse.uml2.uml.ActivityNode; |
| import org.eclipse.uml2.uml.ControlNode; |
| import org.eclipse.uml2.uml.UMLFactory; |
| import org.eclipse.uml2.uml.UMLPackage; |
| |
| /** |
| * @author Phong Nguyen Le |
| * |
| * @since 1.2 |
| */ |
| public class NodeAdapter extends AdapterImpl { |
| |
| protected class MethodElementAdapter extends AdapterImpl //implements INodeChangeListener |
| { |
| /** |
| * |
| * @param msg |
| * @return New nodes whose views need refresh after they have been created |
| */ |
| protected Collection handleNotification(Notification msg) { |
| switch (msg.getFeatureID(DescribableElement.class)) { |
| case UmaPackage.DESCRIBABLE_ELEMENT__PRESENTATION_NAME: |
| setName(msg.getNewStringValue()); |
| break; |
| case UmaPackage.DESCRIBABLE_ELEMENT__SHAPEICON: |
| case UmaPackage.DESCRIBABLE_ELEMENT__NODEICON: |
| // Create a new notify when we've detected that the icons may have changed, |
| // by just forcing a notify on the name, we cause the icons to get |
| // refreshed as well. |
| ActivityNode e = getNode(); |
| e.eNotify(new ENotificationImpl((InternalEObject) e, Notification.SET, |
| UMLPackage.NAMED_ELEMENT__NAME, e.getName(), e.getName(), true)); |
| e.eNotify(msg); |
| break; |
| } |
| return Collections.EMPTY_LIST; |
| } |
| |
| public void notifyChanged(final Notification msg) { |
| if(domain == null || domain.getChangeRecorder() == null) { |
| return; |
| } |
| |
| if (!notificationEnabled) |
| return; |
| notificationEnabled = false; |
| try { |
| final Collection<ActivityNode> newNodesToRefresh = new ArrayList<ActivityNode>(); |
| TxUtil.runInTransaction(domain, new Runnable() { |
| |
| public void run() { |
| newNodesToRefresh.addAll(handleNotification(msg)); |
| } |
| |
| }); |
| if(!newNodesToRefresh.isEmpty()) { |
| TxUtil.runInTransaction(domain, new Runnable() { |
| |
| public void run() { |
| for (ActivityNode node : newNodesToRefresh) { |
| BridgeHelper.getNodeAdapter(node).updateView(); |
| } |
| } |
| |
| }); |
| } |
| } |
| catch(Exception e) { |
| e.printStackTrace(); |
| } finally { |
| notificationEnabled = true; |
| } |
| } |
| |
| public NodeAdapter getNodeAdapter() { |
| return NodeAdapter.this; |
| } |
| |
| } |
| |
| protected MethodElement element; |
| protected boolean notificationEnabled = true; |
| private Collection consumers = new ArrayList(); |
| protected MethodElementAdapter methodElementAdapter; |
| private boolean targetReadOnly; |
| protected BreakdownElementWrapperItemProvider wrapper; |
| protected InternalTransactionalEditingDomain domain; |
| private View view; |
| protected IActionManager actionManager; |
| |
| protected NodeAdapter() { |
| |
| } |
| |
| public IActionManager getActionManager() { |
| if(actionManager != null) { |
| return actionManager; |
| } |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(getNode().getActivity()); |
| return nodeAdapter.getActionManager(); |
| } |
| |
| public NodeAdapter(BreakdownElementWrapperItemProvider wrapper) { |
| this((MethodElement) TngUtil.unwrap(wrapper)); |
| this.wrapper = wrapper; |
| targetReadOnly = wrapper.isReadOnly(); |
| } |
| |
| public NodeAdapter(MethodElement e) { |
| element = e; |
| methodElementAdapter = createMethodElementAdapter(); |
| e.eAdapters().add(methodElementAdapter); |
| } |
| |
| public View getView() { |
| if(view != null) { |
| return view; |
| } |
| View diagramView = getDiagramView(); |
| if(diagramView != null) { |
| return BridgeHelper.getView(diagramView, getNode()); |
| } |
| return null; |
| } |
| |
| private View getDiagramView() { |
| Activity diagram = getDiagram(); |
| if(diagram != null) { |
| NodeAdapter adapter = BridgeHelper.getNodeAdapter(diagram); |
| if(adapter != null) { |
| return adapter.getView(); |
| } |
| } |
| return null; |
| } |
| |
| public void setView(View view) { |
| this.view = view; |
| } |
| |
| public void setEditingDomain(InternalTransactionalEditingDomain domain) { |
| this.domain = domain; |
| } |
| |
| public boolean isTargetReadOnly() { |
| if(targetReadOnly) { |
| return targetReadOnly; |
| } |
| Activity activity = getDiagram(); |
| if(activity != target) { |
| NodeAdapter diagramAdapter = BridgeHelper.getNodeAdapter(activity); |
| if(diagramAdapter != null && diagramAdapter.isTargetReadOnly()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected void basicSetTargetReadOnly(boolean b) { |
| targetReadOnly = b; |
| } |
| |
| protected void setTargetReadOnly(boolean targetReadOnly) { |
| boolean updateView = this.targetReadOnly != targetReadOnly; |
| this.targetReadOnly = targetReadOnly; |
| if(updateView) { |
| updateView(); |
| } |
| } |
| |
| protected void updateView() { |
| View node = getView(); |
| if(node != null) { |
| // node.setMutable(!isTargetReadOnly()); |
| FontStyle style = (FontStyle) node.getStyle(NotationPackage.Literals.FONT_STYLE); |
| // if(style != null) { |
| // Color color = isTargetReadOnly() ? DiagramConstants.READ_ONLY_FONT_COLOR : DiagramConstants.DEFAULT_FONT_COLOR; |
| // style.setFontColor(FigureUtilities.RGBToInteger(color.getRGB()).intValue()); |
| // } |
| if(style == null) { |
| node.createStyle(NotationPackage.Literals.FONT_STYLE); |
| } |
| } |
| } |
| |
| protected MethodElementAdapter createMethodElementAdapter() { |
| return new MethodElementAdapter(); |
| } |
| |
| protected ActivityNode getNode() { |
| if(target instanceof ActivityNode) { |
| return (ActivityNode) target; |
| } |
| return null; |
| } |
| |
| protected Activity getDiagram() { |
| if(target instanceof Activity) { |
| return (Activity) target; |
| } |
| ActivityNode node = getNode(); |
| return node != null ? node.getActivity() : null; |
| } |
| |
| protected ActivityEdge addIncomingConnection(MethodElement source) { |
| ActivityNode srcNode = BridgeHelper.findNode(getDiagram(), source, true); |
| if (srcNode == null) |
| return null; |
| |
| // disable notification of srcNode before associate it with the link |
| // so it will not create duplicate UMA data |
| // |
| NodeAdapter srcNodeAdapter = BridgeHelper.getNodeAdapter(srcNode); |
| boolean notify = srcNodeAdapter != null ? srcNodeAdapter.notificationEnabled : false; |
| try { |
| if(srcNodeAdapter != null) srcNodeAdapter.notificationEnabled = false; |
| ActivityEdge edge = UMLFactory.eINSTANCE.createControlFlow(); |
| edge.setSource(srcNode); |
| edge.setTarget(getNode()); |
| getDiagram().getEdges().add(edge); |
| return edge; |
| } finally { |
| if(srcNodeAdapter != null) srcNodeAdapter.notificationEnabled = notify; |
| } |
| } |
| |
| protected ActivityEdge addOutgoingConnection(MethodElement target) { |
| ActivityNode targetNode = BridgeHelper.findNode(getDiagram(), target); |
| if (targetNode == null) |
| return null; |
| |
| // disable notification of targetNode before associate it with the link |
| // so it will not create duplicate UMA data |
| // |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(targetNode); |
| boolean notify = nodeAdapter != null ? nodeAdapter.notificationEnabled : false; |
| try { |
| if(nodeAdapter != null) nodeAdapter.notificationEnabled = false; |
| ActivityEdge edge = getDiagram().createEdge("", UMLPackage.Literals.CONTROL_FLOW); //$NON-NLS-1$ |
| edge.setSource(getNode()); |
| edge.setTarget(targetNode); |
| return edge; |
| } finally { |
| if(nodeAdapter != null) nodeAdapter.notificationEnabled = notify; |
| } |
| } |
| |
| /** |
| * Adds the given consumer to the consumer list of this node. |
| * |
| * @param consumer |
| */ |
| public void addConsumer(Object consumer) { |
| if (!consumers.contains(consumer)) { |
| consumers.add(consumer); |
| } |
| } |
| |
| /** |
| * Removes the given consumer from the consumer list of this node. Disposes |
| * the node if it does not have any more consumer after this call. Disposing |
| * a node will take care of removing this node's listener from the UMA |
| * object and all the adapters that had been added to the adapter list of |
| * this node. |
| * |
| * @param consumer |
| */ |
| public void removeConsumer(Object consumer) { |
| consumers.remove(consumer); |
| if (consumers.isEmpty()) { |
| // no more consumer of this node, it should be disposed |
| dispose(); |
| } |
| } |
| |
| public void dispose() { |
| // set domain to null before removing methodElementAdapter from the |
| // method element so methodElement can skip handling REMOVE_ADAPTER event |
| // that sometimes requires new transaction to be created and causes dead-lock |
| // |
| domain = null; |
| EObject obj = element; |
| if (obj != null) { |
| if (methodElementAdapter != null) { |
| obj.eAdapters().remove(methodElementAdapter); |
| } |
| } |
| if(target != null) { |
| target.eAdapters().remove(this); |
| } |
| |
| element = null; |
| } |
| |
| public void setName(String newStringValue) { |
| ActivityNode e = getNode(); |
| if(e != null) { |
| e.setName(newStringValue); |
| } |
| } |
| |
| protected void handleNotification(Notification msg) { |
| switch(msg.getFeatureID(ActivityNode.class)) { |
| case UMLPackage.ACTIVITY_NODE__NAME: |
| if (msg.getEventType() == Notification.SET |
| && getElement() instanceof BreakdownElement) { |
| String newName = msg.getNewStringValue(); |
| BreakdownElement e = ((BreakdownElement) getElement()); |
| e.setPresentationName(newName); |
| if (StrUtil.isBlank(e.getName())) { |
| e.setName(newName); |
| } |
| } |
| return; |
| case UMLPackage.ACTIVITY_NODE__INCOMING: // incoming connections |
| ActivityEdge link; |
| switch (msg.getEventType()) { |
| case Notification.ADD: |
| link = (ActivityEdge) msg.getNewValue(); |
| addToUMA(link); |
| return; |
| case Notification.ADD_MANY: |
| for (Iterator iter = ((Collection) msg.getNewValue()) |
| .iterator(); iter.hasNext();) { |
| addToUMA((ActivityEdge) iter.next()); |
| } |
| break; |
| case Notification.REMOVE: |
| link = (ActivityEdge) msg.getOldValue(); |
| if (link.getSource() != null) { |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(link.getSource()); |
| if(nodeAdapter != null) { |
| nodeAdapter.removeFromUMA(link, link |
| .getSource(), (ActivityNode) msg.getNotifier()); |
| } |
| } |
| break; |
| case Notification.REMOVE_MANY: |
| for (Iterator iter = ((Collection) msg.getOldValue()) |
| .iterator(); iter.hasNext();) { |
| link = (ActivityEdge) iter.next(); |
| if (link.getSource() != null) { |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(link.getSource()); |
| if(nodeAdapter != null) { |
| nodeAdapter.removeFromUMA(link, |
| link.getSource(), (ActivityNode) msg.getNotifier()); |
| } |
| } |
| } |
| break; |
| } |
| return; |
| case UMLPackage.ACTIVITY_NODE__OUTGOING: |
| switch (msg.getEventType()) { |
| case Notification.ADD: |
| link = (ActivityEdge) msg.getNewValue(); |
| if (link.getTarget() != null) { |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(link.getTarget()); |
| if(nodeAdapter != null) { |
| nodeAdapter.addToUMA(link); |
| } |
| } |
| break; |
| case Notification.ADD_MANY: |
| for (Iterator iter = ((Collection) msg.getNewValue()) |
| .iterator(); iter.hasNext();) { |
| link = (ActivityEdge) iter.next(); |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(link.getTarget()); |
| if(nodeAdapter != null) { |
| nodeAdapter.addToUMA(link); |
| } |
| } |
| break; |
| case Notification.REMOVE: |
| link = (ActivityEdge) msg.getOldValue(); |
| if (link.getTarget() != null) { |
| removeFromUMA(link, (ActivityNode) msg.getNotifier(), link |
| .getTarget()); |
| } |
| break; |
| case Notification.REMOVE_MANY: |
| for (Iterator iter = ((Collection) msg.getOldValue()) |
| .iterator(); iter.hasNext();) { |
| link = (ActivityEdge) iter.next(); |
| if (link.getTarget() != null) { |
| removeFromUMA(link, (ActivityNode) msg.getNotifier(), link |
| .getTarget()); |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification) |
| */ |
| public void notifyChanged(Notification msg) { |
| if (!notificationEnabled) |
| return; |
| notificationEnabled = false; |
| try { |
| handleNotification(msg); |
| } finally { |
| notificationEnabled = true; |
| } |
| } |
| |
| /** |
| * Adds the data for the newly added node to the UMA model Subclass should |
| * override this method. |
| * |
| * @param position |
| * @param newValue |
| */ |
| protected void addToUmaModel(int position, ActivityNode node) { |
| //TODO: review |
| // if (addedNode.getGraphNode() == null) { |
| // // this node is readded after undo |
| // // |
| // ((NodeImpl) addedNode).basicSetObject(addedNode.getObject()); |
| // } |
| } |
| |
| protected void nodeAdded(int index, ActivityNode node) { |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| if(nodeAdapter == null) { |
| nodeAdapter = addNodeAdapterTo(node); |
| } |
| addToUmaModel(index, node); |
| if(nodeAdapter != null) { |
| nodeAdapter.addConsumer(this); |
| } |
| } |
| |
| protected NodeAdapter addNodeAdapterTo(ActivityNode node) { |
| return null; |
| } |
| |
| /** |
| * Removes the data for the removed node from the UMA model Subclass should |
| * override this method. |
| * |
| * @param node |
| */ |
| protected void removeFromUmaModel(ActivityNode node) { |
| } |
| |
| protected void nodeRemoved(ActivityNode node) { |
| removeFromUmaModel(node); |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| if(nodeAdapter != null) { |
| nodeAdapter.removeConsumer(this); |
| } |
| } |
| |
| protected boolean addToUMA(ActivityEdge link) { |
| if (link.getTarget() == null || link.getSource() == null) |
| return false; |
| return true; |
| } |
| |
| protected void removeFromUMA(ActivityEdge link, ActivityNode oldSource, ActivityNode oldTarget) { |
| MethodElement targetElement = BridgeHelper.getMethodElement(oldTarget); |
| if (targetElement instanceof WorkBreakdownElement) { |
| // this is a direct link |
| // remove WorkOrder |
| // |
| NodeAdapter targetNodeAdapter = BridgeHelper.getNodeAdapter(oldTarget); |
| boolean notify = targetNodeAdapter != null ? targetNodeAdapter.notificationEnabled : false; |
| try { |
| if(targetNodeAdapter != null) { |
| targetNodeAdapter.notificationEnabled = false; |
| } |
| if (BridgeHelper.canRemoveAllPreds(link, oldSource, |
| oldTarget)) { |
| Object pred = BridgeHelper.getMethodElement(oldSource); |
| if(pred instanceof WorkBreakdownElement) { |
| WorkBreakdownElement e = (WorkBreakdownElement) BridgeHelper.getMethodElement(oldTarget); |
| Collection<WorkOrder> workOrders = UmaUtil.findWorkOrder(e, (WorkBreakdownElement) pred, true); |
| if(!workOrders.isEmpty()) { |
| getActionManager().doAction(IActionManager.REMOVE_MANY, e, |
| UmaPackage.Literals.WORK_BREAKDOWN_ELEMENT__LINK_TO_PREDECESSOR, workOrders, -1); |
| } |
| } |
| } |
| } finally { |
| if(targetNodeAdapter != null) { |
| targetNodeAdapter.notificationEnabled = notify; |
| } |
| } |
| } else if(oldTarget instanceof ControlNode && BridgeHelper.isSynchBar(oldTarget)) { |
| // get all the WorkBreakdownElementNodes that this synch bar is |
| // coming to and |
| // remove the WorkOrders with this WorkBreakdownElementNode's |
| // activity as predecessor from them |
| // |
| Collection<ActivityNode> actNodes = new ArrayList<ActivityNode>(); |
| BridgeHelper.getSuccessorNodes(actNodes, oldTarget); |
| for (ActivityNode node : actNodes) { |
| WorkBreakdownElement e = (WorkBreakdownElement) BridgeHelper.getMethodElement(node); |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| boolean notify = nodeAdapter != null ? nodeAdapter.notificationEnabled : false; |
| try { |
| if(nodeAdapter != null) { |
| nodeAdapter.notificationEnabled = false; |
| } |
| if (BridgeHelper.canRemoveAllPreds(link, oldSource, node)) { |
| WorkOrder wo; |
| while ((wo = UmaUtil.findWorkOrder(e, BridgeHelper.getMethodElement(oldSource))) != null) { |
| getActionManager().doAction(IActionManager.REMOVE, e, |
| UmaPackage.Literals.WORK_BREAKDOWN_ELEMENT__LINK_TO_PREDECESSOR, wo, -1); |
| } |
| } |
| } finally { |
| if(nodeAdapter != null) { |
| nodeAdapter.notificationEnabled = notify; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * addDefaultWorkOrder is moved from |
| * GraphicalDataHelper.addDefaultWorkOrder(NamedNodeImpl, |
| * WorkbreakdownElement) Method will set notificationEnabled flag to false |
| * of all adapters of type MethodElementAdapter of Successor object before |
| * creating a WorkOrder object. After creation of WorkOrder (Predecessor |
| * link) restores the notificationEnabled flag of Successor's adapters of |
| * type MethodElementAdapter. |
| * |
| * eg: If an Activity "actA" is extended in another CP. Base "actA" AD |
| * diagram is opened, and Extended "actA" AD Diagram is opened. On creating |
| * predecessor link between any two child activities, creates a extra link |
| * in Extended "actA" AD diagram, because creating workorder will notify the |
| * extended "actA" diagram to draw a link. To avoid duplicate links in the |
| * extended activity diagram, we have to set notificationEnabled flag to |
| * false, before creating a WorkOrder object. |
| * |
| */ |
| public WorkOrder addDefaultWorkOrder(ActivityNode node, |
| WorkBreakdownElement predBreakdownElement) { |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| boolean notify = nodeAdapter.notificationEnabled; |
| Map map = new HashMap(); |
| MethodElement e = nodeAdapter.getElement(); |
| List list = e.eAdapters(); |
| for (Iterator iterator = list.iterator(); iterator.hasNext();) { |
| Object obj = iterator.next(); |
| if (obj instanceof MethodElementAdapter) { |
| NodeAdapter otherNodeAdapter = (NodeAdapter) ((MethodElementAdapter) obj) |
| .getNodeAdapter(); |
| boolean predNotification = nodeAdapter.notificationEnabled; |
| otherNodeAdapter.notificationEnabled = false; |
| map.put(otherNodeAdapter, new Boolean(predNotification)); |
| } |
| } |
| try { |
| nodeAdapter.notificationEnabled = false; |
| WorkOrder wo = UmaUtil.createDefaultWorkOrder((WorkBreakdownElement) e, predBreakdownElement, false); |
| getActionManager().doAction(IActionManager.ADD, e, |
| UmaPackage.Literals.WORK_BREAKDOWN_ELEMENT__LINK_TO_PREDECESSOR, wo, -1); |
| return wo; |
| } finally { |
| nodeAdapter.setNotificationEnabled(notify); |
| Set set = map.keySet(); |
| for (Iterator iter = set.iterator(); iter.hasNext();) { |
| Object object = iter.next(); |
| Object obj = map.get(object); |
| if (obj instanceof Boolean) { |
| boolean prednot = ((Boolean) obj).booleanValue(); |
| ((NodeAdapter) object).notificationEnabled = prednot; |
| } |
| } |
| } |
| } |
| |
| public void setNotificationEnabled(boolean notify) { |
| notificationEnabled = notify; |
| } |
| |
| public boolean isNotificationEnabled() { |
| return notificationEnabled; |
| } |
| |
| public MethodElement getElement() { |
| return element; |
| } |
| |
| public void setTarget(Notifier newTarget) { |
| if(newTarget == null) { |
| dispose(); |
| } |
| super.setTarget(newTarget); |
| } |
| |
| public BreakdownElementWrapperItemProvider getWrapper() { |
| return wrapper; |
| } |
| } |