diff --git a/experiments/exp_scd.py b/experiments/exp_scd.py index 8f2d75e..3e2a110 100644 --- a/experiments/exp_scd.py +++ b/experiments/exp_scd.py @@ -26,6 +26,13 @@ def main(): 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) @@ -50,19 +57,21 @@ def main(): print_tree(tgt, max_depth, depth+1) print("explore...") - print_tree(root, 2) + # print_tree(root, 2) int_type_id = state.read_dict(state.read_root(), "Integer") int_type = UUID(state.read_value(int_type_id)) - scd2 = SCD(scd_node, state) - for el in scd2.list_elements(): - print(el) + # scd2 = SCD(scd_node, state) + # for el in scd2.list_elements(): + # print(el) model_id = state.create_node() scd = SCD(model_id, state) - scd.create_class("A") + scd.create_class("Abstract", abstract=True) + scd.create_class("A", min_c=1, max_c=10) + scd.create_inheritance("A", "Abstract") scd.create_model_ref("Integer", int_type) scd.create_attribute_link("A", "Integer", "size", False) scd.create_class("B") @@ -73,7 +82,7 @@ def main(): tgt_max_c=2, ) - print_tree(model_id, 2) + # print_tree(model_id, 3) conf = Conformance(state, model_id, scd_node) @@ -95,9 +104,9 @@ def main(): print("checking conformance....") conf2 = Conformance(state, inst_id, model_id) - print(conf2.check_nominal(log=True)) + print("conforms?", conf2.check_nominal(log=True)) - # ramify(state, model_id) + ramify(state, model_id) if __name__ == "__main__": main() diff --git a/services/scd.py b/services/scd.py index f21407a..a3a7d69 100644 --- a/services/scd.py +++ b/services/scd.py @@ -90,6 +90,15 @@ class SCD: Returns: Nothing. """ + src, = self.bottom.read_outgoing_elements(self.model, source) + tgt, = self.bottom.read_outgoing_elements(self.model, target) + return self._create_association(name, src, tgt, + src_min_c, src_max_c, + tgt_min_c, tgt_max_c) + + def _create_association(self, name: str, source: UUID, target: UUID, + src_min_c: int = None, src_max_c: int = None, + tgt_min_c: int = None, tgt_max_c: int = None): def set_cardinality(bound: str, value: int): # similar to set_cardinality function defined in create_class @@ -105,10 +114,7 @@ class SCD: self.bottom.create_edge(_c_link, _scd_link, "Morphism") # create association + attributes + morphism links - assoc_edge = self.bottom.create_edge( - *self.bottom.read_outgoing_elements(self.model, source), - *self.bottom.read_outgoing_elements(self.model, target), - ) # create assoc edge + assoc_edge = self.bottom.create_edge(source, target) # create assoc edge self.bottom.create_edge(self.model, assoc_edge, name) # attach to model scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Association") # retrieve type self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link @@ -166,11 +172,13 @@ class SCD: Returns: Nothing. """ + tgt, = self.bottom.read_outgoing_elements(self.model, target) + return self._create_attribute_link(source, tgt, name, optional) + + def _create_attribute_link(self, source: str, target: UUID, name: str, optional: bool): # create attribute link + morphism links - assoc_edge = self.bottom.create_edge( - *self.bottom.read_outgoing_elements(self.model, source), - *self.bottom.read_outgoing_elements(self.model, target), - ) # create v edge + src, = self.bottom.read_outgoing_elements(self.model, source) + assoc_edge = self.bottom.create_edge(src, target) # create v edge self.bottom.create_edge(self.model, assoc_edge, f"{source}_{name}") # attach to model scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink") # retrieve type self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link @@ -215,6 +223,7 @@ class SCD: self.bottom.create_edge(self.model, element_node, name) # attach to model scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef") # retrieve type self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link + return element_node def create_inheritance(self, child: str, parent: str): """ @@ -227,14 +236,25 @@ class SCD: Returns: Nothing. """ + c, = self.bottom.read_outgoing_elements(self.model, child) + p, = self.bottom.read_outgoing_elements(self.model, parent) + return self._create_inheritance(c, p) + + + def _create_inheritance(self, child: UUID, parent: UUID): # create inheritance + morphism links - assoc_edge = self.bottom.create_edge( - *self.bottom.read_outgoing_elements(self.model, child), - *self.bottom.read_outgoing_elements(self.model, parent), - ) # create inheritance edge - self.bottom.create_edge(self.model, assoc_edge, f"{child}_inh_{parent}") # attach to model + inh_edge = self.bottom.create_edge(child, parent) + child_name = self.get_class_name(child) + parent_name = self.get_class_name(parent) + self.bottom.create_edge(self.model, inh_edge, f"{child_name}_inh_{parent_name}") # attach to model scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") # retrieve type - self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link + self.bottom.create_edge(inh_edge, scd_node, "Morphism") # create morphism link + + def get_class_name(self, cls: UUID): + for key in self.bottom.read_keys(self.model): + el, = self.bottom.read_outgoing_elements(self.model, key) + if el == cls: + return key def add_constraint(self, element: str, code: str): """ @@ -278,6 +298,39 @@ class SCD: unsorted.append(f"{key} : {scd_names[element_type_node]}") return sorted(unsorted) + def get_classes(self): + class_node, = self.bottom.read_outgoing_elements(self.scd_model, "Class") + return self.get_typed_by(class_node) + + def get_associations(self): + assoc_node, = self.bottom.read_outgoing_elements(self.scd_model, "Association") + return self.get_typed_by(assoc_node) + + def get_inheritances(self): + inh_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") + return self.get_typed_by(inh_node) + + def get_typed_by(self, type_node: UUID): + name_to_instance = {} + for key in self.bottom.read_keys(self.model): + element, = self.bottom.read_outgoing_elements(self.model, key) + element_types = self.bottom.read_outgoing_elements(element, "Morphism") + if type_node in element_types: + name_to_instance[key] = element + # mapping from instance name to UUID + return name_to_instance + + + def get_attributes(self, class_name: str): + attr_link_node, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink") + class_node, = self.bottom.read_outgoing_elements(self.model, class_name) + name_to_attr = {} + for edge in self.bottom.read_outgoing_edges(class_node): + edge_types = self.bottom.read_outgoing_elements(edge, "Morphism") + if attr_link_node in edge_types: + name_to_attr[key] = edge + return name_to_attr + def delete_element(self, name: str): """ Deletes an element from the model. diff --git a/transformation/ramify.py b/transformation/ramify.py index 2d00a07..8e71c7b 100644 --- a/transformation/ramify.py +++ b/transformation/ramify.py @@ -2,17 +2,152 @@ from state.base import State from uuid import UUID from services.bottom.V0 import Bottom from services.scd import SCD +from framework.conformance import Conformance + def ramify(state: State, model: UUID) -> UUID: - """ - Parameters: - bottom: Bottom-service, wrapping MVS - model: An SCD-conforming meta-model to ramify - """ + + 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) + + scd_metamodel_id = state.read_dict(state.read_root(), "SCD") + 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 = UUID(state.read_value(string_type_id)) + scd = SCD(model, state) - classes = scd.get_classes() - print(classes) + # print_tree(model, 2) - attrs = scd.get_attributes('A') - print(attrs) + # 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_upper_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, typ)) + return result + + ramified = state.create_node() + ramified_scd = SCD(ramified, state) + + string_modelref = ramified_scd.create_model_ref("String", string_type) + + print() + + classes = scd.get_classes() + for class_name, class_node in classes.items(): + # For every class in our original model, create a class: + # - abstract: False + # - min-card: 0 + # - max-card: same as original + upper_card = find_upper_cardinality(class_node, class_upper_card_node) + print('creating class', class_name, "with upper card", upper_card) + ramified_scd.create_class(class_name, abstract=None, max_c=upper_card) + + for (attr_name, attr_type) in get_attributes(class_node): + print(' creating attribute', attr_name, "with type String") + # Every attribute becomes 'string' type + # The string will be a Python expression + ramified_scd._create_attribute_link(class_name, string_modelref, attr_name, optional=False) + + associations = scd.get_associations() + for assoc_name, assoc_node in associations.items(): + # For every association in our original model, create an association: + # - src-min-card: 0 + # - src-max-card: same as original + # - tgt-min-card: 0 + # - tgt-max-card: same as original + src_upper_card = find_upper_cardinality(assoc_node, src_upper_card_node) + tgt_upper_card = find_upper_cardinality(assoc_node, tgt_upper_card_node) + print('creating assoc', assoc_name, "with src upper card", src_upper_card, "and tgt upper card", tgt_upper_card) + ramified_scd._create_association(assoc_name, + bottom.read_edge_source(assoc_node), + bottom.read_edge_target(assoc_node), + src_max_c=src_upper_card, + tgt_max_c=tgt_upper_card) + + for inh_name, inh_node in scd.get_inheritances().items(): + # Re-create inheritance links like in our original model: + print('creating inheritance', inh_name) + ramified_scd._create_inheritance( + bottom.read_edge_source(inh_node), + bottom.read_edge_target(inh_node)) + + # The RAMified meta-model should also conform to 'SCD': + conf = Conformance(state, model, scd_metamodel) + print("conforms?", conf.check_nominal(log=True)) \ No newline at end of file