001    /*
002     * Copyright 2005-2007 The Kuali Foundation
003     * 
004     * 
005     * Licensed under the Educational Community License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     * 
009     * http://www.opensource.org/licenses/ecl2.php
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.kuali.rice.kew.engine.node;
018    
019    import java.util.HashSet;
020    import java.util.Iterator;
021    import java.util.Set;
022    import java.util.StringTokenizer;
023    
024    import org.kuali.rice.kew.engine.RouteContext;
025    import org.kuali.rice.kew.exception.WorkflowRuntimeException;
026    import org.kuali.rice.kew.service.KEWServiceLocator;
027    import org.kuali.rice.kew.util.Utilities;
028    
029    
030    /**
031     * A basic implementation of the JoinEngine which handles join setup and makes determinations
032     * as to when a join condition has been satisfied.
033     *
034     * @author Kuali Rice Team (rice.collab@kuali.org)
035     */
036    public class BasicJoinEngine implements JoinEngine {
037    
038        public static final String EXPECTED_JOINERS = "ExpectedJoiners";
039        public static final String ACTUAL_JOINERS = "ActualJoiners";
040        
041        public void createExpectedJoinState(RouteContext context, RouteNodeInstance joinInstance, RouteNodeInstance previousNodeInstance) {
042            RouteNodeInstance splitNode = previousNodeInstance.getBranch().getSplitNode();
043            if (splitNode == null) {
044                throw new WorkflowRuntimeException("The split node retrieved from node with name '" + previousNodeInstance.getName() + "' and branch with name '" + previousNodeInstance.getBranch().getName() + "' was null");
045            }
046            for (Iterator iter = splitNode.getNextNodeInstances().iterator(); iter.hasNext();) {
047                RouteNodeInstance splitNodeNextNode = (RouteNodeInstance) iter.next();
048                splitNodeNextNode.getBranch().setJoinNode(joinInstance);
049                saveBranch(context, splitNodeNextNode.getBranch());
050                addExpectedJoiner(joinInstance, splitNodeNextNode.getBranch());
051            }
052            joinInstance.setBranch(splitNode.getBranch());
053            joinInstance.setProcess(splitNode.getProcess());
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 || Utilities.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 void saveBranch(RouteContext context, Branch branch) {
103            if (!context.isSimulation()) {
104                KEWServiceLocator.getRouteNodeService().save(branch);
105            }
106        }
107    
108    }