path_calculator.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. from compiler_exceptions import *
  2. from visitor import Visitor
  3. class PathCalculator(Visitor):
  4. """ Computes the states that must be exited and entered for a specific transition if the system is to make
  5. that transition.
  6. """
  7. def visit_ClassDiagram(self, class_diagram):
  8. for c in class_diagram.classes :
  9. c.accept(self)
  10. def visit_Class(self, c):
  11. if c.statechart:
  12. c.statechart.accept(self)
  13. def visit_StateChart(self, statechart):
  14. for node in statechart.basics + statechart.composites:
  15. node.accept(self)
  16. def visit_StateChartNode(self, node):
  17. for transition in node.transitions :
  18. transition.accept(self)
  19. def visit_StateChartTransition(self, transition):
  20. #Find the scope of the transition (lowest common proper ancestor)
  21. #TODO: Could it be made more efficient if we calculated the LCA from the source node and just one of the target nodes?
  22. LCA = self.calculateLCA(transition)
  23. if LCA == None:
  24. #raise CompilerException("Transision with source " + transition.parent_node.name + " and target " + transition.target_nodes + " has no lowest common ancestor node.")
  25. raise CompilerException("Transision with source '" + transition.parent_node.name + "' and target + '" + transition.target_string + "' has no lowest common ancestor node.")
  26. #Calculate exit nodes
  27. transition.exit_nodes = [transition.parent_node]
  28. for node in transition.parent_node.getAncestors() :
  29. if (node == LCA) :
  30. break
  31. transition.exit_nodes.append(node)
  32. #Calculate enter nodes
  33. transition.enter_nodes = []
  34. #we then add the branching paths to the other nodes
  35. for target_node in transition.target.target_nodes :
  36. to_append_list = [(target_node, True)]
  37. for anc in target_node.getAncestors() :
  38. if anc == LCA : #If we reach the LCA in the ancestor hierarchy we break
  39. break;
  40. to_add = True; #boolean value to see if the current ancestor should be added to the result
  41. for enter_node_entry in transition.enter_nodes :
  42. if anc == enter_node_entry[0] :
  43. to_add = False #If we reach an ancestor in the hierarchy that is already listed as enter node, we don't add and break
  44. break
  45. if to_add:
  46. to_append_list.append((anc, False)) #Only the first from the ancestor list should get True
  47. else :
  48. break
  49. to_append_list.reverse() #the enter sequence should be in the reverse order of the ancestor hierarchy
  50. transition.enter_nodes.extend(to_append_list)
  51. # Calculate arena
  52. current = LCA
  53. while not current.is_composite:
  54. current = current.parent
  55. transition.arena = current
  56. def calculateLCA(self, transition):
  57. """
  58. Calculates the lowest common ancestor of a transition
  59. """
  60. for anc in transition.parent_node.getAncestors() :
  61. all_descendants = True
  62. for node in transition.target.getNodes() :
  63. if not node.isDescendantOf(anc) :
  64. all_descendants = False
  65. break
  66. if all_descendants :
  67. return anc
  68. return None