from state.base import State from uuid import UUID from services.bottom.V0 import Bottom from pattern_matching.matcher import Graph, Edge, Vertex import itertools import re from util.timer import Timer class _is_edge: def __repr__(self): return "EDGE" # just a unique symbol that is only equal to itself IS_EDGE = _is_edge() class IS_TYPE: def __init__(self, type): # mvs-node of the type self.type = type def __repr__(self): return f"TYPE({str(self.type)[-4:]})" # def __eq__(self, other): # if not isinstance(other, IS_TYPE): # return False # return other.type == self.type # def __hash__(self): # return self.type.__hash__() 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]") # Converts an object/class diagram in MVS state to the pattern matcher graph type # ModelRefs are flattened def model_to_graph(state: State, model: UUID): with Timer("model_to_graph"): bottom = Bottom(state) graph = Graph() mvs_edges = [] modelrefs = {} def extract_modelref(el): value = bottom.read_value(el) # 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. if bottom.is_edge(el): mvs_edges.append(el) return IS_EDGE if isinstance(value, str): if UUID_REGEX.match(value) != None: # side-effect modelrefs[el] = UUID(value) return None return value # MVS-Nodes become vertices uuid_to_vtx = { node: Vertex(value=extract_modelref(node)) for node in bottom.read_outgoing_elements(model) } graph.vtxs = [ vtx for vtx in uuid_to_vtx.values() ] # For every MSV-Edge, two edges are created (for src and tgt) for mvs_edge in mvs_edges: mvs_src = bottom.read_edge_source(mvs_edge) if mvs_src in uuid_to_vtx: graph.edges.append(Edge( src=uuid_to_vtx[mvs_src], tgt=uuid_to_vtx[mvs_edge], label="outgoing")) mvs_tgt = bottom.read_edge_target(mvs_edge) if mvs_tgt in uuid_to_vtx: graph.edges.append(Edge( src=uuid_to_vtx[mvs_tgt], tgt=uuid_to_vtx[mvs_edge], label="tgt")) for node, ref in modelrefs.items(): # Recursively convert ref'ed model to graph ref_model = model_to_graph(state, ref) # Flatten and create link to ref'ed model graph.vtxs += ref_model.vtxs graph.edges += ref_model.edges graph.edges.append(Edge( src=uuid_to_vtx[node], tgt=ref_model.vtxs[0], # which node to link to?? dirty label="modelref")) # # Add typing information # for i,node in enumerate(bottom.read_outgoing_elements(model)): # type_node, = bottom.read_outgoing_elements(node, "Morphism") # print('node', node, 'has type', type_node) # # We create a Vertex storing the type # type_vertex = Vertex(value=IS_TYPE(type_node)) # graph.vtxs.append(type_vertex) # type_edge = Edge( # src=uuid_to_vtx[node], # tgt=type_vertex, # label="type") # print(type_edge) # graph.edges.append(type_edge) return graph # 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) class RAMCompare: def __init__(self, bottom): self.bottom = bottom type_model_id = bottom.state.read_dict(bottom.state.read_root(), "SCD") self.scd_model = UUID(bottom.state.read_value(type_model_id)) def is_subtype_of(self, supposed_subtype: UUID, supposed_supertype: UUID): inheritance_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") if supposed_subtype == supposed_supertype: # reflexive: return True for outgoing in self.bottom.read_outgoing_edges(supposed_subtype): if inheritance_node in self.bottom.read_outgoing_elements(outgoing, "Morphism"): # 'outgoing' is an inheritance link supertype = self.bottom.read_edge_target(outgoing) if supertype != supposed_subtype: if self.is_subtype_of(supertype, supposed_supertype): return True return False def __call__(self, g_val, h_val): if g_val == None: return h_val == None # mvs-edges (which are converted to vertices) only match with mvs-edges if g_val == IS_EDGE: return h_val == IS_EDGE if h_val == IS_EDGE: return False # types only match with their supertypes if isinstance(g_val, IS_TYPE): if not isinstance(h_val, IS_TYPE): return False g_val_original_type = self.bottom.read_outgoing_elements(g_val.type, "RAMifies") result = self.is_subtype_of(h_val.type, g_val_original_type) print("RESULT", result) return result if isinstance(h_val, IS_TYPE): return False # print(g_val, h_val) return eval(g_val, {}, {'v': h_val})