/*
* ForEachForkAH.java
*
* Created on November 24, 2007, 8:43 PM
*
* Copyright 2007, Britt Miner.
*
*
*
* compsForForking
*
* #{tempForEachForkAHInputItem.getType().getCode()}
*
* inputItem
*
* true
*
* true
*
* noMatch
*
* true
*
* false
*
* noList
*
*/
package org.bminer.jbpm.pd.ah;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
import org.bminer.jbpm.pd.GenericHandler;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
/**
*
* @author bminer
*/
public class ForEachForkAH extends GenericHandler implements ActionHandler {
public Log log = LogFactory.getLog(this.getClass());
// transitions with this prefix won't be used
private String RESERVED_TRANS_PREFIX = "_sys_";
// collection of items for which we will create sub tokens
private String inputCollectionVar;
// property to be accessed to retrieve a String value used for transition matching
// if the collection is not of Strings
private String inputItemProperty;
// variable name under which to store the inputItem object that caused the creation
// of this new child token. The variable will be stored against the new child token.
private String inputItemMappedName;
// match inputCollection items to corresponding transition 'name', or create tokens
// for 'all' combinations of collection items and transitions.
private boolean matchItemWithTrans = true;
// 'true' will be used to reduce the inputCollection to a set prior to matching with
// the available transitions
private boolean matchUnique = false;
// default transition to take for a inputCollection item that has no matching transition name,
// otherwise no token will be generated for that collection item
private String noMatchTrans;
// true if a noMatchTrans should be taken for EACH un-matched item, false if the
// noMatchTrans should be taken only once.
private boolean noMatchDoTransOnce = true;
// true if matched transitions should be taken when a no-match exists, or false
// if only the noMatchTrans(s) should be taken
private boolean noMatchDoMatched = false;
// transition to take if there is nothing in the collection, otherwise
// nothing will be done and we'll halt execution in this node
private String noListTrans;
public void execute(final ExecutionContext ec) throws Exception {
Collection inputs = null; //collection of items for which we would like to fork
final Token token = ec.getToken();
final Node node = ec.getNode();
/* If there is an input list, we'll try to create tokens based on it's entries.
* If there is no list, we'll take the 'noListTrans' transition if
* it has been specified, otherwise we'll do nothing and end execution in this node.
*/
//check for a list...
boolean haveList = false;
if (inputCollectionVar == null || inputCollectionVar.length() == 0) {
log.warn("No 'inputCollectionVar' has been configured for this node.");
}else{
Object o = ec.getContextInstance().getVariable(inputCollectionVar);
if(o == null) {
log.warn("The process variable '"+inputCollectionVar+"' does not exist");
}else{
try{
inputs = (Collection)o;
if(inputs.size() > 0) {
haveList = true;
}else{
log.warn("The input collection '"+inputCollectionVar+"' is empty.");
}
}catch(Exception e) {
log.warn("The process variable '"+inputCollectionVar+"' is not a java.util.Collection! " + e.getMessage());
}
}
}
//bail if there is no list...
if( !haveList ) {
if(noListTrans != null && noListTrans.length() > 0) {
node.leave(ec, noListTrans);
}else{
log.warn("...and no default transition is configured either--we're stuck here.");
}
}else{
//build the available transition list
List transList = this.processTransitions(node);
// list of token/transition sets that will need to be signaled
//for items with matching transitions when 'matchItemWithTtrans' is 'true'
List matchedArgSets = new LinkedList();
//for items that are not matched to specific transtions, or generally when 'matchItemWithTtrans' is 'false'
List unmatchedArgSets = new LinkedList();
// Check the matchCriteria. If we should match all we'll simply send
// tokens for each item in the list down every configured transition, except that no
// tokens will be sent down the noMatchTrans, the noListTrans,
// or any transition whose name begins with "_sys_".
// If we should match item names with equivalent transition names, then we'll send a token
// for each item in the list only down those transitions whose names match the item. If an
// item has no correspondingly named transition, it's token will be sent down the 'noMatchTrans'
// transition if one is configured, or if not, no token will be created.
// Reduce the inputs to a Set if so configured
if(matchUnique) {
inputs = new HashSet(inputs);
}
Map transitionNameCounts = new HashMap();
boolean haveANoMatchEntry = false;
for(Object inputItemObj: inputs) {
String inputItemName = this.getStringValue(inputItemObj, inputItemProperty, ec);
if(!matchItemWithTrans) {
for(Object o2: transList) {
String transName = (String)o2;
String newTokenName = node.getName() + "." + inputItemName + "-" + transName;
if(!matchUnique) {
//Ensure this new token's name is unique
Integer transNameCount = transitionNameCounts.get(inputItemName + transName);
if(transNameCount == null) transNameCount = 0;
transitionNameCounts.put(inputItemName + transName, ++transNameCount);
newTokenName += ("_" + transNameCount);
}
unmatchedArgSets.add(new Object[] {newTokenName, transName});
}
}else if(matchItemWithTrans) {
String transName = null;
//Do we have a valid transition for this input item?
if(transList.contains(inputItemName)) {
transName = inputItemName;
}else if(noMatchTrans != null && noMatchTrans.length() > 0) {
if(!noMatchDoTransOnce || !haveANoMatchEntry) {
transName = noMatchTrans;
haveANoMatchEntry = true;
}
}
// If we haven't assigned a transition name by now, we're simply
// going to ingore that entry.
if(transName != null) {
String newTokenName = node.getName() + "." + inputItemName + "-" + transName;
if(!matchUnique) {
//Ensure this new token's name is unique
Integer transNameCount = transitionNameCounts.get(transName);
if(transNameCount == null) transNameCount = 0;
transitionNameCounts.put(transName, ++transNameCount);
newTokenName += ("_" + transNameCount);
}
//is this transition a valid trans or the configured noMatchTrans?
if(transName.equals(noMatchTrans)) {
unmatchedArgSets.add(new Object[] {newTokenName, transName, inputItemObj});
}else{
matchedArgSets.add(new Object[] {newTokenName, transName, inputItemObj});
}
}
}
}
List argSets = new LinkedList();
argSets.addAll(unmatchedArgSets);
if(noMatchDoMatched || unmatchedArgSets.isEmpty()) {
argSets.addAll(matchedArgSets);
}
// Create each token.
List