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 */
016package org.kuali.rice.kew.engine.node;
017
018import org.kuali.rice.kew.api.WorkflowRuntimeException;
019import org.kuali.rice.kew.engine.RouteContext;
020import org.kuali.rice.kew.service.KEWServiceLocator;
021import org.kuali.rice.krad.util.LegacyUtils;
022
023import java.util.HashSet;
024import java.util.Iterator;
025import java.util.Set;
026import java.util.StringTokenizer;
027
028
029/**
030 * A basic implementation of the JoinEngine which handles join setup and makes determinations
031 * as to when a join condition has been satisfied.
032 *
033 * @author Kuali Rice Team (rice.collab@kuali.org)
034 */
035public class BasicJoinEngine implements JoinEngine {
036
037    public static final String EXPECTED_JOINERS = "ExpectedJoiners";
038    public static final String ACTUAL_JOINERS = "ActualJoiners";
039    
040    public RouteNodeInstance createExpectedJoinState(RouteContext context, RouteNodeInstance joinInstance, RouteNodeInstance previousNodeInstance) {
041        RouteNodeInstance splitNode = previousNodeInstance.getBranch().getSplitNode();
042        if (splitNode == null) {
043            throw new WorkflowRuntimeException("The split node retrieved from node with name '" + previousNodeInstance.getName() + "' and branch with name '" + previousNodeInstance.getBranch().getName() + "' was null");
044        }
045        for (Iterator iter = splitNode.getNextNodeInstances().iterator(); iter.hasNext();) {
046            RouteNodeInstance splitNodeNextNode = (RouteNodeInstance) iter.next();
047            joinInstance = saveNode(context, joinInstance);
048            splitNodeNextNode.getBranch().setJoinNode(joinInstance);
049            addExpectedJoiner(joinInstance, splitNodeNextNode.getBranch());
050        }
051        joinInstance.setBranch(splitNode.getBranch());
052        joinInstance.setProcess(splitNode.getProcess());
053        return joinInstance;
054    }
055    
056    public void addExpectedJoiner(RouteNodeInstance nodeInstance, Branch branch) {
057        addJoinState(nodeInstance, branch, EXPECTED_JOINERS);
058    }
059
060    public void addActualJoiner(RouteNodeInstance nodeInstance, Branch branch) {
061        addJoinState(nodeInstance, branch, ACTUAL_JOINERS);
062    }
063    
064    private void addJoinState(RouteNodeInstance nodeInstance, Branch branch, String key) {
065        NodeState state = nodeInstance.getNodeState(key);
066        if (state == null) {
067            state = new NodeState();
068            state.setKey(key);
069            state.setValue("");
070            state.setNodeInstance(nodeInstance);
071            nodeInstance.addNodeState(state);
072        }
073        state.setValue(state.getValue()+branch.getBranchId()+",");
074    }
075
076    public boolean isJoined(RouteNodeInstance nodeInstance) {
077        NodeState expectedState = nodeInstance.getNodeState(EXPECTED_JOINERS);
078        if (expectedState == null || org.apache.commons.lang.StringUtils.isEmpty(expectedState.getValue())) {
079            return true;
080        }
081        NodeState actualState = nodeInstance.getNodeState(ACTUAL_JOINERS);
082        Set expectedSet = loadIntoSet(expectedState);
083        Set actualSet = loadIntoSet(actualState);
084        for (Iterator iterator = expectedSet.iterator(); iterator.hasNext();) {
085            String value = (String) iterator.next();
086            if (actualSet.contains(value)) {
087                iterator.remove();
088            }            
089        }
090        return expectedSet.size() == 0;
091    }
092    
093    private Set loadIntoSet(NodeState state) {
094        Set set = new HashSet();
095        StringTokenizer tokenizer = new StringTokenizer(state.getValue(), ",");
096        while (tokenizer.hasMoreTokens()) {
097            set.add(tokenizer.nextToken());
098        }
099        return set;
100    }
101
102    private RouteNodeInstance saveNode(RouteContext context, RouteNodeInstance nodeInstance) {
103        if (!context.isSimulation()) {
104            return KEWServiceLocator.getRouteNodeService().save(nodeInstance);
105        } else {
106            // if we are in simulation mode, lets go ahead and assign some id
107            // values to our beans
108            for (RouteNodeInstance routeNodeInstance : nodeInstance.getNextNodeInstances()) {
109                if (routeNodeInstance.getRouteNodeInstanceId() == null) {
110                    routeNodeInstance.setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
111                }
112            }
113            if (nodeInstance.getProcess() != null && nodeInstance.getProcess().getRouteNodeInstanceId() == null) {
114                nodeInstance.getProcess().setRouteNodeInstanceId(context.getEngineState().getNextSimulationId());
115            }
116            if (nodeInstance.getBranch() != null && nodeInstance.getBranch().getBranchId() == null) {
117                nodeInstance.getBranch().setBranchId(context.getEngineState().getNextSimulationId());
118            }
119            return nodeInstance;
120        }
121    }
122
123}