Class diagram can be rendered as object diagram textual syntax, and parsed back, without information loss

This commit is contained in:
Joeri Exelmans 2024-10-03 17:01:13 +02:00
parent f45872d3f7
commit 175edb64d9
14 changed files with 505 additions and 249 deletions

View file

@ -16,7 +16,7 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root:
min_c_node = bottom.create_node(str(min_c_model))
bottom.create_edge(model_root, min_c_node, f"{type_name}.lower_cardinality")
min_c_link = bottom.create_edge(class_node, min_c_node)
bottom.create_edge(model_root, min_c_link, f"{type_name}.lower_cardinality_link")
bottom.create_edge(model_root, min_c_link, f"{type_name}_lower_cardinality")
scd_node, = bottom.read_outgoing_elements(scd_root, "Integer")
scd_link, = bottom.read_outgoing_elements(scd_root, "Class_lower_cardinality")
bottom.create_edge(min_c_node, scd_node, "Morphism")
@ -27,7 +27,7 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root:
max_c_node = bottom.create_node(str(max_c_model))
bottom.create_edge(model_root, max_c_node, f"{type_name}.upper_cardinality")
max_c_link = bottom.create_edge(class_node, max_c_node)
bottom.create_edge(model_root, max_c_link, f"{type_name}.upper_cardinality_link")
bottom.create_edge(model_root, max_c_link, f"{type_name}_upper_cardinality")
scd_node, = bottom.read_outgoing_elements(scd_root, "Integer")
scd_link, = bottom.read_outgoing_elements(scd_root, "Class_upper_cardinality")
bottom.create_edge(max_c_node, scd_node, "Morphism")
@ -36,7 +36,7 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root:
constraint_node = bottom.create_node(f"isinstance(read_value(element),{python_type})")
bottom.create_edge(model_root, constraint_node, f"{type_name}.constraint")
constraint_link = bottom.create_edge(class_node, constraint_node)
bottom.create_edge(model_root, constraint_link, f"{type_name}.constraint_link")
bottom.create_edge(model_root, constraint_link, f"{type_name}_constraint")
scd_node, = bottom.read_outgoing_elements(scd_root, "ActionCode")
scd_link, = bottom.read_outgoing_elements(scd_root, "Element_constraint")
bottom.create_edge(constraint_node, scd_node, "Morphism")

View file

@ -47,10 +47,10 @@ def bootstrap_scd(state: State) -> UUID:
def add_attribute_attributes(attribute_element_name, attribute_element):
_name_model = bottom.create_node()
_name_node = add_node_element(f"{attribute_element_name}.name", str(_name_model))
_name_edge = add_edge_element(f"{attribute_element_name}.name_link", attribute_element, _name_node)
_name_edge = add_edge_element(f"{attribute_element_name}_name", attribute_element, _name_node)
_optional_model = bottom.create_node()
_optional_node = add_node_element(f"{attribute_element_name}.optional", str(_optional_model))
_optional_edge = add_edge_element(f"{attribute_element_name}.optional_link", attribute_element, _optional_node)
_optional_edge = add_edge_element(f"{attribute_element_name}_optional", attribute_element, _optional_node)
return _name_model, _optional_model
##### SCD META-MODEL #####
@ -171,7 +171,7 @@ def bootstrap_scd(state: State) -> UUID:
# # Make Element abstract
abs_model = bottom.create_node()
abs_node = add_node_element(f"Element.abstract", str(abs_model))
abs_edge = add_edge_element(f"Element.abstract_link", element_node, abs_node)
abs_edge = add_edge_element(f"Element_abstract", element_node, abs_node)
Boolean(abs_model, state).create(True)
# create phi(SCD,SCD) to type MCL with itself
@ -220,27 +220,27 @@ def bootstrap_scd(state: State) -> UUID:
add_mcl_morphism("Association_target_lower_cardinality", "AttributeLink")
add_mcl_morphism("Association_target_upper_cardinality", "AttributeLink")
# AttributeLink_name
add_mcl_morphism("AttributeLink_name.name_link", "AttributeLink_name")
add_mcl_morphism("AttributeLink_optional.name_link", "AttributeLink_name")
add_mcl_morphism("Element_constraint.name_link", "AttributeLink_name")
add_mcl_morphism("Class_abstract.name_link", "AttributeLink_name")
add_mcl_morphism("Class_lower_cardinality.name_link", "AttributeLink_name")
add_mcl_morphism("Class_upper_cardinality.name_link", "AttributeLink_name")
add_mcl_morphism("Association_source_lower_cardinality.name_link", "AttributeLink_name")
add_mcl_morphism("Association_source_upper_cardinality.name_link", "AttributeLink_name")
add_mcl_morphism("Association_target_lower_cardinality.name_link", "AttributeLink_name")
add_mcl_morphism("Association_target_upper_cardinality.name_link", "AttributeLink_name")
add_mcl_morphism("AttributeLink_name_name", "AttributeLink_name")
add_mcl_morphism("AttributeLink_optional_name", "AttributeLink_name")
add_mcl_morphism("Element_constraint_name", "AttributeLink_name")
add_mcl_morphism("Class_abstract_name", "AttributeLink_name")
add_mcl_morphism("Class_lower_cardinality_name", "AttributeLink_name")
add_mcl_morphism("Class_upper_cardinality_name", "AttributeLink_name")
add_mcl_morphism("Association_source_lower_cardinality_name", "AttributeLink_name")
add_mcl_morphism("Association_source_upper_cardinality_name", "AttributeLink_name")
add_mcl_morphism("Association_target_lower_cardinality_name", "AttributeLink_name")
add_mcl_morphism("Association_target_upper_cardinality_name", "AttributeLink_name")
# AttributeLink_optional
add_mcl_morphism("AttributeLink_name.optional_link", "AttributeLink_optional")
add_mcl_morphism("AttributeLink_optional.optional_link", "AttributeLink_optional")
add_mcl_morphism("Element_constraint.optional_link", "AttributeLink_optional")
add_mcl_morphism("Class_abstract.optional_link", "AttributeLink_optional")
add_mcl_morphism("Class_lower_cardinality.optional_link", "AttributeLink_optional")
add_mcl_morphism("Class_upper_cardinality.optional_link", "AttributeLink_optional")
add_mcl_morphism("Association_source_lower_cardinality.optional_link", "AttributeLink_optional")
add_mcl_morphism("Association_source_upper_cardinality.optional_link", "AttributeLink_optional")
add_mcl_morphism("Association_target_lower_cardinality.optional_link", "AttributeLink_optional")
add_mcl_morphism("Association_target_upper_cardinality.optional_link", "AttributeLink_optional")
add_mcl_morphism("AttributeLink_name_optional", "AttributeLink_optional")
add_mcl_morphism("AttributeLink_optional_optional", "AttributeLink_optional")
add_mcl_morphism("Element_constraint_optional", "AttributeLink_optional")
add_mcl_morphism("Class_abstract_optional", "AttributeLink_optional")
add_mcl_morphism("Class_lower_cardinality_optional", "AttributeLink_optional")
add_mcl_morphism("Class_upper_cardinality_optional", "AttributeLink_optional")
add_mcl_morphism("Association_source_lower_cardinality_optional", "AttributeLink_optional")
add_mcl_morphism("Association_source_upper_cardinality_optional", "AttributeLink_optional")
add_mcl_morphism("Association_target_lower_cardinality_optional", "AttributeLink_optional")
add_mcl_morphism("Association_target_upper_cardinality_optional", "AttributeLink_optional")
# String
add_mcl_morphism("AttributeLink_name.name", "String")
add_mcl_morphism("AttributeLink_optional.name", "String")
@ -265,7 +265,7 @@ def bootstrap_scd(state: State) -> UUID:
add_mcl_morphism("Association_target_upper_cardinality.optional", "Boolean")
add_mcl_morphism("Element.abstract", "Boolean")
# Class_abstract
add_mcl_morphism("Element.abstract_link", "Class_abstract")
add_mcl_morphism("Element_abstract", "Class_abstract")
return mcl_root

View file

@ -196,7 +196,7 @@ def render_trace_match(state, name_mapping: dict, pattern_m: UUID, host_m: UUID,
render_suffix = f"#line:{color};line.dotted;text:{color} : matchedWith"
for pattern_el_name, host_el_name in name_mapping.items():
print(pattern_el_name, host_el_name)
# print(pattern_el_name, host_el_name)
try:
pattern_el, = bottom.read_outgoing_elements(pattern_m, pattern_el_name)
host_el, = bottom.read_outgoing_elements(host_m, host_el_name)

View file

@ -0,0 +1,121 @@
# Parser for Object Diagrams textual concrete syntax
from lark import Lark, logger, Transformer
from lark.indenter import Indenter
from services.od import OD
from services.scd import SCD
from uuid import UUID
grammar = r"""
%import common.WS_INLINE
%ignore WS_INLINE
%ignore COMMENT
%declare _INDENT _DEDENT
?start: (_NL | object )*
IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/
COMMENT: /#.*/
# newline
_NL: /(\r?\n[\t ]*)+/
literal: INT
| STR
| BOOL
INT: /[0-9]+/
STR: /"[^"]*"/
| /'[^']*'/
BOOL: "True" | "False"
object: [IDENTIFIER] ":" IDENTIFIER [link] _NL [_INDENT slot+ _DEDENT]
link: "(" IDENTIFIER "->" IDENTIFIER ")"
slot: IDENTIFIER "=" literal _NL
"""
class TreeIndenter(Indenter):
NL_type = '_NL'
OPEN_PAREN_types = []
CLOSE_PAREN_types = []
INDENT_type = '_INDENT'
DEDENT_type = '_DEDENT'
tab_len = 4
parser = Lark(grammar, parser='lalr', postlex=TreeIndenter())
# given a concrete syntax text string, and a meta-model, parses the CS
def parse_od(state, cs_text, mm):
tree = parser.parse(cs_text)
m = state.create_node()
od = OD(mm, m, state)
int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer")))
class T(Transformer):
def __init__(self, visit_tokens):
super().__init__(visit_tokens)
self.obj_counter = 0
def IDENTIFIER(self, token):
return str(token)
def INT(self, token):
return int(token)
def BOOL(self, token):
return token == "True"
def STR(self, token):
return str(token[1:-1]) # strip the ""
def literal(self, el):
return el[0]
def link(self, el):
[src, tgt] = el
return (src, tgt)
def slot(self, el):
[attr_name, value] = el
return (attr_name, value)
def object(self, el):
[obj_name, type_name, link] = el[0:3]
if obj_name == None:
# object/link names are optional
# generate a unique name if no name given
obj_name = f"__o{self.obj_counter}"
self.obj_counter += 1
if link == None:
obj_node = od.create_object(obj_name, type_name)
else:
src, tgt = link
if tgt == "Integer":
if state.read_dict(m, "Integer") == None:
scd = SCD(m, state)
scd.create_model_ref("Integer", int_mm_id)
od.create_link(obj_name, type_name, src, tgt)
# Create slots
slots = el[3:]
for attr_name, value in slots:
value_name = f"{obj_name}.{attr_name}"
# watch out: in Python, 'bool' is subtype of 'int'
# so we must check for 'bool' first
if isinstance(value, bool):
tgt = od.create_boolean_value(value_name, value)
elif isinstance(value, int):
tgt = od.create_integer_value(value_name, value)
elif isinstance(value, str):
tgt = od.create_string_value(value_name, value)
else:
raise Exception("Unimplemented type "+value)
od.create_slot(attr_name, obj_name, tgt)
return obj_name
t = T(visit_tokens=True).transform(tree)
return m

View file

@ -0,0 +1 @@
This directory contains the parser and renderer for the textual concrete syntax for Object Diagrams.

View file

@ -0,0 +1,43 @@
# Renderer for Object Diagrams textual concrete syntax
from services import od
from services.bottom.V0 import Bottom
import json
def display_value(val: any):
if isinstance(val, str):
return '"'+val+'"'
elif isinstance(val, int) or isinstance(val, bool):
return str(val)
else:
raise Exception("don't know how to display value" + str(val))
def render_od(state, m_id, mm_id, hide_names=True):
bottom = Bottom(state)
output = ""
m_od = od.OD(mm_id, m_id, state)
def display_name(name: str):
# object names that start with "__" are hidden
return name if (name[0:2] != "__" or not hide_names) else ""
def write_attributes(object_node):
o = ""
for attr_name, slot_node in m_od.get_slots(object_node):
value = m_od.read_slot(slot_node)
o += f" {attr_name} = {display_value(value)}\n"
return o
for class_name, objects in m_od.get_all_objects().items():
for object_name, object_node in objects.items():
output += f"{display_name(object_name)}:{class_name}\n"
output += write_attributes(object_node)
for assoc_name, links in m_od.get_all_links().items():
for link_name, (link_edge, src_name, tgt_name) in links.items():
output += f"{display_name(link_name)}:{assoc_name} ({src_name} -> {tgt_name})\n"
# links can also have slots:
output += write_attributes(link_edge)
return output

View file

@ -12,7 +12,8 @@ from services.bottom.V0 import Bottom
from services.primitives.integer_type import Integer
from pattern_matching import mvs_adapter
from pattern_matching.matcher import MatcherVF2
from renderer import plantuml
from concrete_syntax import plantuml
from concrete_syntax.textual_od import parser, renderer
import sys
@ -30,39 +31,8 @@ def main():
int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer")))
string_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "String")))
# def print_tree(root, max_depth, depth=0):
# print(" "*depth, "root=", root, "value=", state.read_value(root))
# src,tgt = state.read_edge(root)
# if src != None:
# print(" "*depth, "src...")
# print_tree(src, max_depth, depth+1)
# if tgt != None:
# print(" "*depth, "tgt...")
# print_tree(tgt, max_depth, depth+1)
# for edge in state.read_outgoing(root):
# for edge_label in state.read_outgoing(edge):
# [_,tgt] = state.read_edge(edge_label)
# label = state.read_value(tgt)
# print(" "*depth, " key:", label)
# [_, tgt] = state.read_edge(edge)
# value = state.read_value(tgt)
# if value != None:
# print(" "*depth, " ->", tgt, " (value:", value, ")")
# else:
# print(" "*depth, " ->", tgt)
# if depth < max_depth:
# if isinstance(value, str) and len(value) == 36:
# i = None
# try:
# i = UUID(value)
# except ValueError as e:
# # print("invalid UUID:", value)
# pass
# if i != None:
# print_tree(i, max_depth, depth+1)
# print_tree(tgt, max_depth, depth+1)
# Meta-model for our DSL
def create_dsl_mm_api():
# Create DSL MM with SCD API
dsl_mm_id = state.create_node()
dsl_mm_scd = SCD(dsl_mm_id, state)
dsl_mm_scd.create_class("Animal", abstract=True)
@ -79,72 +49,120 @@ def main():
tgt_min_c=1,
tgt_max_c=None,
)
return dsl_mm_id
print(dsl_mm_scd.list_elements())
def create_dsl_mm_parser():
# Create DSL MM with parser
dsl_mm_cs = """
# Integer:ModelRef
Bear:Class
Animal:Class
abstract = True
Man:Class
lower_cardinality = 1
upper_cardinality = 2
Man_weight:AttributeLink (Man -> Integer)
name = "weight"
optional = False
afraidOf:Association (Man -> Animal)
source_lower_cardinality = 0
target_lower_cardinality = 1
Man_inh_Animal:Inheritance (Man -> Animal)
Bear_inh_Animal:Inheritance (Bear -> Animal)
"""
dsl_mm_id = parser.parse_od(state, dsl_mm_cs, mm=scd_mm_id)
return dsl_mm_id
conf = Conformance(state, dsl_mm_id, scd_mm_id)
print("conforms?", conf.check_nominal(log=True))
# Model in our DSL
def create_dsl_m_api():
# Create DSL M with OD API
dsl_m_id = state.create_node()
dsl_m_od = OD(dsl_mm_id, dsl_m_id, state)
# dsl_m_od.create_object("animal", "Animal")
dsl_m_od.create_object("george", "Man")
dsl_m_od.create_slot("weight", "george",
dsl_m_od.create_integer_value("george.weight", 80))
# "george_weight"
dsl_m_od.create_object("bear1", "Bear")
dsl_m_od.create_object("bear2", "Bear")
dsl_m_od.create_link("georgeAfraidOfBear1", "afraidOf", "george", "bear1")
dsl_m_od.create_link("georgeAfraidOfBear2", "afraidOf", "george", "bear2")
return dsl_m_id
def create_dsl_m_parser():
# Create DSL M with parser
dsl_m_cs = """
george :Man
weight = 80
bear1 :Bear
bear2 :Bear
:afraidOf (george -> bear1)
:afraidOf (george -> bear2)
"""
dsl_m_id = parser.parse_od(state, dsl_m_cs, mm=dsl_mm_id)
return dsl_m_id
conf2 = Conformance(state, dsl_m_id, dsl_mm_id)
print("DSL instance conforms?", conf2.check_nominal(log=True))
print(conf2.type_mapping)
# dsl_mm_id = create_dsl_mm_api()
dsl_mm_id = create_dsl_mm_parser()
print("DSL MM:")
print("--------------------------------------")
print(renderer.render_od(state, dsl_mm_id, scd_mm_id, hide_names=False))
print("--------------------------------------")
conf = Conformance(state, dsl_mm_id, scd_mm_id)
print("Conformance DSL_MM -> SCD_MM?", conf.check_nominal(log=True))
# dsl_m_id = create_dsl_m_api()
dsl_m_id = create_dsl_m_parser()
print("DSL M:")
print("--------------------------------------")
print(renderer.render_od(state, dsl_m_id, dsl_mm_id, hide_names=False))
print("--------------------------------------")
conf = Conformance(state, dsl_m_id, dsl_mm_id)
print("Conformance DSL_M -> DSL_MM?", conf.check_nominal(log=True))
# RAMify MM
prefix = "RAM_" # all ramified types can be prefixed to distinguish them a bit more
ramified_mm_id = ramify(state, dsl_mm_id, prefix)
ramified_int_mm_id = ramify(state, int_mm_id, prefix)
# LHS of our rule
lhs_id = state.create_node()
lhs_od = OD(ramified_mm_id, lhs_id, state)
lhs_od.create_object("man", prefix+"Man")
lhs_od.create_slot(prefix+"weight", "man", lhs_od.create_string_value(f"man.{prefix}weight", 'v < 99'))
lhs_od.create_object("scaryAnimal", prefix+"Animal")
lhs_od.create_link("manAfraidOfAnimal", prefix+"afraidOf", "man", "scaryAnimal")
conf3 = Conformance(state, lhs_id, ramified_mm_id)
print("LHS conforms?", conf3.check_nominal(log=True))
conf = Conformance(state, lhs_id, ramified_mm_id)
print("Conformance LHS_M -> RAM_DSL_MM?", conf.check_nominal(log=True))
# RHS of our rule
rhs_id = state.create_node()
rhs_od = OD(ramified_mm_id, rhs_id, state)
rhs_od.create_object("man", prefix+"Man")
rhs_od.create_slot(prefix+"weight", "man", rhs_od.create_string_value(f"man.{prefix}weight", 'v + 5'))
rhs_od.create_object("bill", prefix+"Man")
rhs_od.create_slot(prefix+"weight", "bill", rhs_od.create_string_value(f"bill.{prefix}weight", '100'))
rhs_od.create_link("billAfraidOfMan", prefix+"afraidOf", "bill", "man")
conf4 = Conformance(state, rhs_id, ramified_mm_id)
print("RHS conforms?", conf4.check_nominal(log=True))
conf = Conformance(state, rhs_id, ramified_mm_id)
print("Conformance RHS_M -> RAM_DSL_MM?", conf.check_nominal(log=True))
def render_ramification():
uml = (""
# Render original and RAMified meta-models
+ plantuml.render_package("Meta-Model", plantuml.render_class_diagram(state, dsl_mm_id))
+ plantuml.render_package("RAMified Meta-Model", plantuml.render_class_diagram(state, ramified_mm_id))
+ plantuml.render_package("DSL Meta-Model", plantuml.render_class_diagram(state, dsl_mm_id))
+ plantuml.render_package("Int Meta-Model", plantuml.render_class_diagram(state, int_mm_id))
+ plantuml.render_package("RAMified DSL Meta-Model", plantuml.render_class_diagram(state, ramified_mm_id))
+ plantuml.render_package("RAMified Int Meta-Model", plantuml.render_class_diagram(state, ramified_int_mm_id))
# Render RAMification traceability links
+ plantuml.render_trace_ramifies(state, dsl_mm_id, ramified_mm_id)
+ plantuml.render_trace_ramifies(state, int_mm_id, ramified_int_mm_id)
)
# Render pattern
@ -182,8 +200,11 @@ def main():
for name_mapping in generator:
rewriter.rewrite(state, lhs_id, rhs_id, ramified_mm_id, name_mapping, dsl_m_id, dsl_mm_id)
conf = Conformance(state, dsl_m_id, dsl_mm_id)
print("Conformance DSL_M (after rewrite) -> DSL_MM?", conf.check_nominal(log=True))
# Render match
uml_match = plantuml.render_trace_match(state, name_mapping, rhs_id, dsl_m_id)
uml_match = plantuml.render_trace_match(state, name_mapping, rhs_id, dsl_m_id, 'orange')
# Stop matching after rewrite
break
@ -197,14 +218,13 @@ def main():
return uml
conf5 = Conformance(state, dsl_m_id, dsl_mm_id)
print("Updated model conforms?", conf5.check_nominal(log=True))
# plantuml_str = render_all_matches()
# plantuml_str = render_rewrite()
print()
print("==============================================")
# print()
# print("==============================================")
print(render_all_matches())
# print(render_rewrite())
# print(plantuml_str)
if __name__ == "__main__":

View file

@ -556,7 +556,7 @@ class Conformance:
attr_type_element, = self.bottom.read_outgoing_elements(self.type_model, attr_type)
self.bottom.create_edge(attr_element, attr_type_element, "Morphism")
# attribute link
attr_link_element, = self.bottom.read_outgoing_elements(self.model, f"{m_name}.{attr_name}_link")
attr_link_element, = self.bottom.read_outgoing_elements(self.model, f"{m_name}_{attr_name}")
attr_link_type_element, = self.bottom.read_outgoing_elements(self.type_model, f"{tm_name}_{attr_name}")
self.bottom.create_edge(attr_link_element, attr_link_type_element, "Morphism")
except ValueError:

View file

@ -133,10 +133,10 @@ class MatcherVF2:
self.guest = guest
self.compare_fn = compare_fn
with Timer("find_connected_components - guest"):
# with Timer("find_connected_components - guest"):
self.guest_vtx_to_component, self.guest_component_to_vtxs = find_connected_components(guest)
print("number of guest connected components:", len(self.guest_component_to_vtxs))
# print("number of guest connected components:", len(self.guest_component_to_vtxs))
def match(self):
yield from self._match(

View file

@ -28,12 +28,12 @@ class _is_modelref:
return "REF"
IS_MODELREF = _is_modelref()
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:]})"
# 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:]})"
class NamedNode(Vertex):
def __init__(self, value, name):
@ -74,7 +74,7 @@ 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-
# Converts an object diagram in MVS state to the pattern matcher graph type
# ModelRefs are flattened
def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
with Timer("model_to_graph"):
# with Timer("model_to_graph"):
od = OD(model, metamodel, state)
scd = SCD(model, state)
scd_mm = SCD(metamodel, state)
@ -107,7 +107,7 @@ def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
if isinstance(value, str):
if UUID_REGEX.match(value) != None:
# side-effect
modelrefs[el] = (UUID(value),name)
modelrefs[el] = (UUID(value), name)
return MVSNode(IS_MODELREF, el, name)
return MVSNode(value, el, name)
@ -131,29 +131,37 @@ def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
label="tgt"))
for node, (ref, name) in modelrefs.items():
for node, (ref_m, name) in modelrefs.items():
vtx = uuid_to_vtx[node]
# Get MM of ref'ed model
type_node, = bottom.read_outgoing_elements(node, "Morphism")
print("modelref type node:", type_node)
ref_mm, = bottom.read_outgoing_elements(node, "Morphism")
# print("modelref type node:", type_node)
# Recursively convert ref'ed model to graph
ref_model = model_to_graph(state, ref, type_node, prefix=name+'/')
# ref_graph = model_to_graph(state, ref_m, ref_mm, prefix=name+'/')
# 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"))
vtx.modelref = (ref_m, ref_mm)
# We no longer flatten:
# # 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"))
def add_types(node):
vtx = uuid_to_vtx[node]
type_node, = bottom.read_outgoing_elements(node, "Morphism")
# Put the type straigt into the Vertex-object
uuid_to_vtx[node].typ = type_node
# Put the type straight into the Vertex-object
# The benefit is that our Vertex-matching callback can then be coded cleverly, look at the types first, resulting in better performance
vtx.typ = type_node
# We used to put the types in separate nodes, but we no longer do this:
# The old approach (creating special vertices containing the types), commented out:
# print('node', node, 'has type', type_node)
# We create a Vertex storing the type
@ -190,6 +198,7 @@ def model_to_graph(state: State, model: UUID, metamodel: UUID, prefix=""):
def match_od(state, host_m, host_mm, pattern_m, pattern_mm):
# 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, host_od):
@ -239,6 +248,20 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm):
return False
return self.match_types(g_vtx.typ, h_vtx.typ)
if hasattr(g_vtx, 'modelref'):
if not hasattr(h_vtx, 'modelref'):
return False
g_ref_m, g_ref_mm = g_vtx.modelref
h_ref_m, h_ref_mm = h_vtx.modelref
nested_matches = [m for m in match_od(state, h_ref_m, h_ref_mm, g_ref_m, g_ref_mm)]
# print('nested_matches:', nested_matches)
if len(nested_matches) == 0:
return False
elif len(nested_matches) == 1:
return True
else:
raise Exception("We have a problem: there is more than 1 match in the nested models.")
# Then, match by value
if g_vtx.value == None:
@ -257,20 +280,20 @@ def match_od(state, host_m, host_mm, pattern_m, pattern_mm):
if h_vtx.value == IS_MODELREF:
return False
# print(g_vtx.value, h_vtx.value)
def get_slot(h_vtx, slot_name: str):
slot_node = self.host_od.get_slot(h_vtx.node_id, slot_name)
return slot_node
# # print(g_vtx.value, h_vtx.value)
# def get_slot(h_vtx, slot_name: str):
# slot_node = self.host_od.get_slot(h_vtx.node_id, slot_name)
# return slot_node
def read_int(slot: UUID):
i = Integer(slot, self.bottom.state)
return i.read()
# def read_int(slot: UUID):
# i = Integer(slot, self.bottom.state)
# return i.read()
try:
return eval(g_vtx.value, {}, {
'v': h_vtx.value,
'get_slot': functools.partial(get_slot, h_vtx),
'read_int': read_int,
# 'get_slot': functools.partial(get_slot, h_vtx),
# 'read_int': read_int,
})
except Exception as e:
return False

View file

@ -52,11 +52,6 @@ class OD:
return object_node
# def read_slot_boolean(self, obj_node: str, attr_name: str):
# slot = self.get_slot(obj_node, attr_name)
# if slot != None:
# return Boolean(slot, self.bottom.state).read()
def get_class_of_object(self, object_name: str):
object_node, = self.bottom.read_outgoing_elements(self.model, object_name) # get the object
return self._get_class_of_object(object_node)
@ -73,9 +68,10 @@ class OD:
class_name = self.get_class_of_object(object_name)
attr_link_name = get_attr_link_name(class_name, attr_name)
# An attribute-link is indistinguishable from an ordinary link:
return self.create_link(
slot_id = self.create_link(
get_attr_link_name(object_name, attr_name),
attr_link_name, object_name, target_name)
return slot_id
def get_slot(self, object_node: UUID, attr_name: str):
# I really don't like how complex and inefficient it is to read an attribute of an object...
@ -86,8 +82,23 @@ class OD:
if type_edge in self.bottom.read_outgoing_elements(outgoing_edge, "Morphism"):
slot_ref = self.bottom.read_edge_target(outgoing_edge)
return slot_ref
# slot_node = UUID(self.bottom.read_value(slot_ref))
# return slot_node
def get_slots(self, object_node):
attrlink_node = get_scd_mm_attributelink_node(self.bottom)
slots = []
outgoing_links = self.bottom.read_outgoing_edges(object_node)
for l in outgoing_links:
for type_of_link in self.bottom.read_outgoing_elements(l, "Morphism"):
for type_of_type_of_link in self.bottom.read_outgoing_elements(type_of_link, "Morphism"):
if type_of_type_of_link == attrlink_node:
# hooray, we have a slot
attr_name = get_attr_name(self.bottom, type_of_link)
slots.append((attr_name, l))
return slots
def read_slot(self, slot_id):
tgt = self.bottom.read_edge_target(slot_id)
return read_primitive_value(self.bottom, tgt, self.type_model)
def create_integer_value(self, name: str, value: int):
from services.primitives.integer_type import Integer
@ -99,6 +110,16 @@ class OD:
self.create_model_ref(name, "Integer", int_node)
return name
def create_boolean_value(self, name: str, value: bool):
from services.primitives.boolean_type import Boolean
bool_node = self.bottom.create_node()
bool_service = Boolean(bool_node, self.bottom.state)
bool_service.create(value)
# name = 'int'+str(value) # name of the ref to the created integer
# By convention, the type model must have a ModelRef named "Integer"
self.create_model_ref(name, "Boolean", bool_node)
return name
def create_string_value(self, name: str, value: str):
from services.primitives.string_type import String
string_node = self.bottom.create_node()
@ -132,10 +153,10 @@ class OD:
i += 1
type_edge, = self.bottom.read_outgoing_elements(self.type_model, assoc_name)
link_id = self._create_link(link_name, type_edge, src_obj_node, tgt_obj_node)
return link_id
return self._create_link(link_name, type_edge, src_obj_node, tgt_obj_node)
def _create_link(self, link_name: str, type_edge: str, src_obj_node: UUID, tgt_obj_node: UUID):
def _create_link(self, link_name: str, type_edge: UUID, src_obj_node: UUID, tgt_obj_node: UUID):
# the link itself is unlabeled:
link_edge = self.bottom.create_edge(src_obj_node, tgt_obj_node)
# it is only in the context of the model, that the link has a name:
@ -146,6 +167,33 @@ class OD:
def get_objects(self, class_node):
return get_typed_by(self.bottom, self.model, class_node)
def get_all_objects(self):
scd_mm = get_scd_mm(self.bottom)
class_node = get_scd_mm_class_node(self.bottom)
all_classes = OD(scd_mm, self.type_model, self.bottom.state).get_objects(class_node)
result = {}
for class_name, class_node in all_classes.items():
objects = self.get_objects(class_node)
result[class_name] = objects
return result
def get_all_links(self):
scd_mm = get_scd_mm(self.bottom)
assoc_node = get_scd_mm_assoc_node(self.bottom)
all_classes = OD(scd_mm, self.type_model, self.bottom.state).get_objects(assoc_node)
result = {}
for assoc_name, assoc_node in all_classes.items():
links = self.get_objects(assoc_node)
m = {}
for link_name, link_edge in links.items():
src_node = self.bottom.read_edge_source(link_edge)
tgt_node = self.bottom.read_edge_target(link_edge)
src_name = get_object_name(self.bottom, self.model, src_node)
tgt_name = get_object_name(self.bottom, self.model, tgt_node)
m[link_name] = (link_edge, src_name, tgt_name)
result[assoc_name] = m
return result
def get_object_name(self, obj: UUID):
for key in self.bottom.read_keys(self.model):
for el in self.bottom.read_outgoing_elements(self.model, key):
@ -224,6 +272,10 @@ def get_object_name(bottom: Bottom, model: UUID, object_node: UUID):
if el == object_node:
return key
def get_type2(bottom: Bottom, mm: UUID, object_node: UUID):
type_node, = bottom.read_outgoing_elements(object_node, "Morphism")
return type_node, get_object_name(bottom, mm, type_node)
def find_outgoing_typed_by(bottom, src: UUID, type_node: UUID):
edges = []
for outgoing_edge in bottom.read_outgoing_edges(src):

View file

@ -43,7 +43,7 @@ class SCD:
_c_node = self.bottom.create_node(str(_c_model)) # store UUID of primitive value model
self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality") # link to model root
_c_link = self.bottom.create_edge(class_node, _c_node) # link class to attribute
self.bottom.create_edge(self.model, _c_link, f"{name}.{bound}_cardinality_link") # link attr link to model root
self.bottom.create_edge(self.model, _c_link, f"{name}_{bound}_cardinality") # link attr link to model root
# retrieve types from metamodel
_scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer")
_scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Class_{bound}_cardinality")
@ -63,7 +63,7 @@ class SCD:
abstract_node = self.bottom.create_node(str(abstract_model))
self.bottom.create_edge(self.model, abstract_node, f"{name}.abstract")
abstract_link = self.bottom.create_edge(class_node, abstract_node)
self.bottom.create_edge(self.model, abstract_link, f"{name}.abstract_link")
self.bottom.create_edge(self.model, abstract_link, f"{name}_abstract")
scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean")
scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "Class_abstract")
self.bottom.create_edge(abstract_node, scd_node, "Morphism")
@ -110,7 +110,7 @@ class SCD:
_c_node = self.bottom.create_node(str(_c_model))
self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality")
_c_link = self.bottom.create_edge(assoc_edge, _c_node)
self.bottom.create_edge(self.model, _c_link, f"{name}.{bound}_cardinality_link")
self.bottom.create_edge(self.model, _c_link, f"{name}_{bound}_cardinality")
_scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer")
_scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Association_{bound}_cardinality")
self.bottom.create_edge(_c_node, _scd_node, "Morphism")
@ -193,7 +193,7 @@ class SCD:
name_node = self.bottom.create_node(str(name_model))
self.bottom.create_edge(self.model, name_node, f"{source}_{name}.name")
name_link = self.bottom.create_edge(assoc_edge, name_node)
self.bottom.create_edge(self.model, name_link, f"{source}_{name}.name_link")
self.bottom.create_edge(self.model, name_link, f"{source}_{name}_name")
scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "String")
scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink_name")
self.bottom.create_edge(name_node, scd_node, "Morphism")
@ -205,7 +205,7 @@ class SCD:
optional_node = self.bottom.create_node(str(optional_model))
self.bottom.create_edge(self.model, optional_node, f"{source}_{name}.optional")
optional_link = self.bottom.create_edge(assoc_edge, optional_node)
self.bottom.create_edge(self.model, optional_link, f"{source}_{name}.optional_link")
self.bottom.create_edge(self.model, optional_link, f"{source}_{name}_optional")
scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean")
scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink_optional")
self.bottom.create_edge(optional_node, scd_node, "Morphism")
@ -278,7 +278,7 @@ class SCD:
code_node = self.bottom.create_node(code)
self.bottom.create_edge(self.model, code_node, f"{element}.constraint")
code_link = self.bottom.create_edge(element_node, code_node)
self.bottom.create_edge(self.model, code_link, f"{element}.constraint_link")
self.bottom.create_edge(self.model, code_link, f"{element}_constraint")
scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "ActionCode")
scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "Element_constraint")
self.bottom.create_edge(code_node, scd_node, "Morphism")

View file

@ -29,7 +29,7 @@ def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
# - min-card: 0
# - max-card: same as original
upper_card = od.find_cardinality(bottom, class_node, od.get_scd_mm_class_uppercard_node(bottom))
print('creating class', class_name, "with card 0 ..", upper_card)
# print('creating class', class_name, "with card 0 ..", upper_card)
ramified_class = ramified_scd.create_class(prefix+class_name, abstract=None, max_c=upper_card)
# traceability link
bottom.create_edge(ramified_class, class_node, RAMIFIES_LABEL)
@ -41,7 +41,7 @@ def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
# ramified_scd._create_attribute_link(prefix+class_name, string_modelref, "constraint", optional=True)
for (attr_name, attr_edge) in od.get_attributes(bottom, class_node):
print(' creating attribute', attr_name, "with type String")
# print(' creating attribute', attr_name, "with type String")
# Every attribute becomes 'string' type
# The string will be a Python expression
ramified_attr_link = ramified_scd._create_attribute_link(prefix+class_name, string_modelref, prefix+attr_name, optional=True)
@ -58,7 +58,7 @@ def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
_, src_upper_card, _, tgt_upper_card = m_scd.get_assoc_cardinalities(assoc_node)
src = m_scd.get_class_name(bottom.read_edge_source(assoc_node))
tgt = m_scd.get_class_name(bottom.read_edge_target(assoc_node))
print('creating assoc', src, "->", tgt, ", name =", assoc_name, ", src card = 0 ..", src_upper_card, "and tgt card = 0 ..", tgt_upper_card)
# print('creating assoc', src, "->", tgt, ", name =", assoc_name, ", src card = 0 ..", src_upper_card, "and tgt card = 0 ..", tgt_upper_card)
ramified_assoc = ramified_scd.create_association(
prefix+assoc_name, prefix+src, prefix+tgt,
src_max_c=src_upper_card,
@ -70,15 +70,13 @@ def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
# Re-create inheritance links like in our original model:
src = m_scd.get_class_name(bottom.read_edge_source(inh_node))
tgt = m_scd.get_class_name(bottom.read_edge_target(inh_node))
print('creating inheritance link', prefix+src, '->', prefix+tgt)
# print('creating inheritance link', prefix+src, '->', prefix+tgt)
ramified_inh_link = ramified_scd.create_inheritance(prefix+src, prefix+tgt)
# Double-check: The RAMified meta-model should also conform to 'SCD':
conf = Conformance(state, ramified, scd_metamodel)
if not conf.check_nominal(log=True):
raise Exception("Unexpected error: RAMified MM does not conform to SCD MM")
else:
print("RAMification successful.")
return ramified

View file

@ -13,19 +13,18 @@ from services.primitives.integer_type import Integer
def process_rule(state, lhs: UUID, rhs: UUID):
bottom = Bottom(state)
# : bottom.read_outgoing_elements(rhs, name)[0]
to_delete = { name for name in bottom.read_keys(lhs) if name not in bottom.read_keys(rhs) }
to_create = { name for name in bottom.read_keys(rhs) if name not in bottom.read_keys(lhs) }
common = { name for name in bottom.read_keys(lhs) if name in bottom.read_keys(rhs) }
print("to_delete:", to_delete)
print("to_create:", to_create)
# print("to_delete:", to_delete)
# print("to_create:", to_create)
return to_delete, to_create, common
# Rewrite is performed in-place
# Also updates the mapping in-place, to become RHS -> host
def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to_transform: UUID, mm: UUID):
# Rewrite is performed in-place (modifying `host_m`)
# Also updates the `mapping` in-place, to become RHS -> host
def rewrite(state, lhs_m: UUID, rhs_m: UUID, pattern_mm: UUID, name_mapping: dict, host_m: UUID, mm: UUID):
bottom = Bottom(state)
scd_metamodel_id = state.read_dict(state.read_root(), "SCD")
@ -36,125 +35,124 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
assoc_type = od.get_scd_mm_assoc_node(bottom)
modelref_type = od.get_scd_mm_modelref_node(bottom)
m_od = od.OD(mm, m_to_transform, bottom.state)
rhs_od = od.OD(rhs_mm, rhs, bottom.state)
m_od = od.OD(mm, host_m, bottom.state)
rhs_od = od.OD(pattern_mm, rhs_m, bottom.state)
print('rhs type:', od.get_type(bottom, rhs))
to_delete, to_create, common = process_rule(state, lhs, rhs)
to_delete, to_create, common = process_rule(state, lhs_m, rhs_m)
# Perform deletions
for pattern_name_to_delete in to_delete:
# For every name in `to_delete`, look up the name of the matched element in the host graph
model_element_name_to_delete = match_mapping[pattern_name_to_delete]
print('deleting', model_element_name_to_delete)
model_el_name_to_delete = name_mapping[pattern_name_to_delete]
# print('deleting', model_el_name_to_delete)
# Look up the matched element in the host graph
element_to_delete, = bottom.read_outgoing_elements(m_to_transform, model_element_name_to_delete)
el_to_delete, = bottom.read_outgoing_elements(host_m, model_el_name_to_delete)
# Delete
bottom.delete_element(element_to_delete)
bottom.delete_element(el_to_delete)
# Remove from mapping
del match_mapping[pattern_name_to_delete]
del name_mapping[pattern_name_to_delete]
# extended_mapping = dict(match_mapping) # will be extended with created elements
# extended_mapping = dict(name_mapping) # will be extended with created elements
edges_to_create = [] # postpone creation of edges after creation of nodes
# Perform creations
for pattern_name_to_create in to_create:
print('creating', pattern_name_to_create)
# print('creating', pattern_name_to_create)
# We have to come up with a name for the element-to-create in the host graph
i = 0
while True:
model_element_name_to_create = pattern_name_to_create + str(i) # use the label of the element in the RHS as a basis
if len(bottom.read_outgoing_elements(m_to_transform, model_element_name_to_create)) == 0:
model_el_name_to_create = pattern_name_to_create + str(i) # use the label of the element in the RHS as a basis
if len(bottom.read_outgoing_elements(host_m, model_el_name_to_create)) == 0:
break # found an available name
# Determine the type of the thing to create
rhs_element_to_create, = bottom.read_outgoing_elements(rhs, pattern_name_to_create)
rhs_type = od.get_type(bottom, rhs_element_to_create)
rhs_el_to_create, = bottom.read_outgoing_elements(rhs_m, pattern_name_to_create)
rhs_type = od.get_type(bottom, rhs_el_to_create)
original_type = ramify.get_original_type(bottom, rhs_type)
if original_type != None:
# Now get the type of the type
if od.is_typed_by(bottom, original_type, class_type):
# It's type is typed by Class -> it's an object
print(' -> creating object')
o = m_od._create_object(model_element_name_to_create, original_type)
match_mapping[pattern_name_to_create] = model_element_name_to_create
# print(' -> creating object')
o = m_od._create_object(model_el_name_to_create, original_type)
name_mapping[pattern_name_to_create] = model_el_name_to_create
elif od.is_typed_by(bottom, original_type, attr_link_type):
print(' -> postpone (is attribute link)')
edges_to_create.append((pattern_name_to_create, rhs_element_to_create, original_type, 'attribute link', rhs_type, model_element_name_to_create))
# print(' -> postpone (is attribute link)')
edges_to_create.append((pattern_name_to_create, rhs_el_to_create, original_type, 'attribute link', model_el_name_to_create))
elif od.is_typed_by(bottom, original_type, assoc_type):
print(' -> postpone (is link)')
edges_to_create.append((pattern_name_to_create, rhs_element_to_create, original_type, 'link', rhs_type, model_element_name_to_create))
# print(' -> postpone (is link)')
edges_to_create.append((pattern_name_to_create, rhs_el_to_create, original_type, 'link', model_el_name_to_create))
else:
original_type_name = od.get_object_name(bottom, mm, original_type)
print(" -> warning: don't know about", original_type_name)
else:
print(" -> no original (un-RAMified) type")
# print(" -> no original (un-RAMified) type")
# assume the type of the object is already the original type
# this is because primitive types (e.g., Integer) are not RAMified
type_name = od.get_object_name(bottom, rhs_mm, rhs_type)
type_name = od.get_object_name(bottom, pattern_mm, rhs_type)
if type_name == "String":
s_model = UUID(bottom.read_value(rhs_element_to_create))
python_expr = String(s_model, bottom.state).read()
# Assume the string is a Python expression to evaluate
python_expr = String(UUID(bottom.read_value(rhs_el_to_create)), bottom.state).read()
result = eval(python_expr, {}, {})
print('result:', result)
# Write the result into the host model.
# This will be the *value* of an attribute. The attribute-link (connecting an object to the attribute) will be created as an edge later.
if isinstance(result, int):
m_od.create_integer_value(model_element_name_to_create, result)
m_od.create_integer_value(model_el_name_to_create, result)
elif isinstance(result, str):
m_od.create_string_value(model_element_name_to_create, result)
match_mapping[pattern_name_to_create] = model_element_name_to_create
m_od.create_string_value(model_el_name_to_create, result)
name_mapping[pattern_name_to_create] = model_el_name_to_create
else:
raise Exception(f"RHS element '{pattern_name_to_create}' needs to be created in host, but has no un-RAMified type, and I don't know what to do with it. It's type is", type_name)
print('match_mapping:', match_mapping)
print("create edges....")
for pattern_name_to_create, rhs_element_to_create, original_type, original_type_name, rhs_type, model_element_name_to_create in edges_to_create:
print('creating', pattern_name_to_create)
# print("create edges....")
for pattern_name_to_create, rhs_el_to_create, original_type, original_type_name, model_el_name_to_create in edges_to_create:
# print('creating', pattern_name_to_create)
if original_type_name == 'attribute link':
print(' -> creating attribute link')
src = bottom.read_edge_source(rhs_element_to_create)
src_name = od.get_object_name(bottom, rhs, src)
tgt = bottom.read_edge_target(rhs_element_to_create)
tgt_name = od.get_object_name(bottom, rhs, tgt)
obj_name = match_mapping[src_name] # name of object in host graph to create slot for
attr_name = od.get_object_name(bottom, mm, original_type)
class_name = m_od.get_class_of_object(obj_name)
# Just when you thought the code couldn't get any dirtier:
attribute_name = attr_name[len(class_name)+1:]
# print(attribute_name, obj_name, match_mapping[tgt_name])
m_od.create_slot(attribute_name, obj_name, match_mapping[tgt_name])
# print(' -> creating attribute link')
src = bottom.read_edge_source(rhs_el_to_create)
src_name = od.get_object_name(bottom, rhs_m, src)
tgt = bottom.read_edge_target(rhs_el_to_create)
tgt_name = od.get_object_name(bottom, rhs_m, tgt)
obj_name = name_mapping[src_name] # name of object in host graph to create slot for
orig_attr_name = od.get_attr_name(bottom, original_type)
m_od.create_slot(orig_attr_name, obj_name, name_mapping[tgt_name])
elif original_type_name == 'link':
print(' -> creating link')
src = bottom.read_edge_source(rhs_element_to_create)
src_name = od.get_object_name(bottom, rhs, src)
tgt = bottom.read_edge_target(rhs_element_to_create)
tgt_name = od.get_object_name(bottom, rhs, tgt)
obj_name = match_mapping[src_name] # name of object in host graph to create slot for
# print(' -> creating link')
src = bottom.read_edge_source(rhs_el_to_create)
src_name = od.get_object_name(bottom, rhs_m, src)
tgt = bottom.read_edge_target(rhs_el_to_create)
tgt_name = od.get_object_name(bottom, rhs_m, tgt)
obj_name = name_mapping[src_name] # name of object in host graph to create slot for
attr_name = od.get_object_name(bottom, mm, original_type)
class_name = m_od.get_class_of_object(obj_name)
# print(attr_name, obj_name, match_mapping[tgt_name])
m_od.create_link(model_element_name_to_create, attr_name, obj_name, match_mapping[tgt_name])
m_od.create_link(model_el_name_to_create, attr_name, obj_name, name_mapping[tgt_name])
# Perform updates
for pattern_element_name in common:
model_element_name = match_mapping[pattern_element_name]
print('updating', model_element_name)
model_element, = bottom.read_outgoing_elements(m_to_transform, model_element_name)
host_type = od.get_type(bottom, model_element)
# Perform updates (only on values)
for pattern_el_name in common:
model_el_name = name_mapping[pattern_el_name]
# print('updating', model_el_name)
model_el, = bottom.read_outgoing_elements(host_m, model_el_name)
host_type = od.get_type(bottom, model_el)
if od.is_typed_by(bottom, host_type, class_type):
print(' -> is classs')
# print(' -> is classs')
# nothing to do
pass
elif od.is_typed_by(bottom, host_type, attr_link_type):
print(' -> is attr link')
# print(' -> is attr link')
# nothing to do
pass
elif od.is_typed_by(bottom, host_type, modelref_type):
print(' -> is modelref')
old_value = od.read_primitive_value(bottom, model_element, mm)
rhs_element, = bottom.read_outgoing_elements(rhs, pattern_element_name)
expr = od.read_primitive_value(bottom, rhs_element, rhs_mm)
# print(' -> is modelref')
old_value = od.read_primitive_value(bottom, model_el, mm)
rhs_el, = bottom.read_outgoing_elements(rhs_m, pattern_el_name)
expr = od.read_primitive_value(bottom, rhs_el, pattern_mm)
result = eval(expr, {}, {'v': old_value})
# print('eval result=', result)
if isinstance(result, int):
# overwrite the old value, in-place
referred_model_id = UUID(bottom.read_value(model_element))
referred_model_id = UUID(bottom.read_value(model_el))
Integer(referred_model_id, state).create(result)
else:
raise Exception("Unimplemented type. Value:", result)
else:
raise Exception("Don't know what to do with element of type", host_type)