001 /**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.kew.engine.transition;
017
018 import org.kuali.rice.kew.engine.RouteContext;
019 import org.kuali.rice.kew.engine.node.*;
020 import org.kuali.rice.kew.exception.RouteManagerException;
021
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.Set;
026
027
028 /**
029 * The DynamicTransitionEngine operates on a {@link DynamicNode} and takes the next node instances returned
030 * by the node and runs returns them in a TransitionResult after doing some processing and validation on them.
031 *
032 * @see DynamicNode
033 *
034 * @author Kuali Rice Team (rice.collab@kuali.org)
035 */
036 public class DynamicTransitionEngine extends TransitionEngine {
037
038 // TODO interate the all the nodes and attach the dynamic node as the 'process'
039 // don't include the dynamic node instance in the routing structure - require a correctly built graph
040 // change dynamic node signature to next node because of above
041 // reconcile branching if necessary
042 public RouteNodeInstance transitionTo(RouteNodeInstance dynamicNodeInstance, RouteContext context) throws Exception {
043 dynamicNodeInstance.setInitial(false);
044 dynamicNodeInstance.setActive(false);
045 DynamicNode dynamicNode = (DynamicNode) getNode(dynamicNodeInstance.getRouteNode(), DynamicNode.class);
046 DynamicResult result = dynamicNode.transitioningInto(context, dynamicNodeInstance, getRouteHelper());
047 RouteNodeInstance nextNodeInstance = result.getNextNodeInstance();
048 RouteNodeInstance finalNodeInstance = null;
049 if (result.isComplete()) {
050 dynamicNodeInstance.setComplete(true);
051 finalNodeInstance = getFinalNodeInstance(dynamicNodeInstance, context);
052 if (nextNodeInstance == null) {
053 nextNodeInstance = finalNodeInstance;
054 }
055 }
056
057 if (nextNodeInstance !=null) {
058 initializeNodeGraph(context, dynamicNodeInstance, nextNodeInstance, new HashSet<RouteNodeInstance>(), finalNodeInstance);
059 }
060 return nextNodeInstance;
061 }
062
063 public ProcessResult isComplete(RouteContext context) throws Exception {
064 throw new UnsupportedOperationException("isComplete() should not be invoked on a Dynamic node!");
065 }
066
067 public Transition transitionFrom(RouteContext context, ProcessResult processResult) throws Exception {
068
069 Transition transition = new Transition();
070 RouteNodeInstance dynamicNodeInstance = context.getNodeInstance().getProcess();
071 DynamicNode dynamicNode = (DynamicNode) getNode(dynamicNodeInstance.getRouteNode(), DynamicNode.class);
072 DynamicResult result = dynamicNode.transitioningOutOf(context, getRouteHelper());
073 if (result.getNextNodeInstance() == null && result.getNextNodeInstances().isEmpty() && result.isComplete()) {
074 dynamicNodeInstance.setComplete(true);
075 RouteNodeInstance finalNodeInstance = getFinalNodeInstance(dynamicNodeInstance, context);
076 if (finalNodeInstance != null) {
077 transition.getNextNodeInstances().add(finalNodeInstance);
078 }
079 } else {
080 if (result.getNextNodeInstance() != null) {
081 result.getNextNodeInstance().setProcess(dynamicNodeInstance);
082 transition.getNextNodeInstances().add(result.getNextNodeInstance());
083 }
084 for (Iterator iter = result.getNextNodeInstances().iterator(); iter.hasNext();) {
085 RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iter.next();
086 nextNodeInstance.setProcess(dynamicNodeInstance);
087 }
088 transition.getNextNodeInstances().addAll(result.getNextNodeInstances());
089 }
090 return transition;
091 }
092
093 /**
094 * This method checks the next node returned by the user and walks the resulting node graph, filling in required data where possible.
095 * Will throw errors if there is a problem with what the implementer has returned to us. This allows them to do things like return next
096 * nodes with no attached branches, and we will go ahead and generate the branches for them, etc.
097 */
098 private void initializeNodeGraph(RouteContext context, RouteNodeInstance dynamicNodeInstance, RouteNodeInstance nodeInstance, Set<RouteNodeInstance> nodeInstances, RouteNodeInstance finalNodeInstance) throws Exception {
099 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 }