path_calculator.py 2.9 KB

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