blob: c6f39ea8f2019090514fd6e56a6231dc450b2385 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2012 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.moka.fuml.activities;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.papyrus.moka.fuml.activities.IActivityEdgeInstance;
import org.eclipse.papyrus.moka.fuml.activities.IDecisionNodeActivation;
import org.eclipse.papyrus.moka.fuml.activities.IToken;
import org.eclipse.papyrus.moka.fuml.commonbehavior.IExecution;
import org.eclipse.papyrus.moka.fuml.commonbehavior.IParameterValue;
import org.eclipse.papyrus.moka.fuml.commonbehavior.ParameterValue;
import org.eclipse.papyrus.moka.fuml.debug.Debug;
import org.eclipse.papyrus.moka.fuml.simpleclassifiers.BooleanValue;
import org.eclipse.papyrus.moka.fuml.simpleclassifiers.IValue;
import org.eclipse.uml2.uml.ActivityEdge;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.DecisionNode;
import org.eclipse.uml2.uml.ObjectFlow;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.ValueSpecification;
public class DecisionNodeActivation extends ControlNodeActivation implements IDecisionNodeActivation {
/*
* The current execution of the decision input behavior (if any).
*/
public IExecution decisionInputExecution;
@Override
public void fire(List<IToken> incomingTokens) {
// Get the decision values and test them on each guard.
// Forward the offer over the edges for which the test succeeds.
Debug.println("[fire] Decision node " + this.node.getName() + "...");
// List<Token> incomingTokens = this.takeOfferedTokens();
List<IToken> removedControlTokens = this.removeJoinedControlTokens(incomingTokens);
List<IValue> decisionValues = this.getDecisionValues(incomingTokens);
List<IActivityEdgeInstance> outgoingEdges = this.getOutgoingEdges();
for (int i = 0; i < outgoingEdges.size(); i++) {
IActivityEdgeInstance edgeInstance = outgoingEdges.get(i);
ValueSpecification guard = edgeInstance.getEdge().getGuard();
List<IToken> offeredTokens = new ArrayList<IToken>();
for (int j = 0; j < incomingTokens.size(); j++) {
IToken incomingToken = incomingTokens.get(j);
IValue decisionValue = decisionValues.get(j);
if (this.test(guard, decisionValue)) {
offeredTokens.add(incomingToken);
}
}
if (offeredTokens.size() > 0) {
for (int j = 0; j < removedControlTokens.size(); j++) {
IToken removedControlToken = removedControlTokens.get(j);
offeredTokens.add(removedControlToken);
}
edgeInstance.sendOffer(offeredTokens);
}
}
}
public List<IValue> getDecisionValues(List<IToken> incomingTokens) {
// If there is neither a decision input flow nor a decision input
// behavior, then return the set of values from the incoming tokens.
// [In this case, the single incoming edge must be an object flow.]
// If there is a decision input flow, but no decision input behavior,
// then return a list of the decision input values equal in size to the
// number of incoming tokens.
// If there is both a decision input flow and a decision input behavior,
// then execute the decision input behavior once for each incoming token
// and return the set of resulting values.
// If the primary incoming edge is an object flow, then the value on
// each object token is passed to the decision input behavior, along
// with the decision input flow value, if any.
// If the primary incoming edge is a control flow, then the decision
// input behavior only receives the decision input flow, if any.
IValue decisionInputValue = this.getDecisionInputFlowValue();
List<IValue> decisionValues = new ArrayList<IValue>();
for (int i = 0; i < incomingTokens.size(); i++) {
IToken incomingToken = incomingTokens.get(i);
IValue value = this.executeDecisionInputBehavior(incomingToken.getValue(), decisionInputValue);
decisionValues.add(value);
}
// Debug.println("[getDecisionValues] " + decisionValues.size() +
// " decision value(s):");
for (int i = 0; i < decisionValues.size(); i++) {
IValue decisionValue = decisionValues.get(i);
Debug.println("[getDecisionValues] decisionValues[" + i + "] = " + decisionValue);
}
return decisionValues;
}
public IValue executeDecisionInputBehavior(IValue inputValue, IValue decisionInputValue) {
// Create the decision input execution from the decision input behavior.
// If the behavior has input parameter(s), set the input parameter(s) of
// the execution to the given value(s).
// Execute the decision input execution and then remove it.
// Return the value of the output parameter of the execution.
// If there is no decision input behavior, the decision input value is
// returned, if one is given, otherwise the input value is used as the
// decision value.
Debug.println("[executeDecisionBehavior] inputValue = " + inputValue);
Behavior decisionInputBehavior = ((DecisionNode) (this.node)).getDecisionInput();
IValue decisionInputResult = null;
if (decisionInputBehavior == null) {
if (decisionInputValue != null) {
decisionInputResult = decisionInputValue;
} else {
decisionInputResult = inputValue;
}
} else {
this.decisionInputExecution = this.getExecutionLocus().getFactory().createExecution(decisionInputBehavior, this.getExecutionContext());
int i = 1;
int j = 0;
while ((j == 0 | (j == 1 & decisionInputValue != null)) & i <= decisionInputBehavior.getOwnedParameters().size()) {
Parameter parameter = decisionInputBehavior.getOwnedParameters().get(i - 1);
if (parameter.getDirection().equals(ParameterDirectionKind.IN_LITERAL) | parameter.getDirection().equals(ParameterDirectionKind.INOUT_LITERAL)) {
ParameterValue inputParameterValue = new ParameterValue();
inputParameterValue.parameter = parameter;
j = j + 1;
if (j == 1 && inputValue != null) {
inputParameterValue.values.add(inputValue);
} else {
inputParameterValue.values.add(decisionInputValue);
}
this.decisionInputExecution.setParameterValue(inputParameterValue);
}
i = i + 1;
}
this.decisionInputExecution.execute();
List<IParameterValue> outputParameterValues = this.decisionInputExecution.getOutputParameterValues();
decisionInputExecution.destroy();
if (outputParameterValues.get(0).getValues().size() == 0) {
// FIXME Added for connection with debug API
// When execution stops due to client request, outputParameterValues is empty
// Just puts true. Execution flow is supposed to stop "by itself" then
decisionInputResult = new BooleanValue();
((BooleanValue) decisionInputResult).value = true;
} else {
decisionInputResult = outputParameterValues.get(0).getValues().get(0);
}
}
return decisionInputResult;
}
@Override
public void terminate() {
// Terminate the decision input execution, if any, and then terminate
// this activation.
if (this.decisionInputExecution != null) {
this.decisionInputExecution.terminate();
}
super.terminate();
}
@Override
public Boolean isReady() {
// Check that all incoming edges have sources that are offering tokens.
// [This should be at most two incoming edges, if there is a decision
// input flow.]
int i = 1;
boolean ready = true;
while (ready & i <= this.incomingEdges.size()) {
ready = this.incomingEdges.get(i - 1).hasOffer();
i = i + 1;
}
return ready;
}
@Override
public List<IToken> takeOfferedTokens() {
// Get tokens from the incoming edge that is not the decision input
// flow.
ObjectFlow decisionInputFlow = ((DecisionNode) (this.node)).getDecisionInputFlow();
List<IToken> allTokens = new ArrayList<IToken>();
List<IActivityEdgeInstance> incomingEdges = this.getIncomingEdges();
for (int i = 0; i < incomingEdges.size(); i++) {
IActivityEdgeInstance edgeInstance = incomingEdges.get(i);
if (edgeInstance.getEdge() != decisionInputFlow) {
List<IToken> tokens = edgeInstance.takeOfferedTokens();
for (int j = 0; j < tokens.size(); j++) {
allTokens.add(tokens.get(j));
}
}
}
return allTokens;
}
public IValue getDecisionInputFlowValue() {
// Take the next token available on the decision input flow, if any, and
// return its value.
IActivityEdgeInstance decisionInputFlowInstance = this.getDecisionInputFlowInstance();
IValue value = null;
if (decisionInputFlowInstance != null) {
List<IToken> tokens = decisionInputFlowInstance.takeOfferedTokens();
if (tokens.size() > 0) {
value = tokens.get(0).getValue();
}
}
return value;
}
public IActivityEdgeInstance getDecisionInputFlowInstance() {
// Get the activity edge instance for the decision input flow, if any.
ActivityEdge decisionInputFlow = ((DecisionNode) (this.node)).getDecisionInputFlow();
IActivityEdgeInstance edgeInstance = null;
if (decisionInputFlow != null) {
int i = 1;
while (edgeInstance == null & i <= this.getIncomingEdges().size()) {
IActivityEdgeInstance incomingEdge = this.getIncomingEdges().get(i - 1);
if (incomingEdge.getEdge() == decisionInputFlow) {
edgeInstance = incomingEdge;
}
i = i + 1;
}
}
return edgeInstance;
}
public Boolean test(ValueSpecification guard, IValue value) {
// Test if the given value matches the guard. If there is no guard,
// return true.
boolean guardResult = true;
if (guard != null) {
IValue guardValue = this.getExecutionLocus().getExecutor().evaluate(guard);
guardResult = guardValue.equals(value);
}
return guardResult;
}
public List<IToken> removeJoinedControlTokens(List<IToken> incomingTokens) {
// If the primary incoming edge is an object flow, then remove any
// control tokens from the incoming tokens and return them.
// [Control tokens may effectively be offered on an object flow outgoing
// from a join node that has both control and object flows incoming.]
List<IToken> removedControlTokens = new ArrayList<IToken>();
if (this.hasObjectFlowInput()) {
int i = 1;
while (i <= incomingTokens.size()) {
IToken token = incomingTokens.get(i - 1);
if (token.isControl()) {
removedControlTokens.add(token);
incomingTokens.remove(i - 1);
i = i - 1;
}
i = i + 1;
}
}
return removedControlTokens;
}
public Boolean hasObjectFlowInput() {
// Check that the primary incoming edge is an object flow.
ActivityEdge decisionInputFlow = ((DecisionNode) (this.node)).getDecisionInputFlow();
boolean isObjectFlow = false;
int i = 1;
while (!isObjectFlow & i <= this.incomingEdges.size()) {
ActivityEdge edge = this.incomingEdges.get(i - 1).getEdge();
isObjectFlow = edge != decisionInputFlow & edge instanceof ObjectFlow;
i = i + 1;
}
return isObjectFlow;
}
}