|
@@ -2,80 +2,82 @@ from sccd.compiler.compiler_exceptions import *
|
|
|
from sccd.compiler.visitor import Visitor
|
|
|
|
|
|
class PathCalculator(Visitor):
|
|
|
- """ Computes the states that must be exited and entered for a specific transition if the system is to make
|
|
|
- that transition.
|
|
|
- """
|
|
|
-
|
|
|
- def visit_ClassDiagram(self, class_diagram):
|
|
|
- for c in class_diagram.classes :
|
|
|
- c.accept(self)
|
|
|
+ """ Computes the states that must be exited and entered for a specific transition if the system is to make
|
|
|
+ that transition.
|
|
|
+ """
|
|
|
+
|
|
|
+ def visit_ClassDiagram(self, class_diagram):
|
|
|
+ for c in class_diagram.classes :
|
|
|
+ c.accept(self)
|
|
|
|
|
|
- def visit_Class(self, c):
|
|
|
- if c.statechart:
|
|
|
- c.statechart.accept(self)
|
|
|
-
|
|
|
- def visit_StateChart(self, statechart):
|
|
|
- for node in statechart.basics + statechart.composites:
|
|
|
- node.accept(self)
|
|
|
-
|
|
|
- def visit_StateChartNode(self, node):
|
|
|
- for transition in node.transitions :
|
|
|
- transition.accept(self)
|
|
|
-
|
|
|
- def visit_StateChartTransition(self, transition):
|
|
|
- #Find the scope of the transition (lowest common proper ancestor)
|
|
|
- #TODO: Could it be made more efficient if we calculated the LCA from the source node and just one of the target nodes?
|
|
|
- LCA = self.calculateLCA(transition)
|
|
|
-
|
|
|
- if LCA == None:
|
|
|
- #raise CompilerException("Transision with source " + transition.parent_node.name + " and target " + transition.target_nodes + " has no lowest common ancestor node.")
|
|
|
- raise CompilerException("Transision with source '" + transition.parent_node.name + "' and target + '" + transition.target_string + "' has no lowest common ancestor node.")
|
|
|
+ def visit_Class(self, c):
|
|
|
+ if c.statechart:
|
|
|
+ c.statechart.accept(self)
|
|
|
+
|
|
|
+ def visit_StateChart(self, statechart):
|
|
|
+ for node in statechart.basics + statechart.composites:
|
|
|
+ node.accept(self)
|
|
|
+
|
|
|
+ def visit_StateChartNode(self, node):
|
|
|
+ for transition in node.transitions :
|
|
|
+ transition.accept(self)
|
|
|
+
|
|
|
+ def visit_StateChartTransition(self, transition):
|
|
|
+ #Find the scope of the transition (lowest common proper ancestor)
|
|
|
+ #TODO: Could it be made more efficient if we calculated the LCA from the source node and just one of the target nodes?
|
|
|
+ LCA = self.calculateLCA(transition)
|
|
|
+
|
|
|
+ if LCA == None:
|
|
|
+ #raise CompilerException("Transision with source " + transition.parent_node.name + " and target " + transition.target_nodes + " has no lowest common ancestor node.")
|
|
|
+ raise CompilerException("Transision with source '" + transition.parent_node.name + "' and target '" + transition.target_string + "' has no lowest common ancestor node.")
|
|
|
|
|
|
- #Calculate exit nodes
|
|
|
- transition.exit_nodes = [transition.parent_node]
|
|
|
- for node in transition.parent_node.getAncestors() :
|
|
|
- if (node == LCA) :
|
|
|
- break
|
|
|
- transition.exit_nodes.append(node)
|
|
|
-
|
|
|
- #Calculate enter nodes
|
|
|
- transition.enter_nodes = []
|
|
|
-
|
|
|
- #we then add the branching paths to the other nodes
|
|
|
- for target_node in transition.target.target_nodes :
|
|
|
- to_append_list = [(target_node, True)]
|
|
|
- for anc in target_node.getAncestors() :
|
|
|
- if anc == LCA : #If we reach the LCA in the ancestor hierarchy we break
|
|
|
- break;
|
|
|
- to_add = True; #boolean value to see if the current ancestor should be added to the result
|
|
|
- for enter_node_entry in transition.enter_nodes :
|
|
|
- if anc == enter_node_entry[0] :
|
|
|
- to_add = False #If we reach an ancestor in the hierarchy that is already listed as enter node, we don't add and break
|
|
|
- break
|
|
|
- if to_add:
|
|
|
- to_append_list.append((anc, False)) #Only the first from the ancestor list should get True
|
|
|
- else :
|
|
|
- break
|
|
|
-
|
|
|
- to_append_list.reverse() #the enter sequence should be in the reverse order of the ancestor hierarchy
|
|
|
- transition.enter_nodes.extend(to_append_list)
|
|
|
+ #Calculate exit nodes
|
|
|
+ transition.exit_nodes = [transition.parent_node]
|
|
|
+ for node in transition.parent_node.getAncestors() :
|
|
|
+ if (node == LCA) :
|
|
|
+ break
|
|
|
+ transition.exit_nodes.append(node)
|
|
|
+ if node.is_parallel_state:
|
|
|
+ raise CompilerException("Transision with source '" + transition.parent_node.name + "' and target '" + transition.target_string + "' exits a parallel component.")
|
|
|
+
|
|
|
+ #Calculate enter nodes
|
|
|
+ transition.enter_nodes = []
|
|
|
+
|
|
|
+ #we then add the branching paths to the other nodes
|
|
|
+ for target_node in transition.target.target_nodes :
|
|
|
+ to_append_list = [(target_node, True)]
|
|
|
+ for anc in target_node.getAncestors() :
|
|
|
+ if anc == LCA : #If we reach the LCA in the ancestor hierarchy we break
|
|
|
+ break;
|
|
|
+ to_add = True; #boolean value to see if the current ancestor should be added to the result
|
|
|
+ for enter_node_entry in transition.enter_nodes :
|
|
|
+ if anc == enter_node_entry[0] :
|
|
|
+ to_add = False #If we reach an ancestor in the hierarchy that is already listed as enter node, we don't add and break
|
|
|
+ break
|
|
|
+ if to_add:
|
|
|
+ to_append_list.append((anc, False)) #Only the first from the ancestor list should get True
|
|
|
+ else :
|
|
|
+ break
|
|
|
+
|
|
|
+ to_append_list.reverse() #the enter sequence should be in the reverse order of the ancestor hierarchy
|
|
|
+ transition.enter_nodes.extend(to_append_list)
|
|
|
|
|
|
- # Calculate arena
|
|
|
- current = LCA
|
|
|
- while not current.is_composite:
|
|
|
- current = current.parent
|
|
|
- transition.arena = current
|
|
|
+ # Calculate arena
|
|
|
+ current = LCA
|
|
|
+ while not current.is_composite:
|
|
|
+ current = current.parent
|
|
|
+ transition.arena = current
|
|
|
|
|
|
- def calculateLCA(self, transition):
|
|
|
- """
|
|
|
- Calculates the lowest common ancestor of a transition
|
|
|
- """
|
|
|
- for anc in transition.parent_node.getAncestors() :
|
|
|
- all_descendants = True
|
|
|
- for node in transition.target.getNodes() :
|
|
|
- if not node.isDescendantOf(anc) :
|
|
|
- all_descendants = False
|
|
|
- break
|
|
|
- if all_descendants :
|
|
|
- return anc
|
|
|
- return None
|
|
|
+ def calculateLCA(self, transition):
|
|
|
+ """
|
|
|
+ Calculates the lowest common ancestor of a transition
|
|
|
+ """
|
|
|
+ for anc in transition.parent_node.getAncestors() :
|
|
|
+ all_descendants = True
|
|
|
+ for node in transition.target.getNodes() :
|
|
|
+ if not node.isDescendantOf(anc) :
|
|
|
+ all_descendants = False
|
|
|
+ break
|
|
|
+ if all_descendants :
|
|
|
+ return anc
|
|
|
+ return None
|