Implement PlantUML generation for class diagrams

This commit is contained in:
Joeri Exelmans 2024-09-12 12:45:45 +02:00
parent 89b7c83440
commit 40552b8dcf
6 changed files with 192 additions and 127 deletions

View file

@ -12,6 +12,7 @@ from services.bottom.V0 import Bottom
from services.primitives.integer_type import Integer from services.primitives.integer_type import Integer
from pattern_matching import mvs_adapter from pattern_matching import mvs_adapter
from pattern_matching.matcher import MatcherVF2 from pattern_matching.matcher import MatcherVF2
from renderer import plantuml
import sys import sys
@ -106,16 +107,17 @@ def main():
print(conf2.type_mapping) print(conf2.type_mapping)
# RAMify MM # RAMify MM
ramified_mm_id = ramify(state, dsl_mm_id) prefix = "RAM_" # all ramified types can be prefixed to distinguish them a bit more
ramified_mm_id = ramify(state, dsl_mm_id, prefix)
# LHS of our rule # LHS of our rule
lhs_id = state.create_node() lhs_id = state.create_node()
lhs_od = OD(ramified_mm_id, lhs_id, state) lhs_od = OD(ramified_mm_id, lhs_id, state)
lhs_od.create_object("man", "Man") lhs_od.create_object("man", prefix+"Man")
lhs_od.create_slot("weight", "man", lhs_od.create_string_value("man.weight", 'v < 99')) lhs_od.create_slot(prefix+"weight", "man", lhs_od.create_string_value(f"man.{prefix}weight", 'v < 99'))
lhs_od.create_object("scaryAnimal", "Animal") lhs_od.create_object("scaryAnimal", prefix+"Animal")
lhs_od.create_link("manAfraidOfAnimal", "afraidOf", "man", "scaryAnimal") lhs_od.create_link("manAfraidOfAnimal", prefix+"afraidOf", "man", "scaryAnimal")
conf3 = Conformance(state, lhs_id, ramified_mm_id) conf3 = Conformance(state, lhs_id, ramified_mm_id)
print("LHS conforms?", conf3.check_nominal(log=True)) print("LHS conforms?", conf3.check_nominal(log=True))
@ -124,13 +126,13 @@ def main():
rhs_id = state.create_node() rhs_id = state.create_node()
rhs_od = OD(ramified_mm_id, rhs_id, state) rhs_od = OD(ramified_mm_id, rhs_id, state)
rhs_od.create_object("man", "Man") rhs_od.create_object("man", prefix+"Man")
rhs_od.create_slot("weight", "man", rhs_od.create_string_value("man.weight", 'v + 5')) rhs_od.create_slot(prefix+"weight", "man", rhs_od.create_string_value(f"man.{prefix}weight", 'v + 5'))
rhs_od.create_object("bill", "Man") rhs_od.create_object("bill", prefix+"Man")
rhs_od.create_slot("weight", "bill", rhs_od.create_string_value("bill.weight", '100')) rhs_od.create_slot(prefix+"weight", "bill", rhs_od.create_string_value(f"bill.{prefix}weight", '100'))
rhs_od.create_link("billAfraidOfMan", "afraidOf", "bill", "man") rhs_od.create_link("billAfraidOfMan", prefix+"afraidOf", "bill", "man")
conf4 = Conformance(state, rhs_id, ramified_mm_id) conf4 = Conformance(state, rhs_id, ramified_mm_id)
print("RHS conforms?", conf4.check_nominal(log=True)) print("RHS conforms?", conf4.check_nominal(log=True))
@ -163,5 +165,8 @@ def main():
conf5 = Conformance(state, dsl_m_id, dsl_mm_id) conf5 = Conformance(state, dsl_m_id, dsl_mm_id)
print("Updated model conforms?", conf5.check_nominal(log=True)) print("Updated model conforms?", conf5.check_nominal(log=True))
print(plantuml.render_ramification(state, dsl_mm_id, ramified_mm_id))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

88
renderer/plantuml.py Normal file
View file

@ -0,0 +1,88 @@
from services import scd, od
from services.bottom.V0 import Bottom
from transformation import ramify
def render_class_diagram(state, model):
bottom = Bottom(state)
model_scd = scd.SCD(model, state)
model_od = od.OD(od.get_scd_mm(bottom), model, state)
output = ""
# Render classes
for name, class_node in model_scd.get_classes().items():
if model_od.read_slot_boolean(class_node, "abstract"):
output += f"\nabstract class {name}"
else:
output += f"\nclass {name}"
# Render attributes
output += " {"
for (attr_name, attr_edge) in od.get_attributes(bottom, class_node):
tgt_name = model_scd.get_class_name(bottom.read_edge_target(attr_edge))
output += f"\n {attr_name} : {tgt_name}"
output += "\n}"
output += "\n"
# Render inheritance links
for inh_node in model_scd.get_inheritances().values():
src_name = model_scd.get_class_name(bottom.read_edge_source(inh_node))
tgt_name = model_scd.get_class_name(bottom.read_edge_target(inh_node))
output += f"\n{tgt_name} <|-- {src_name}"
output += "\n"
for assoc_name, assoc_edge in model_scd.get_associations().items():
src_name = model_scd.get_class_name(bottom.read_edge_source(assoc_edge))
tgt_name = model_scd.get_class_name(bottom.read_edge_target(assoc_edge))
src_lower_card, src_upper_card, tgt_lower_card, tgt_upper_card = model_scd.get_assoc_cardinalities(assoc_edge)
if src_lower_card == None:
src_lower_card = 0
if src_upper_card == None:
src_upper_card = "*"
if tgt_lower_card == None:
tgt_lower_card = 0
if tgt_upper_card == None:
tgt_upper_card = "*"
src_card = f"{src_lower_card} .. {src_upper_card}"
tgt_card = f"{tgt_lower_card} .. {tgt_upper_card}"
if src_card == "0 .. *":
src_card = " " # hide cardinality
if tgt_card == "1 .. 1":
tgt_card = " " # hide cardinality
output += f'\n{src_name} "{src_card}" --> "{tgt_card}" {tgt_name} : {assoc_name}'
return output
def render_package(name, contents):
output = ""
output += f'\npackage "{name}" {{'
output += contents
output += '\n}'
return output
def render_ramification(state, mm, ramified_mm):
bottom = Bottom(state)
output = (
render_package("original", render_class_diagram(state, mm))
+ '\n'
+ render_package("RAMified", render_class_diagram(state, ramified_mm))
)
mm_scd = scd.SCD(mm, state)
ramified_mm_scd = scd.SCD(ramified_mm, state)
output += "\n"
for ram_name, ram_class_node in ramified_mm_scd.get_classes().items():
original_class, = bottom.read_outgoing_elements(ram_class_node, ramify.RAMIFIES_LABEL)
original_name = mm_scd.get_class_name(original_class)
output += f"\n{ram_name} ..> {original_name} : RAMifies"
return output

View file

@ -39,14 +39,7 @@ class OD:
get_scd_mm(self.bottom), # the type model of our type model get_scd_mm(self.bottom), # the type model of our type model
self.type_model, self.type_model,
self.bottom.state) self.bottom.state)
# # Read the 'abstract' slot of the class is_abstract = mm_od.read_slot_boolean(class_node, "abstract")
abstract_slot = mm_od.get_slot(class_node, "abstract")
print('abstract_slot:', abstract_slot)
if abstract_slot != None:
is_abstract = Boolean(abstract_slot, self.bottom.state).read()
else:
is_abstract = False
if is_abstract: if is_abstract:
raise Exception("Cannot instantiate abstract class!") raise Exception("Cannot instantiate abstract class!")
@ -56,6 +49,11 @@ class OD:
return object_node 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): def get_class_of_object(self, object_name: str):
object_node, = self.bottom.read_outgoing_elements(self.model, object_name) # get the object object_node, = self.bottom.read_outgoing_elements(self.model, object_name) # get the object
return self._get_class_of_object(object_node) return self._get_class_of_object(object_node)
@ -71,7 +69,6 @@ class OD:
def create_slot(self, attr_name: str, object_name: str, target_name: str): def create_slot(self, attr_name: str, object_name: str, target_name: str):
class_name = self.get_class_of_object(object_name) class_name = self.get_class_of_object(object_name)
attr_link_name = get_attr_link_name(class_name, attr_name) attr_link_name = get_attr_link_name(class_name, attr_name)
print('attr_link_name:', attr_link_name)
# An attribute-link is indistinguishable from an ordinary link: # An attribute-link is indistinguishable from an ordinary link:
return self.create_link( return self.create_link(
get_attr_link_name(object_name, attr_name), get_attr_link_name(object_name, attr_name),
@ -81,7 +78,6 @@ class OD:
# I really don't like how complex and inefficient it is to read an attribute of an object... # I really don't like how complex and inefficient it is to read an attribute of an object...
class_name = self._get_class_of_object(object_node) class_name = self._get_class_of_object(object_node)
attr_link_name = get_attr_link_name(class_name, attr_name) attr_link_name = get_attr_link_name(class_name, attr_name)
print(attr_link_name)
type_edge, = self.bottom.read_outgoing_elements(self.type_model, attr_link_name) type_edge, = self.bottom.read_outgoing_elements(self.type_model, attr_link_name)
for outgoing_edge in self.bottom.read_outgoing_edges(object_node): for outgoing_edge in self.bottom.read_outgoing_edges(object_node):
if type_edge in self.bottom.read_outgoing_elements(outgoing_edge, "Morphism"): if type_edge in self.bottom.read_outgoing_elements(outgoing_edge, "Morphism"):
@ -130,7 +126,6 @@ class OD:
if len(self.bottom.read_outgoing_elements(self.model, link_name)) == 0: if len(self.bottom.read_outgoing_elements(self.model, link_name)) == 0:
break break
i += 1 i += 1
print('link_name:', link_name)
type_edge, = self.bottom.read_outgoing_elements(self.type_model, assoc_name) type_edge, = self.bottom.read_outgoing_elements(self.type_model, assoc_name)
@ -171,6 +166,9 @@ def get_scd_mm_class_node(bottom: Bottom):
def get_scd_mm_attributelink_node(bottom: Bottom): def get_scd_mm_attributelink_node(bottom: Bottom):
return get_scd_mm_node(bottom, "AttributeLink") return get_scd_mm_node(bottom, "AttributeLink")
def get_scd_mm_attributelink_name_node(bottom: Bottom):
return get_scd_mm_node(bottom, "AttributeLink_name")
def get_scd_mm_assoc_node(bottom: Bottom): def get_scd_mm_assoc_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Association") return get_scd_mm_node(bottom, "Association")
@ -182,8 +180,65 @@ def get_scd_mm_node(bottom: Bottom, node_name: str):
node, = bottom.read_outgoing_elements(scd_metamodel, node_name) node, = bottom.read_outgoing_elements(scd_metamodel, node_name)
return node return node
def get_scd_mm_class_uppercard_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Class_upper_cardinality")
def get_scd_mm_class_lowercard_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Class_lower_cardinality")
def get_scd_mm_assoc_src_uppercard_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Association_source_upper_cardinality")
def get_scd_mm_assoc_src_lowercard_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Association_source_lower_cardinality")
def get_scd_mm_assoc_tgt_uppercard_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Association_target_upper_cardinality")
def get_scd_mm_assoc_tgt_lowercard_node(bottom: Bottom):
return get_scd_mm_node(bottom, "Association_target_lower_cardinality")
def get_object_name(bottom: Bottom, model: UUID, object_node: UUID): def get_object_name(bottom: Bottom, model: UUID, object_node: UUID):
for key in bottom.read_keys(model): for key in bottom.read_keys(model):
for el in bottom.read_outgoing_elements(model, key): for el in bottom.read_outgoing_elements(model, key):
if el == object_node: if el == object_node:
return key return key
def find_outgoing_typed_by(bottom, src: UUID, type_node: UUID):
edges = []
for outgoing_edge in bottom.read_outgoing_edges(src):
for typedBy in bottom.read_outgoing_elements(outgoing_edge, "Morphism"):
if typedBy == type_node:
edges.append(outgoing_edge)
break
return edges
def navigate_modelref(bottom, node: UUID):
uuid = bottom.read_value(node)
return UUID(uuid)
def find_cardinality(bottom, class_node: UUID, type_node: UUID):
upper_card_edges = find_outgoing_typed_by(bottom, class_node, type_node)
if len(upper_card_edges) == 1:
ref = bottom.read_edge_target(upper_card_edges[0])
integer, = bottom.read_outgoing_elements(
navigate_modelref(bottom, ref),
"integer")
# finally, the value we're looking for:
return bottom.read_value(integer)
def get_attributes(bottom, class_node: UUID):
attr_link_node = get_scd_mm_attributelink_node(bottom)
attr_link_name_node = get_scd_mm_attributelink_name_node(bottom)
attr_edges = find_outgoing_typed_by(bottom, class_node, attr_link_node)
result = []
for attr_edge in attr_edges:
name_edge, = find_outgoing_typed_by(bottom, attr_edge, attr_link_name_node)
if name_edge == None:
raise Exception("Expected attribute to have a name...")
ref_name = bottom.read_edge_target(name_edge)
string, = bottom.read_outgoing_elements(
navigate_modelref(bottom, ref_name),
"string")
attr_name = bottom.read_value(string)
# ref_type = bottom.read_edge_target(attr_edge)
# typ = navigate_modelref(bottom, ref_type)
result.append((attr_name, attr_edge))
return result

View file

@ -4,6 +4,7 @@ from services.bottom.V0 import Bottom
from services.primitives.boolean_type import Boolean from services.primitives.boolean_type import Boolean
from services.primitives.integer_type import Integer from services.primitives.integer_type import Integer
from services.primitives.string_type import String from services.primitives.string_type import String
from services import od
import re import re
@ -341,6 +342,14 @@ class SCD:
name_to_attr[name] = edge name_to_attr[name] = edge
return name_to_attr return name_to_attr
def get_assoc_cardinalities(self, assoc_edge):
src_lower_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_src_lowercard_node(self.bottom))
src_upper_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_src_uppercard_node(self.bottom))
tgt_lower_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_tgt_lowercard_node(self.bottom))
tgt_upper_card = od.find_cardinality(self.bottom, assoc_edge, od.get_scd_mm_assoc_tgt_uppercard_node(self.bottom))
return src_lower_card, src_upper_card, tgt_lower_card, tgt_upper_card
def delete_element(self, name: str): def delete_element(self, name: str):
""" """
Deletes an element from the model. Deletes an element from the model.

View file

@ -1,122 +1,34 @@
from state.base import State from state.base import State
from uuid import UUID from uuid import UUID
from services.bottom.V0 import Bottom from services.bottom.V0 import Bottom
from services.scd import SCD from services import scd, od
from framework.conformance import Conformance from framework.conformance import Conformance
RAMIFIES_LABEL = "RAMifies" RAMIFIES_LABEL = "RAMifies"
def ramify(state: State, model: UUID, prefix = "") -> UUID: def ramify(state: State, model: UUID, prefix = "RAM_") -> UUID:
# 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)
bottom = Bottom(state) bottom = Bottom(state)
scd_metamodel_id = state.read_dict(state.read_root(), "SCD") scd_metamodel_id = state.read_dict(state.read_root(), "SCD")
scd_metamodel = UUID(state.read_value(scd_metamodel_id)) scd_metamodel = UUID(state.read_value(scd_metamodel_id))
class_upper_card_node, = bottom.read_outgoing_elements(scd_metamodel, "Class_upper_cardinality")
src_upper_card_node, = bottom.read_outgoing_elements(scd_metamodel, "Association_source_upper_cardinality")
tgt_upper_card_node, = bottom.read_outgoing_elements(scd_metamodel, "Association_target_upper_cardinality")
attr_link_node, = bottom.read_outgoing_elements(scd_metamodel, "AttributeLink")
attr_link_name_node, = bottom.read_outgoing_elements(scd_metamodel, "AttributeLink_name")
glob_constr_node, = bottom.read_outgoing_elements(scd_metamodel, "GlobalConstraint")
inheritance_node, = bottom.read_outgoing_elements(scd_metamodel, "Inheritance")
string_type_id = state.read_dict(state.read_root(), "String") string_type_id = state.read_dict(state.read_root(), "String")
string_type = UUID(state.read_value(string_type_id)) string_type = UUID(state.read_value(string_type_id))
scd = SCD(model, state) m_scd = scd.SCD(model, state)
# print_tree(model, 2)
# for el in SCD(scd_metamodel, state).list_elements():
# print(el)
def find_outgoing_typed_by(src: UUID, type_node: UUID):
edges = []
for outgoing_edge in bottom.read_outgoing_edges(src):
for typedBy in bottom.read_outgoing_elements(outgoing_edge, "Morphism"):
if typedBy == type_node:
edges.append(outgoing_edge)
break
return edges
def navigate_modelref(node: UUID):
uuid = bottom.read_value(node)
return UUID(uuid)
def find_cardinality(class_node: UUID, type_node: UUID):
upper_card_edges = find_outgoing_typed_by(class_node, type_node)
if len(upper_card_edges) == 1:
ref = bottom.read_edge_target(upper_card_edges[0])
integer, = bottom.read_outgoing_elements(
navigate_modelref(ref),
"integer")
# finally, the value we're looking for:
return bottom.read_value(integer)
def get_attributes(class_node: UUID):
attr_edges = find_outgoing_typed_by(class_node, attr_link_node)
result = []
for attr_edge in attr_edges:
name_edge, = find_outgoing_typed_by(attr_edge, attr_link_name_node)
if name_edge == None:
raise Exception("Expected attribute to have a name...")
ref_name = bottom.read_edge_target(name_edge)
string, = bottom.read_outgoing_elements(
navigate_modelref(ref_name),
"string")
attr_name = bottom.read_value(string)
ref_type = bottom.read_edge_target(attr_edge)
typ = navigate_modelref(ref_type)
result.append((attr_name, attr_edge))
return result
ramified = state.create_node() ramified = state.create_node()
ramified_scd = SCD(ramified, state) ramified_scd = scd.SCD(ramified, state)
string_modelref = ramified_scd.create_model_ref("String", string_type) string_modelref = ramified_scd.create_model_ref("String", string_type)
print() classes = m_scd.get_classes()
classes = scd.get_classes()
for class_name, class_node in classes.items(): for class_name, class_node in classes.items():
# For every class in our original model, create a class: # For every class in our original model, create a class:
# - abstract: False # - abstract: False
# - min-card: 0 # - min-card: 0
# - max-card: same as original # - max-card: same as original
upper_card = find_cardinality(class_node, class_upper_card_node) 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) ramified_class = ramified_scd.create_class(prefix+class_name, abstract=None, max_c=upper_card)
# traceability link # traceability link
@ -128,7 +40,7 @@ def ramify(state: State, model: UUID, prefix = "") -> UUID:
# Optional constraint on the object # Optional constraint on the object
# ramified_scd._create_attribute_link(prefix+class_name, string_modelref, "constraint", optional=True) # ramified_scd._create_attribute_link(prefix+class_name, string_modelref, "constraint", optional=True)
for (attr_name, attr_edge) in get_attributes(class_node): 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 # Every attribute becomes 'string' type
# The string will be a Python expression # The string will be a Python expression
@ -136,17 +48,16 @@ def ramify(state: State, model: UUID, prefix = "") -> UUID:
# traceability link # traceability link
bottom.create_edge(ramified_attr_link, attr_edge, RAMIFIES_LABEL) bottom.create_edge(ramified_attr_link, attr_edge, RAMIFIES_LABEL)
associations = scd.get_associations() associations = m_scd.get_associations()
for assoc_name, assoc_node in associations.items(): for assoc_name, assoc_node in associations.items():
# For every association in our original model, create an association: # For every association in our original model, create an association:
# - src-min-card: 0 # - src-min-card: 0
# - src-max-card: same as original # - src-max-card: same as original
# - tgt-min-card: 0 # - tgt-min-card: 0
# - tgt-max-card: same as original # - tgt-max-card: same as original
src_upper_card = find_cardinality(assoc_node, src_upper_card_node) _, src_upper_card, _, tgt_upper_card = m_scd.get_assoc_cardinalities(assoc_node)
tgt_upper_card = find_cardinality(assoc_node, tgt_upper_card_node) src = m_scd.get_class_name(bottom.read_edge_source(assoc_node))
src = scd.get_class_name(bottom.read_edge_source(assoc_node)) tgt = m_scd.get_class_name(bottom.read_edge_target(assoc_node))
tgt = 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( ramified_assoc = ramified_scd.create_association(
prefix+assoc_name, prefix+src, prefix+tgt, prefix+assoc_name, prefix+src, prefix+tgt,
@ -155,10 +66,10 @@ def ramify(state: State, model: UUID, prefix = "") -> UUID:
# traceability link # traceability link
bottom.create_edge(ramified_assoc, assoc_node, RAMIFIES_LABEL) bottom.create_edge(ramified_assoc, assoc_node, RAMIFIES_LABEL)
for inh_name, inh_node in scd.get_inheritances().items(): for inh_name, inh_node in m_scd.get_inheritances().items():
# Re-create inheritance links like in our original model: # Re-create inheritance links like in our original model:
src = scd.get_class_name(bottom.read_edge_source(inh_node)) src = m_scd.get_class_name(bottom.read_edge_source(inh_node))
tgt = scd.get_class_name(bottom.read_edge_target(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) ramified_inh_link = ramified_scd.create_inheritance(prefix+src, prefix+tgt)

View file

@ -175,6 +175,3 @@ def rewrite(state, lhs: UUID, rhs: UUID, rhs_mm: UUID, match_mapping: dict, m_to
Integer(UUID(old_value), state).create(result) Integer(UUID(old_value), state).create(result)
else: else:
raise Exception("Unimplemented type. Value:", result) raise Exception("Unimplemented type. Value:", result)
# type_name = od.get_object_name()