-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implemented new FlowNavigator, refactoring, docu
Issue #237
- Loading branch information
Showing
10 changed files
with
931 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 0 additions & 54 deletions
54
open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/navigation/BPMNFlowIterator.java
This file was deleted.
Oops, something went wrong.
162 changes: 130 additions & 32 deletions
162
open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/navigation/BPMNFlowNavigator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,167 @@ | ||
package org.openbpmn.bpmn.navigation; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Comparator; | ||
import java.util.Iterator; | ||
import java.util.LinkedHashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
import java.util.logging.Logger; | ||
|
||
import org.openbpmn.bpmn.elements.Gateway; | ||
import org.openbpmn.bpmn.elements.SequenceFlow; | ||
import org.openbpmn.bpmn.elements.core.BPMNElementNode; | ||
import org.openbpmn.bpmn.exceptions.BPMNValidationException; | ||
|
||
/** | ||
* The BPMNFlowNavigator can be used to navigate through a BPMN model. Based on | ||
* a Source BPMNElement the navigator routes to the next Flow Element fulfilling | ||
* a | ||
* given criteria. For example you can find all Event Nodes followed by an | ||
* Activity Node. Or you can navigate to the next Activity from an Event. | ||
* <p> | ||
* The BPMNFlowNavigator expects a filter argument (Functional Interface | ||
* Predicate) with one BPMNElementNode argument that return a boolean value. | ||
* <p> | ||
* In case the filter does not specify a Gateway, Gateway Nodes are ignored. | ||
* | ||
*/ | ||
public class BPMNFlowNavigator<T> implements Iterator<BPMNElementNode> { | ||
|
||
private Predicate<BPMNElementNode> filter; | ||
protected static Logger logger = Logger.getLogger(BPMNElementNode.class.getName()); | ||
|
||
private Predicate<BPMNElementNode> filter = null; | ||
private Predicate<String> conditionEvaluator = null; | ||
private int index; | ||
BPMNElementNode bpmnElementNode = null; | ||
List<SequenceFlow> outgoingFlows; | ||
Iterator<SequenceFlow> flowIterator; | ||
BPMNElementNode targetNode = null; | ||
private List<BPMNElementNode> targetNodes; | ||
|
||
/** | ||
* Creates a new BPMNFlowNavigator with a given filter criteria. | ||
* The method collects all BPMNElements following the given start element and | ||
* matching the given filter | ||
* | ||
* @param bpmnElementNode | ||
* @param filter | ||
* @throws BPMNValidationException | ||
*/ | ||
public BPMNFlowNavigator(BPMNElementNode bpmnElementNode, Predicate<BPMNElementNode> filter) | ||
throws BPMNValidationException { | ||
this.bpmnElementNode = bpmnElementNode; | ||
this.filter = filter; | ||
this.targetNodes = new ArrayList<>(); | ||
this.index = 0; | ||
|
||
// find outgoing flows | ||
outgoingFlows = new ArrayList<>(bpmnElementNode.getOutgoingSequenceFlows()); | ||
|
||
// if we have more than one outgoing flow, we thrown an exception | ||
if (outgoingFlows.size() > 1) { | ||
throw new BPMNValidationException( | ||
"Element '" + bpmnElementNode.getId() + "' should not have more than one outgoing SequenceFlow!"); | ||
} | ||
|
||
// find all elements | ||
findValidNodes(bpmnElementNode); | ||
} | ||
|
||
public BPMNElementNode findTarget(Predicate<T> filter) { | ||
BPMNElementNode result = null; | ||
|
||
return result; | ||
/** | ||
* Creates a new BPMNFlowNavigator with a given filter criteria. | ||
* The method collects all BPMNElements following the given start element and | ||
* matching the given filter | ||
* | ||
* @param bpmnElementNode | ||
* @param filter | ||
* @param conditionEvaluator optional conditional evaluator | ||
* @throws BPMNValidationException | ||
*/ | ||
public BPMNFlowNavigator(BPMNElementNode bpmnElementNode, Predicate<BPMNElementNode> filter, | ||
Predicate<String> conditionEvaluator) | ||
throws BPMNValidationException { | ||
this.filter = filter; | ||
this.conditionEvaluator = conditionEvaluator; | ||
this.targetNodes = new ArrayList<>(); | ||
this.index = 0; | ||
// find all elements | ||
findValidNodes(bpmnElementNode); | ||
} | ||
|
||
@Override | ||
public boolean hasNext() { | ||
while (index < outgoingFlows.size()) { | ||
return index < targetNodes.size(); | ||
} | ||
|
||
SequenceFlow flow = outgoingFlows.get(index); | ||
@Override | ||
public BPMNElementNode next() { | ||
if (!hasNext()) { | ||
throw new IllegalStateException("No more elements"); | ||
} | ||
return targetNodes.get(index++); | ||
} | ||
|
||
targetNode = flow.getTargetElement(); | ||
/** | ||
* Iterates tough all outgoing sequence flows and tests if the target element | ||
* matches the filter criteria. | ||
* <p> | ||
* If a element does not match the filter criteria and is an instance of a | ||
* Gateway, the method will recursive call all following elements of the gateway | ||
* node. | ||
* | ||
* @param currentNode | ||
*/ | ||
private void findValidNodes(BPMNElementNode currentNode) { | ||
Set<SequenceFlow> flowSet = currentNode.getOutgoingSequenceFlows(); | ||
// Check if the sequence flow has a condition and evaluate it if a | ||
// conditionEvaluator is defined | ||
if (currentNode instanceof Gateway && conditionEvaluator != null) { | ||
flowSet = filterConditionalFlows((Gateway) currentNode, flowSet); | ||
} | ||
|
||
if (filter.test(targetNode)) { | ||
return true; | ||
for (SequenceFlow flow : flowSet) { | ||
BPMNElementNode node = flow.getTargetElement(); | ||
if (filter.test(node)) { | ||
targetNodes.add(node); | ||
} else { | ||
// if the node is a Gateway, recursively search its outgoing flows | ||
if (node instanceof Gateway) { | ||
findValidNodes(node); | ||
} | ||
} | ||
index++; | ||
|
||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public BPMNElementNode next() { | ||
if (!hasNext()) { | ||
throw new IllegalStateException("No more elements"); | ||
/** | ||
* This method filters all conditional flows outgoing from a gateway that did | ||
* not eval to true by the given conditionEvaluator. The result is a Set with | ||
* exactly one flow ! | ||
* | ||
* | ||
* @return | ||
*/ | ||
private Set<SequenceFlow> filterConditionalFlows(Gateway gateway, Set<SequenceFlow> flowSet) { | ||
LinkedHashSet<SequenceFlow> result = new LinkedHashSet<SequenceFlow>(); | ||
|
||
List<SequenceFlow> sortedList = new ArrayList<>(flowSet); | ||
// Sort the list using a comparator that places empty conditions at the end | ||
sortedList.sort(Comparator.comparing(flow -> { | ||
String condition = flow.getConditionExpression(); | ||
return (condition == null || condition.trim().isEmpty()) ? 1 : 0; | ||
})); | ||
|
||
// now we eval each condition. As soon as we have a match or the condition is | ||
// empty we stop. | ||
for (SequenceFlow flow : sortedList) { | ||
String condition = flow.getConditionExpression(); | ||
if (condition != null && (conditionEvaluator.test(condition) == true)) { | ||
// the condition matches - return this as the result; | ||
result.add(flow); | ||
return result; | ||
|
||
} | ||
// In case the condition is empty this is the default flow... | ||
if (condition == null || condition.trim().isEmpty()) { | ||
// default condition | ||
result.add(flow); | ||
return result; | ||
} | ||
} | ||
// return (T) outgoingFlows.get(index++); | ||
index++; | ||
return targetNode; | ||
// no match - should not happen | ||
logger.warning("Gateway " + gateway.getName() + " (" + gateway.getId() | ||
+ ") has conditional flows but non of the outgoing sequence flows is matching the condition!"); | ||
return result; | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.