blob: 5918b3ad270c776dddb23f1d71954d7be19fcf96 [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
* Jeremie Tatibouet (CEA LIST) - Apply fix fUML12-10 certain boolean flags are not properly initialized in come cases
* Jeremie Tatibouet (CEA LIST) - Apply fix fUML12-34 AcceptEventActionActivation::match should match instances of descendants of a trigger's signal
*
*****************************************************************************/
package org.eclipse.papyrus.moka.fuml.Semantics.impl.Actions.BasicActions;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.papyrus.moka.fuml.Semantics.Actions.BasicActions.IActionActivation;
import org.eclipse.papyrus.moka.fuml.Semantics.Actions.BasicActions.IPinActivation;
import org.eclipse.papyrus.moka.fuml.Semantics.Activities.IntermediateActivities.IActivityEdgeInstance;
import org.eclipse.papyrus.moka.fuml.Semantics.Activities.IntermediateActivities.IActivityNodeActivation;
import org.eclipse.papyrus.moka.fuml.Semantics.Activities.IntermediateActivities.IActivityNodeActivationGroup;
import org.eclipse.papyrus.moka.fuml.Semantics.Activities.IntermediateActivities.IObjectToken;
import org.eclipse.papyrus.moka.fuml.Semantics.Activities.IntermediateActivities.IToken;
import org.eclipse.papyrus.moka.fuml.Semantics.Classes.Kernel.IFeatureValue;
import org.eclipse.papyrus.moka.fuml.Semantics.Classes.Kernel.ILink;
import org.eclipse.papyrus.moka.fuml.Semantics.Classes.Kernel.IValue;
import org.eclipse.papyrus.moka.fuml.Semantics.impl.Activities.IntermediateActivities.ActivityEdgeInstance;
import org.eclipse.papyrus.moka.fuml.Semantics.impl.Activities.IntermediateActivities.ActivityNodeActivation;
import org.eclipse.papyrus.moka.fuml.Semantics.impl.Activities.IntermediateActivities.ControlToken;
import org.eclipse.papyrus.moka.fuml.Semantics.impl.Activities.IntermediateActivities.ForkNodeActivation;
import org.eclipse.papyrus.moka.fuml.Semantics.impl.Activities.IntermediateActivities.ObjectToken;
import org.eclipse.papyrus.moka.fuml.Semantics.impl.Classes.Kernel.BooleanValue;
import org.eclipse.papyrus.moka.fuml.debug.Debug;
import org.eclipse.uml2.uml.Action;
import org.eclipse.uml2.uml.ActivityNode;
import org.eclipse.uml2.uml.ConditionalNode;
import org.eclipse.uml2.uml.InputPin;
import org.eclipse.uml2.uml.LiteralBoolean;
import org.eclipse.uml2.uml.LoopNode;
import org.eclipse.uml2.uml.OutputPin;
import org.eclipse.uml2.uml.Pin;
import org.eclipse.uml2.uml.UMLFactory;
public abstract class ActionActivation extends ActivityNodeActivation implements IActionActivation {
/*
* The activations of the pins owned by the action of this action
* activation.
*/
public List<IPinActivation> pinActivations = new ArrayList<IPinActivation>();
/*
* Whether this action activation is already firing. This attribute is only
* used if the action for this action activation has isLocallyReentrant =
* false (the default). If isLocallyReentrant=true, then firing always just
* remains false.
*/
public Boolean firing;
@Override
public void run() {
// Run this action activation and any outoging fork node attached to it.
super.run();
if (this.outgoingEdges.size() > 0) {
this.outgoingEdges.get(0).getTarget().run();
}
this.firing = false;
}
@Override
public List<IToken> takeOfferedTokens() {
// If the action is not locally reentrant, then mark this activation as
// firing.
// Take any incoming offers of control tokens, then concurrently fire
// all input pin activations.
// Note: This is included here to happen in the same isolation scope as
// the isReady test.
this.firing = !((Action) this.node).isLocallyReentrant();
List<IToken> offeredTokens = new ArrayList<IToken>();
List<IActivityEdgeInstance> incomingEdges = this.incomingEdges;
for (int i = 0; i < incomingEdges.size(); i++) {
IActivityEdgeInstance incomingEdge = incomingEdges.get(i);
List<IToken> tokens = incomingEdge.takeOfferedTokens();
for (int j = 0; j < tokens.size(); j++) {
IToken token = tokens.get(j);
token.withdraw();
offeredTokens.add(token);
}
}
Action action = (Action) (this.node);
// *** Fire all input pins concurrently. ***
List<InputPin> inputPins = getInputs(action); // CHANGED from: action.getInputs();
for (Iterator<InputPin> i = inputPins.iterator(); i.hasNext();) {
InputPin pin = i.next();
IPinActivation pinActivation = this.getPinActivation(pin);
List<IToken> tokens = pinActivation.takeOfferedTokens();
pinActivation.fire(tokens);
for (int j = 0; j < tokens.size(); j++) {
IToken token = tokens.get(j);
offeredTokens.add(token);
}
}
return offeredTokens;
}
@Override
public void fire(List<IToken> incomingTokens) {
// Do the main action behavior then concurrently fire all output pin
// activations
// and offer a single control token. Then activate the action again,
// if it is still ready to fire and has at least one token actually
// being
// offered to it.
do {
Debug.println("[fire] Action " + this.node.getName() + "...");
Debug.println("[event] Fire activity=" + this.getActivityExecution().getBehavior().getName() + " action=" + this.node.getName());
this.doAction();
incomingTokens = this.completeAction();
} while (incomingTokens.size() > 0);
}
@Override
public void terminate() {
// Terminate this action activation and any outgoing fork node attached
// to it.
super.terminate();
if (this.outgoingEdges.size() > 0) {
this.outgoingEdges.get(0).getTarget().terminate();
}
}
public List<IToken> completeAction() {
// Concurrently fire all output pin activations and offer a single
// control token. Then check if the action should fire again
// and, if so, return additional incoming tokens for this.
this.sendOffers();
Debug.println("[fire] Checking if " + this.node.getName() + " should fire again...");
_beginIsolation();
List<IToken> incomingTokens = new ArrayList<IToken>();
this.firing = false;
if (this.isReady()) {
incomingTokens = this.takeOfferedTokens();
this.firing = this.isFiring() & incomingTokens.size() > 0;
}
_endIsolation();
return incomingTokens;
}
@Override
public Boolean isReady() {
// In addition to the default condition, check that, if the action has
// isLocallyReentrant=false, then the activation is not currently
// firing,
// and that the sources of all incoming edges (control flows) have
// offers and all input pin activations are ready.
// [This assumes that all edges directly incoming to the action are
// control flows.]
boolean ready = super.isReady() & (((Action) this.node).isLocallyReentrant() | !this.isFiring());
int i = 1;
while (ready & i <= this.incomingEdges.size()) {
ready = this.incomingEdges.get(i - 1).hasOffer();
i = i + 1;
}
List<InputPin> inputPins = getInputs((Action) this.node); // CHANGED from: ((Action)(this.node)).getInputs();
int j = 1;
while (ready & j <= inputPins.size()) {
ready = this.getPinActivation(inputPins.get(j - 1)).isReady();
j = j + 1;
}
return ready;
}
public Boolean isFiring() {
// Indicate whether this action activation is currently firing or not.
return this.firing == null ? false : this.firing; // ADDED check for null
}
public abstract void doAction();
public void sendOffers() {
// Fire all output pins and send offers on all outgoing control flows.
Action action = (Action) (this.node);
// *** Send offers from all output pins concurrently. ***
List<OutputPin> outputPins = getOutputs(action); // CHANGED from: action.getOutputs();
for (Iterator<OutputPin> i = outputPins.iterator(); i.hasNext();) {
OutputPin outputPin = i.next();
IPinActivation pinActivation = this.getPinActivation(outputPin);
pinActivation.sendUnofferedTokens();
}
// Send offers on all outgoing control flows.
if (this.outgoingEdges.size() > 0) {
List<IToken> tokens = new ArrayList<IToken>();
tokens.add(new ControlToken());
this.addTokens(tokens);
this.outgoingEdges.get(0).sendOffer(tokens);
}
}
@Override
public void createNodeActivations() {
// Create node activations for the input and output pins of the action
// for this activation.
// [Note: Pins are owned by their actions, not by the enclosing activity
// (or group), so they must be activated through the action activation.]
Action action = (Action) (this.node);
List<ActivityNode> inputPinNodes = new ArrayList<ActivityNode>();
List<InputPin> inputPins = getInputs(action); // CHANGED from: action.getInputs();
for (int i = 0; i < inputPins.size(); i++) {
InputPin inputPin = inputPins.get(i);
inputPinNodes.add(inputPin);
}
this.group.createNodeActivations(inputPinNodes);
for (int i = 0; i < inputPinNodes.size(); i++) {
ActivityNode node = inputPinNodes.get(i);
this.addPinActivation((IPinActivation) (this.group.getNodeActivation(node)));
}
List<ActivityNode> outputPinNodes = new ArrayList<ActivityNode>();
List<OutputPin> outputPins = getOutputs(action); // CHANGED from: action.getOutputs();
for (int i = 0; i < outputPins.size(); i++) {
OutputPin outputPin = outputPins.get(i);
outputPinNodes.add(outputPin);
}
this.group.createNodeActivations(outputPinNodes);
for (int i = 0; i < outputPinNodes.size(); i++) {
ActivityNode node = outputPinNodes.get(i);
this.addPinActivation((IPinActivation) (this.group.getNodeActivation(node)));
}
}
@Override
public void addOutgoingEdge(IActivityEdgeInstance edge) {
// If there are no outgoing activity edge instances, create a single
// activity edge instance with a fork node execution at the other end.
// Add the give edge to the fork node execution that is the target of
// the activity edge instance out of this action execution.
// [This assumes that all edges directly outgoing from the action are
// control flows, with an implicit fork for offers out of the action.]
IActivityNodeActivation forkNodeActivation;
if (this.outgoingEdges.size() == 0) {
forkNodeActivation = new ForkNodeActivation();
forkNodeActivation.setRunning(false); // fUML12-10 certain boolean flags are not properly initialized in come cases
IActivityEdgeInstance newEdge = new ActivityEdgeInstance();
super.addOutgoingEdge(newEdge);
forkNodeActivation.addIncomingEdge(newEdge);
} else {
forkNodeActivation = this.outgoingEdges.get(0).getTarget();
}
forkNodeActivation.addOutgoingEdge(edge);
}
public void addPinActivation(IPinActivation pinActivation) {
// Add a pin activation to this action activation.
this.pinActivations.add(pinActivation);
pinActivation.setActionActivation(this);
}
public IPinActivation getPinActivation(Pin pin) {
// Precondition: The given pin is owned by the action of the action
// activation.
// Return the pin activation corresponding to the given pin.
IPinActivation pinActivation = null;
int i = 1;
while (pinActivation == null & i <= this.pinActivations.size()) {
IPinActivation thisPinActivation = this.pinActivations.get(i - 1);
if (thisPinActivation.getNode() == pin) {
pinActivation = thisPinActivation;
}
i = i + 1;
}
return pinActivation;
}
public void putToken(OutputPin pin, IValue value) {
// Precondition: The action execution has fired and the given pin is
// owned by the action of the action execution.
// Place a token for the given value on the pin activation corresponding
// to the given output pin.
Debug.println("[putToken] node = " + this.node.getName());
IObjectToken token = new ObjectToken();
token.setValue(value);
IPinActivation pinActivation = this.getPinActivation(pin);
pinActivation.addToken(token);
}
public void putTokens(OutputPin pin, List<IValue> values) {
// Precondition: The action execution has fired and the given pin is
// owned by the action of the action execution.
// Place tokens for the given values on the pin activation corresponding
// to the given output pin.
// Debug.println("[putTokens] node = " + this.node.getName());
for (int i = 0; i < values.size(); i++) {
IValue value = values.get(i);
this.putToken(pin, value);
}
}
public List<IValue> getTokens(InputPin pin) {
// Precondition: The action execution has fired and the given pin is
// owned by the action of the action execution.
// Get any tokens held by the pin activation corresponding to the given
// input pin and return them
// (but leave the tokens on the pin).
Debug.println("[getTokens] node = " + this.node.getName() + ", pin = " + pin.getName());
IPinActivation pinActivation = this.getPinActivation(pin);
List<IToken> tokens = pinActivation.getUnofferedTokens();
List<IValue> values = new ArrayList<IValue>();
for (int i = 0; i < tokens.size(); i++) {
IToken token = tokens.get(i);
IValue value = ((IObjectToken) token).getValue();
if (value != null) {
values.add(value);
}
}
return values;
}
public List<IValue> takeTokens(InputPin pin) {
// Precondition: The action execution has fired and the given pin is
// owned by the action of the action execution.
// Take any tokens held by the pin activation corresponding to the given
// input pin and return them.
Debug.println("[takeTokens] node = " + this.node.getName() + ", pin = " + pin.getName());
IPinActivation pinActivation = this.getPinActivation(pin);
List<IToken> tokens = pinActivation.takeUnofferedTokens();
List<IValue> values = new ArrayList<IValue>();
for (int i = 0; i < tokens.size(); i++) {
IToken token = tokens.get(i);
IValue value = ((IObjectToken) token).getValue();
if (value != null) {
values.add(value);
}
}
return values;
}
@Override
public Boolean isSourceFor(IActivityEdgeInstance edgeInstance) {
// If this action has an outgoing fork node, check that the fork node is
// the source of the given edge instance.
boolean isSource = false;
if (this.outgoingEdges.size() > 0) {
isSource = this.outgoingEdges.get(0).getTarget().isSourceFor(edgeInstance);
}
return isSource;
}
public Boolean valueParticipatesInLink(IValue value, ILink link) {
// Test if the given value participates in the given link.
List<IFeatureValue> linkFeatureValues = link.getFeatureValues();
boolean participates = false;
int i = 1;
while (!participates & i <= linkFeatureValues.size()) {
participates = linkFeatureValues.get(i - 1).getValues().get(0).equals(value);
i = i + 1;
}
return participates;
}
public BooleanValue makeBooleanValue(Boolean value) {
// Make a Boolean value using the built-in Boolean primitive type.
// [This ensures that Boolean values created internally are the same as
// the default used for evaluating Boolean literals.]
LiteralBoolean booleanLiteral = UMLFactory.eINSTANCE.createLiteralBoolean();
booleanLiteral.setValue(value);
return (BooleanValue) (this.getExecutionLocus().getExecutor().evaluate(booleanLiteral));
}
public void initialize(ActivityNode node, IActivityNodeActivationGroup group) {
// fUML12-10 certain boolean flags are not properly initialized in come cases
super.initialize(node, group);
this.firing = false;
}
// ADDED:
protected static List<InputPin> getInputs(Action action) {
return action instanceof LoopNode ? ((LoopNode) action).getLoopVariableInputs() : action.getInputs();
}
protected static List<OutputPin> getOutputs(Action action) {
return action instanceof LoopNode ? ((LoopNode) action).getResults() : action instanceof ConditionalNode ? ((ConditionalNode) action).getResults() : action.getOutputs();
}
}