| //------------------------------------------------------------------------------ |
| // 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.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.edit.provider.ITreeItemContentProvider; |
| import org.eclipse.emf.transaction.RollbackException; |
| import org.eclipse.epf.diagram.core.DiagramCorePlugin; |
| import org.eclipse.epf.diagram.model.util.TxUtil; |
| import org.eclipse.epf.library.edit.IFilter; |
| import org.eclipse.epf.library.edit.command.ActionManager; |
| import org.eclipse.epf.library.edit.command.IActionManager; |
| import org.eclipse.epf.library.edit.process.BSActivityItemProvider; |
| import org.eclipse.epf.library.edit.process.BreakdownElementWrapperItemProvider; |
| import org.eclipse.epf.library.edit.process.IBSItemProvider; |
| import org.eclipse.epf.library.edit.util.DescriptorPropUtil; |
| import org.eclipse.epf.library.edit.util.ProcessUtil; |
| import org.eclipse.epf.library.edit.util.Suppression; |
| import org.eclipse.epf.library.edit.util.TngUtil; |
| import org.eclipse.epf.uma.Activity; |
| import org.eclipse.epf.uma.BreakdownElement; |
| import org.eclipse.epf.uma.MethodElement; |
| import org.eclipse.epf.uma.TaskDescriptor; |
| import org.eclipse.epf.uma.UmaPackage; |
| import org.eclipse.gmf.runtime.notation.Diagram; |
| import org.eclipse.gmf.runtime.notation.Edge; |
| import org.eclipse.gmf.runtime.notation.View; |
| import org.eclipse.uml2.uml.ActivityEdge; |
| import org.eclipse.uml2.uml.ActivityNode; |
| import org.eclipse.uml2.uml.ActivityPartition; |
| import org.eclipse.uml2.uml.UMLPackage; |
| |
| /** |
| * @author Phong Nguyen Le |
| * |
| * @since 1.2 |
| */ |
| public class DiagramAdapter extends NodeAdapter { |
| |
| private Activity baseAct; |
| |
| protected IFilter filter; |
| |
| private Suppression suppression; |
| |
| protected long umaLastModified; |
| |
| |
| public DiagramAdapter(BreakdownElementWrapperItemProvider wrapper) { |
| this((Activity) TngUtil.unwrap(wrapper)); |
| this.wrapper = wrapper; |
| basicSetTargetReadOnly(wrapper.isReadOnly()); |
| } |
| |
| public DiagramAdapter(Activity e) { |
| super(e); |
| |
| // listens to change in the base activity if there is any |
| // |
| if (ProcessUtil.isExtendingOrLocallyContributing(e)) { |
| baseAct = (Activity) e.getVariabilityBasedOnElement(); |
| baseAct.eAdapters().add(methodElementAdapter); |
| } |
| } |
| |
| public void dispose() { |
| // dispose all node adapters of the child nodes |
| // |
| for (Iterator<?> iter = getNodes().iterator(); iter.hasNext();) { |
| ActivityNode node = (ActivityNode) iter.next(); |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| if (nodeAdapter != null) { |
| nodeAdapter.dispose(); |
| } |
| } |
| |
| super.dispose(); |
| |
| if (baseAct != null) { |
| baseAct.eAdapters().remove(methodElementAdapter); |
| } |
| |
| } |
| |
| protected class ActivityAdapter extends MethodElementAdapter { |
| protected Collection handleNotification(Notification msg) { |
| Collection newNodesToRefresh = new ArrayList(); |
| switch (msg.getFeatureID(Activity.class)) { |
| case UmaPackage.ACTIVITY__BREAKDOWN_ELEMENTS: |
| switch (msg.getEventType()) { |
| case Notification.ADD: |
| if (msg.getNewValue() instanceof TaskDescriptor) { |
| TaskDescriptor td = (TaskDescriptor) msg.getNewValue(); |
| if (DescriptorPropUtil.getDesciptorPropUtil() |
| .getGreenParentDescriptor(td) != null) { |
| break; |
| } |
| } |
| ActivityNode node = (ActivityNode) addNode(msg |
| .getNewValue()); |
| if (node != null) { |
| if (msg.getNotifier() == baseAct) { |
| BridgeHelper.getNodeAdapter(node) |
| .basicSetTargetReadOnly(true); |
| newNodesToRefresh.add(node); |
| } |
| } |
| break; |
| case Notification.REMOVE: |
| removeNode(msg.getOldValue()); |
| break; |
| case Notification.ADD_MANY: |
| Collection nodes = addNodes((Collection) msg.getNewValue()); |
| if (msg.getNotifier() == baseAct) { |
| for (Iterator iter = nodes.iterator(); iter.hasNext();) { |
| node = (ActivityNode) iter.next(); |
| BridgeHelper.getNodeAdapter(node) |
| .basicSetTargetReadOnly(true); |
| newNodesToRefresh.add(node); |
| } |
| } |
| break; |
| case Notification.REMOVE_MANY: |
| removeNodes((Collection) msg.getOldValue()); |
| break; |
| case Notification.MOVE: |
| moveNode(msg.getNewValue()); |
| break; |
| case Notification.SET: |
| replaceNode(msg.getOldValue(), msg.getNewValue()); |
| break; |
| } |
| break; |
| default: |
| return super.handleNotification(msg); |
| } |
| return newNodesToRefresh; |
| } |
| } |
| |
| protected void handleNotification(Notification msg) { |
| switch(msg.getFeatureID(org.eclipse.uml2.uml.Activity.class)) { |
| case UMLPackage.ACTIVITY__NODE: |
| Collection collection; |
| switch (msg.getEventType()) { |
| case Notification.ADD: |
| nodeAdded(msg.getPosition(), (ActivityNode) msg |
| .getNewValue()); |
| return; |
| case Notification.REMOVE: |
| nodeRemoved((ActivityNode) msg.getOldValue()); |
| return; |
| case Notification.ADD_MANY: |
| collection = (Collection) msg.getNewValue(); |
| for (Iterator iter = collection.iterator(); iter |
| .hasNext();) { |
| ActivityNode node = (ActivityNode) iter.next(); |
| nodeAdded(msg.getPosition(), node); |
| } |
| |
| return; |
| case Notification.REMOVE_MANY: |
| collection = (Collection) msg.getOldValue(); |
| for (Iterator iter = collection.iterator(); iter |
| .hasNext();) { |
| nodeRemoved((ActivityNode) iter.next()); |
| } |
| return; |
| } |
| } |
| |
| super.handleNotification(msg); |
| } |
| |
| protected void replaceNode(Object oldElement, Object newElement) { |
| |
| } |
| |
| protected MethodElementAdapter createMethodElementAdapter() { |
| return new ActivityAdapter(); |
| } |
| |
| protected final void populateLinks() { |
| populateLinks(getNodes()); |
| } |
| |
| protected void populateLinks(List<ActivityNode> selectedNodes) { |
| // fill outgoing/incoming connection lists of all nodes |
| // |
| int size = selectedNodes.size(); |
| boolean[] notifies = new boolean[size]; |
| try { |
| // disable notification for all nodes in this diagram to avoid |
| // unwanted concurrent modification of their connection list |
| // |
| for (int i = 0; i < size; i++) { |
| ActivityNode node = selectedNodes.get(i); |
| notifies[i] = node.eDeliver(); |
| node.eSetDeliver(false); |
| } |
| for (ActivityNode node : selectedNodes) { |
| populateLinks(node, false); |
| } |
| } finally { |
| // restore notification flag |
| // |
| for (int i = 0; i < size; i++) { |
| selectedNodes.get(i).eSetDeliver(notifies[i]); |
| } |
| } |
| } |
| |
| protected List getNodes() { |
| org.eclipse.uml2.uml.Activity diagram = getDiagram(); |
| return diagram != null ? getDiagram().getNodes() : Collections.emptyList(); |
| } |
| |
| protected org.eclipse.uml2.uml.Activity getDiagram() { |
| return (org.eclipse.uml2.uml.Activity) getTarget(); |
| } |
| |
| protected boolean removeNode(Object obj) { |
| if (!TngUtil.isInstanceOf(getBreakdownElementTypes(), obj)) |
| return false; |
| |
| ActivityNode node = BridgeHelper.findNode(getDiagram(), obj); |
| if (node == null) |
| return false; |
| |
| for (Iterator iter = node.getOutgoings().iterator(); iter.hasNext();) { |
| ActivityEdge link = (ActivityEdge) iter.next(); |
| link.setTarget(null); |
| } |
| |
| for (Iterator iter = node.getIncomings().iterator(); iter.hasNext();) { |
| ActivityEdge link = (ActivityEdge) iter.next(); |
| link.setSource(null); |
| } |
| node.getOutgoings().clear(); |
| node.getIncomings().clear(); |
| |
| getNodes().remove(node); |
| return true; |
| } |
| |
| protected void removeNodes(Collection collection) { |
| for (Iterator iter = collection.iterator(); iter.hasNext();) { |
| removeNode(iter.next()); |
| } |
| } |
| |
| protected ActivityNode addNode(Object obj) { |
| ActivityNode node = addNode(getNodes(), obj); |
| if (node == null) |
| return node; |
| populateLinks(node, true); |
| return node; |
| } |
| |
| protected Collection addNodes(Collection collection) { |
| List nodes = new ArrayList(); |
| for (Iterator iter = collection.iterator(); iter.hasNext();) { |
| addNode(nodes, iter.next()); |
| } |
| |
| // use addAll() to avoid unnecessary notifications |
| // |
| getNodes().addAll(nodes); |
| return nodes; |
| } |
| |
| /** |
| * In Process WBS, if breakdownelement is moved up or down, diagram should |
| * be updated accordingly. Sub-class should override this method if diagram |
| * needs update on move. |
| * |
| * @param newValue |
| */ |
| public void moveNode(Object oldValue) { |
| } |
| |
| /** |
| * Populates the incoming/outgoing links of the given node |
| * |
| * @param node |
| */ |
| protected void populateLinks(ActivityNode node, boolean disableNotification) { |
| // int size = 0; |
| // boolean[] notifies = null; |
| // try { |
| // if (disableNotification) { |
| // size = getNodes().size(); |
| // notifies = new boolean[size]; |
| // // disable notification for all nodes in this diagram to avoid |
| // // unwanted concurrent modification of their connection list |
| // // |
| // for (int i = 0; i < size; i++) { |
| // Node child = ((Node) getNodes().get(i)); |
| // notifies[i] = child.eDeliver(); |
| // child.eSetDeliver(false); |
| // } |
| // } |
| // |
| // GraphNode graphNode = node.getGraphNode(); |
| // if (graphNode != null) { |
| // GraphicalDataHelper.fillConnections(node, graphNode); |
| // } |
| // } finally { |
| // if (disableNotification) { |
| // // restore notification flag |
| // // |
| // for (int i = 0; i < size; i++) { |
| // ((EObject) getNodes().get(i)).eSetDeliver(notifies[i]); |
| // } |
| // } |
| // } |
| } |
| |
| /** |
| * Creates new node for this diagram for the given MethodElement. |
| * |
| * @param e |
| * @return |
| */ |
| protected ActivityNode toNode(MethodElement e) { |
| ActivityNode node = newNode(e); |
| if (node == null) |
| return null; |
| initializeNode(node, e); |
| return node; |
| } |
| |
| protected void initializeNodeAdapter(NodeAdapter nodeAdapter) { |
| nodeAdapter.setEditingDomain(domain); |
| nodeAdapter.actionManager = actionManager; |
| } |
| |
| private void initializeNode(ActivityNode node, MethodElement e) { |
| String name = BridgeHelper.getNodeName(e); |
| node.setName(name); |
| |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| if (nodeAdapter != null && nodeAdapter.getElement() != e) { |
| nodeAdapter.dispose(); |
| nodeAdapter = null; |
| } |
| if (nodeAdapter == null) { |
| nodeAdapter = createNodeAdapter(e); |
| initializeNodeAdapter(nodeAdapter); |
| node.eAdapters().add(nodeAdapter); |
| } |
| } |
| |
| protected NodeAdapter createNodeAdapter(MethodElement e) { |
| return null; |
| } |
| |
| protected ActivityNode newNode(MethodElement e) { |
| return null; |
| } |
| |
| /** |
| * Creates a new node for the given method element <code>obj</code> and |
| * add it to the given collection of nodes |
| * |
| * @param nodes |
| * @param obj |
| * method element |
| * @return |
| */ |
| protected ActivityNode addNode(Collection nodes, Object obj) { |
| if (TngUtil.isInstanceOf(getBreakdownElementTypes(), obj)) { |
| ActivityNode node = toNode((MethodElement) obj); |
| if (node != null) { |
| nodes.add(node); |
| return node; |
| } |
| } |
| return null; |
| } |
| |
| protected List getBreakdownElementTypes() { |
| return Collections.singletonList(BreakdownElement.class); |
| } |
| |
| public Activity getActivity() { |
| return (Activity) element; |
| } |
| |
| protected void removeFromUmaModel(ActivityNode removedNode) { |
| Object e; |
| if ((e = BridgeHelper.getMethodElement(removedNode)) instanceof BreakdownElement) { |
| getActionManager().doAction(IActionManager.REMOVE, getActivity(), |
| UmaPackage.Literals.ACTIVITY__BREAKDOWN_ELEMENTS, e, -1); |
| } |
| |
| super.removeFromUmaModel(removedNode); |
| } |
| |
| protected void extractChildren(ITreeItemContentProvider adapter, Object object, Collection children) { |
| // disable rollup before getting the children |
| // |
| boolean oldRolledUp = false; |
| if(adapter instanceof BSActivityItemProvider) { |
| BSActivityItemProvider itemProvider = (BSActivityItemProvider)adapter; |
| oldRolledUp = itemProvider.isRolledUp(); |
| itemProvider.basicSetRolledUp(false); |
| } |
| else if(adapter instanceof IBSItemProvider){ |
| IBSItemProvider itemProvider = (IBSItemProvider)adapter; |
| oldRolledUp = itemProvider.isRolledUp(); |
| itemProvider.setRolledUp(false); |
| } |
| |
| try { |
| // filter out the suppressed elements |
| // |
| for (Iterator iter = adapter.getChildren(object).iterator(); iter.hasNext();) { |
| Object child = iter.next(); |
| if(!getSuppression().isSuppressed(child)) { |
| children.add(child); |
| } |
| } |
| |
| // don't filter suppressed elements |
| // |
| //children.addAll(adapter.getChildren(object)); |
| } |
| finally { |
| // restore the rolled-up flag |
| // |
| if(adapter instanceof IBSItemProvider) { |
| ((IBSItemProvider)adapter).setRolledUp(oldRolledUp); |
| } |
| } |
| } |
| |
| public Suppression getSuppression() { |
| return suppression; |
| } |
| |
| public void setSuppression(Suppression suppression) { |
| this.suppression = suppression; |
| } |
| |
| protected void updateView(Collection<?> selectedNodes) throws InterruptedException, RollbackException { |
| updateView(getView(), selectedNodes); |
| } |
| |
| private static boolean isWorkBreakdownElementType(View node) { |
| // hack by using the visual IDs defined in AD model |
| // StructuredActivityNodeEditPart 1007 Activity |
| // StructuredActivityNode2EditPart 1010 Phase |
| // StructuredActivityNode3EditPart 1011 Iteration |
| // ActivityParameterNodeEditPart 1009 TaskDescriptor |
| // ActivityParameterNode2EditPart 1012 Milestone |
| // |
| |
| String type = node.getType(); |
| return type != null && ("1007".equals(type) //$NON-NLS-1$ |
| || "1010".equals(type) //$NON-NLS-1$ |
| || "1011".equals(type) //$NON-NLS-1$ |
| || "1009".equals(type) //$NON-NLS-1$ |
| || "1012".equals(type)); //$NON-NLS-1$ |
| } |
| |
| private static void updateView(View view, Collection<?> selectedNodes) { |
| // show the selected nodes and hide all the other |
| // |
| for (Iterator<?> iter = view.getChildren().iterator(); iter.hasNext();) { |
| View node = (View) iter.next(); |
| if(selectedNodes.contains(node.getElement())) { |
| NodeAdapter adapter = BridgeHelper.getNodeAdapter(node.getElement()); |
| if(adapter != null) { |
| adapter.updateView(); |
| } |
| // if(!node.isVisible()) { |
| node.setVisible(true); |
| // } |
| } |
| else { |
| if(node.getElement() instanceof ActivityNode) { |
| if(node.isVisible()) { |
| node.setVisible(false); |
| } |
| } |
| else if(node.getElement() instanceof ActivityPartition) { |
| updateView(node, selectedNodes); |
| } |
| // this is a work around to not show any work breakdown element |
| // node that does not have any model reference (View.element) |
| // GMF returns the container view's element if the child node's element |
| // is not set. Therefore, if the child node is shown, it will be displayed |
| // as a node of parent activity. Deleting this node in editor will delete |
| // the parent activity as result. |
| // |
| else if(isWorkBreakdownElementType(node) && (!node.isSetElement() || node.getElement() == view.getElement())) { |
| if(node.isVisible()) { |
| node.setVisible(false); |
| } |
| } |
| else { |
| if(!node.isVisible()) { |
| node.setVisible(true); |
| } |
| } |
| } |
| } |
| } |
| |
| protected void updateEdges(Collection selectedNodes) throws InterruptedException, RollbackException { |
| for (Iterator iter = getView().getChildren().iterator(); iter.hasNext();) { |
| View node = (View) iter.next(); |
| if(selectedNodes.contains(node.getElement())) { |
| NodeAdapter adapter = BridgeHelper.getNodeAdapter(node.getElement()); |
| adapter.updateView(); |
| // if(!node.isVisible()) { |
| node.setVisible(true); |
| setEdgeVisibility(node, true); |
| // } |
| } |
| else { |
| if(node.getElement() instanceof ActivityNode) { |
| if(node.isVisible()) { |
| setEdgeVisibility(node, false); |
| } |
| } |
| else { |
| if(!node.isVisible()) { |
| node.setVisible(true); |
| setEdgeVisibility(node, true); |
| } |
| } |
| } |
| } |
| } |
| |
| private void setEdgeVisibility(View view, boolean visibility){ |
| Diagram diagram = view.getDiagram(); |
| for (Iterator iterator = diagram.getEdges().iterator(); iterator |
| .hasNext();) { |
| Edge edge = (Edge) iterator.next(); |
| if(edge.getSource() == view || edge.getTarget() == view){ |
| if(visibility){ |
| view.setVisible(true); |
| } |
| edge.setVisible(visibility); |
| } |
| } |
| } |
| /** |
| * Populates the diagram with the data from the UMA model. Subclass should |
| * override this method. |
| */ |
| public void populateDiagram() { |
| boolean notify = notificationEnabled; |
| try { |
| notificationEnabled = false; |
| final List<ActivityNode> selectedNodes = new ArrayList<ActivityNode>(); |
| org.eclipse.uml2.uml.Activity diagram = getDiagram(); |
| TxUtil.runInTransaction(diagram, new Runnable() { |
| |
| public void run() { |
| selectedNodes.addAll(populateNodes()); |
| populateLinks(selectedNodes); |
| } |
| |
| }); |
| |
| // add this diagram to the consumer list of all nodes so they will not |
| // be disposed |
| // before this diagram. |
| // |
| for (ActivityNode node : selectedNodes) { |
| NodeAdapter nodeAdapter = BridgeHelper.getNodeAdapter(node); |
| if(nodeAdapter != null) { |
| nodeAdapter.addConsumer(this); |
| } |
| } |
| |
| TxUtil.runInTransaction(diagram, new Runnable() { |
| |
| public void run() { |
| try { |
| updateEdges(selectedNodes); |
| updateView(selectedNodes); |
| } catch(Exception e) { |
| DiagramCorePlugin.getDefault().getLogger().logError(e); |
| } |
| } |
| |
| }); |
| } |
| catch(Exception e) { |
| DiagramCorePlugin.getDefault().getLogger().logError(e); |
| } |
| finally { |
| notificationEnabled = notify; |
| } |
| } |
| |
| protected Collection populateNodes() { |
| return Collections.EMPTY_LIST; |
| } |
| |
| public void setFilter(IFilter filter) { |
| this.filter = filter; |
| } |
| |
| public IActionManager getActionManager() { |
| if(actionManager == null) { |
| actionManager = new ActionManager() { |
| public boolean doAction(int actionType, EObject object, EStructuralFeature feature, Object value, int index) { |
| boolean ret = super.doAction(actionType, object, feature, value, index); |
| if(ret) { |
| umaLastModified = System.currentTimeMillis(); |
| } |
| return ret; |
| } |
| }; |
| } |
| return actionManager; |
| } |
| |
| public long getUmaLastModified() { |
| return umaLastModified; |
| } |
| |
| public IFilter getFilter() { |
| return filter; |
| } |
| } |