View Javadoc

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.transition;
18  
19  import org.kuali.rice.kew.engine.RouteContext;
20  import org.kuali.rice.kew.engine.node.*;
21  import org.kuali.rice.kew.exception.RouteManagerException;
22  
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Set;
27  
28  
29  /**
30   * The DynamicTransitionEngine operates on a {@link DynamicNode} and takes the next node instances returned 
31   * by the node and runs returns them in a TransitionResult after doing some processing and validation on them.
32   * 
33   * @see DynamicNode
34   * 
35   * @author Kuali Rice Team (rice.collab@kuali.org)
36   */
37  public class DynamicTransitionEngine extends TransitionEngine {
38  
39      // TODO interate the all the nodes and attach the dynamic node as the 'process'
40      // don't include the dynamic node instance in the routing structure - require a correctly built graph
41      // change dynamic node signiture to next node because of above
42      // reconcile branching if necessary
43      public RouteNodeInstance transitionTo(RouteNodeInstance dynamicNodeInstance, RouteContext context) throws Exception {
44          dynamicNodeInstance.setInitial(false);
45          dynamicNodeInstance.setActive(false);
46          DynamicNode dynamicNode = (DynamicNode) getNode(dynamicNodeInstance.getRouteNode(), DynamicNode.class);
47          DynamicResult result = dynamicNode.transitioningInto(context, dynamicNodeInstance, getRouteHelper());
48          RouteNodeInstance nextNodeInstance = result.getNextNodeInstance();
49          RouteNodeInstance finalNodeInstance = null;
50          if (result.isComplete()) {
51              dynamicNodeInstance.setComplete(true);
52              finalNodeInstance = getFinalNodeInstance(dynamicNodeInstance, context); 
53              if (nextNodeInstance == null) {
54                  nextNodeInstance = finalNodeInstance;
55              }
56          }
57        
58          if (nextNodeInstance !=null) {
59              initializeNodeGraph(context, dynamicNodeInstance, nextNodeInstance, new HashSet<RouteNodeInstance>(), finalNodeInstance);
60          }
61          return nextNodeInstance;   
62      }
63      
64      public ProcessResult isComplete(RouteContext context) throws Exception {
65          throw new UnsupportedOperationException("isComplete() should not be invoked on a Dynamic node!");
66      }
67      
68      public Transition transitionFrom(RouteContext context, ProcessResult processResult) throws Exception {
69          
70          Transition transition = new Transition();
71          RouteNodeInstance dynamicNodeInstance = context.getNodeInstance().getProcess();
72          DynamicNode dynamicNode = (DynamicNode) getNode(dynamicNodeInstance.getRouteNode(), DynamicNode.class);
73          DynamicResult result = dynamicNode.transitioningOutOf(context, getRouteHelper());
74          if (result.getNextNodeInstance() == null && result.getNextNodeInstances().isEmpty() && result.isComplete()) {
75              dynamicNodeInstance.setComplete(true);
76              RouteNodeInstance finalNodeInstance = getFinalNodeInstance(dynamicNodeInstance, context);
77              if (finalNodeInstance != null) {
78                  transition.getNextNodeInstances().add(finalNodeInstance);    
79              }
80          } else {
81              if (result.getNextNodeInstance() != null) {
82                  result.getNextNodeInstance().setProcess(dynamicNodeInstance);
83                  transition.getNextNodeInstances().add(result.getNextNodeInstance());    
84              }
85              for (Iterator iter = result.getNextNodeInstances().iterator(); iter.hasNext();) {
86                  RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iter.next();
87                  nextNodeInstance.setProcess(dynamicNodeInstance);
88              }
89              transition.getNextNodeInstances().addAll(result.getNextNodeInstances());
90          }
91          return transition;
92      }
93  
94      /**
95       * This method checks the next node returned by the user and walks the resulting node graph, filling in required data where possible.
96       * Will throw errors if there is a problem with what the implementor has returned to us. This allows them to do things like return next
97       * nodes with no attached branches, and we will go ahead and generate the branches for them, etc.
98       */
99      private void initializeNodeGraph(RouteContext context, RouteNodeInstance dynamicNodeInstance, RouteNodeInstance nodeInstance, Set<RouteNodeInstance> nodeInstances, RouteNodeInstance finalNodeInstance) throws Exception {
100         if (nodeInstances.contains(nodeInstance)) {
101             throw new RouteManagerException("A cycle was detected in the node graph returned from the dynamic node.", context);
102         }
103         nodeInstances.add(nodeInstance);
104         nodeInstance.setProcess(dynamicNodeInstance);
105         List<RouteNodeInstance> nextNodeInstances = nodeInstance.getNextNodeInstances();
106         
107         if (nextNodeInstances.size() > 1) {
108             // TODO implement this feature
109 //            throw new UnsupportedOperationException("Need to implement support for branch generation!");
110         }
111         for (RouteNodeInstance nextNodeInstance : nextNodeInstances)
112         {
113             initializeNodeGraph(context, dynamicNodeInstance, nextNodeInstance, nodeInstances, finalNodeInstance);
114         }
115         if (nextNodeInstances.isEmpty() && finalNodeInstance != null) {
116             nodeInstance.addNextNodeInstance(finalNodeInstance);
117         }
118     }
119 
120     private RouteNodeInstance getFinalNodeInstance(RouteNodeInstance dynamicNodeInstance, RouteContext context) throws Exception {
121         List<RouteNode> nextNodes = dynamicNodeInstance.getRouteNode().getNextNodes();
122         if (nextNodes.size() > 1) {
123             throw new RouteManagerException("There should only be 1 next node following a dynamic node, there were " + nextNodes.size(), context);
124         }
125         RouteNodeInstance finalNodeInstance = null;
126         if (!nextNodes.isEmpty()) {
127             finalNodeInstance = getRouteHelper().getNodeFactory().createRouteNodeInstance(context.getDocument().getDocumentId(), (RouteNode) nextNodes.get(0));
128             finalNodeInstance.setBranch(dynamicNodeInstance.getBranch());
129             finalNodeInstance.setProcess(dynamicNodeInstance.getProcess());
130         }
131         return finalNodeInstance;
132     }
133 }