blob: 6d4dace20151c5cff822d67ac330532779a475d2 [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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Jeremie Tatibouet (CEA LIST)
*
*****************************************************************************/
package org.eclipse.papyrus.moka.fuml.statemachines.Semantics.StateMachines;
import static org.eclipse.papyrus.moka.fuml.statemachines.Activator.logger;
import org.eclipse.papyrus.moka.fuml.Semantics.Classes.Kernel.IBooleanValue;
import org.eclipse.papyrus.moka.fuml.Semantics.Classes.Kernel.IEvaluation;
import org.eclipse.papyrus.moka.fuml.Semantics.CommonBehaviors.BasicBehaviors.IExecution;
import org.eclipse.papyrus.moka.fuml.Semantics.CommonBehaviors.Communications.IEventOccurrence;
import org.eclipse.papyrus.moka.fuml.statemachines.interfaces.Semantics.StateMachines.ICompletionEventOccurrence;
import org.eclipse.papyrus.moka.fuml.statemachines.interfaces.Semantics.StateMachines.IRegionActivation;
import org.eclipse.papyrus.moka.fuml.statemachines.interfaces.Semantics.StateMachines.ITransitionActivation;
import org.eclipse.papyrus.moka.fuml.statemachines.interfaces.Semantics.StateMachines.IVertexActivation;
import org.eclipse.papyrus.moka.fuml.statemachines.interfaces.Semantics.StateMachines.TransitionMetadata;
import org.eclipse.papyrus.moka.fuml.statemachines.interfaces.Semantics.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);
logger.info(this.getNode().getName()+" => 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 +")";
}
}