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 }