Coverage Report - org.kuali.rice.kew.engine.node.hierarchyrouting.HierarchyRoutingNode
 
Classes in this File Line Coverage Branch Coverage Complexity
HierarchyRoutingNode
0%
0/234
0%
0/74
2.538
HierarchyRoutingNode$1
N/A
N/A
2.538
HierarchyRoutingNode$InnerTransitionResult
0%
0/8
N/A
2.538
 
 1  
 /*
 2  
  * Copyright 2005-2007 The Kuali Foundation
 3  
  * 
 4  
  * 
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  * 
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.kuali.rice.kew.engine.node.hierarchyrouting;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 import java.util.Arrays;
 21  
 import java.util.Collection;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 
 27  
 import javax.xml.XMLConstants;
 28  
 import javax.xml.namespace.QName;
 29  
 
 30  
 import org.apache.commons.lang.StringUtils;
 31  
 import org.apache.log4j.Logger;
 32  
 import org.kuali.rice.core.api.reflect.ObjectDefinition;
 33  
 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
 34  
 import org.kuali.rice.core.api.reflect.ObjectDefinition;
 35  
 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
 36  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 37  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 38  
 import org.kuali.rice.kew.engine.RouteContext;
 39  
 import org.kuali.rice.kew.engine.RouteHelper;
 40  
 import org.kuali.rice.kew.engine.node.Branch;
 41  
 import org.kuali.rice.kew.engine.node.DynamicNode;
 42  
 import org.kuali.rice.kew.engine.node.DynamicResult;
 43  
 import org.kuali.rice.kew.engine.node.NoOpNode;
 44  
 import org.kuali.rice.kew.engine.node.NodeState;
 45  
 import org.kuali.rice.kew.engine.node.Process;
 46  
 import org.kuali.rice.kew.engine.node.RequestsNode;
 47  
 import org.kuali.rice.kew.engine.node.RouteNode;
 48  
 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
 49  
 import org.kuali.rice.kew.engine.node.SimpleJoinNode;
 50  
 import org.kuali.rice.kew.engine.node.SimpleSplitNode;
 51  
 import org.kuali.rice.kew.engine.node.hierarchyrouting.HierarchyProvider.Stop;
 52  
 import org.kuali.rice.kew.engine.transition.SplitTransitionEngine;
 53  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 54  
 import org.kuali.rice.kew.util.Utilities;
 55  
 
 56  
 
 57  
 /**
 58  
  * Generic hierarchy routing node
 59  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 60  
  */
 61  0
 public class HierarchyRoutingNode implements DynamicNode {
 62  0
     protected final Logger LOG = Logger.getLogger(getClass());
 63  
 
 64  
     /**
 65  
      * The RouteNode configuration parameter that specifies the hierarchy provider implementation
 66  
      */
 67  
     public static final String HIERARCHY_PROVIDER = "hierarchyProvider";
 68  
     /**
 69  
      * RouteNodeInstance NodeState key for id of stop
 70  
      */
 71  
     public static final String STOP_ID = "stop_id";
 72  
 
 73  
     protected static final String SPLIT_PROCESS_NAME = "Hierarchy Split";
 74  
     protected static final String JOIN_PROCESS_NAME = "Hierarchy Join";
 75  
     protected static final String REQUEST_PROCESS_NAME = "Hierarchy Request";
 76  
     protected static final String NO_STOP_NAME = "No stop";
 77  
 
 78  
     // constants for the process state in tracking stops we've traveled
 79  
     private static final String VISITED_STOPS = "visited_stops";
 80  
     private static final String V_STOPS_DEL = ",";
 81  
     
 82  
     private static final String INITIAL_SPLIT_NODE_MARKER = "InitialSplitNode";
 83  
 
 84  
     /**
 85  
      * Loads hierarchy provider class via the GlobalResourceLoader
 86  
      * @param nodeInstance the current RouteNodeInstance
 87  
      * @param context the current RouteContext
 88  
      * @return the HierarchyProvider implementation, as specified by the HIERARCHY_PROVIDER config parameter
 89  
      */
 90  
     protected HierarchyProvider getHierarchyProvider(RouteNodeInstance nodeInstance, RouteContext context) {
 91  0
         Map<String, String> cfgMap = Utilities.getKeyValueCollectionAsMap(nodeInstance.getRouteNode().getConfigParams());
 92  0
         String hierarchyProviderClass = cfgMap.get(HIERARCHY_PROVIDER); 
 93  0
         if (StringUtils.isEmpty(hierarchyProviderClass)) {
 94  0
             throw new WorkflowRuntimeException("hierarchyProvider configuration parameter not set for HierarchyRoutingNode: " + nodeInstance.getName());
 95  
         }
 96  0
         QName qn = QName.valueOf(hierarchyProviderClass);
 97  
         ObjectDefinition od;
 98  0
         if (XMLConstants.NULL_NS_URI.equals(qn.getNamespaceURI())) {
 99  0
             od = new ObjectDefinition(qn.getLocalPart());
 100  
         } else {
 101  0
             od = new ObjectDefinition(qn.getLocalPart(), qn.getNamespaceURI());
 102  
         }
 103  0
         HierarchyProvider hp = (HierarchyProvider) GlobalResourceLoader.getObject(od);
 104  0
         hp.init(nodeInstance, context);
 105  0
         return hp;
 106  
     }
 107  
 
 108  
     public DynamicResult transitioningInto(RouteContext context, RouteNodeInstance dynamicNodeInstance, RouteHelper helper) throws Exception {
 109  
 
 110  0
         HierarchyProvider provider = getHierarchyProvider(dynamicNodeInstance, context);
 111  0
         DocumentType documentType = setUpDocumentType(provider, context.getDocument().getDocumentType(), dynamicNodeInstance);
 112  0
         RouteNode splitNode = documentType.getNamedProcess(SPLIT_PROCESS_NAME).getInitialRouteNode();
 113  
 
 114  
         //set up initial SplitNodeInstance
 115  0
         RouteNodeInstance splitNodeInstance = helper.getNodeFactory().createRouteNodeInstance(context.getDocument().getDocumentId(), splitNode);
 116  0
         splitNodeInstance.setBranch(dynamicNodeInstance.getBranch());
 117  0
         markAsInitialSplitNode(splitNodeInstance);
 118  
         
 119  0
         int i = 0;
 120  0
         List<Stop> stops = provider.getLeafStops(context);
 121  0
         if (stops.isEmpty()) {
 122  
             // if we have no stops, then just return a no-op node with IU-UNIV attached, this will terminate the process
 123  0
             RouteNode noStopNode = documentType.getNamedProcess(NO_STOP_NAME).getInitialRouteNode();
 124  0
             RouteNodeInstance noChartOrgInstance = helper.getNodeFactory().createRouteNodeInstance(context.getDocument().getDocumentId(), noStopNode);
 125  0
             noChartOrgInstance.setBranch(dynamicNodeInstance.getBranch());
 126  
 
 127  0
             provider.setStop(noChartOrgInstance, null);
 128  
 
 129  0
             return new DynamicResult(true, noChartOrgInstance);
 130  
         }
 131  0
         for (Stop stop: stops) {
 132  0
             RouteNode requestNode = getStopRequestNode(stop, documentType);
 133  0
             createInitialRequestNodeInstance(provider, stop, splitNodeInstance, dynamicNodeInstance, requestNode);
 134  0
         }
 135  
 
 136  0
         return new DynamicResult(false, splitNodeInstance);
 137  
     }
 138  
 
 139  
     public DynamicResult transitioningOutOf(RouteContext context, RouteHelper helper) throws Exception {
 140  
         // process initial nodes govern transitioning within the process
 141  
         // the process node will be the hierarchy node, so that's what we need to grab
 142  0
         HierarchyProvider provider = getHierarchyProvider(context.getNodeInstance().getProcess(), context);
 143  
         
 144  0
         RouteNodeInstance processInstance = context.getNodeInstance().getProcess();
 145  0
         RouteNodeInstance curStopNode = context.getNodeInstance();
 146  0
         Map<String, RouteNodeInstance> stopRequestNodeMap = new HashMap<String, RouteNodeInstance>();
 147  0
         findStopRequestNodes(provider, context, stopRequestNodeMap);//SpringServiceLocator.getRouteNodeService().findProcessNodeInstances(processInstance);
 148  
         
 149  0
         Stop stop = provider.getStop(curStopNode);
 150  
 
 151  0
         if (provider.isRoot(stop)) {
 152  0
             return new DynamicResult(true, null);
 153  
         }        
 154  
         
 155  
         //create a join node for the next node and attach any sibling orgs to the join
 156  
         //if no join node is necessary i.e. no siblings create a requests node
 157  0
         InnerTransitionResult transition = canTransitionFrom(provider, stop, stopRequestNodeMap.values(), helper);
 158  0
         DynamicResult result = null;
 159  0
         if (transition.isCanTransition()) {
 160  0
             DocumentType documentType = context.getDocument().getDocumentType();
 161  
             // make a simple requests node
 162  0
             RouteNodeInstance requestNode = createNextStopRequestNodeInstance(provider, context, stop, processInstance, helper);
 163  
 
 164  0
             if (transition.getSiblings().isEmpty()) {
 165  0
                 result = new DynamicResult(false, requestNode);
 166  
             } else {
 167  
                 /* join stuff not working
 168  
 
 169  
                 //create a join to transition us to the next org
 170  
                 RouteNode joinPrototype = documentType.getNamedProcess(JOIN_PROCESS_NAME).getInitialRouteNode();
 171  
                 RouteNodeInstance joinNode = helper.getNodeFactory().createRouteNodeInstance(context.getDocument().getDocumentId(), joinPrototype);
 172  
                 LOG.debug("Created join node: " + joinNode);
 173  
                 String branchName = "Branch for join " + provider.getStopIdentifier(stop);
 174  
                 Branch joinBranch = helper.getNodeFactory().createBranch(branchName, null, joinNode);
 175  
                 LOG.debug("Created branch for join node: " + joinBranch);
 176  
                 joinNode.setBranch(joinBranch);
 177  
 
 178  
                 for (RouteNodeInstance sibling: transition.getSiblings()) {
 179  
                     LOG.debug("Adding expected joiner: " + sibling.getRouteNodeInstanceId() + " " + provider.getStop(sibling));
 180  
                     helper.getJoinEngine().addExpectedJoiner(joinNode, sibling.getBranch());
 181  
                 }
 182  
 
 183  
                 ///XXX: can't get stop from node that hasn't been saved yet maybe...need to follow up on this...comes back as 'root'
 184  
                 LOG.debug("Adding as stop after join: " + requestNode.getRouteNodeInstanceId() + " " + provider.getStop(requestNode));
 185  
                 //set the next org after the join
 186  
                 joinNode.addNextNodeInstance(requestNode);
 187  
 
 188  
                 result = new DynamicResult(false, joinNode);
 189  
                 
 190  
                 */
 191  
             }
 192  
 
 193  0
         } else {
 194  0
             result = new DynamicResult(false, null);
 195  
         }
 196  0
         result.getNextNodeInstances().addAll(getNewlyAddedOrgRouteInstances(provider, context, helper));
 197  0
         return result;
 198  
     }
 199  
     
 200  
     private void findStopRequestNodes(HierarchyProvider provider, RouteContext context, Map<String, RouteNodeInstance> stopRequestNodes) {
 201  0
         List<RouteNodeInstance> nodeInstances = KEWServiceLocator.getRouteNodeService().getFlattenedNodeInstances(context.getDocument(), true);
 202  0
         for (RouteNodeInstance nodeInstance: nodeInstances) {
 203  0
             if (provider.hasStop(nodeInstance)) {
 204  0
                 LOG.debug("Stop node instance: " + nodeInstance);
 205  0
                 stopRequestNodes.put(nodeInstance.getRouteNodeInstanceId(), nodeInstance);
 206  
             }
 207  
         }
 208  
         
 209  0
     }
 210  
 
 211  
     private RouteNodeInstance createNextStopRequestNodeInstance(HierarchyProvider provider, RouteContext context, Stop stop, RouteNodeInstance processInstance, RouteHelper helper) {
 212  0
         Stop futureStop = provider.getParent(stop);
 213  0
         LOG.debug("Creating next stop request node instance " + provider.getStopIdentifier(futureStop) + " as parent of " + provider.getStopIdentifier(stop));
 214  0
         RouteNode requestsPrototype = getStopRequestNode(futureStop, context.getDocument().getDocumentType());
 215  0
         RouteNodeInstance requestNode = helper.getNodeFactory().createRouteNodeInstance(context.getDocument().getDocumentId(), requestsPrototype);
 216  0
         requestNode.setBranch(processInstance.getBranch());
 217  0
         NodeState ns = new NodeState();
 218  0
         ns.setKey(STOP_ID);
 219  0
         ns.setValue(provider.getStopIdentifier(futureStop));
 220  0
         requestNode.addNodeState(ns);
 221  0
         provider.setStop(requestNode, futureStop);
 222  0
         LOG.debug("Stop set on request node: " + provider.getStop(requestNode));
 223  0
         addStopToProcessState(provider, processInstance, futureStop);
 224  0
         return requestNode;
 225  
     }
 226  
 
 227  
     /**
 228  
      * i can only transition from this if all the nodes left are completed immediate siblings
 229  
      * 
 230  
      * @param org
 231  
      * @param requestNodes
 232  
      * @return List of Nodes that are siblings to the org passed in
 233  
      */
 234  
     private InnerTransitionResult canTransitionFrom(HierarchyProvider provider, Stop currentStop, Collection<RouteNodeInstance> requestNodes, RouteHelper helper) {
 235  0
         LOG.debug("Testing whether we can transition from stop: " + currentStop);
 236  0
         Stop parent = provider.getParent(currentStop);
 237  0
         InnerTransitionResult result = new InnerTransitionResult();
 238  0
         result.setCanTransition(false);
 239  
 
 240  0
         for (RouteNodeInstance requestNode: requestNodes) {
 241  0
             if (!provider.hasStop(requestNode)) {
 242  0
                 LOG.debug("request node " + requestNode.getName() + " does not have a stop associated with it");
 243  0
                 continue;
 244  
             }
 245  
 
 246  0
             Stop requestNodeStop = provider.getStop(requestNode);
 247  0
             LOG.debug("Request node: " + requestNode.getRouteNodeInstanceId() + " has stop " + requestNodeStop.toString());
 248  0
             if (requestNodeStop != null && provider.equals(currentStop, requestNodeStop)) {
 249  0
                 LOG.debug("Skipping node " + requestNode.getName() + " because it is associated with the current stop");
 250  0
                 continue;
 251  
             }
 252  
 
 253  
 
 254  0
             Stop stop = provider.getStop(requestNode);
 255  
 
 256  0
             LOG.debug("Found an outstanding stop: " + stop);
 257  
 
 258  0
             boolean isChildOfMyParent = isDescendent(provider, parent, stop);
 259  
 
 260  0
             if (isChildOfMyParent) {
 261  0
                 LOG.debug("Found stop node whose parent is my parent:");
 262  0
                 LOG.debug("Stop: " + stop);
 263  0
                 LOG.debug("Node: " + requestNode);
 264  
 
 265  
                 // if any sibling request node is active, then I can't transition
 266  0
                 if (requestNode.isActive()) {
 267  
                     // can't transition
 268  0
                     result.getSiblings().clear();
 269  0
                     return result;
 270  
                 }
 271  
 
 272  
                 /* join stuff not working
 273  
                 // if it's a direct sibling
 274  
                 if (provider.equals(parent, provider.getParent(stop))) {
 275  
                     LOG.debug("Adding stop " + provider.getStopIdentifier(stop) + " as sibling to " + provider.getStopIdentifier(currentStop));
 276  
                     result.getSiblings().add(requestNode);
 277  
                 }
 278  
                 */
 279  
             }
 280  0
         }
 281  0
         result.setCanTransition(true);
 282  0
         return result;
 283  
     }
 284  
 
 285  
     protected boolean isDescendent(HierarchyProvider provider, Stop parent, Stop otherStop) {
 286  0
         return provider.isRoot(parent) || hasAsParent(provider, parent, otherStop);
 287  
     }
 288  
 
 289  0
     private static class InnerTransitionResult {
 290  
         private boolean canTransition;
 291  0
         private List<RouteNodeInstance> siblings = new ArrayList<RouteNodeInstance>();
 292  
 
 293  
         public boolean isCanTransition() {
 294  0
             return canTransition;
 295  
         }
 296  
 
 297  
         public void setCanTransition(boolean canTransition) {
 298  0
             this.canTransition = canTransition;
 299  0
         }
 300  
 
 301  
         public List<RouteNodeInstance> getSiblings() {
 302  0
             return siblings;
 303  
         }
 304  
 
 305  
         public void setSiblings(List<RouteNodeInstance> siblings) {
 306  0
             this.siblings = siblings;
 307  0
         }
 308  
     }
 309  
 
 310  
     private static void markAsInitialSplitNode(RouteNodeInstance splitNode) {
 311  0
         NodeState ns = new NodeState();
 312  0
         ns.setKey(INITIAL_SPLIT_NODE_MARKER);
 313  0
         ns.setValue(INITIAL_SPLIT_NODE_MARKER);
 314  
             
 315  0
             splitNode.addNodeState(ns);
 316  0
     }
 317  
 
 318  
     /**
 319  
      * @param routeNodeInstance
 320  
      * @return
 321  
      */
 322  
     private static boolean isInitialSplitNode(RouteNodeInstance routeNodeInstance) {
 323  0
         return routeNodeInstance.getNodeState(INITIAL_SPLIT_NODE_MARKER) != null;
 324  
     }
 325  
 
 326  
     /**
 327  
      * Adds the org to the process state 
 328  
      * @param processInstance
 329  
      * @param org
 330  
      */
 331  
     private void addStopToProcessState(HierarchyProvider provider, RouteNodeInstance processInstance, Stop stop) {
 332  0
         String stopStateName = provider.getStopIdentifier(stop);
 333  0
         NodeState visitedStopsState = processInstance.getNodeState(VISITED_STOPS);
 334  0
         if (visitedStopsState == null) {
 335  0
             NodeState ns = new NodeState();
 336  0
             ns.setKey(VISITED_STOPS);
 337  0
             ns.setValue(stopStateName + V_STOPS_DEL);
 338  
                 
 339  0
                 processInstance.addNodeState(ns);
 340  0
         } else if (! getVisitedStopsList(processInstance).contains(stopStateName)) {
 341  0
             visitedStopsState.setValue(visitedStopsState.getValue() + stopStateName + V_STOPS_DEL);
 342  
         }
 343  0
     }
 344  
     
 345  
     /**
 346  
      * @param process
 347  
      * @return List of stop strings on the process state
 348  
      */
 349  
     private static List<String> getVisitedStopsList(RouteNodeInstance process) {
 350  0
         return Arrays.asList(process.getNodeState(VISITED_STOPS).getValue().split(V_STOPS_DEL));
 351  
     }
 352  
     
 353  
     /**
 354  
      * Determines if the org has been routed to or will be.
 355  
      * @param stop
 356  
      * @param process
 357  
      * @return boolean if this is an org we would not hit in routing
 358  
      */
 359  
     private boolean isNewStop(HierarchyProvider provider, Stop stop, RouteNodeInstance process) {
 360  
         
 361  0
         String orgStateName = provider.getStopIdentifier(stop);
 362  0
         List<String> visitedOrgs = getVisitedStopsList(process);
 363  0
         boolean isInVisitedList = visitedOrgs.contains(orgStateName);
 364  0
         if (isInVisitedList) {
 365  0
             return false;
 366  
         }
 367  0
         boolean willEventualRouteThere = false;
 368  
         //determine if we will eventually route to this chart anyway
 369  0
         for (Iterator<String> iter = visitedOrgs.iterator(); iter.hasNext() && willEventualRouteThere == false; ) {
 370  0
             String visitedStopStateName = iter.next();
 371  0
             Stop visitedStop = provider.getStopByIdentifier(visitedStopStateName);
 372  0
             willEventualRouteThere = hasAsParent(provider, stop, visitedStop) || willEventualRouteThere;
 373  0
         }
 374  0
         return ! willEventualRouteThere;
 375  
     }
 376  
 
 377  
     /**
 378  
      * Creates a Org Request RouteNodeInstance that is a child of the passed in split.  This is used to create the initial 
 379  
      * request RouteNodeInstances off the begining split.
 380  
      * @param org
 381  
      * @param splitNodeInstance
 382  
      * @param processInstance
 383  
      * @param requestsNode
 384  
      * @return Request RouteNodeInstance bound to the passed in split as a 'nextNodeInstance'
 385  
      */
 386  
     private RouteNodeInstance createInitialRequestNodeInstance(HierarchyProvider provider, Stop stop, RouteNodeInstance splitNodeInstance, RouteNodeInstance processInstance, RouteNode requestsNode) {
 387  0
         String branchName = "Branch " + provider.getStopIdentifier(stop);
 388  0
         RouteNodeInstance orgRequestInstance = SplitTransitionEngine.createSplitChild(branchName, requestsNode, splitNodeInstance);
 389  0
         splitNodeInstance.addNextNodeInstance(orgRequestInstance);
 390  0
         NodeState ns = new NodeState();
 391  0
         ns.setKey(STOP_ID);
 392  0
         ns.setValue(provider.getStopIdentifier(stop));
 393  
         
 394  0
         orgRequestInstance.addNodeState(ns);
 395  0
         provider.setStop(orgRequestInstance, stop);
 396  0
         addStopToProcessState(provider, processInstance, stop);
 397  0
         return orgRequestInstance;
 398  
     }
 399  
     
 400  
     /**
 401  
      * Check the xml and determine there are any orgs declared that we will not travel through on our current trajectory.
 402  
      * @param context
 403  
      * @param helper
 404  
      * @return RouteNodeInstances for any orgs we would not have traveled through that are now in the xml.
 405  
      * @throws Exception
 406  
      */
 407  
     private List<RouteNodeInstance> getNewlyAddedOrgRouteInstances(HierarchyProvider provider, RouteContext context, RouteHelper helper) throws Exception {
 408  0
         RouteNodeInstance processInstance = context.getNodeInstance().getProcess();
 409  0
         RouteNodeInstance chartOrgNode = context.getNodeInstance();
 410  
         //check for new stops in the xml
 411  0
         List<Stop> stops = provider.getLeafStops(context);
 412  0
         List<RouteNodeInstance> newStopsRoutingTo = new ArrayList<RouteNodeInstance>();
 413  0
         for (Stop stop: stops) {
 414  0
             if (isNewStop(provider, stop, processInstance)) {
 415  
                 //the idea here is to always use the object referenced by the engine so simulation can occur
 416  0
                 List<RouteNodeInstance> processNodes = chartOrgNode.getPreviousNodeInstances();
 417  0
                 for (RouteNodeInstance splitNodeInstance: processNodes) {
 418  0
                     if (isInitialSplitNode(splitNodeInstance)) {                        
 419  0
                         RouteNode requestsNode = getStopRequestNode(stop, context.getDocument().getDocumentType());
 420  0
                         RouteNodeInstance newOrgRequestNode = createInitialRequestNodeInstance(provider, stop, splitNodeInstance, processInstance, requestsNode);
 421  0
                         newStopsRoutingTo.add(newOrgRequestNode);
 422  0
                     }
 423  
                 }
 424  0
             }
 425  
         }
 426  0
         return newStopsRoutingTo;
 427  
     }    
 428  
     
 429  
     /**
 430  
      * @param parent
 431  
      * @param child
 432  
      * @return true - if child or one of it's eventual parents reports to parent false - if child or one of it's eventual parents does not report to parent
 433  
      */
 434  
     private boolean hasAsParent(HierarchyProvider provider, Stop parent, Stop child) {
 435  0
         if (provider.isRoot(child)) {
 436  0
             return false;
 437  0
         } else if (provider.equals(parent, child)) {
 438  0
             return true;
 439  
         } else {
 440  0
             child = provider.getParent(child);
 441  0
             return hasAsParent(provider, parent, child);
 442  
         }
 443  
     }
 444  
 
 445  
 
 446  
     /**
 447  
      * Make the 'floating' split, join and request RouteNodes that will be independent processes. These are the prototypes from which our RouteNodeInstance will belong
 448  
      * 
 449  
      * @param documentType
 450  
      * @param dynamicNodeInstance
 451  
      */
 452  
     private DocumentType setUpDocumentType(HierarchyProvider provider, DocumentType documentType, RouteNodeInstance dynamicNodeInstance) {
 453  0
         boolean altered = false;
 454  0
         if (documentType.getNamedProcess(SPLIT_PROCESS_NAME) == null) {
 455  0
             RouteNode splitNode = getSplitNode(dynamicNodeInstance);
 456  0
             documentType.addProcess(getPrototypeProcess(splitNode, documentType));
 457  0
             altered = true;
 458  
         }
 459  0
         if (documentType.getNamedProcess(JOIN_PROCESS_NAME) == null) {
 460  0
             RouteNode joinNode = getJoinNode(dynamicNodeInstance);
 461  0
             documentType.addProcess(getPrototypeProcess(joinNode, documentType));
 462  0
             altered = true;
 463  
         }
 464  0
         if (documentType.getNamedProcess(REQUEST_PROCESS_NAME) == null) {
 465  0
             RouteNode requestsNode = getRequestNode(provider, dynamicNodeInstance);
 466  0
             documentType.addProcess(getPrototypeProcess(requestsNode, documentType));
 467  0
             altered = true;
 468  
         }
 469  0
         if (documentType.getNamedProcess(NO_STOP_NAME) == null) {
 470  0
             RouteNode noChartOrgNode = getNoChartOrgNode(dynamicNodeInstance);
 471  0
             documentType.addProcess(getPrototypeProcess(noChartOrgNode, documentType));
 472  0
             altered = true;
 473  
         }
 474  0
         if (altered) {
 475  
                 //side step normal version etc. because it's a pain.
 476  0
             KEWServiceLocator.getDocumentTypeService().save(documentType);
 477  
         }
 478  0
         return KEWServiceLocator.getDocumentTypeService().findByName(documentType.getName());
 479  
     }
 480  
 
 481  
     /**
 482  
      * Places a Process on the documentType wrapping the node and setting the node as the process's initalRouteNode
 483  
      * 
 484  
      * @param node
 485  
      * @param documentType
 486  
      * @return Process wrapping the node passed in
 487  
      */
 488  
     protected Process getPrototypeProcess(RouteNode node, DocumentType documentType) {
 489  0
         Process process = new Process();
 490  0
         process.setDocumentType(documentType);
 491  0
         process.setInitial(false);
 492  0
         process.setInitialRouteNode(node);
 493  0
         process.setName(node.getRouteNodeName());
 494  0
         return process;
 495  
     }
 496  
 
 497  
     /**
 498  
      * @param process
 499  
      * @return Route Node of the JoinNode that will be prototype for the split RouteNodeInstances generated by this component
 500  
      */
 501  
     private static RouteNode getSplitNode(RouteNodeInstance process) {
 502  0
         RouteNode dynamicNode = process.getRouteNode();
 503  0
         RouteNode splitNode = new RouteNode();
 504  0
         splitNode.setActivationType(dynamicNode.getActivationType());
 505  0
         splitNode.setDocumentType(dynamicNode.getDocumentType());
 506  0
         splitNode.setFinalApprovalInd(dynamicNode.getFinalApprovalInd());
 507  0
         splitNode.setExceptionWorkgroupId(dynamicNode.getExceptionWorkgroupId());
 508  0
         splitNode.setMandatoryRouteInd(dynamicNode.getMandatoryRouteInd());
 509  0
         splitNode.setNodeType(SimpleSplitNode.class.getName());
 510  0
         splitNode.setRouteMethodCode("FR");
 511  0
         splitNode.setRouteMethodName(null);
 512  0
         splitNode.setRouteNodeName(SPLIT_PROCESS_NAME);
 513  0
         return splitNode;
 514  
         //SubRequests
 515  
     }
 516  
 
 517  
     /**
 518  
      * @param process
 519  
      * @return Route Node of the JoinNode that will be prototype for the join RouteNodeInstances generated by this component
 520  
      */
 521  
     private static RouteNode getJoinNode(RouteNodeInstance process) {
 522  0
         RouteNode dynamicNode = process.getRouteNode();
 523  0
         RouteNode joinNode = new RouteNode();
 524  0
         joinNode.setActivationType(dynamicNode.getActivationType());
 525  0
         joinNode.setDocumentType(dynamicNode.getDocumentType());
 526  0
         joinNode.setFinalApprovalInd(dynamicNode.getFinalApprovalInd());
 527  0
         joinNode.setExceptionWorkgroupId(dynamicNode.getExceptionWorkgroupId());
 528  0
         joinNode.setMandatoryRouteInd(dynamicNode.getMandatoryRouteInd());
 529  0
         joinNode.setNodeType(SimpleJoinNode.class.getName());
 530  0
         joinNode.setRouteMethodCode("FR");
 531  0
         joinNode.setRouteMethodName(null);
 532  0
         joinNode.setRouteNodeName(JOIN_PROCESS_NAME);
 533  0
         return joinNode;
 534  
     }
 535  
 
 536  
     /**
 537  
      * @param process
 538  
      * @return RouteNode of RequestsNode that will be prototype for RouteNodeInstances having requets that are generated by this component
 539  
      */
 540  
     private RouteNode getRequestNode(HierarchyProvider provider, RouteNodeInstance process) {
 541  0
         RouteNode dynamicNode = process.getRouteNode();
 542  0
         RouteNode requestsNode = new RouteNode();
 543  0
         requestsNode.setActivationType(dynamicNode.getActivationType());
 544  0
         requestsNode.setDocumentType(dynamicNode.getDocumentType());
 545  0
         requestsNode.setFinalApprovalInd(dynamicNode.getFinalApprovalInd());
 546  0
         requestsNode.setExceptionWorkgroupId(dynamicNode.getExceptionWorkgroupId());
 547  0
         requestsNode.setMandatoryRouteInd(dynamicNode.getMandatoryRouteInd());
 548  0
         requestsNode.setNodeType(RequestsNode.class.getName());
 549  0
         requestsNode.setRouteMethodCode("FR");
 550  0
         requestsNode.setRouteMethodName(process.getRouteNode().getRouteMethodName());
 551  0
         requestsNode.setRouteNodeName(REQUEST_PROCESS_NAME);
 552  0
         provider.configureRequestNode(process, requestsNode);
 553  0
         return requestsNode;
 554  
     }
 555  
 
 556  
     /**
 557  
      * @param process
 558  
      * @return RouteNode of a no-op node which will be used if the user sends no Chart+Org XML to this routing component.
 559  
      */
 560  
     private static RouteNode getNoChartOrgNode(RouteNodeInstance process) {
 561  0
         RouteNode dynamicNode = process.getRouteNode();
 562  0
         RouteNode noChartOrgNOde = new RouteNode();
 563  0
         noChartOrgNOde.setActivationType(dynamicNode.getActivationType());
 564  0
         noChartOrgNOde.setDocumentType(dynamicNode.getDocumentType());
 565  0
         noChartOrgNOde.setFinalApprovalInd(dynamicNode.getFinalApprovalInd());
 566  0
         noChartOrgNOde.setExceptionWorkgroupId(dynamicNode.getExceptionWorkgroupId());
 567  0
         noChartOrgNOde.setMandatoryRouteInd(dynamicNode.getMandatoryRouteInd());
 568  0
         noChartOrgNOde.setNodeType(NoOpNode.class.getName());
 569  0
         noChartOrgNOde.setRouteMethodCode("FR");
 570  0
         noChartOrgNOde.setRouteMethodName(null);
 571  0
         noChartOrgNOde.setRouteNodeName(NO_STOP_NAME);
 572  0
         return noChartOrgNOde;
 573  
     }
 574  
 
 575  
  
 576  
     
 577  
     // methods which can be overridden to change the chart org routing node behavior
 578  
     
 579  
     protected RouteNode getStopRequestNode(Stop stop, DocumentType documentType) {
 580  0
         return documentType.getNamedProcess(REQUEST_PROCESS_NAME).getInitialRouteNode();
 581  
     }
 582  
     
 583  
 }