| /***************************************************************************** |
| * 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 java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.papyrus.moka.fuml.commonbehavior.IEventOccurrence; |
| import org.eclipse.papyrus.moka.fuml.loci.ISemanticVisitor; |
| import org.eclipse.uml2.uml.TransitionKind; |
| import org.eclipse.uml2.uml.Vertex; |
| |
| public abstract class VertexActivation extends StateMachineSemanticVisitor implements IVertexActivation { |
| |
| // Status of the current vertex |
| protected StateMetadata status; |
| |
| // Incoming transitions of that vertex |
| protected List<ITransitionActivation> incomingTransitionActivations; |
| |
| // Outgoing transitions of that vertex |
| protected List<ITransitionActivation> outgoingTransitionActivations; |
| |
| public VertexActivation() { |
| super(); |
| this.setStatus(StateMetadata.IDLE); |
| this.incomingTransitionActivations = new ArrayList<ITransitionActivation>(); |
| this.outgoingTransitionActivations = new ArrayList<ITransitionActivation>(); |
| } |
| |
| public VertexActivation getParentVertexActivation() { |
| // The parent state of a vertex is either a StateMachineExecution or a |
| // StateActivation |
| IRegionActivation regionActivation = (IRegionActivation) this.getParent(); |
| if (regionActivation != null) { |
| if (regionActivation.getParent() instanceof StateMachineExecution) { |
| return null; |
| } else { |
| return (VertexActivation) regionActivation.getParent(); |
| } |
| } |
| return null; |
| } |
| |
| public IRegionActivation getOwningRegionActivation() { |
| // In general for a vertex activation its owning region activation |
| // is its direct parent. Not that is not true for the exit point |
| // activation as well as the entry point activation. This operation |
| // is therefore overridden in these two context |
| return (IRegionActivation) this.parent; |
| } |
| |
| public void setStatus(StateMetadata state) { |
| this.status = state; |
| } |
| |
| public StateMetadata getStatus() { |
| return this.status; |
| } |
| |
| public void addIncomingTransition(ITransitionActivation activation) { |
| this.incomingTransitionActivations.add(activation); |
| } |
| |
| public void addOutgoingTransition(ITransitionActivation activation) { |
| this.outgoingTransitionActivations.add(activation); |
| } |
| |
| public List<ITransitionActivation> getOutgoingTransitions() { |
| return this.outgoingTransitionActivations; |
| } |
| |
| public List<ITransitionActivation> getIncomingTransitions() { |
| return this.incomingTransitionActivations; |
| } |
| |
| public IVertexActivation getVertexActivation(Vertex vertex) { |
| // By default return nothing. Must be overridden by state activation; |
| return null; |
| } |
| |
| public final void tagOutgoingTransitions(TransitionMetadata status, boolean staticCheck) { |
| // Assign the given status (runtime or analysis) to all outgoing transitions of |
| // this vertex |
| for (ITransitionActivation transitionActivation : this.outgoingTransitionActivations) { |
| if (staticCheck) { |
| transitionActivation.setAnalyticalStatus(status); |
| } else { |
| transitionActivation.setStatus(status); |
| } |
| } |
| } |
| |
| public final void tagIncomingTransitions(TransitionMetadata status, boolean staticCheck) { |
| // Assign the given status (runtime or analysis) to all incoming transitions of |
| // this vertex |
| for (ITransitionActivation transitionActivation : this.incomingTransitionActivations) { |
| if (staticCheck) { |
| transitionActivation.setAnalyticalStatus(status); |
| } else { |
| transitionActivation.setStatus(status); |
| } |
| } |
| } |
| |
| public List<IVertexActivation> getAscendingHierarchy() { |
| // Provides the hierarchy of state activations starting from the current |
| // element. This list is ordered from the innermost element to the outermost |
| // element |
| List<IVertexActivation> hierarchy = new ArrayList<IVertexActivation>(); |
| List<ISemanticVisitor> contextChain = this.getContextChain(); |
| for (ISemanticVisitor element : contextChain) { |
| if (element instanceof StateActivation) { |
| hierarchy.add((StateActivation) element); |
| } |
| } |
| return hierarchy; |
| } |
| |
| public void enter(ITransitionActivation enteringTransition, IEventOccurrence eventOccurrence, |
| IRegionActivation leastCommonAncestor) { |
| // When a vertex is entered its parent may need to be entered as well. Such |
| // situation |
| // occurs when the parent is not active while there is an attempt to enter the |
| // current |
| // vertex activation. What is important here is that entry rule is applied |
| // recursively |
| // until the least common ancestor is reached. |
| IRegionActivation owningRegionActivation = this.getOwningRegionActivation(); |
| if (leastCommonAncestor != null && owningRegionActivation != null |
| && leastCommonAncestor != owningRegionActivation) { |
| IVertexActivation vertexActivation = (IVertexActivation) owningRegionActivation.getParent(); |
| if (vertexActivation != null) { |
| vertexActivation.enter(enteringTransition, eventOccurrence, leastCommonAncestor); |
| } |
| } |
| |
| this.setStatus(StateMetadata.ACTIVE); |
| this.tagOutgoingTransitions(TransitionMetadata.REACHED, false); |
| } |
| |
| public void exit(ITransitionActivation exitingTransition, IEventOccurrence eventOccurrence, |
| IRegionActivation leastCommonAncestor) { |
| // When a vertex is exited its parent may need to be exited too. Such situation |
| // typically |
| // occurs when the current vertex is exited through a transition that cross |
| // boundaries of |
| // the parent state (and maybe also border its own parent). This implies that |
| // from the current |
| // vertex and until the least common ancestor is reached all states are exited |
| // recursively. |
| this.tagIncomingTransitions(TransitionMetadata.NONE, false); |
| this.setStatus(StateMetadata.IDLE); |
| |
| IRegionActivation owningRegionActivation = this.getOwningRegionActivation(); |
| if (leastCommonAncestor != null && owningRegionActivation != null |
| && leastCommonAncestor != owningRegionActivation) { |
| IVertexActivation vertexActivation = (VertexActivation) owningRegionActivation.getParent(); |
| if (vertexActivation != null) { |
| vertexActivation.exit(exitingTransition, eventOccurrence, leastCommonAncestor); |
| } |
| } |
| } |
| |
| public boolean isActive() { |
| // By default is is possible to assess if a vertex is active by checking |
| // if its status is ACTIVE. Note this operation is overriden in the context |
| // of state activations which require a presence within the state-machine |
| // configuration. |
| return this.status.equals(StateMetadata.ACTIVE); |
| } |
| |
| public IRegionActivation getLeastCommonAncestor(IVertexActivation targetVertexActivation, |
| TransitionKind transitionKind) { |
| // Determine the semantic visitor being the least common ancestor between |
| // the current vertex activation and the target vertex activation (provided as |
| // a parameter). The analysis is based on a comparative analysis vertices |
| // (source and |
| // target) hierarchies. |
| IRegionActivation leastCommonAncestor = null; |
| ISemanticVisitor sourceHierachyNode = null; |
| ISemanticVisitor targetHierarchyNode = null; |
| List<ISemanticVisitor> sourceHierarchy = this.getContextChain(); |
| List<ISemanticVisitor> targetHierarchy = targetVertexActivation.getContextChain(); |
| int sourceHierarchyIndex = sourceHierarchy.size(); |
| int targetHierarchyIndex = targetHierarchy.size(); |
| // Check if a difference can be found in between the two subsets |
| // delimited by the common index. Iterate until the least common |
| // ancestor is found or the two subsets have been reviewed |
| while (leastCommonAncestor == null && sourceHierarchyIndex > 0 && targetHierarchyIndex > 0) { |
| sourceHierachyNode = sourceHierarchy.get(sourceHierarchyIndex - 1); |
| targetHierarchyNode = targetHierarchy.get(targetHierarchyIndex - 1); |
| if (sourceHierachyNode != targetHierarchyNode) { |
| leastCommonAncestor = this.getRegionActivation(sourceHierachyNode); |
| } else { |
| sourceHierarchyIndex = sourceHierarchyIndex - 1; |
| targetHierarchyIndex = targetHierarchyIndex - 1; |
| } |
| } |
| // It may happen than no difference could found in the hierarchy subsets |
| // previously reviewed. This indicate two possible situations: |
| // 1. The source and the target are the same. |
| // 2. There is containing / container relationship existing between |
| // the source and the target. |
| if (leastCommonAncestor == null) { |
| if (sourceHierarchyIndex == 0 && targetHierarchyIndex == 0) { |
| leastCommonAncestor = this.getRegionActivation(sourceHierarchy.get(sourceHierarchyIndex + 1)); |
| } else { |
| if (this.getVertexActivation((Vertex) targetVertexActivation.getNode()) != null) { |
| if (transitionKind == TransitionKind.EXTERNAL_LITERAL) { |
| leastCommonAncestor = this.getRegionActivation(sourceHierarchy.get(sourceHierarchyIndex)); |
| } else { |
| leastCommonAncestor = this.getRegionActivation(targetHierarchy.get(targetHierarchyIndex - 1)); |
| } |
| } else { |
| leastCommonAncestor = this.getRegionActivation(sourceHierarchy.get(sourceHierarchyIndex - 1)); |
| } |
| } |
| } |
| return leastCommonAncestor; |
| } |
| |
| private IRegionActivation getRegionActivation(ISemanticVisitor semanticVisitor) { |
| // If the given semantic visitor is a region activation then this activation |
| // is returned. Otherwise if the visitor is a vertex activation then its |
| // parent region activation is returned. |
| IRegionActivation regionActivation = null; |
| if (semanticVisitor instanceof IRegionActivation) { |
| regionActivation = (IRegionActivation) semanticVisitor; |
| } else if (semanticVisitor instanceof VertexActivation) { |
| regionActivation = (IRegionActivation) ((IVertexActivation) semanticVisitor).getParent(); |
| } |
| return regionActivation; |
| } |
| |
| public boolean isEnterable(ITransitionActivation enteringTransition, boolean staticCheck) { |
| // By default a vertex has no prerequisites that need to be full-filled |
| // to be entered. Nevertheless some vertex (e.g., join or exit) have such |
| // prerequisites. Therefore this method is intended to be overridden in vertex |
| // activation sub-classes. |
| return true; |
| } |
| |
| public boolean isExitable(ITransitionActivation exitingTransition, boolean staticCheck) { |
| // By default a vertex has no prerequisites that need to be full-filled to be |
| // entered |
| // Nevertheless some vertex (e.g., Fork) have such prerequisite. Therefore this |
| // method |
| // is intended to be overridden in vertex activation sub-classes. |
| return true; |
| } |
| |
| public void terminate() { |
| // Terminate applied by a vertex activation does nothing by default. However it |
| // is intended |
| // to be overridden by sub-classe(s) |
| return; |
| } |
| |
| public boolean canPropagateExecution(ITransitionActivation enteringTransition, IEventOccurrence eventOccurrence, |
| IRegionActivation leastCommonAncestor) { |
| // The common behavior of all kind of vertices is that when the propagation |
| // analysis is done |
| // if a the target is a vertex that is nested within a hierarchy then the |
| // analysis |
| // must be recursively propagated to the parent vertices. |
| boolean propagate = true; |
| if (leastCommonAncestor != null) { |
| IRegionActivation parentRegionActivation = this.getOwningRegionActivation(); |
| if (leastCommonAncestor != parentRegionActivation) { |
| IVertexActivation vertexActivation = (IVertexActivation) parentRegionActivation.getParent(); |
| if (vertexActivation != null) { |
| propagate = vertexActivation.canPropagateExecution(enteringTransition, eventOccurrence, |
| leastCommonAncestor); |
| } |
| } |
| } |
| return propagate; |
| } |
| } |