Browse Source

Conformance checker relies much more on ODAPI rather than duplicating its logic + Fix error in bootstrap of primitive types

Joeri Exelmans 1 year ago
parent
commit
a26ceef10f
5 changed files with 183 additions and 264 deletions
  1. 4 2
      api/cd.py
  2. 52 18
      api/od.py
  3. 17 19
      bootstrap/primitive.py
  4. 109 224
      framework/conformance.py
  5. 1 1
      services/od.py

+ 4 - 2
api/cd.py

@@ -49,7 +49,7 @@ class CDAPI:
         self.transitive_sub_types = { type_name: set(get_transitive_sub_types(type_name)) for type_name in self.direct_sub_types } 
         self.transitive_sub_types = { type_name: set(get_transitive_sub_types(type_name)) for type_name in self.direct_sub_types } 
         self.transitive_super_types = { type_name: set(get_transitive_super_types(type_name)) for type_name in self.direct_super_types }
         self.transitive_super_types = { type_name: set(get_transitive_super_types(type_name)) for type_name in self.direct_super_types }
 
 
-    def get_type(self, type_name: str):
+    def get(self, type_name: str):
         return self.bottom.read_outgoing_elements(self.m, type_name)[0]
         return self.bottom.read_outgoing_elements(self.m, type_name)[0]
 
 
     def is_direct_subtype(self, super_type_name: str, sub_type_name: str):
     def is_direct_subtype(self, super_type_name: str, sub_type_name: str):
@@ -68,7 +68,9 @@ class CDAPI:
 
 
     # # The edge connecting an object to the value of a slot must be named `{object_name}_{attr_name}`
     # # The edge connecting an object to the value of a slot must be named `{object_name}_{attr_name}`
     def get_attr_link_name(self, class_name, attr_name):
     def get_attr_link_name(self, class_name, attr_name):
-        return self.type_model_names[self.find_attribute_type(class_name, attr_name)]
+        attr_type_link = self.find_attribute_type(class_name, attr_name)
+        if attr_type_link != None:
+            return self.type_model_names[attr_type_link]
 
 
     # Attributes are inherited, so when we instantiate an attribute of a class, the AttributeLink may contain the name of the superclass
     # Attributes are inherited, so when we instantiate an attribute of a class, the AttributeLink may contain the name of the superclass
     def find_attribute_type(self, class_name: str, attr_name: str):
     def find_attribute_type(self, class_name: str, attr_name: str):

+ 52 - 18
api/od.py

@@ -16,6 +16,9 @@ def build_name_mapping(state, m):
         mapping[element] = name
         mapping[element] = name
     return mapping
     return mapping
 
 
+class NoSuchSlotException(Exception):
+    pass
+
 # Object Diagram API
 # Object Diagram API
 # Intended to replace the 'services.od.OD' class eventually
 # Intended to replace the 'services.od.OD' class eventually
 class ODAPI:
 class ODAPI:
@@ -25,7 +28,7 @@ class ODAPI:
         self.m = m
         self.m = m
         self.mm = mm
         self.mm = mm
         self.od = od.OD(mm, m, state)
         self.od = od.OD(mm, m, state)
-        self.cd = cd.CDAPI(state, mm)
+        self.cdapi = cd.CDAPI(state, mm)
 
 
         self.create_boolean_value = self.od.create_boolean_value
         self.create_boolean_value = self.od.create_boolean_value
         self.create_integer_value = self.od.create_integer_value
         self.create_integer_value = self.od.create_integer_value
@@ -36,15 +39,16 @@ class ODAPI:
 
 
     # Called after every change - makes querying faster but modifying slower
     # Called after every change - makes querying faster but modifying slower
     def __recompute_mappings(self):
     def __recompute_mappings(self):
-        self.obj_to_name = {**build_name_mapping(self.state, self.m), **build_name_mapping(self.state, self.mm)}
-        # self.obj_to_type = {}
+        self.m_obj_to_name = build_name_mapping(self.state, self.m)
+        self.mm_obj_to_name = build_name_mapping(self.state, self.mm)
         self.type_to_objs = { type_name : set() for type_name in self.bottom.read_keys(self.mm)}
         self.type_to_objs = { type_name : set() for type_name in self.bottom.read_keys(self.mm)}
         for m_name in self.bottom.read_keys(self.m):
         for m_name in self.bottom.read_keys(self.m):
             m_element, = self.bottom.read_outgoing_elements(self.m, m_name)
             m_element, = self.bottom.read_outgoing_elements(self.m, m_name)
             tm_element = self.get_type(m_element)
             tm_element = self.get_type(m_element)
-            tm_name = self.obj_to_name[tm_element]
-            # self.obj_to_type[m_name] = tm_name
-            self.type_to_objs[tm_name].add(m_name)
+            if tm_element in self.mm_obj_to_name:
+                tm_name = self.mm_obj_to_name[tm_element]
+                # self.obj_to_type[m_name] = tm_name
+                self.type_to_objs[tm_name].add(m_name)
 
 
     def get_value(self, obj: UUID):
     def get_value(self, obj: UUID):
         return od.read_primitive_value(self.bottom, obj, self.mm)[0]
         return od.read_primitive_value(self.bottom, obj, self.mm)[0]
@@ -58,21 +62,44 @@ class ODAPI:
     def get_slot(self, obj: UUID, attr_name: str):
     def get_slot(self, obj: UUID, attr_name: str):
         slot = self.od.get_slot(obj, attr_name)
         slot = self.od.get_slot(obj, attr_name)
         if slot == None:
         if slot == None:
-            raise Exception(f"Object '{self.obj_to_name[obj]}' has no slot '{attr_name}'")
+            raise NoSuchSlotException(f"Object '{self.m_obj_to_name[obj]}' has no slot '{attr_name}'")
         return slot
         return slot
 
 
     def get_slot_link(self, obj: UUID, attr_name: str):
     def get_slot_link(self, obj: UUID, attr_name: str):
         return self.od.get_slot_link(obj, attr_name)
         return self.od.get_slot_link(obj, attr_name)
 
 
-    def get_outgoing(self, obj: UUID, assoc_name: str):
-        return od.find_outgoing_typed_by(self.bottom, src=obj, type_node=self.bottom.read_outgoing_elements(self.mm, assoc_name)[0])
-
-    def get_incoming(self, obj: UUID, assoc_name: str):
-        return od.find_incoming_typed_by(self.bottom, tgt=obj, type_node=self.bottom.read_outgoing_elements(self.mm, assoc_name)[0])
+    # Parameter 'include_subtypes': whether to include subtypes of the given association
+    def get_outgoing(self, obj: UUID, assoc_name: str, include_subtypes=True):
+        outgoing = self.bottom.read_outgoing_edges(obj)
+        result = []
+        for o in outgoing:
+            try:
+                type_of_outgoing_link = self.get_type_name(o)
+            except:
+                continue # OK, not all edges are typed
+            if (include_subtypes and self.cdapi.is_subtype(super_type_name=assoc_name, sub_type_name=type_of_outgoing_link)
+                or not include_subtypes and type_of_outgoing_link == assoc_name):
+                    result.append(o)
+        return result
+
+
+    # Parameter 'include_subtypes': whether to include subtypes of the given association
+    def get_incoming(self, obj: UUID, assoc_name: str, include_subtypes=True):
+        incoming = self.bottom.read_incoming_edges(obj)
+        result = []
+        for i in incoming:
+            try:
+                type_of_incoming_link = self.get_type_name(i)
+            except:
+                continue # OK, not all edges are typed
+            if (include_subtypes and self.cdapi.is_subtype(super_type_name=assoc_name, sub_type_name=type_of_incoming_link)
+                or not include_subtypes and type_of_incoming_link == assoc_name):
+                    result.append(i)
+        return result
 
 
     def get_all_instances(self, type_name: str, include_subtypes=True):
     def get_all_instances(self, type_name: str, include_subtypes=True):
         if include_subtypes:
         if include_subtypes:
-            all_types = self.cd.transitive_sub_types[type_name]
+            all_types = self.cdapi.transitive_sub_types[type_name]
         else:
         else:
             all_types = set([type_name])
             all_types = set([type_name])
         obj_names = [obj_name for type_name in all_types for obj_name in self.type_to_objs[type_name]]
         obj_names = [obj_name for type_name in all_types for obj_name in self.type_to_objs[type_name]]
@@ -89,7 +116,6 @@ class ODAPI:
             [name for name in self.bottom.read_keys(self.m) if self.bottom.read_outgoing_elements(self.m, name)[0] == obj] +
             [name for name in self.bottom.read_keys(self.m) if self.bottom.read_outgoing_elements(self.m, name)[0] == obj] +
             [name for name in self.bottom.read_keys(self.mm) if self.bottom.read_outgoing_elements(self.mm, name)[0] == obj]
             [name for name in self.bottom.read_keys(self.mm) if self.bottom.read_outgoing_elements(self.mm, name)[0] == obj]
         )[0]
         )[0]
-        return self.obj_to_name[obj]
 
 
     def get(self, name: str):
     def get(self, name: str):
         return self.bottom.read_outgoing_elements(self.m, name)[0]
         return self.bottom.read_outgoing_elements(self.m, name)[0]
@@ -98,8 +124,8 @@ class ODAPI:
         return self.get_name(self.get_type(obj))
         return self.get_name(self.get_type(obj))
 
 
     def is_instance(obj: UUID, type_name: str, include_subtypes=True):
     def is_instance(obj: UUID, type_name: str, include_subtypes=True):
-        typ = self.cd.get_type(type_name)
-        types = set(typ) if not include_subtypes else self.cd.transitive_sub_types[type_name]
+        typ = self.cdapi.get_type(type_name)
+        types = set(typ) if not include_subtypes else self.cdapi.transitive_sub_types[type_name]
         for type_of_obj in self.bottom.read_outgoing_elements(obj, "Morphism"):
         for type_of_obj in self.bottom.read_outgoing_elements(obj, "Morphism"):
             if type_of_obj in types:
             if type_of_obj in types:
                 return True
                 return True
@@ -109,12 +135,20 @@ class ODAPI:
         self.bottom.delete_element(obj)
         self.bottom.delete_element(obj)
         self.__recompute_mappings()
         self.__recompute_mappings()
 
 
+    # Does the class of the object have the given attribute?
     def has_slot(self, obj: UUID, attr_name: str):
     def has_slot(self, obj: UUID, attr_name: str):
         class_name = self.get_name(self.get_type(obj))
         class_name = self.get_name(self.get_type(obj))
         return self.od.get_attr_link_name(class_name, attr_name) != None
         return self.od.get_attr_link_name(class_name, attr_name) != None
 
 
     def get_slot_value(self, obj: UUID, attr_name: str):
     def get_slot_value(self, obj: UUID, attr_name: str):
-        return self.get_value(self.get_slot(obj, attr_name))
+        slot = self.get_slot(obj, attr_name)
+        return self.get_value(slot)
+
+    def get_slot_value_default(self, obj: UUID, attr_name: str, default: any):
+        try:
+            return self.get_slot_value(obj, attr_name)
+        except NoSuchSlotException:
+            return default
 
 
     # create or update slot value
     # create or update slot value
     def set_slot_value(self, obj: UUID, attr_name: str, new_value: any, is_code=False):
     def set_slot_value(self, obj: UUID, attr_name: str, new_value: any, is_code=False):
@@ -130,7 +164,7 @@ class ODAPI:
             self.bottom.delete_element(old_target) # this also deletes the slot-link
             self.bottom.delete_element(old_target) # this also deletes the slot-link
 
 
         new_target = self.create_primitive_value(target_name, new_value, is_code)
         new_target = self.create_primitive_value(target_name, new_value, is_code)
-        slot_type = self.cd.find_attribute_type(self.get_type_name(obj), attr_name)
+        slot_type = self.cdapi.find_attribute_type(self.get_type_name(obj), attr_name)
         new_link = self.od._create_link(link_name, slot_type, obj, new_target)
         new_link = self.od._create_link(link_name, slot_type, obj, new_target)
         self.__recompute_mappings()
         self.__recompute_mappings()
 
 

+ 17 - 19
bootstrap/primitive.py

@@ -4,36 +4,35 @@ from services.primitives.integer_type import Integer
 from services.primitives.actioncode_type import ActionCode
 from services.primitives.actioncode_type import ActionCode
 
 
 
 
-def bootstrap_type(type_name: str, scd_root: UUID, model_root: UUID, integer_type: UUID, state: State):
+def bootstrap_type(type_name: str, scd_root: UUID, model_root: UUID, state: State):
     bottom = Bottom(state)
     bottom = Bottom(state)
     # create class
     # create class
-    class_node = bottom.create_node()  # create class node
-    bottom.create_edge(model_root, class_node, type_name)  # attach to model
-    scd_node, = bottom.read_outgoing_elements(scd_root, "Class")  # retrieve type
-    bottom.create_edge(class_node, scd_node, "Morphism")  # create morphism link
+    type_class_node = bottom.create_node()  # create class node
+    bottom.create_edge(model_root, type_class_node, type_name)  # attach to model
+    class_class_node, = bottom.read_outgoing_elements(scd_root, "Class")  # retrieve type
+    bottom.create_edge(type_class_node, class_class_node, "Morphism")  # create morphism link
+    scd_int_node, = bottom.read_outgoing_elements(scd_root, "Integer")
     # set min_cardinality
     # set min_cardinality
     min_c_model = bottom.create_node()
     min_c_model = bottom.create_node()
     Integer(min_c_model, state).create(1)
     Integer(min_c_model, state).create(1)
     min_c_node = bottom.create_node(str(min_c_model))
     min_c_node = bottom.create_node(str(min_c_model))
     bottom.create_edge(model_root, min_c_node, f"{type_name}.lower_cardinality")
     bottom.create_edge(model_root, min_c_node, f"{type_name}.lower_cardinality")
-    min_c_link = bottom.create_edge(class_node, min_c_node)
+    min_c_link = bottom.create_edge(type_class_node, min_c_node)
     bottom.create_edge(model_root, min_c_link, f"{type_name}_lower_cardinality")
     bottom.create_edge(model_root, min_c_link, f"{type_name}_lower_cardinality")
-    scd_node = integer_type
     scd_link, = bottom.read_outgoing_elements(scd_root, "Class_lower_cardinality")
     scd_link, = bottom.read_outgoing_elements(scd_root, "Class_lower_cardinality")
-    bottom.create_edge(min_c_node, scd_node, "Morphism")
+    bottom.create_edge(min_c_node, scd_int_node, "Morphism")
     bottom.create_edge(min_c_link, scd_link, "Morphism")
     bottom.create_edge(min_c_link, scd_link, "Morphism")
     # set max_cardinality
     # set max_cardinality
     max_c_model = bottom.create_node()
     max_c_model = bottom.create_node()
     Integer(max_c_model, state).create(1)
     Integer(max_c_model, state).create(1)
     max_c_node = bottom.create_node(str(max_c_model))
     max_c_node = bottom.create_node(str(max_c_model))
     bottom.create_edge(model_root, max_c_node, f"{type_name}.upper_cardinality")
     bottom.create_edge(model_root, max_c_node, f"{type_name}.upper_cardinality")
-    max_c_link = bottom.create_edge(class_node, max_c_node)
+    max_c_link = bottom.create_edge(type_class_node, max_c_node)
     bottom.create_edge(model_root, max_c_link, f"{type_name}_upper_cardinality")
     bottom.create_edge(model_root, max_c_link, f"{type_name}_upper_cardinality")
-    scd_node = integer_type
     scd_link, = bottom.read_outgoing_elements(scd_root, "Class_upper_cardinality")
     scd_link, = bottom.read_outgoing_elements(scd_root, "Class_upper_cardinality")
-    bottom.create_edge(max_c_node, scd_node, "Morphism")
+    bottom.create_edge(max_c_node, scd_int_node, "Morphism")
     bottom.create_edge(max_c_link, scd_link, "Morphism")
     bottom.create_edge(max_c_link, scd_link, "Morphism")
-    return class_node
+    return type_class_node
 
 
 def bootstrap_constraint(class_node, type_name: str, python_type: str, scd_root: UUID, model_root: UUID, actioncode_type: UUID, state: State):
 def bootstrap_constraint(class_node, type_name: str, python_type: str, scd_root: UUID, model_root: UUID, actioncode_type: UUID, state: State):
     bottom = Bottom(state)
     bottom = Bottom(state)
@@ -50,12 +49,12 @@ def bootstrap_constraint(class_node, type_name: str, python_type: str, scd_root:
     
     
 def bootstrap_primitive_types(scd_root, state, integer_type, boolean_type, float_type, string_type, type_type, actioncode_type):
 def bootstrap_primitive_types(scd_root, state, integer_type, boolean_type, float_type, string_type, type_type, actioncode_type):
     # Order is important: Integer must come first
     # Order is important: Integer must come first
-    class_integer    = bootstrap_type("Integer",    scd_root, integer_type,    integer_type, state)
-    class_type       = bootstrap_type("Type",       scd_root, type_type,       integer_type, state)
-    class_boolean    = bootstrap_type("Boolean",    scd_root, boolean_type,    integer_type, state)
-    class_float      = bootstrap_type("Float",      scd_root, float_type,      integer_type, state)
-    class_string     = bootstrap_type("String",     scd_root, string_type,     integer_type, state)
-    class_actioncode = bootstrap_type("ActionCode", scd_root, actioncode_type, integer_type, state)
+    class_integer    = bootstrap_type("Integer",    scd_root, integer_type,    state)
+    class_type       = bootstrap_type("Type",       scd_root, type_type,       state)
+    class_boolean    = bootstrap_type("Boolean",    scd_root, boolean_type,    state)
+    class_float      = bootstrap_type("Float",      scd_root, float_type,      state)
+    class_string     = bootstrap_type("String",     scd_root, string_type,     state)
+    class_actioncode = bootstrap_type("ActionCode", scd_root, actioncode_type, state)
 
 
     # Can only create constraints after ActionCode type has been created:
     # Can only create constraints after ActionCode type has been created:
     bootstrap_constraint(class_integer,    "Integer",    "int",   scd_root, integer_type,    actioncode_type, state)
     bootstrap_constraint(class_integer,    "Integer",    "int",   scd_root, integer_type,    actioncode_type, state)
@@ -64,4 +63,3 @@ def bootstrap_primitive_types(scd_root, state, integer_type, boolean_type, float
     bootstrap_constraint(class_float,      "Float",      "float", scd_root, float_type,      actioncode_type, state)
     bootstrap_constraint(class_float,      "Float",      "float", scd_root, float_type,      actioncode_type, state)
     bootstrap_constraint(class_string,     "String",     "str",   scd_root, string_type,     actioncode_type, state)
     bootstrap_constraint(class_string,     "String",     "str",   scd_root, string_type,     actioncode_type, state)
     bootstrap_constraint(class_actioncode, "ActionCode", "str",   scd_root, actioncode_type, actioncode_type, state)
     bootstrap_constraint(class_actioncode, "ActionCode", "str",   scd_root, actioncode_type, actioncode_type, state)
-

+ 109 - 224
framework/conformance.py

@@ -32,36 +32,33 @@ def render_conformance_check_result(error_list):
 
 
 
 
 class Conformance:
 class Conformance:
+    # Parameter 'constraint_check_subtypes': whether to check local type-level constraints also on subtypes.
     def __init__(self, state: State, model: UUID, type_model: UUID, constraint_check_subtypes=True):
     def __init__(self, state: State, model: UUID, type_model: UUID, constraint_check_subtypes=True):
         self.state = state
         self.state = state
         self.bottom = Bottom(state)
         self.bottom = Bottom(state)
-        type_model_id = state.read_dict(state.read_root(), "SCD")
-        self.scd_model = UUID(state.read_value(type_model_id))
         self.model = model
         self.model = model
         self.type_model = type_model
         self.type_model = type_model
-        self.constraint_check_subtypes = constraint_check_subtypes # for a class-level constraint, also check the constraint on the subtypes of that class? In other words, are constraints inherited.
-        self.type_mapping: Dict[str, str] = {}
-        self.model_names = {
-            # map model elements to their names to prevent iterating too much
-            self.bottom.read_outgoing_elements(self.model, e)[0]: e
-            for e in self.bottom.read_keys(self.model)
-        }
-        self.type_model_names = {
-            # map type model elements to their names to prevent iterating too much
-            self.bottom.read_outgoing_elements(self.type_model, e)[0]
-                : e for e in self.bottom.read_keys(self.type_model)
-        }
-        self.primitive_values: Dict[UUID, Any] = {}
+        self.constraint_check_subtypes = constraint_check_subtypes
+
+        # MCL
+        type_model_id = state.read_dict(state.read_root(), "SCD")
+        self.scd_model = UUID(state.read_value(type_model_id))
+
+        # Helpers
+        self.cdapi = CDAPI(state, type_model)
+        self.odapi = ODAPI(state, model, type_model)
+        self.type_odapi = ODAPI(state, type_model, self.scd_model)
+
+        # Pre-computed:
         self.abstract_types: List[str] = []
         self.abstract_types: List[str] = []
         self.multiplicities: Dict[str, Tuple] = {}
         self.multiplicities: Dict[str, Tuple] = {}
         self.source_multiplicities: Dict[str, Tuple] = {}
         self.source_multiplicities: Dict[str, Tuple] = {}
         self.target_multiplicities: Dict[str, Tuple] = {}
         self.target_multiplicities: Dict[str, Tuple] = {}
+
+        # ?
         self.structures = {}
         self.structures = {}
-        self.matches = {}
         self.candidates = {}
         self.candidates = {}
 
 
-        self.cdapi = CDAPI(state, type_model)
-        self.odapi = ODAPI(state, model, type_model)
 
 
     def check_nominal(self, *, log=False):
     def check_nominal(self, *, log=False):
         """
         """
@@ -103,120 +100,38 @@ class Conformance:
     #             print(e)
     #             print(e)
     #         return False
     #         return False
 
 
-    def read_attribute(self, element: UUID, attr_name: str):
-        """
-        Read an attribute value attached to an element
-
-        Args:
-            element: UUID of the element
-            attr_name: name of the attribute to read
-
-        Returns:
-            The value of hte attribute, if no attribute with given name is found, returns None
-        """
-        if element in self.type_model_names:
-            # type model element
-            element_name = self.type_model_names[element]
-            model = self.type_model
-        else:
-            # model element
-            element_name = self.model_names[element]
-            model = self.model
-        try:
-            attr_elem, = self.bottom.read_outgoing_elements(model, f"{element_name}.{attr_name}")
-            return self.primitive_values.get(attr_elem, self.bottom.read_value(UUID(self.bottom.read_value(attr_elem))))
-        except ValueError:
-            return None
-
-    def deref_primitive_values(self):
-        """
-        Prefetch the values stored in referenced primitive type models
-        """
-        ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef")
-        string_element, = self.bottom.read_outgoing_elements(self.scd_model, "String")
-        boolean_element, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean")
-        integer_element, = self.bottom.read_outgoing_elements(self.scd_model, "Integer")
-        t_deref = []
-        t_refs = []
-        for tm_element, tm_name in self.type_model_names.items():
-            morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
-            if ref_element in morphisms:
-                t_refs.append(self.type_model_names[tm_element])
-            elif string_element in morphisms:
-                t_deref.append(tm_element)
-            elif boolean_element in morphisms:
-                t_deref.append(tm_element)
-            elif integer_element in morphisms:
-                t_deref.append(tm_element)
-
-        for elem in t_deref:
-            primitive_model = UUID(self.bottom.read_value(elem))
-            primitive_value_node, = self.bottom.read_outgoing_elements(primitive_model)
-            primitive_value = self.bottom.read_value(primitive_value_node)
-            self.primitive_values[elem] = primitive_value
-
-        for m_name, tm_name in self.type_mapping.items():
-            if tm_name in t_refs:
-                # dereference
-                m_element, = self.bottom.read_outgoing_elements(self.model, m_name)
-                primitive_model = UUID(self.bottom.read_value(m_element))
-                try:
-                    primitive_value_node, = self.bottom.read_outgoing_elements(primitive_model)
-                    primitive_value = self.bottom.read_value(primitive_value_node)
-                    self.primitive_values[m_element] = primitive_value
-                except ValueError:
-                    pass  # multiple elements in model indicate that we're not dealing with a primitive
-
     def precompute_multiplicities(self):
     def precompute_multiplicities(self):
         """
         """
         Creates an internal representation of type multiplicities that is
         Creates an internal representation of type multiplicities that is
         more easily queryable that the state graph
         more easily queryable that the state graph
         """
         """
-        for tm_element, tm_name in self.type_model_names.items():
-            # class abstract flags and multiplicities
-            abstract = self.read_attribute(tm_element, "abstract")
-            lc = self.read_attribute(tm_element, "lower_cardinality")
-            uc = self.read_attribute(tm_element, "upper_cardinality")
+        for clss_name, clss in self.type_odapi.get_all_instances("Class"):
+            abstract = self.type_odapi.get_slot_value_default(clss, "abstract", default=False)
             if abstract:
             if abstract:
-                self.abstract_types.append(tm_name)
+                self.abstract_types.append(clss_name)
+
+            lc = self.type_odapi.get_slot_value_default(clss, "lower_cardinality", default=0)
+            uc = self.type_odapi.get_slot_value_default(clss, "upper_cardinality", default=float('inf'))
             if lc or uc:
             if lc or uc:
-                mult = (
-                    lc if lc != None else float("-inf"),
-                    uc if uc != None else float("inf")
-                )
-                self.multiplicities[tm_name] = mult
+                self.multiplicities[clss_name] = (lc, uc)
+
+        for assoc_name, assoc in self.type_odapi.get_all_instances("Association"):
             # multiplicities for associations
             # multiplicities for associations
-            slc = self.read_attribute(tm_element, "source_lower_cardinality")
-            suc = self.read_attribute(tm_element, "source_upper_cardinality")
+            slc = self.type_odapi.get_slot_value_default(assoc, "source_lower_cardinality", default=0)
+            suc = self.type_odapi.get_slot_value_default(assoc, "source_upper_cardinality", default=float('inf'))
             if slc or suc:
             if slc or suc:
-                mult = (
-                    # slc if slc != None else float("-inf"),
-                    slc if slc != None else 0,
-                    suc if suc != None else float("inf")
-                )
-                self.source_multiplicities[tm_name] = mult
-            tlc = self.read_attribute(tm_element, "target_lower_cardinality")
-            tuc = self.read_attribute(tm_element, "target_upper_cardinality")
+                self.source_multiplicities[assoc_name] = (slc, suc)
+            tlc = self.type_odapi.get_slot_value_default(assoc, "target_lower_cardinality", default=0)
+            tuc = self.type_odapi.get_slot_value_default(assoc, "target_upper_cardinality", default=float('inf'))
             if tlc or tuc:
             if tlc or tuc:
-                mult = (
-                    # tlc if tlc != None else float("-inf"),
-                    tlc if tlc != None else 0,
-                    tuc if tuc != None else float("inf")
-                )
-                self.target_multiplicities[tm_name] = mult
+                self.target_multiplicities[assoc_name] = (tlc, tuc)
+
+        for attr_name, attr in self.type_odapi.get_all_instances("AttributeLink"):
             # optional for attribute links
             # optional for attribute links
-            opt = self.read_attribute(tm_element, "optional")
+            opt = self.type_odapi.get_slot_value(attr, "optional")
             if opt != None:
             if opt != None:
-                self.source_multiplicities[tm_name] = (0, float('inf'))
-                self.target_multiplicities[tm_name] = (0 if opt else 1, 1)
-
-    def get_type(self, element: UUID):
-        """
-        Retrieve the type of an element (wrt. current type model)
-        """
-        morphisms = self.bottom.read_outgoing_elements(element, "Morphism")
-        tm_element, = [m for m in morphisms if m in self.type_model_names.keys()]
-        return tm_element
+                self.source_multiplicities[attr_name] = (0, float('inf'))
+                self.target_multiplicities[attr_name] = (0 if opt else 1, 1)
 
 
     def check_typing(self):
     def check_typing(self):
         """
         """
@@ -224,24 +139,15 @@ class Conformance:
         link exists to some element of type_model
         link exists to some element of type_model
         """
         """
         errors = []
         errors = []
-        ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef")
-        model_names = self.bottom.read_keys(self.model)
-        for m_name in model_names:
-            m_element, = self.bottom.read_outgoing_elements(self.model, m_name)
-            try:
-                tm_element = self.get_type(m_element)
-                tm_name = self.type_model_names[tm_element]
-                self.type_mapping[m_name] = tm_name
-                if ref_element in self.bottom.read_outgoing_elements(tm_element, "Morphism"):
-                    sub_m = UUID(self.bottom.read_value(m_element))
-                    sub_tm = UUID(self.bottom.read_value(tm_element))
-                    nested_errors = Conformance(self.state, sub_m, sub_tm).check_nominal()
-                    errors += [f"In ModelRef ({m_name}):" + err for err in nested_errors]
-            except ValueError as e:
-                import traceback
-                traceback.format_exc(e)
-                # no or too many morphism links found
-                errors.append(f"Incorrectly typed element: '{m_name}'")
+
+        # Recursively do a conformance check for each ModelRef
+        for ref_name, ref in self.type_odapi.get_all_instances("ModelRef"):
+            sub_mm = UUID(self.bottom.read_value(ref))
+            for ref_inst_name, ref_inst in self.odapi.get_all_instances(ref_name):
+                sub_m = UUID(self.bottom.read_value(ref_inst))
+                nested_errors = Conformance(self.state, sub_m, sub_mm).check_nominal()
+                errors += [f"In ModelRef ({m_name}):" + err for err in nested_errors]
+
         return errors
         return errors
 
 
     def check_link_typing(self):
     def check_link_typing(self):
@@ -249,97 +155,76 @@ class Conformance:
         for each link, check whether its source and target are of a valid type
         for each link, check whether its source and target are of a valid type
         """
         """
         errors = []
         errors = []
-        for m_name, tm_name in self.type_mapping.items():
-            m_element, = self.bottom.read_outgoing_elements(self.model, m_name)
-            m_source = self.bottom.read_edge_source(m_element)
-            m_target = self.bottom.read_edge_target(m_element)
-            if m_source == None or m_target == None:
-                # element is not a link
-                continue
-            tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name)
-            tm_source = self.bottom.read_edge_source(tm_element)
-            tm_target = self.bottom.read_edge_target(tm_element)
-            # check if source is typed correctly
-            source_name = self.model_names[m_source]
-            source_type_actual = self.type_mapping[source_name]
-            source_type_expected = self.type_model_names[tm_source]
-            if not self.cdapi.is_subtype(super_type_name=source_type_expected, sub_type_name=source_type_actual):
-                errors.append(f"Invalid source type '{source_type_actual}' for element '{m_name}'")
-            # check if target is typed correctly
-            target_name = self.model_names[m_target]
-            target_type_actual = self.type_mapping[target_name]
-            target_type_expected = self.type_model_names[tm_target]
-            if not self.cdapi.is_subtype(super_type_name=source_type_expected, sub_type_name=source_type_actual):
-                errors.append(f"Invalid target type '{target_type_actual}' for element '{m_name}'")
+        for tm_name, tm_element in self.type_odapi.get_all_instances("Association") + self.type_odapi.get_all_instances("AttributeLink"):
+            for m_name, m_element in self.odapi.get_all_instances(tm_name):
+                m_source = self.bottom.read_edge_source(m_element)
+                m_target = self.bottom.read_edge_target(m_element)
+                if m_source == None or m_target == None:
+                    # element is not a link
+                    continue
+                # tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name)
+                tm_source = self.bottom.read_edge_source(tm_element)
+                tm_target = self.bottom.read_edge_target(tm_element)
+                # check if source is typed correctly
+                # source_name = self.odapi.m_obj_to_name[m_source]
+                source_type_actual = self.odapi.get_type_name(m_source)
+                source_type_expected = self.odapi.mm_obj_to_name[tm_source]
+                if not self.cdapi.is_subtype(super_type_name=source_type_expected, sub_type_name=source_type_actual):
+                    errors.append(f"Invalid source type '{source_type_actual}' for link '{m_name}:{tm_name}'")
+                # check if target is typed correctly
+                # target_name = self.odapi.m_obj_to_name[m_target]
+                target_type_actual = self.odapi.get_type_name(m_target)
+                target_type_expected = self.odapi.mm_obj_to_name[tm_target]
+                if not self.cdapi.is_subtype(super_type_name=source_type_expected, sub_type_name=source_type_actual):
+                    errors.append(f"Invalid target type '{target_type_actual}' for link '{m_name}:{tm_name}'")
         return errors
         return errors
 
 
     def check_multiplicities(self):
     def check_multiplicities(self):
         """
         """
         Check whether multiplicities for all types are respected
         Check whether multiplicities for all types are respected
         """
         """
-        self.deref_primitive_values()
         self.precompute_multiplicities()
         self.precompute_multiplicities()
         errors = []
         errors = []
-        for type_name in self.type_model_names.values():
+        for class_name, clss in self.type_odapi.get_all_instances("Class"):
+        # for type_name in self.odapi.mm_obj_to_name.values():
             # abstract classes
             # abstract classes
-            if type_name in self.abstract_types:
-                count = list(self.type_mapping.values()).count(type_name)
+            if class_name in self.abstract_types:
+                count = len(self.odapi.get_all_instances(class_name, include_subtypes=False))
                 if count > 0:
                 if count > 0:
-                    errors.append(f"Invalid instantiation of abstract class: '{type_name}'")
+                    errors.append(f"Invalid instantiation of abstract class: '{class_name}'")
             # class multiplicities
             # class multiplicities
-            if type_name in self.multiplicities:
-                lc, uc = self.multiplicities[type_name]
-                count = 0
-                for sub_type in self.cdapi.transitive_sub_types[type_name]:
-                    count += list(self.type_mapping.values()).count(sub_type)
+            if class_name in self.multiplicities:
+                lc, uc = self.multiplicities[class_name]
+                count = len(self.odapi.get_all_instances(class_name, include_subtypes=True))
                 if count < lc or count > uc:
                 if count < lc or count > uc:
-                    errors.append(f"Cardinality of type exceeds valid multiplicity range: '{type_name}' ({count})")
+                    errors.append(f"Cardinality of type exceeds valid multiplicity range: '{class_name}' ({count})")
 
 
+        for assoc_name, assoc in self.type_odapi.get_all_instances("Association") + self.type_odapi.get_all_instances("AttributeLink"):
             # association/attribute source multiplicities
             # association/attribute source multiplicities
-            if type_name in self.source_multiplicities:
+            if assoc_name in self.source_multiplicities:
                 # type is an association
                 # type is an association
-                type_obj, = self.bottom.read_outgoing_elements(self.type_model, type_name)
-                tgt_type_obj = self.bottom.read_edge_target(type_obj)
-                tgt_type_name = self.type_model_names[tgt_type_obj]
-                lc, uc = self.source_multiplicities[type_name]
-                for obj_name, obj_type_name in self.type_mapping.items():
-                    if self.cdapi.is_subtype(super_type_name=tgt_type_name, sub_type_name=obj_type_name):
-                        # obj's type has this incoming association -> now we will count the number of links typed by it
-                        count = 0
-                        obj, = self.bottom.read_outgoing_elements(self.model, obj_name)
-                        incoming = self.bottom.read_incoming_edges(obj)
-                        for i in incoming:
-                            try:
-                                type_of_incoming_link = self.type_mapping[self.model_names[i]]
-                                if self.cdapi.is_subtype(super_type_name=type_name, sub_type_name=type_of_incoming_link):
-                                    count += 1
-                            except KeyError:
-                                pass  # for elements not part of model, e.g. morphism links
-                        if count < lc or count > uc:
-                            errors.append(f"Source cardinality of type '{type_name}' ({count}) out of bounds ({lc}..{uc}) in '{obj_name}'.")
+                assoc, = self.bottom.read_outgoing_elements(self.type_model, assoc_name)
+                tgt_type_obj = self.bottom.read_edge_target(assoc)
+                tgt_type_name = self.odapi.mm_obj_to_name[tgt_type_obj]
+                lc, uc = self.source_multiplicities[assoc_name]
+                for obj_name, obj in self.odapi.get_all_instances(tgt_type_name, include_subtypes=True):
+                    # obj's type has this incoming association -> now we will count the number of links typed by it
+                    count = len(self.odapi.get_incoming(obj, assoc_name, include_subtypes=True))
+                    if count < lc or count > uc:
+                        errors.append(f"Source cardinality of type '{assoc_name}' ({count}) out of bounds ({lc}..{uc}) in '{obj_name}'.")
 
 
             # association/attribute target multiplicities
             # association/attribute target multiplicities
-            if type_name in self.target_multiplicities:
+            if assoc_name in self.target_multiplicities:
                 # type is an association
                 # type is an association
-                type_obj, = self.bottom.read_outgoing_elements(self.type_model, type_name)
+                type_obj, = self.bottom.read_outgoing_elements(self.type_model, assoc_name)
                 src_type_obj = self.bottom.read_edge_source(type_obj)
                 src_type_obj = self.bottom.read_edge_source(type_obj)
-                src_type_name = self.type_model_names[src_type_obj]
-                lc, uc = self.target_multiplicities[type_name]
-                for obj_name, obj_type_name in self.type_mapping.items():
-                    if self.cdapi.is_subtype(super_type_name=src_type_name, sub_type_name=obj_type_name):
-                        # obj's type has this outgoing association -> now we will count the number of links typed by it
-                        count = 0
-                        obj, = self.bottom.read_outgoing_elements(self.model, obj_name)
-                        outgoing = self.bottom.read_outgoing_edges(obj)
-                        for o in outgoing:
-                            try:
-                                type_of_outgoing_link = self.type_mapping[self.model_names[o]]
-                                if self.cdapi.is_subtype(super_type_name=type_name, sub_type_name=type_of_outgoing_link):
-                                    count += 1
-                            except KeyError:
-                                pass  # for elements not part of model, e.g. morphism links
-                        if count < lc or count > uc:
-                            errors.append(f"Target cardinality of type '{type_name}' ({count}) out of bounds ({lc}..{uc}) in '{obj_name}'.")
+                src_type_name = self.odapi.mm_obj_to_name[src_type_obj]
+                lc, uc = self.target_multiplicities[assoc_name]
+                for obj_name, obj in self.odapi.get_all_instances(src_type_name, include_subtypes=True):
+                    # obj's type has this outgoing association -> now we will count the number of links typed by it
+                    count = len(self.odapi.get_outgoing(obj, assoc_name, include_subtypes=True))
+                    if count < lc or count > uc:
+                        errors.append(f"Target cardinality of type '{assoc_name}' ({count}) out of bounds ({lc}..{uc}) in '{obj_name}'.")
         return errors
         return errors
 
 
     def evaluate_constraint(self, code, **kwargs):
     def evaluate_constraint(self, code, **kwargs):
@@ -447,7 +332,7 @@ class Conformance:
         # collect types
         # collect types
         class_element, = self.bottom.read_outgoing_elements(self.scd_model, "Class")
         class_element, = self.bottom.read_outgoing_elements(self.scd_model, "Class")
         association_element, = self.bottom.read_outgoing_elements(self.scd_model, "Association")
         association_element, = self.bottom.read_outgoing_elements(self.scd_model, "Association")
-        for tm_element, tm_name in self.type_model_names.items():
+        for tm_element, tm_name in self.odapi.mm_obj_to_name.items():
             # retrieve elements that tm_element is a morphism of
             # retrieve elements that tm_element is a morphism of
             morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
             morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
             morphism, = [m for m in morphisms if m in scd_elements]
             morphism, = [m for m in morphisms if m in scd_elements]
@@ -457,7 +342,7 @@ class Conformance:
         # collect type structures
         # collect type structures
         # retrieve AttributeLink to check whether element is a morphism of AttributeLink
         # retrieve AttributeLink to check whether element is a morphism of AttributeLink
         attr_link_element, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink")
         attr_link_element, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink")
-        for tm_element, tm_name in self.type_model_names.items():
+        for tm_element, tm_name in self.odapi.mm_obj_to_name.items():
             # retrieve elements that tm_element is a morphism of
             # retrieve elements that tm_element is a morphism of
             morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
             morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
             morphism, = [m for m in morphisms if m in scd_elements]
             morphism, = [m for m in morphisms if m in scd_elements]
@@ -465,8 +350,8 @@ class Conformance:
             if attr_link_element == morphism:
             if attr_link_element == morphism:
                 # retrieve attributes of attribute link, i.e. 'name' and 'optional'
                 # retrieve attributes of attribute link, i.e. 'name' and 'optional'
                 attrs = self.bottom.read_outgoing_elements(tm_element)
                 attrs = self.bottom.read_outgoing_elements(tm_element)
-                name_model_node, = filter(lambda x: self.type_model_names.get(x, "").endswith(".name"), attrs)
-                opt_model_node, = filter(lambda x: self.type_model_names.get(x, "").endswith(".optional"), attrs)
+                name_model_node, = filter(lambda x: self.odapi.m_obj_to_name.get(x, "").endswith(".name"), attrs)
+                opt_model_node, = filter(lambda x: self.odapi.m_obj_to_name.get(x, "").endswith(".optional"), attrs)
                 # get attr name value
                 # get attr name value
                 name_model = UUID(self.bottom.read_value(name_model_node))
                 name_model = UUID(self.bottom.read_value(name_model_node))
                 name_node, = self.bottom.read_outgoing_elements(name_model)
                 name_node, = self.bottom.read_outgoing_elements(name_model)
@@ -477,9 +362,9 @@ class Conformance:
                 opt = self.bottom.read_value(opt_node)
                 opt = self.bottom.read_value(opt_node)
                 # get attr type name
                 # get attr type name
                 source_type_node = self.bottom.read_edge_source(tm_element)
                 source_type_node = self.bottom.read_edge_source(tm_element)
-                source_type_name = self.type_model_names[source_type_node]
+                source_type_name = self.odapi.mm_obj_to_name[source_type_node]
                 target_type_node = self.bottom.read_edge_target(tm_element)
                 target_type_node = self.bottom.read_edge_target(tm_element)
-                target_type_name = self.type_model_names[target_type_node]
+                target_type_name = self.odapi.mm_obj_to_name[target_type_node]
                 # add attribute to the structure of its source type
                 # add attribute to the structure of its source type
                 # attribute is stored as a (name, optional, type) triple
                 # attribute is stored as a (name, optional, type) triple
                 self.structures.setdefault(source_type_name, set()).add((name, opt, target_type_name))
                 self.structures.setdefault(source_type_name, set()).add((name, opt, target_type_name))
@@ -493,7 +378,7 @@ class Conformance:
         # filter out abstract types, as they cannot be instantiated
         # filter out abstract types, as they cannot be instantiated
         # retrieve Class_abstract to check whether element is a morphism of Class_abstract
         # retrieve Class_abstract to check whether element is a morphism of Class_abstract
         class_abs_element, = self.bottom.read_outgoing_elements(self.scd_model, "Class_abstract")
         class_abs_element, = self.bottom.read_outgoing_elements(self.scd_model, "Class_abstract")
-        for tm_element, tm_name in self.type_model_names.items():
+        for tm_element, tm_name in self.odapi.mm_obj_to_name.items():
             # retrieve elements that tm_element is a morphism of
             # retrieve elements that tm_element is a morphism of
             morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
             morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
             morphism, = [m for m in morphisms if m in scd_elements]
             morphism, = [m for m in morphisms if m in scd_elements]
@@ -506,7 +391,7 @@ class Conformance:
                 is_abstract = self.bottom.read_value(abst_node)
                 is_abstract = self.bottom.read_value(abst_node)
                 # retrieve type name
                 # retrieve type name
                 source_node = self.bottom.read_edge_source(tm_element)
                 source_node = self.bottom.read_edge_source(tm_element)
-                type_name = self.type_model_names[source_node]
+                type_name = self.odapi.mm_obj_to_name[source_node]
                 if is_abstract:
                 if is_abstract:
                     self.structures.pop(type_name)
                     self.structures.pop(type_name)
 
 
@@ -516,7 +401,7 @@ class Conformance:
         """
         """
         ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef")
         ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef")
         # matching
         # matching
-        for m_element, m_name in self.model_names.items():
+        for m_element, m_name in self.odapi.m_obj_to_name.items():
             is_edge = self.bottom.read_edge_source(m_element) != None
             is_edge = self.bottom.read_edge_source(m_element) != None
             print('element:', m_element, 'name:', m_name, 'is_edge', is_edge)
             print('element:', m_element, 'name:', m_name, 'is_edge', is_edge)
             for type_name, structure in self.structures.items():
             for type_name, structure in self.structures.items():
@@ -567,23 +452,23 @@ class Conformance:
                         print('  add to candidates:', m_name, type_name)
                         print('  add to candidates:', m_name, type_name)
                         self.candidates.setdefault(m_name, set()).add(type_name)
                         self.candidates.setdefault(m_name, set()).add(type_name)
         # filter out candidates for links based on source and target types
         # filter out candidates for links based on source and target types
-        for m_element, m_name in self.model_names.items():
+        for m_element, m_name in self.odapi.m_obj_to_name.items():
             is_edge = self.bottom.read_edge_source(m_element) != None
             is_edge = self.bottom.read_edge_source(m_element) != None
             if is_edge and m_name in self.candidates:
             if is_edge and m_name in self.candidates:
                 m_source = self.bottom.read_edge_source(m_element)
                 m_source = self.bottom.read_edge_source(m_element)
                 m_target = self.bottom.read_edge_target(m_element)
                 m_target = self.bottom.read_edge_target(m_element)
                 print(self.candidates)
                 print(self.candidates)
-                source_candidates = self.candidates[self.model_names[m_source]]
-                target_candidates = self.candidates[self.model_names[m_target]]
+                source_candidates = self.candidates[self.odapi.m_obj_to_name[m_source]]
+                target_candidates = self.candidates[self.odapi.m_obj_to_name[m_target]]
                 remove = set()
                 remove = set()
                 for candidate_name in self.candidates[m_name]:
                 for candidate_name in self.candidates[m_name]:
                     candidate_element, = self.bottom.read_outgoing_elements(self.type_model, candidate_name)
                     candidate_element, = self.bottom.read_outgoing_elements(self.type_model, candidate_name)
-                    candidate_source = self.type_model_names[self.bottom.read_edge_source(candidate_element)]
+                    candidate_source = self.odapi.mm_obj_to_name[self.bottom.read_edge_source(candidate_element)]
                     if candidate_source not in source_candidates:
                     if candidate_source not in source_candidates:
                         if len(source_candidates.intersection(set(self.odapi.transitive_sub_types[candidate_source]))) == 0:
                         if len(source_candidates.intersection(set(self.odapi.transitive_sub_types[candidate_source]))) == 0:
                         # if len(source_candidates.intersection(set(self.sub_types[candidate_source]))) == 0:
                         # if len(source_candidates.intersection(set(self.sub_types[candidate_source]))) == 0:
                             remove.add(candidate_name)
                             remove.add(candidate_name)
-                    candidate_target = self.type_model_names[self.bottom.read_edge_target(candidate_element)]
+                    candidate_target = self.odapi.mm_obj_to_name[self.bottom.read_edge_target(candidate_element)]
                     if candidate_target not in target_candidates:
                     if candidate_target not in target_candidates:
                         if len(target_candidates.intersection(set(self.odapi.transitive_sub_types[candidate_target]))) == 0:
                         if len(target_candidates.intersection(set(self.odapi.transitive_sub_types[candidate_target]))) == 0:
                         # if len(target_candidates.intersection(set(self.sub_types[candidate_target]))) == 0:
                         # if len(target_candidates.intersection(set(self.sub_types[candidate_target]))) == 0:

+ 1 - 1
services/od.py

@@ -372,7 +372,7 @@ def read_primitive_value(bottom, modelref: UUID, mm: UUID):
     if not is_typed_by(bottom, typ, get_scd_mm_modelref_node(bottom)):
     if not is_typed_by(bottom, typ, get_scd_mm_modelref_node(bottom)):
         raise Exception("Assertion failed: argument must be typed by ModelRef", typ)
         raise Exception("Assertion failed: argument must be typed by ModelRef", typ)
     referred_model = UUID(bottom.read_value(modelref))
     referred_model = UUID(bottom.read_value(modelref))
-    typ_name = get_object_name(bottom, mm, typ)
+    typ_name = get_object_name(bottom, model=mm, object_node=typ)
     if typ_name == "Integer":
     if typ_name == "Integer":
         return Integer(referred_model, bottom.state).read(), typ_name
         return Integer(referred_model, bottom.state).read(), typ_name
     elif typ_name == "String":
     elif typ_name == "String":