/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.exec;
import org.openflexo.antar.Assignment;
import org.openflexo.antar.Conditional;
import org.openflexo.antar.ControlGraph;
import org.openflexo.antar.Declaration;
import org.openflexo.antar.Procedure;
import org.openflexo.antar.ProcedureCall;
import org.openflexo.antar.Type;
import org.openflexo.antar.expr.ArithmeticBinaryOperator;
import org.openflexo.antar.expr.BinaryOperatorExpression;
import org.openflexo.antar.expr.BooleanBinaryOperator;
import org.openflexo.antar.expr.Constant;
import org.openflexo.antar.expr.Expression;
import org.openflexo.antar.expr.Variable;
import org.openflexo.foundation.exec.expr.StoredTokensOnPrecondition;
import org.openflexo.foundation.exec.inst.StoreTokenOnPrecondition;
import org.openflexo.foundation.wkf.edge.FlexoPostCondition;
import org.openflexo.foundation.wkf.edge.TokenEdge;
import org.openflexo.foundation.wkf.node.AbstractNode;
import org.openflexo.foundation.wkf.node.ActionNode;
import org.openflexo.foundation.wkf.node.FlexoPreCondition;
import org.openflexo.foundation.wkf.node.SelfExecutableActionNode;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.toolbox.StringUtils;
import org.openflexo.toolbox.ToolBox;
public class SendTokenToPrecondition extends ControlGraphBuilder {
private FlexoPreCondition precondition;
private FlexoPostCondition<?, ?> edge;
private SendTokenToPrecondition(FlexoPreCondition precondition, FlexoPostCondition<?, ?> edge) {
super();
this.edge = edge;
this.precondition = precondition;
if (edge == null && precondition.getIncomingPostConditions().size() == 1) {
// No need to make it ambigous, consider the only one incoming edge
this.edge = precondition.getIncomingPostConditions().firstElement();
}
}
public static ControlGraphBuilder getSendTokenToPreconditionBuilder(FlexoPreCondition precondition, FlexoPostCondition<?, ?> edge)
throws NotSupportedException, InvalidModelException {
return new SendTokenToPrecondition(precondition, edge);
}
/**
* Returns control graph associated to token sending to precondition
*
* @param node
* @return the computed control graph
* @throws NotSupportedException
* when an element contained in the model is not currently supported by execution model
* @throws InvalidModelException
* when the model is not conform (validation should have failed) and thus workflow cannot be computed
*/
public static ControlGraph sendTokenToPrecondition(FlexoPreCondition precondition, FlexoPostCondition<?, ?> edge,
boolean interprocedural) throws NotSupportedException, InvalidModelException {
ControlGraphBuilder cgBuilder = getSendTokenToPreconditionBuilder(precondition, edge);
AbstractNode edgeOrigin = null;
if (edge != null) {
edgeOrigin = edge.getStartNode();
}
if (interprocedural) {
Procedure procedure = cgBuilder.makeProcedure();
ProcedureCall returned = new ProcedureCall(procedure);
if (precondition.getIncomingPostConditions().size() > 1) {
if (edge == null) {
throw new InvalidModelException("Precondition receives a token from a null origin edge");
}
returned.addArgument(new Constant.IntegerConstant(edge.getFlexoID()));
}
returned.appendHeaderComment("Precondition " + precondition.getName() + " receive a new token from node "
+ (edgeOrigin != null ? edgeOrigin.getName() : "null"), true);
return returned;
} else {
ControlGraph returned = cgBuilder.makeControlGraph(interprocedural);
returned.appendHeaderComment("Precondition " + precondition.getName() + " receive a new token from node "
+ (edgeOrigin != null ? edgeOrigin.getName() : "null"), true);
return returned;
}
}
@Override
protected String getProcedureComment() {
StringBuffer returned = new StringBuffer();
if (getPrecondition().getAttachedBeginNode() != null) {
returned.append(FlexoLocalization.localizedForKeyWithParams(
"this_method_represents_code_to_be_executed_when_precondition_($0)_receive_a_new_token", getPrecondition().getName()));
} else {
returned.append(FlexoLocalization.localizedForKeyWithParams(
"this_method_represents_code_to_be_executed_when_precondition_of_node_($0)_receive_a_new_token", getPrecondition()
.getAttachedNode().getName()));
}
if (getPrecondition().getIncomingPostConditions().size() > 1) {
returned.append(StringUtils.LINE_SEPARATOR);
returned.append(StringUtils.LINE_SEPARATOR);
returned.append("@param " + getEdgeVariable().getName() + " ");
returned.append(FlexoLocalization.localizedForKey("identifier_of_edge_which_activates_this_node"));
}
return returned.toString();
}
private Variable edgeVariable = null;
private Variable getEdgeVariable() {
if (edgeVariable == null) {
edgeVariable = new Variable("edgeId");
}
return edgeVariable;
}
private Variable tokenIncrementVariable = null;
private Variable getTokenIncrementVariable() {
if (tokenIncrementVariable == null) {
tokenIncrementVariable = new Variable("tokenIncrement");
}
return tokenIncrementVariable;
}
/**
* Overrides parent's method by providing precondition identifier as argument
*/
@Override
protected Procedure makeProcedure() throws InvalidModelException, NotSupportedException {
if (getPrecondition().getIncomingPostConditions().size() > 1) {
return new Procedure(getProcedureName(), makeControlGraph(true), getProcedureComment(), new Procedure.ProcedureParameter(
getEdgeVariable(), new Type("int")));
} else {
return super.makeProcedure();
}
}
private ControlGraph makeControlGraphForTokenIncrementSetting() {
Declaration decl = new Declaration(new Type("int"), getTokenIncrementVariable(), null);
boolean hasDifferentValues = false;
int tokenIncrementValue = 1;
for (FlexoPostCondition edge : getPrecondition().getIncomingPostConditions()) {
if (edge instanceof TokenEdge) {
if (!hasDifferentValues && ((TokenEdge) edge).getTokenIncrem() != tokenIncrementValue) {
hasDifferentValues = true;
}
tokenIncrementValue = ((TokenEdge) edge).getTokenIncrem();
}
}
if (!hasDifferentValues) {
decl.setInitializationValue(new Constant.IntegerConstant(tokenIncrementValue));
return decl;
}
Conditional testAndSet = null;
Conditional conditional = null;
for (FlexoPostCondition edge : getPrecondition().getIncomingPostConditions()) {
Expression condition = new BinaryOperatorExpression(BooleanBinaryOperator.EQUALS, getEdgeVariable(),
new Constant.IntegerConstant(edge.getFlexoID()));
Conditional currentConditional = new Conditional(condition, new Assignment(getTokenIncrementVariable(),
new Constant.IntegerConstant(edge instanceof TokenEdge ? ((TokenEdge) edge).getTokenIncrem() : 1)),
"Precondition receive token from edge " + edge.getDerivedNameFromStartingObject());
if (conditional != null) {
conditional.setElseStatement(currentConditional);
} else {
testAndSet = currentConditional;
}
conditional = currentConditional;
}
return makeSequentialControlGraph(decl, testAndSet);
}
@Override
protected ControlGraph makeControlGraph(boolean interprocedural) throws InvalidModelException, NotSupportedException {
Expression tokenIncrement = null;
ControlGraph setTokenIncrement = null;
if (interprocedural && getPrecondition().getIncomingPostConditions().size() > 1) {
setTokenIncrement = makeControlGraphForTokenIncrementSetting();
tokenIncrement = getTokenIncrementVariable();
} else {
int tokenIncrementValue = edge != null ? edge.getTokenIncrem() : 1;
tokenIncrement = new Constant.IntegerConstant(tokenIncrementValue);
}
StoredTokensOnPrecondition storedTokensOnPrecondition = new StoredTokensOnPrecondition(precondition);
Expression storedTokensOnPreconditionIncrementedWithCurrentTokenIncrement = new BinaryOperatorExpression(
ArithmeticBinaryOperator.ADDITION, storedTokensOnPrecondition, tokenIncrement);
if (precondition.getInitTokenNbr() != 0) {
storedTokensOnPreconditionIncrementedWithCurrentTokenIncrement = new BinaryOperatorExpression(
ArithmeticBinaryOperator.ADDITION, storedTokensOnPreconditionIncrementedWithCurrentTokenIncrement,
new Constant.IntegerConstant(precondition.getInitTokenNbr()));
}
Expression condition = new BinaryOperatorExpression(BooleanBinaryOperator.GREATER_THAN_OR_EQUALS,
storedTokensOnPreconditionIncrementedWithCurrentTokenIncrement,
new Constant.IntegerConstant(precondition.getTriggerLevel()));
StoreTokenOnPrecondition STORE_TOKEN = new StoreTokenOnPrecondition(precondition);
if (precondition.getAttachedNode() instanceof ActionNode && !(precondition.getAttachedNode() instanceof SelfExecutableActionNode)
&& ((ActionNode) precondition.getAttachedNode()).isNormalNode()) {
// If this is a FlexoAction, just store token
return STORE_TOKEN;
}
else {
if (precondition.getIncomingPostConditions().size() == 1 && precondition.getIncomingPostConditions().firstElement() == edge
&& tokenIncrement instanceof Constant.IntegerConstant && ((Constant.IntegerConstant) tokenIncrement).getValue() == 1
&& precondition.getInitTokenNbr() == 0 && precondition.getTriggerLevel() == 1) {
// In this special case where i am the only one edge arriving to this precondition
// and if init token number was set to one, i skip the test, asserting this is
// trivial workflow logic
return NodeActivation.activateNode(precondition.getAttachedNode(), precondition, interprocedural);
} else {
return makeSequentialControlGraph(
setTokenIncrement,
new Conditional(condition, NodeActivation.activateNode(precondition.getAttachedNode(), precondition,
interprocedural), STORE_TOKEN, "Test if precondition"
+ (precondition.getAttachedBeginNode() != null ? " " + precondition.getName() : "") + " of node "
+ precondition.getAttachedNode().getName() + " is triggering"));
}
}
}
@Override
protected String getProcedureName() {
if (getPrecondition() != null && getPrecondition().getAttachedNode() != null) {
if (getPrecondition().getAttachedBeginNode() != null) {
return "precondition_" + ToolBox.capitalize(ToolBox.getJavaName(getPrecondition().getName())) + "_of_"
+ ToolBox.capitalize(ToolBox.getJavaName(getPrecondition().getAttachedNode().getName())) + "_"
+ getPrecondition().getFlexoID() + "_receiveNewToken";
} else {
return "precondition_" + ToolBox.capitalize(ToolBox.getJavaName(getPrecondition().getAttachedNode().getName())) + "_"
+ getPrecondition().getFlexoID() + "_receiveNewToken";
}
} else if (getPrecondition() != null) {
return "precondition_" + ToolBox.capitalize(ToolBox.getJavaName(getPrecondition().getName())) + "_of_???_"
+ getPrecondition().getFlexoID() + "_receiveNewToken";
} else {
return "precondition_???_receiveNewToken";
}
}
public FlexoPostCondition<?, ?> getEdge() {
return edge;
}
public AbstractNode getEdgeOrigin() {
if (getEdge() != null) {
return getEdge().getStartNode();
}
return null;
}
public FlexoPreCondition getPrecondition() {
return precondition;
}
}