//------------------------------------------------------------------------------ // 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.authoring.gef.util; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.edit.provider.AdapterFactoryTreeIterator; import org.eclipse.emf.edit.provider.ITreeItemContentProvider; import org.eclipse.epf.authoring.ui.AuthoringUIResources; import org.eclipse.epf.diagram.model.ActivityDetailDiagram; import org.eclipse.epf.diagram.model.ActivityDiagram; import org.eclipse.epf.diagram.model.Diagram; import org.eclipse.epf.diagram.model.Link; import org.eclipse.epf.diagram.model.Node; import org.eclipse.epf.diagram.model.RoleNode; import org.eclipse.epf.diagram.model.TaskNode; import org.eclipse.epf.diagram.model.TypedNode; import org.eclipse.epf.diagram.model.WorkBreakdownElementNode; import org.eclipse.epf.diagram.model.WorkProductDependencyDiagram; import org.eclipse.epf.diagram.model.util.GraphicalDataHelper; import org.eclipse.epf.library.edit.TngAdapterFactory; import org.eclipse.epf.library.edit.process.IBSItemProvider; import org.eclipse.epf.library.edit.util.ProcessUtil; import org.eclipse.epf.library.edit.util.TngUtil; import org.eclipse.epf.uma.Activity; import org.eclipse.epf.uma.RoleDescriptor; import org.eclipse.epf.uma.TaskDescriptor; import org.eclipse.epf.uma.WorkBreakdownElement; import org.eclipse.epf.uma.WorkProductDescriptor; /** * Validating routines for diagram editing * * @author Phong Nguyen Le * @author Shashidhar Kannoori * @since 1.0 */ public final class Validation { private static final String errMsg_CanNotConnect = AuthoringUIResources.DiagramValidation_err_cannot_connect_text; private static final String errMsg_SamePredAndSucc = AuthoringUIResources.DiagramValidation_err_samepredandsuc_text; private static final String errMsg_CanNotDelete = AuthoringUIResources.DiagramValidation_err_cannot_delete_text; /** * Checks if user can connect the given nodes. * * @param source * @param target * @return null if connection is allowed, error message otherwise. */ public static String checkConnect(Node source, Node target) { if (source.getDiagram() != target.getDiagram()) return AuthoringUIResources.DiagramValidation_err_cannot_connect_text; if (source.getDiagram() instanceof ActivityDiagram) { AdapterFactory adapterFactory = TngAdapterFactory.INSTANCE .getWBS_ComposedAdapterFactory(); Object adapter = adapterFactory.adapt(source.getDiagram() .getObject(), ITreeItemContentProvider.class); Object proc = null; if (adapter instanceof IBSItemProvider) { proc = ((IBSItemProvider) adapter).getTopItem(); } // get all breakdown elements in this process List<Object> allElements = new ArrayList<Object>(); for (Iterator iter = new AdapterFactoryTreeIterator(adapterFactory, proc); iter.hasNext();) { Object obj = iter.next(); allElements.add(TngUtil.unwrap(obj)); } if (proc != null) { // If target is ReadOnly (means extends) and allow the link if // its parent activity is a Contributor if (target.getDiagram().isReadOnly()) return errMsg_CanNotConnect; if (source.getObject() instanceof WorkBreakdownElement) { if (target.getObject() instanceof WorkBreakdownElement) { if (ProcessUtil.checkCircular( (WorkBreakdownElement) target.getObject(), (WorkBreakdownElement) source.getObject(), allElements)) { return errMsg_SamePredAndSucc; } if (target.isReadOnly()) return errMsg_CanNotConnect; } else if (target instanceof TypedNode) { // should not allow incoming connection to start node // and FreeText. if (((TypedNode) target).getType() == TypedNode.START || ((TypedNode) target).getType() == TypedNode.FREE_TEXT) { return errMsg_CanNotConnect; } Collection targetNodes = GraphicalDataHelper .getTargetNodes((TypedNode) target, WorkBreakdownElementNode.class); WorkBreakdownElement pred = (WorkBreakdownElement) source .getObject(); for (Iterator iter = targetNodes.iterator(); iter .hasNext();) { Node node = ((Node) iter.next()); if (node.isReadOnly()) return errMsg_CanNotConnect; if (ProcessUtil.checkCircular( (WorkBreakdownElement) node.getObject(), pred, allElements)) { return errMsg_SamePredAndSucc; } } } } else if (source instanceof TypedNode) { if (target == source) { return errMsg_CanNotConnect; } // should not allow outgoing connection from End Node and // Free Text if (((TypedNode) source).getType() == TypedNode.END || ((TypedNode) source).getType() == TypedNode.FREE_TEXT) { return errMsg_CanNotConnect; } if (target instanceof TypedNode) { /* * If source and target are TypedNodes, avoid circular * loop connection. */ if (checkSyncBarCircularLooop((TypedNode) source, (TypedNode) target) != null) { return errMsg_CanNotConnect; } // should not allow incoming connection to start node // and FreeText. if (((TypedNode) target).getType() == TypedNode.START || ((TypedNode) target).getType() == TypedNode.FREE_TEXT) { return errMsg_CanNotConnect; } Collection srcNodes = GraphicalDataHelper .getSourceNodes((TypedNode) source, WorkBreakdownElementNode.class); if (!srcNodes.isEmpty()) { Collection targetNodes = GraphicalDataHelper .getTargetNodes((TypedNode) target, WorkBreakdownElementNode.class); for (Iterator iter = targetNodes.iterator(); iter .hasNext();) { Node node = ((Node) iter.next()); if (node.isReadOnly()) return errMsg_CanNotConnect; WorkBreakdownElement succ = (WorkBreakdownElement) node .getObject(); for (Iterator iterator = srcNodes.iterator(); iterator .hasNext();) { WorkBreakdownElement pred = (WorkBreakdownElement) ((Node) iterator .next()).getObject(); if (ProcessUtil.checkCircular(succ, pred, allElements)) { return errMsg_SamePredAndSucc; } } } } } else if (target instanceof WorkBreakdownElementNode) { if (target.isReadOnly()) return errMsg_CanNotConnect; Collection srcNodes = GraphicalDataHelper .getSourceNodes((TypedNode) source, WorkBreakdownElementNode.class); WorkBreakdownElement succ = (WorkBreakdownElement) ((Node) target) .getObject(); for (Iterator iterator = srcNodes.iterator(); iterator .hasNext();) { WorkBreakdownElement pred = (WorkBreakdownElement) ((Node) iterator .next()).getObject(); if (ProcessUtil.checkCircular(succ, pred, allElements)) { return errMsg_SamePredAndSucc; } } } } } } else if (source.getDiagram() instanceof ActivityDetailDiagram) { if (source.isReadOnly() || target.isReadOnly()) { return errMsg_CanNotConnect; } if ((source.getObject() instanceof RoleDescriptor || target .getObject() instanceof RoleDescriptor) || (source.getObject() instanceof TaskDescriptor && target .getObject() instanceof TaskDescriptor) || (source.getObject() instanceof WorkProductDescriptor && target .getObject() instanceof WorkProductDescriptor)) { return errMsg_CanNotConnect; } else { // check for duplicate connection // for (Iterator iter = source.getOutgoingConnections().iterator(); iter .hasNext();) { Link link = (Link) iter.next(); if (link.getTarget() == target) { return errMsg_CanNotConnect; } } } } else if (source.getDiagram() instanceof WorkProductDependencyDiagram) { if (source.isReadOnly() || target.isReadOnly()) { return errMsg_CanNotConnect; } } return null; } public static String checkDelete(Link link) { // if (link.getTarget() != null // && link.getTarget().getDiagram() instanceof ActivityDiagram) { // if (link.getTarget().isReadOnly()){ // if(link.getSource() instanceof TypedNode){ // if(((TypedNode)link.getSource()).getType() == TypedNode.DECISION){ // return null; // } // } // return errMsg_CanNotDelete; // } // if (link.getTarget() instanceof TypedNode) { // // TypedNode typednode = (TypedNode)link.getTarget(); // if(typednode.getType() == TypedNode.DECISION){ // return null; // } // if(link.getSource() instanceof TypedNode){ // typednode = (TypedNode)link.getSource(); // if(typednode.getType() == TypedNode.DECISION){ // return null; // } // } // // if (link.getSource().isReadOnly()) // return errMsg_CanNotDelete; // // if (link.getSource().getObject() instanceof WorkBreakdownElement // || (link.getSource() instanceof TypedNode && !GraphicalDataHelper // .getSourceNodes((TypedNode) link.getSource(), // WorkBreakdownElementNode.class) // .isEmpty())) { // // Collection targetNodes = // GraphicalDataHelper.getTargetNodes((TypedNode) link.getTarget(), // WorkBreakdownElementNode.class); // for (Iterator iter = targetNodes.iterator(); iter.hasNext();) { // Node node = (Node) iter.next(); // if (node.isReadOnly()) { // return errMsg_CanNotDelete; // } // } // } // // } // if (link.getSource() instanceof TypedNode) { // TypedNode typednode = (TypedNode)link.getSource(); // if(typednode.getType() == TypedNode.DECISION){ // return null; // } // if (link.getTarget().isReadOnly()) // return errMsg_CanNotDelete; // } // } else if ((link.getTarget() != null && // link.getTarget().isReadOnly()) // || (link.getSource() != null && link.getSource().isReadOnly())) { // return errMsg_CanNotDelete; // } // return null; Node source = link.getSource(); Node target = link.getTarget(); // Special handle for Activity Detail Diagram links cannot be deleted. if (target != null && target.getDiagram() instanceof ActivityDetailDiagram) { return errMsg_CanNotDelete; } if (target != null && target.getDiagram() instanceof ActivityDiagram) { // Target = breakdownelement if (target instanceof WorkBreakdownElementNode) { // Source = Breakdown element if (source instanceof WorkBreakdownElementNode) { if (link.getTarget().isReadOnly()) return errMsg_CanNotDelete; else return null; } // Source = TypedNode if (source instanceof TypedNode) { // Source = Decision Node TypedNode sourceNode = (TypedNode) source; if (!target.isReadOnly()) { return null; } else if (sourceNode.getType() == TypedNode.DECISION) { return null; } else if (sourceNode.getType() == TypedNode.SYNCH_BAR) { return checkSyncBarIncomingLinks(sourceNode); } else if (sourceNode.isReadOnly()) { return errMsg_CanNotDelete; } } }// Target is TypedNode else if (target instanceof TypedNode) { TypedNode typedNode = (TypedNode) target; // if Target is TypedNode and its a DecisionPoint - should be // black. if (typedNode.getType() == TypedNode.DECISION) { return null; } // If Target is TypedNode and and Source is WorkBreakdownElement // and readonly // Link should be green. if (source instanceof WorkBreakdownElementNode) { // Check Target - is having any indirect(outgoing // connnection) contains decision points. if (checkSyncBarOutgoingLinks((TypedNode) target) == null) return null; if (source.isReadOnly()) return errMsg_CanNotDelete; } if (source instanceof TypedNode) { TypedNode sourceTypedNode = (TypedNode) source; // Source is Typed Node and its a decision point return // null. if (sourceTypedNode.getType() == TypedNode.DECISION) { return null; } // if source is SyncBar and check all its incoming // connections. if (sourceTypedNode.getType() == TypedNode.SYNCH_BAR) { return checkSyncBarIncomingLinks(sourceTypedNode); } } } } return null; } public static String checkDelete(Node node) { if (node.isReadOnly()) return errMsg_CanNotDelete; Diagram diagram = node.getDiagram(); if (diagram instanceof ActivityDiagram) { if (node instanceof TypedNode) { Collection targetNodes = GraphicalDataHelper.getTargetNodes( (TypedNode) node, WorkBreakdownElementNode.class); for (Iterator iter = targetNodes.iterator(); iter.hasNext();) { Node target = (Node) iter.next(); if (target.isReadOnly()) { return errMsg_CanNotDelete; } } } } else if (diagram instanceof ActivityDetailDiagram) { if (node instanceof RoleNode) { return errMsg_CanNotDelete; } } return null; } public static String checkReconnect(Node source, Node target, Link link) { if (source.getDiagram() instanceof ActivityDiagram) { if (link.getTarget() != null) { if (link.getTarget().isReadOnly()) { return errMsg_CanNotConnect; } else { if (link.getTarget() instanceof TypedNode) { Collection targetNodes = GraphicalDataHelper .getTargetNodes((TypedNode) link.getTarget(), WorkBreakdownElementNode.class); for (Iterator iter = targetNodes.iterator(); iter .hasNext();) { Node node = (Node) iter.next(); if (node.isReadOnly()) { return errMsg_CanNotConnect; } } } } } } return checkConnect(source, target, link); } /* * Method will not allow multiple link between two nodes. Needs to call * CreakLinkCommand and ReconnectLinkCommand. Any action on a Link should * call this checkConnect(Node source, Node target, Link link) before allow * to connect. */ public static String checkConnect(Node source, Node target, Link link) { if (source == target && !(source.getObject() instanceof Activity)) { return errMsg_CanNotConnect; } List links = source.getOutgoingConnections(); if (links != null) { // This is need for reconnect to same target node. if (links.contains(link)) { if (link.getTarget().equals(target)) { return null; } } // This is need for new connect and reconnect to different target // node. for (Iterator iter = links.iterator(); iter.hasNext();) { Link linkx = (Link) iter.next(); Object linkxtarget = linkx.getTarget(); if (linkxtarget != null && linkxtarget.equals(target)) { return errMsg_CanNotConnect; } } } return checkConnect(source, target); } /* * To avoid circular looping between synchronization bars. If SycnBar1 -> * SyncBar2 (connected) then SyncBar2 -> SyncBar1 cannot connect. if * syncbar1 -> syncbar2->syncbar3 then syncbar3 -> syncbar1 disallowed. * */ public static String checkSyncBarCircularLooop(TypedNode sNode, TypedNode tNode) { List list = tNode.getOutgoingConnections(); if (!list.isEmpty() && list.size() > 0) { for (Iterator iterator = list.iterator(); iterator.hasNext();) { Link link = (Link) iterator.next(); Node typednode = link.getTarget(); if (sNode.equals(typednode)) return errMsg_CanNotConnect; if (typednode instanceof TypedNode) { return checkSyncBarCircularLooop(sNode, (TypedNode) typednode); } } } return null; } /* * Method to Check if SyncBar inComming connections have any source is a * readonly. @return */ public static String checkSyncBarIncomingLinks(TypedNode typedNode) { for (Iterator iter = typedNode.getIncomingConnections().iterator(); iter .hasNext();) { Link link = (Link) iter.next(); Node source = link.getSource(); if (source instanceof WorkBreakdownElementNode) { if (source.isReadOnly()) return errMsg_CanNotDelete; } else if (source instanceof TypedNode) { if (((TypedNode) source).getType() == TypedNode.SYNCH_BAR) if (checkSyncBarIncomingLinks((TypedNode) source) != null) return errMsg_CanNotDelete; } } return null; } /* * Method to check if synchronization bar outgoing connection has any target * is readonly. */ public static String checkSyncBarOutgoingLinks(TypedNode typedNode) { for (Iterator iter = typedNode.getOutgoingConnections().iterator(); iter .hasNext();) { Link link = (Link) iter.next(); Node target = link.getTarget(); if (target instanceof WorkBreakdownElementNode) { if (target.isReadOnly()) return errMsg_CanNotDelete; } else if (target instanceof TypedNode) { if (((TypedNode) target).getType() == TypedNode.SYNCH_BAR) if (checkSyncBarOutgoingLinks((TypedNode) target) != null) return errMsg_CanNotDelete; } } return null; } /** * Check to see link is readonly. */ public static boolean isReadOnly(Link link) { Node target = link.getTarget(); Node source = link.getSource(); if (target != null && target.getDiagram() instanceof ActivityDiagram) { return checkDelete(link) != null; } else if (target != null && target.getDiagram() instanceof ActivityDetailDiagram) { if ((source != null && source instanceof TaskNode && source .isReadOnly()) || (target instanceof TaskNode && target.isReadOnly())) return true; } return false; } }