View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.engine.transition;
17  
18  import org.kuali.rice.kew.engine.RouteContext;
19  import org.kuali.rice.kew.engine.node.*;
20  import org.kuali.rice.kew.exception.RouteManagerException;
21  
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Set;
26  
27  
28  /**
29   * The DynamicTransitionEngine operates on a {@link DynamicNode} and takes the next node instances returned 
30   * by the node and runs returns them in a TransitionResult after doing some processing and validation on them.
31   * 
32   * @see DynamicNode
33   * 
34   * @author Kuali Rice Team (rice.collab@kuali.org)
35   */
36  public class DynamicTransitionEngine extends TransitionEngine {
37  
38      // TODO interate the all the nodes and attach the dynamic node as the 'process'
39      // don't include the dynamic node instance in the routing structure - require a correctly built graph
40      // change dynamic node signiture to next node because of above
41      // reconcile branching if necessary
42      public RouteNodeInstance transitionTo(RouteNodeInstance dynamicNodeInstance, RouteContext context) throws Exception {
43          dynamicNodeInstance.setInitial(false);
44          dynamicNodeInstance.setActive(false);
45          DynamicNode dynamicNode = (DynamicNode) getNode(dynamicNodeInstance.getRouteNode(), DynamicNode.class);
46          DynamicResult result = dynamicNode.transitioningInto(context, dynamicNodeInstance, getRouteHelper());
47          RouteNodeInstance nextNodeInstance = result.getNextNodeInstance();
48          RouteNodeInstance finalNodeInstance = null;
49          if (result.isComplete()) {
50              dynamicNodeInstance.setComplete(true);
51              finalNodeInstance = getFinalNodeInstance(dynamicNodeInstance, context); 
52              if (nextNodeInstance == null) {
53                  nextNodeInstance = finalNodeInstance;
54              }
55          }
56        
57          if (nextNodeInstance !=null) {
58              initializeNodeGraph(context, dynamicNodeInstance, nextNodeInstance, new HashSet<RouteNodeInstance>(), finalNodeInstance);
59          }
60          return nextNodeInstance;   
61      }
62      
63      public ProcessResult isComplete(RouteContext context) throws Exception {
64          throw new UnsupportedOperationException("isComplete() should not be invoked on a Dynamic node!");
65      }
66      
67      public Transition transitionFrom(RouteContext context, ProcessResult processResult) throws Exception {
68          
69          Transition transition = new Transition();
70          RouteNodeInstance dynamicNodeInstance = context.getNodeInstance().getProcess();
71          DynamicNode dynamicNode = (DynamicNode) getNode(dynamicNodeInstance.getRouteNode(), DynamicNode.class);
72          DynamicResult result = dynamicNode.transitioningOutOf(context, getRouteHelper());
73          if (result.getNextNodeInstance() == null && result.getNextNodeInstances().isEmpty() && result.isComplete()) {
74              dynamicNodeInstance.setComplete(true);
75              RouteNodeInstance finalNodeInstance = getFinalNodeInstance(dynamicNodeInstance, context);
76              if (finalNodeInstance != null) {
77                  transition.getNextNodeInstances().add(finalNodeInstance);    
78              }
79          } else {
80              if (result.getNextNodeInstance() != null) {
81                  result.getNextNodeInstance().setProcess(dynamicNodeInstance);
82                  transition.getNextNodeInstances().add(result.getNextNodeInstance());    
83              }
84              for (Iterator iter = result.getNextNodeInstances().iterator(); iter.hasNext();) {
85                  RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iter.next();
86                  nextNodeInstance.setProcess(dynamicNodeInstance);
87              }
88              transition.getNextNodeInstances().addAll(result.getNextNodeInstances());
89          }
90          return transition;
91      }
92  
93      /**
94       * This method checks the next node returned by the user and walks the resulting node graph, filling in required data where possible.
95       * 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
96       * nodes with no attached branches, and we will go ahead and generate the branches for them, etc.
97       */
98      private void initializeNodeGraph(RouteContext context, RouteNodeInstance dynamicNodeInstance, RouteNodeInstance nodeInstance, Set<RouteNodeInstance> nodeInstances, RouteNodeInstance finalNodeInstance) throws Exception {
99          if (nodeInstances.contains(nodeInstance)) {
100             throw new RouteManagerException("A cycle was detected in the node graph returned from the dynamic node.", context);
101         }
102         nodeInstances.add(nodeInstance);
103         nodeInstance.setProcess(dynamicNodeInstance);
104         List<RouteNodeInstance> nextNodeInstances = nodeInstance.getNextNodeInstances();
105         
106         if (nextNodeInstances.size() > 1) {
107             // TODO implement this feature
108 //            throw new UnsupportedOperationException("Need to implement support for branch generation!");
109         }
110         for (RouteNodeInstance nextNodeInstance : nextNodeInstances)
111         {
112             initializeNodeGraph(context, dynamicNodeInstance, nextNodeInstance, nodeInstances, finalNodeInstance);
113         }
114         if (nextNodeInstances.isEmpty() && finalNodeInstance != null) {
115             nodeInstance.addNextNodeInstance(finalNodeInstance);
116         }
117     }
118 
119     private RouteNodeInstance getFinalNodeInstance(RouteNodeInstance dynamicNodeInstance, RouteContext context) throws Exception {
120         List<RouteNode> nextNodes = dynamicNodeInstance.getRouteNode().getNextNodes();
121         if (nextNodes.size() > 1) {
122             throw new RouteManagerException("There should only be 1 next node following a dynamic node, there were " + nextNodes.size(), context);
123         }
124         RouteNodeInstance finalNodeInstance = null;
125         if (!nextNodes.isEmpty()) {
126             finalNodeInstance = getRouteHelper().getNodeFactory().createRouteNodeInstance(context.getDocument().getDocumentId(), (RouteNode) nextNodes.get(0));
127             finalNodeInstance.setBranch(dynamicNodeInstance.getBranch());
128             finalNodeInstance.setProcess(dynamicNodeInstance.getProcess());
129         }
130         return finalNodeInstance;
131     }
132 }