From 07c695f9e2f2f64cb7bb69c86b353b4913fa5751 Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Mon, 2 Sep 2024 15:39:03 +0200 Subject: [PATCH] Add object diagrams service --- experiments/exp_scd.py | 79 ++++++++++++++++++++++++++++++++++++++ services/od.py | 86 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 experiments/exp_scd.py create mode 100644 services/od.py diff --git a/experiments/exp_scd.py b/experiments/exp_scd.py new file mode 100644 index 0000000..2e206be --- /dev/null +++ b/experiments/exp_scd.py @@ -0,0 +1,79 @@ +# Simple Class Diagram experiment + +from state.devstate import DevState +from bootstrap.scd import bootstrap_scd +from uuid import UUID +from services.scd import SCD +from framework.conformance import Conformance +from services.od import OD + +import sys + +def main(): + state = DevState() + root = state.read_root() # id: 0 + scd_node = bootstrap_scd(state) + scd_node2 = state.read_dict(root, "SCD") + # print(root, scd_node, scd_node2) + + def print_tree(root, max_depth, depth=0): + print(" "*depth, "root=", root, "value=", state.read_value(root)) + 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) + + print("explore...") + print_tree(root, 1) + + model_id = state.create_node() + scd = SCD(model_id, state) + scd.create_class("A") + scd.create_class("B") + scd.create_association("A2B", "A", "B", + src_min_c=1, + src_max_c=1, + tgt_min_c=1, + tgt_max_c=2, + ) + + print_tree(model_id, 1) + + conf = Conformance(state, model_id, scd_node) + print("Check nominal conformance...") + print(conf.check_nominal(log=True)) + print("Check structural conformance...") + print(conf.check_structural(log=True)) + print("Check nominal conformance (again)...") + print(conf.check_nominal(log=True)) + + inst_id = state.create_node() + od = OD(model_id, inst_id, state) + + od.create_object("a", "A") + od.create_object("b", "B") + od.create_link("A2B", "a", "b") + + conf2 = Conformance(state, inst_id, model_id) + print(conf2.check_nominal(log=True)) + +if __name__ == "__main__": + main() diff --git a/services/od.py b/services/od.py new file mode 100644 index 0000000..4df282b --- /dev/null +++ b/services/od.py @@ -0,0 +1,86 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom +from services.primitives.integer_type import Integer +from services.primitives.string_type import String + +# Object Diagrams service + +class OD: + + def __init__(self, type_model: UUID, model: UUID, state: State): + """ + Implements services for the object diagrams LTM. + Implementation is done in terms of services provided by LTM-bottom. + + Args: + type_model: The SCD-conforming class diagram that contains the types of this object diagram + model: UUID of the (OD) model to manipulate + """ + + self.type_model = type_model + self.model = model + self.bottom = Bottom(state) + + + def create_object(self, name: str, class_name: str): + class_node, = self.bottom.read_outgoing_elements(self.type_model, class_name) + + + abstract_nodes = self.bottom.read_outgoing_elements(self.type_model, f"{class_name}.abstract") + if len(abstract_nodes) == 1: + is_abstract = self.bottom.read_value(abstract_node) + else: + # 'abstract' is optional attribute, default is False + is_abstract = False + + print("class", name, "is abstract?", is_abstract) + + if is_abstract: + raise Exception("Cannot instantiate abstract class!") + + object_node = self.bottom.create_node() + self.bottom.create_edge(self.model, object_node, name) # attach to model + self.bottom.create_edge(object_node, class_node, "Morphism") # typed-by link + + return object_node + + + def create_slot(self, object_name: str, attr_name: str, value: UUID): + attr_node = self.bottom.read_outgoing_elements(self.type_model, attr_name) # get the attribute + object_node, = self.bottom.read_outgoing_elements(self.model, object_name) # get the object + slot_node = value + + # generate a unique name for the slot + i = 0; + while len(self.bottom.read_outgoing_elements(self.model, f"{object_name}.{attr_name}{i}")) != 0: + i += 1 + + self.bottom.create_edge(self.model, slot_node, f"{object_name}.{attr_name}{i}") # attach to model root + slot_link = self.bottom.create_edge(object_node, slot_node) # attach to object + self.bottom.create_edge(self.model, slot_link, f"{object_name}.{attr_name}{i}_link") # attach attr-link to model + + self.bottom.create_edge(slot_node, attr_node, "Morphism") # slot typed-by attribute + slot_link_type, = self.bottom.read_outgoing_elements(self.type_model, "AttributeLink") + self.bottom.create_edge(slot_link, slot_link_type) + + + def create_link(self, assoc_name: str, src_obj_name: str, tgt_obj_name: str): + src_obj_node, = self.bottom.read_outgoing_elements(self.model, src_obj_name) + tgt_obj_node, = self.bottom.read_outgoing_elements(self.model, tgt_obj_name) + + link_edge = self.bottom.create_edge(src_obj_node, tgt_obj_node); + + # generate a unique name for the link + i = 0; + while True: + link_name = f"{assoc_name}{i}" + if len(self.bottom.read_outgoing_elements(self.model, link_name)) == 0: + break + i += 1 + + self.bottom.create_edge(self.model, link_edge, link_name) + + type_edge, = self.bottom.read_outgoing_elements(self.type_model, assoc_name) + self.bottom.create_edge(link_edge, type_edge, "Morphism") +