blob: 07ea0b34ad101bb2e8c61744b2077f7c761113e8 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2015 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:
* Jeremie Tatibouet (CEA LIST)
*
*****************************************************************************/
package org.eclipse.papyrus.moka.pssm.statemachines;
import org.eclipse.papyrus.moka.fuml.commonbehavior.IEventOccurrence;
import org.eclipse.papyrus.moka.fuml.commonbehavior.IExecution;
import org.eclipse.papyrus.moka.fuml.simpleclassifiers.IBooleanValue;
import org.eclipse.papyrus.moka.fuml.simpleclassifiers.IEvaluation;
import org.eclipse.papyrus.moka.pssm.values.ISM_OpaqueExpressionEvaluation;
import org.eclipse.uml2.uml.Behavior;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.OpaqueExpression;
import org.eclipse.uml2.uml.Transition;
import org.eclipse.uml2.uml.ValueSpecification;
public abstract class TransitionActivation extends StateMachineSemanticVisitor implements ITransitionActivation {
// The source activation of this transition activation
protected IVertexActivation vertexSourceActivation;
// The target activation of this transition activation
protected IVertexActivation vertexTargetActivation;
// The runtime status (NONE, REACHED, TRAVERSED) of the transition
protected TransitionMetadata status;
// Least common ancestor of the source and the target. This is materialized
// by the region activation that is the common ancestor of the source and the
// target.
private IRegionActivation leastCommonAncestor;
// The static status (NONE, REACHED, TRAVERSED) of the transition
protected TransitionMetadata analyticalStatus;
// The last event occurrence used during static analysis.
private IEventOccurrence lastTriggeringEventOccurrence;
// The last verdict when the execution was propagated over this transition.
private boolean lastPropagation;
public TransitionActivation() {
super();
this.status = TransitionMetadata.NONE;
this.analyticalStatus = TransitionMetadata.NONE;
this.leastCommonAncestor = null;
this.lastTriggeringEventOccurrence = null;
this.lastPropagation = false;
}
public TransitionMetadata getStatus() {
return status;
}
public void setStatus(TransitionMetadata state) {
this.status = state;
}
public void setAnalyticalStatus(TransitionMetadata status) {
this.analyticalStatus = status;
}
public TransitionMetadata getAnalyticalStatus() {
return this.analyticalStatus;
}
public IVertexActivation getSourceActivation() {
return vertexSourceActivation;
}
public void setSourceActivation(IVertexActivation vertexSourceActivation) {
this.vertexSourceActivation = vertexSourceActivation;
}
public IVertexActivation getTargetActivation() {
return vertexTargetActivation;
}
public void setTargetActivation(IVertexActivation vertexTargetActivation) {
this.vertexTargetActivation = vertexTargetActivation;
}
public boolean isReached(boolean staticCheck) {
/// Convenience operation which returns true if the status of this transition
// is REACHED; false otherwise.
boolean reached = true;
if (staticCheck) {
reached = this.analyticalStatus.equals(TransitionMetadata.REACHED);
} else {
reached = this.status.equals(TransitionMetadata.REACHED);
}
return reached;
}
public boolean isTraversed(boolean staticCheck) {
// Convenience operation which returns true if the status of this transition
// is TRAVERSED; false otherwise.
boolean traversed = true;
if (staticCheck) {
traversed = this.analyticalStatus.equals(TransitionMetadata.TRAVERSED);
} else {
traversed = this.status.equals(TransitionMetadata.TRAVERSED);
}
return traversed;
}
@Override
public boolean isVisitorFor(NamedElement node) {
// Determine if this visitor is a semantic visitor for the node
// provided as a parameter.This case is verified if the node is
// the same as the transition attached to the semantic visitor or
// if the node matches a transition that is redefined (directly or
// indirectly) by the transition attached to this semantic visitor.
boolean isVisitor = super.isVisitorFor(node);
if (!isVisitor) {
Transition transition = ((Transition) this.node).getRedefinedTransition();
while (!isVisitor && transition != null) {
if (transition == node) {
isVisitor = true;
} else {
transition = transition.getRedefinedTransition();
}
}
}
return isVisitor;
}
public boolean isTriggered() {
// Check if the transition is triggered. A transition is triggered
// if it declares triggers or if it redefines a transition that itself
// declares triggers. This check applies recursively on the redefinition
// hierarchy.
Transition transition = (Transition) this.node;
boolean isTriggered = false;
if (!transition.getTriggers().isEmpty()) {
isTriggered = true;
}
while (!isTriggered && transition.getRedefinedTransition() != null) {
transition = transition.getRedefinedTransition();
if (!transition.getTriggers().isEmpty()) {
isTriggered = true;
}
}
return isTriggered;
}
public boolean isGuarded() {
// Check if the transition is guarded. A transition is guarded if it declares
// a guard or if a redefine transition that itself declares a guar. This check
// applies recursively on the redefinition hierarchy
Transition transition = (Transition) this.node;
boolean isGuarded = false;
if (transition.getGuard() != null) {
isGuarded = true;
}
while (!isGuarded && transition.getRedefinedTransition() != null) {
transition = transition.getRedefinedTransition();
if (transition.getGuard() != null) {
isGuarded = true;
}
}
return isGuarded;
}
public boolean evaluateGuard(IEventOccurrence eventOccurrence) {
// Evaluate the guard specification thanks to an evaluation.
// The evaluation does not presume of the type of the guard specification.
boolean result = true;
Transition transition = (Transition) this.node;
Constraint guard = transition.getGuard();
while (guard == null && transition.getRedefinedTransition() != null) {
transition = transition.getRedefinedTransition();
guard = transition.getGuard();
}
if (guard != null) {
ValueSpecification specification = guard.getSpecification();
if (specification != null) {
IEvaluation evaluation = this.getExecutionLocus().getFactory().createEvaluation(specification);
if (specification instanceof OpaqueExpression) {
((ISM_OpaqueExpressionEvaluation) evaluation).setContext(this.getExecutionContext());
((ISM_OpaqueExpressionEvaluation) evaluation).initialize(eventOccurrence);
}
if (evaluation != null) {
IBooleanValue evaluationResult = (IBooleanValue) evaluation.evaluate();
result = evaluationResult.getValue();
}
}
}
return result;
}
public boolean hasTrigger(IEventOccurrence eventOccurrence) {
// Return true if the event occurrence matches a trigger of this transition.
// false otherwise. If the transition declares no trigger but redefines another
// transition then if that transition has a trigger that matches the event
// occurrence
// the redefining transition is considered has being able to react to the event
// occurrence.
// The rule applies recursively.
Transition transition = (Transition) this.node;
boolean match = eventOccurrence.matchAny(transition.getTriggers());
while (!match && transition.getRedefinedTransition() != null) {
transition = transition.getRedefinedTransition();
match = eventOccurrence.matchAny(transition.getTriggers());
}
return match;
}
public boolean canFireOn(IEventOccurrence eventOccurrence) {
// A transition is can fire when:
// 1. It has a trigger that matches the dispatched event occurrence.
// 2. Its guard evaluates to true.
// 3. A valid path can found to the next state machine configuration.
// Note: If the dispatched event is a completion event, the transition matches
// this latter
// if it has no trigger and the transition leaves the state from which the
// completion event
// was generated.
boolean reactive = this.hasTrigger(eventOccurrence) && this.evaluateGuard(eventOccurrence)
&& this.canPropagateExecution(eventOccurrence);
if (reactive && eventOccurrence instanceof ICompletionEventOccurrence) {
reactive = this.getSourceActivation() == ((ICompletionEventOccurrence) eventOccurrence).getScope();
}
return reactive;
}
public boolean canPropagateExecution(IEventOccurrence eventOccurrence) {
// Evaluate the possibility to propagate the static analysis through this
// transition activation.
// Two situations can occur:
// 1. The transition has already been "traversed" with using the same event
// occurrence. This means
// we already know the execution can be propagated through the transiton
// activation. Hence true
// is returned and the propagation stops.
// 2. The transition has not already been "traversed" using this event
// occurrence. The consequence
// is that the analysis is propagated through the target vertex activation.
boolean propagate = true;
if (this.lastTriggeringEventOccurrence != eventOccurrence) {
propagate = this.vertexTargetActivation.canPropagateExecution(this, eventOccurrence,
this.getLeastCommonAncestor());
this.lastTriggeringEventOccurrence = eventOccurrence;
this.lastPropagation = propagate;
} else {
propagate = this.lastPropagation;
}
return propagate;
}
public void tryExecuteEffect(IEventOccurrence eventOccurrence) {
// Execute the effect owned by the transition (if any). If there
// is no effect but the transition redefines another transition, then
// the effect of this transition is executed instead. This rule
// applies recursively.
Transition transition = (Transition) this.getNode();
Behavior effect = transition.getEffect();
while (effect == null && transition.getRedefinedTransition() != null) {
transition = transition.getRedefinedTransition();
effect = transition.getEffect();
}
if (effect != null) {
IExecution execution = this.getExecutionFor(transition.getEffect(), eventOccurrence);
if (execution != null) {
execution.execute();
}
}
}
public void fire(IEventOccurrence eventOccurrence) {
// The fire sequence is broken into the following set of actions
// 1 - Exit the source (depends on the kind of transition that is currently
// used)
// 2 - Execute the effect (if one exists for that transition)
// 3 - Enter the target (depends on the kind of transition that is currently
// used)
this.exitSource(eventOccurrence);
this.tryExecuteEffect(eventOccurrence);
this.setStatus(TransitionMetadata.TRAVERSED);
this.enterTarget(eventOccurrence);
}
public IRegionActivation getLeastCommonAncestor() {
// Return the common ancestor of the source and the target. This common ancestor
// is
// a region activation
if (this.vertexSourceActivation.getParentVertexActivation() != this.vertexTargetActivation
.getParentVertexActivation()) {
if (this.leastCommonAncestor == null) {
this.leastCommonAncestor = this.vertexSourceActivation
.getLeastCommonAncestor(this.vertexTargetActivation, ((Transition) this.getNode()).getKind());
}
}
return this.leastCommonAncestor;
}
public String toString() {
String representation = "[" + this.getSourceActivation() + "] -> [" + this.getTargetActivation() + "] (";
if (this.isReached(false)) {
representation += "REACHED";
} else if (this.isTraversed(false)) {
representation += "TRAVERSED";
} else {
representation += "NONE";
}
return representation + ")";
}
}