package ee.telekom.workflow.graph.node.gateway;
import org.apache.commons.lang3.tuple.Pair;
import ee.telekom.workflow.graph.GraphEngine;
import ee.telekom.workflow.graph.GraphInstance;
import ee.telekom.workflow.graph.Token;
import ee.telekom.workflow.graph.node.gateway.condition.Condition;
/**
* Gateway, that is implements the "exclusive choice" workflow pattern and the "structured loop" pattern. At the same time it can be used
* as a means to jump within the workflow graph similar to "goto" statements. It routes the incoming token based on the evaluation of
* runtime conditions to a subsequent branch.
* <p>
* {@link XorFork}s do not need to be used necessarily in a balanced fork-join block as {@link AndFork}/{@link OrFork}s do in connection
* with {@link AndJoin} and {@link CancellingDiscriminator}s. They may be used in pair with a {@link XorJoin}, but do not have to be. In
* that respect, the {@link XorFork} may be understood as a "goto" jump statement known from older programming languages. As such, it may
* be used to implement classic if-elseif-else, do-while/repeat-until or while/do constructs.<br>
* The underlying token model slightly restricts the use of "random" goto jumps. Namely, you may NOT cross balanced fork-join block
* structure boundaries of {@link AndFork} and {@link OrFork}s. That means, it is neither allowed to jump into a fork-join block from nodes
* outside the block nor is it allowed to exit a fork-join with an {@link XorFork} jump.
* <p>
* The figure below depicts the five valid usage patterns of {@link XorFork}s:
* <ol>
* <li>A {@link XorFork}-{@link XorJoin} block modelling an if-elseif/if-else block
* <li>A single {@link XorFork} modelling an if-elseif/if-else block (semantically identical to the previous usage pattern)
* <li>A single {@link XorFork} modelling an if-elseif/if-else block with each subsequent branch ending implicitly.
* <li>A repeat-until/do-while loop
* <li>A while-do loop
* <li>A random "goto" jump.
*
* <pre>
* +-(condition1)-[3]--+
* [1]--[XOR] [XOR]--[6]
* +-(condition2)-[4]--+
*
* +-(condition1)-[3]--+
* [1]--[XOR] [6]
* +-(condition2)-[4]--+
*
* +-(condition1)-[3]
* [1]--[XOR]
* +-(condition2)-[4]
*
* [1]--[2]--[XOR]-(else)-[4]
* | |(condition1)
* +----<----+
*
* +-(else)-[5]
* |
* [1]--[XOR]-(condition1)-[3]--[4]
* | |
* +----------<-----------+
*
* +-(goto)-[5]
* |
* [1]--[XOR]-(condition1)-[3]--[XOR]--[4]
* | |
* +--------------<--------------+
* </pre>
* Note that while an {@link OrFork} executes 0 to all subsequent branches, this kind of gateway selects 0 to 1
* subsequent branches depending on the transition conditions. If no subsequent branches is selected, the incoming
* token is terminated.
*/
public class XorFork extends AbstractConditionalGateway{
public XorFork( int id ){
super( id );
}
public XorFork( int id, String name ){
super( id, name );
}
@Override
public void execute( GraphEngine engine, Token token ){
GraphInstance instance = token.getInstance();
for( Pair<Condition, String> pair : getConditions() ){
Condition condition = pair.getLeft();
String transitionName = pair.getRight();
if( condition == null || condition.evaluate( instance ) ){
engine.complete( token, null, transitionName );
return;
}
}
// No suitable branch was found. Terminating the token's execution
engine.terminate( token );
}
@Override
public void cancel( GraphEngine engine, Token token ){
// Tokens cannot "wait" at this kind of node since the execution
// is synchronous. Hence, no "cancel" action is required.
}
}