mvs_adapter.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. from state.base import State
  2. from uuid import UUID
  3. from services.bottom.V0 import Bottom
  4. from pattern_matching.matcher import Graph, Edge, Vertex
  5. import itertools
  6. import re
  7. from util.timer import Timer
  8. class _is_edge:
  9. def __repr__(self):
  10. return "EDGE"
  11. # just a unique symbol that is only equal to itself
  12. IS_EDGE = _is_edge()
  13. class IS_TYPE:
  14. def __init__(self, type):
  15. # mvs-node of the type
  16. self.type = type
  17. def __repr__(self):
  18. return f"TYPE({str(self.type)[-4:]})"
  19. # def __eq__(self, other):
  20. # if not isinstance(other, IS_TYPE):
  21. # return False
  22. # return other.type == self.type
  23. # def __hash__(self):
  24. # return self.type.__hash__()
  25. UUID_REGEX = re.compile(r"[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z]-[0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]")
  26. # Converts an object/class diagram in MVS state to the pattern matcher graph type
  27. # ModelRefs are flattened
  28. def model_to_graph(state: State, model: UUID):
  29. with Timer("model_to_graph"):
  30. bottom = Bottom(state)
  31. graph = Graph()
  32. mvs_edges = []
  33. modelrefs = {}
  34. def extract_modelref(el):
  35. value = bottom.read_value(el)
  36. # If the value of the el is a ModelRef (only way to detect this is to match a regex - not very clean), then extract it. We'll create a link to the referred model later.
  37. if bottom.is_edge(el):
  38. mvs_edges.append(el)
  39. return IS_EDGE
  40. if isinstance(value, str):
  41. if UUID_REGEX.match(value) != None:
  42. # side-effect
  43. modelrefs[el] = UUID(value)
  44. return None
  45. return value
  46. # MVS-Nodes become vertices
  47. uuid_to_vtx = { node: Vertex(value=extract_modelref(node)) for node in bottom.read_outgoing_elements(model) }
  48. graph.vtxs = [ vtx for vtx in uuid_to_vtx.values() ]
  49. # For every MSV-Edge, two edges are created (for src and tgt)
  50. for mvs_edge in mvs_edges:
  51. mvs_src = bottom.read_edge_source(mvs_edge)
  52. if mvs_src in uuid_to_vtx:
  53. graph.edges.append(Edge(
  54. src=uuid_to_vtx[mvs_src],
  55. tgt=uuid_to_vtx[mvs_edge],
  56. label="outgoing"))
  57. mvs_tgt = bottom.read_edge_target(mvs_edge)
  58. if mvs_tgt in uuid_to_vtx:
  59. graph.edges.append(Edge(
  60. src=uuid_to_vtx[mvs_tgt],
  61. tgt=uuid_to_vtx[mvs_edge],
  62. label="tgt"))
  63. for node, ref in modelrefs.items():
  64. # Recursively convert ref'ed model to graph
  65. ref_model = model_to_graph(state, ref)
  66. # Flatten and create link to ref'ed model
  67. graph.vtxs += ref_model.vtxs
  68. graph.edges += ref_model.edges
  69. graph.edges.append(Edge(
  70. src=uuid_to_vtx[node],
  71. tgt=ref_model.vtxs[0], # which node to link to?? dirty
  72. label="modelref"))
  73. # # Add typing information
  74. # for i,node in enumerate(bottom.read_outgoing_elements(model)):
  75. # type_node, = bottom.read_outgoing_elements(node, "Morphism")
  76. # print('node', node, 'has type', type_node)
  77. # # We create a Vertex storing the type
  78. # type_vertex = Vertex(value=IS_TYPE(type_node))
  79. # graph.vtxs.append(type_vertex)
  80. # type_edge = Edge(
  81. # src=uuid_to_vtx[node],
  82. # tgt=type_vertex,
  83. # label="type")
  84. # print(type_edge)
  85. # graph.edges.append(type_edge)
  86. return graph
  87. # Function object for pattern matching. Decides whether to match host and guest vertices, where guest is a RAMified instance (e.g., the attributes are all strings with Python expressions), and the host is an instance (=object diagram) of the original model (=class diagram)
  88. class RAMCompare:
  89. def __init__(self, bottom):
  90. self.bottom = bottom
  91. type_model_id = bottom.state.read_dict(bottom.state.read_root(), "SCD")
  92. self.scd_model = UUID(bottom.state.read_value(type_model_id))
  93. def is_subtype_of(self, supposed_subtype: UUID, supposed_supertype: UUID):
  94. inheritance_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance")
  95. if supposed_subtype == supposed_supertype:
  96. # reflexive:
  97. return True
  98. for outgoing in self.bottom.read_outgoing_edges(supposed_subtype):
  99. if inheritance_node in self.bottom.read_outgoing_elements(outgoing, "Morphism"):
  100. # 'outgoing' is an inheritance link
  101. supertype = self.bottom.read_edge_target(outgoing)
  102. if supertype != supposed_subtype:
  103. if self.is_subtype_of(supertype, supposed_supertype):
  104. return True
  105. return False
  106. def __call__(self, g_val, h_val):
  107. if g_val == None:
  108. return h_val == None
  109. # mvs-edges (which are converted to vertices) only match with mvs-edges
  110. if g_val == IS_EDGE:
  111. return h_val == IS_EDGE
  112. if h_val == IS_EDGE:
  113. return False
  114. # types only match with their supertypes
  115. if isinstance(g_val, IS_TYPE):
  116. if not isinstance(h_val, IS_TYPE):
  117. return False
  118. g_val_original_type = self.bottom.read_outgoing_elements(g_val.type, "RAMifies")
  119. result = self.is_subtype_of(h_val.type, g_val_original_type)
  120. print("RESULT", result)
  121. return result
  122. if isinstance(h_val, IS_TYPE):
  123. return False
  124. # print(g_val, h_val)
  125. return eval(g_val, {}, {'v': h_val})