From e70eae2286ee8a9b578cc3c322026dc57680096e Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Tue, 8 Oct 2024 21:08:06 +0200 Subject: [PATCH] PlantUML: render class cardinalities. Extend constraint checker API. --- bootstrap/primitive.py | 24 +-- concrete_syntax/plantuml/renderer.py | 15 +- concrete_syntax/textual_od/parser.py | 7 +- .../{exp_plantuml.py => exp_metacircular.py} | 11 +- experiments/exp_scd.plantuml | 139 ++++++++++++++++ experiments/{exp_scd.py => exp_woods.py} | 156 +++++++----------- framework/conformance.py | 28 +++- services/od.py | 9 + services/scd.py | 5 + 9 files changed, 252 insertions(+), 142 deletions(-) rename experiments/{exp_plantuml.py => exp_metacircular.py} (66%) create mode 100644 experiments/exp_scd.plantuml rename experiments/{exp_scd.py => exp_woods.py} (64%) diff --git a/bootstrap/primitive.py b/bootstrap/primitive.py index 1934ec1..4c1fff9 100644 --- a/bootstrap/primitive.py +++ b/bootstrap/primitive.py @@ -36,13 +36,9 @@ def bootstrap_type(type_name: str, scd_root: UUID, model_root: UUID, integer_typ return class_node def bootstrap_constraint(class_node, type_name: str, python_type: str, scd_root: UUID, model_root: UUID, actioncode_type: UUID, state: State): - - # set constraint - # chicken-and-egg problem: we cannot create an action-code constraint because the action-code MM doesn't exist yet - bottom = Bottom(state) constraint_model = bottom.create_node() - ActionCode(constraint_model, state).create(f"isinstance(read_value(element),{python_type})") + ActionCode(constraint_model, state).create(f"isinstance(read_value(this),{python_type})") constraint_node = bottom.create_node(str(constraint_model)) bottom.create_edge(model_root, constraint_node, f"{type_name}.constraint") constraint_link = bottom.create_edge(class_node, constraint_node) @@ -52,24 +48,6 @@ def bootstrap_constraint(class_node, type_name: str, python_type: str, scd_root: bottom.create_edge(constraint_node, scd_node, "Morphism") bottom.create_edge(constraint_link, scd_link, "Morphism") - -# def bootstrap_type_type(scd_root: UUID, model_root: UUID, integer_type: UUID, actioncode_type: UUID, state: State): - - -# def bootstrap_boolean_type(scd_root: UUID, model_root: UUID, integer_type: UUID, actioncode_type: UUID, state: State): - - -# def bootstrap_integer_type(scd_root: UUID, model_root: UUID, integer_type: UUID, actioncode_type: UUID, state: State): - - -# def bootstrap_float_type(scd_root: UUID, model_root: UUID, integer_type: UUID, actioncode_type: UUID, state: State): - - -# def bootstrap_string_type(scd_root: UUID, model_root: UUID, integer_type: UUID, actioncode_type: UUID, state: State): - -# def bootstrap_actioncode_type(scd_root: UUID, model_root: UUID, integer_type: UUID, actioncode_type: UUID, state: State): -# # we store action code as Python string: - 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 class_integer = bootstrap_type("Integer", scd_root, integer_type, integer_type, state) diff --git a/concrete_syntax/plantuml/renderer.py b/concrete_syntax/plantuml/renderer.py index 0e18e86..0955a3c 100644 --- a/concrete_syntax/plantuml/renderer.py +++ b/concrete_syntax/plantuml/renderer.py @@ -23,10 +23,19 @@ def render_class_diagram(state, model, prefix_ids=""): if slot != None: is_abstract, _ = od.read_primitive_value(bottom, slot, model_od.type_model) - if is_abstract: - output += f"\nabstract class \"{name}\" as {make_id(class_node)}" + lower_card, upper_card = model_scd.get_class_cardinalities(class_node) + + if lower_card == None and upper_card == None: + card_spec = "" else: - output += f"\nclass \"{name}\" as {make_id(class_node)}" + card_spec = f"{0 if lower_card == None else lower_card}..{"*" if upper_card == None else upper_card}" + + if is_abstract: + output += f"\nabstract class \"{name} {card_spec}\" as {make_id(class_node)}" + else: + output += f"\nclass \"{name} {card_spec}\" as {make_id(class_node)}" + + # Render attributes output += " {" diff --git a/concrete_syntax/textual_od/parser.py b/concrete_syntax/textual_od/parser.py index d7a8dee..d4f8652 100644 --- a/concrete_syntax/textual_od/parser.py +++ b/concrete_syntax/textual_od/parser.py @@ -29,8 +29,11 @@ BOOL: "True" | "False" CODE: /`[^`]*`/ INDENTED_CODE: /```[^`]*```/ -object: [IDENTIFIER] ":" IDENTIFIER [link_spec] ["{" slot* "}"] +# name (optional) type +object: [IDENTIFIER] ":" IDENTIFIER [link_spec] ["{" slot* "}"] + link_spec: "(" IDENTIFIER "->" IDENTIFIER ")" + slot: IDENTIFIER "=" literal ";" """ @@ -78,7 +81,7 @@ def parse_od(state, cs_text, mm): space_count += 1 lines = token.split('\n')[1:-1] for line in lines: - if line[0:space_count] != ' '*space_count: + if len(line) >= space_count and line[0:space_count] != ' '*space_count: raise Exception("wrong indentation of INDENTED_CODE") unindented_lines = [l[space_count:] for l in lines] return _Code('\n'.join(unindented_lines)) diff --git a/experiments/exp_plantuml.py b/experiments/exp_metacircular.py similarity index 66% rename from experiments/exp_plantuml.py rename to experiments/exp_metacircular.py index 2f14472..c591ee0 100644 --- a/experiments/exp_plantuml.py +++ b/experiments/exp_metacircular.py @@ -1,16 +1,7 @@ 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 -from transformation.ramify import ramify -from transformation import rewriter -from services.bottom.V0 import Bottom -from services.primitives.integer_type import Integer -from pattern_matching import mvs_adapter -from pattern_matching.matcher import MatcherVF2 -from renderer import plantuml +from concrete_syntax.plantuml import renderer as plantuml def main(): state = DevState() diff --git a/experiments/exp_scd.plantuml b/experiments/exp_scd.plantuml new file mode 100644 index 0000000..0d52b6b --- /dev/null +++ b/experiments/exp_scd.plantuml @@ -0,0 +1,139 @@ +package "DSL Meta-Model" { +class "Bear" as 00000000_0000_0000_0000_00000000046d { +} +abstract class "Animal" as 00000000_0000_0000_0000_000000000474 { +} +class "Man" as 00000000_0000_0000_0000_000000000491 { + weight : Integer +} + +00000000_0000_0000_0000_000000000474 <|-- 00000000_0000_0000_0000_000000000491 +00000000_0000_0000_0000_000000000474 <|-- 00000000_0000_0000_0000_00000000046d + +00000000_0000_0000_0000_000000000491 " " --> "1 .. *" 00000000_0000_0000_0000_000000000474 : afraidOf +} +package "Int Meta-Model" { +class "Integer" as 00000000_0000_0000_0000_000000000094 { +} + + +} +package "RAMified DSL Meta-Model" { +class "RAM_Bear" as 00000000_0000_0000_0000_0000000005bb { +} +class "RAM_Animal" as 00000000_0000_0000_0000_0000000005c5 { +} +class "RAM_Man" as 00000000_0000_0000_0000_0000000005cf { + RAM_weight : ActionCode +} + +00000000_0000_0000_0000_0000000005c5 <|-- 00000000_0000_0000_0000_0000000005cf +00000000_0000_0000_0000_0000000005c5 <|-- 00000000_0000_0000_0000_0000000005bb + +00000000_0000_0000_0000_0000000005cf " " --> "0 .. *" 00000000_0000_0000_0000_0000000005c5 : RAM_afraidOf +} +package "RAMified Int Meta-Model" { +class "RAM_Integer" as 00000000_0000_0000_0000_00000000064c { +} + + +} +00000000_0000_0000_0000_0000000005bb ..> 00000000_0000_0000_0000_00000000046d #line:green;text:green : RAMifies +00000000_0000_0000_0000_0000000005c5 ..> 00000000_0000_0000_0000_000000000474 #line:green;text:green : RAMifies +00000000_0000_0000_0000_0000000005cf ..> 00000000_0000_0000_0000_000000000491 #line:green;text:green : RAMifies +00000000_0000_0000_0000_0000000005cf::RAM_weight ..> 00000000_0000_0000_0000_000000000491::weight #line:green;text:green : RAMifies +00000000_0000_0000_0000_00000000064c ..> 00000000_0000_0000_0000_000000000094 #line:green;text:green : RAMifies +package "LHS" { +map "scaryAnimal : RAM_Animal" as 00000000_0000_0000_0000_00000000068a { +} +map "man : RAM_Man" as 00000000_0000_0000_0000_00000000066d { +RAM_weight => `v > 60` +} + +00000000_0000_0000_0000_00000000066d -> 00000000_0000_0000_0000_00000000068a : :RAM_afraidOf +} +00000000_0000_0000_0000_00000000068a ..> 00000000_0000_0000_0000_0000000005c5 #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_00000000066d ..> 00000000_0000_0000_0000_0000000005cf #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_00000000066d::RAM_weight ..> 00000000_0000_0000_0000_0000000005cf::RAM_weight #line:blue;text:blue : instanceOf + +package "RHS" { +map "man : RAM_Man" as 00000000_0000_0000_0000_000000000699 { +RAM_weight => `v + 5` +} +map "bill : RAM_Man" as 00000000_0000_0000_0000_0000000006b6 { +RAM_weight => `100` +} + +00000000_0000_0000_0000_0000000006b6 -> 00000000_0000_0000_0000_000000000699 : :RAM_afraidOf +} +00000000_0000_0000_0000_000000000699 ..> 00000000_0000_0000_0000_0000000005cf #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000699::RAM_weight ..> 00000000_0000_0000_0000_0000000005cf::RAM_weight #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_0000000006b6 ..> 00000000_0000_0000_0000_0000000005cf #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_0000000006b6::RAM_weight ..> 00000000_0000_0000_0000_0000000005cf::RAM_weight #line:blue;text:blue : instanceOf + +package "Model (before rewrite)" { +map "bear2 : Bear" as 00000000_0000_0000_0000_000000000597 { +} +map "bear1 : Bear" as 00000000_0000_0000_0000_000000000590 { +} +map "george : Man" as 00000000_0000_0000_0000_000000000573 { +weight => 80 +} + +00000000_0000_0000_0000_000000000573 -> 00000000_0000_0000_0000_000000000590 : :afraidOf +00000000_0000_0000_0000_000000000573 -> 00000000_0000_0000_0000_000000000597 : :afraidOf +} +00000000_0000_0000_0000_000000000597 ..> 00000000_0000_0000_0000_00000000046d #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000590 ..> 00000000_0000_0000_0000_00000000046d #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000573 ..> 00000000_0000_0000_0000_000000000491 #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000573::weight ..> 00000000_0000_0000_0000_000000000491::weight #line:blue;text:blue : instanceOf + +00000000_0000_0000_0000_00000000068a ..> 00000000_0000_0000_0000_000000000590 #line:red;line.dotted;text:red : matchedWith +00000000_0000_0000_0000_00000000066d ..> 00000000_0000_0000_0000_000000000573 #line:red;line.dotted;text:red : matchedWith +00000000_0000_0000_0000_00000000066d::RAM_weight ..> 00000000_0000_0000_0000_000000000573::weight #line:red;line.dotted;text:red : matchedWith +package "Model (after rewrite 0)" { +map "bear2 : Bear" as 00000000_0000_0000_0000_0000000006db { +} +map "george : Man" as 00000000_0000_0000_0000_0000000006e9 { +weight => 85 +} +map "bill0 : Man" as 00000000_0000_0000_0000_000000000723 { +weight => 100 +} + +00000000_0000_0000_0000_000000000723 -> 00000000_0000_0000_0000_0000000006e9 : :afraidOf +00000000_0000_0000_0000_0000000006e9 -> 00000000_0000_0000_0000_0000000006db : :afraidOf +} +00000000_0000_0000_0000_000000000699 ..> 00000000_0000_0000_0000_0000000006e9 #line:red;line.dotted;text:red : matchedWith +00000000_0000_0000_0000_000000000699::RAM_weight ..> 00000000_0000_0000_0000_0000000006e9::weight #line:red;line.dotted;text:red : matchedWith +00000000_0000_0000_0000_0000000006b6 ..> 00000000_0000_0000_0000_000000000723 #line:red;line.dotted;text:red : matchedWith +00000000_0000_0000_0000_0000000006db ..> 00000000_0000_0000_0000_00000000046d #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_0000000006e9 ..> 00000000_0000_0000_0000_000000000491 #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_0000000006e9::weight ..> 00000000_0000_0000_0000_000000000491::weight #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000723 ..> 00000000_0000_0000_0000_000000000491 #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000723::weight ..> 00000000_0000_0000_0000_000000000491::weight #line:blue;text:blue : instanceOf + +00000000_0000_0000_0000_00000000068a ..> 00000000_0000_0000_0000_000000000597 #line:orange;line.dotted;text:orange : matchedWith +00000000_0000_0000_0000_00000000066d ..> 00000000_0000_0000_0000_000000000573 #line:orange;line.dotted;text:orange : matchedWith +00000000_0000_0000_0000_00000000066d::RAM_weight ..> 00000000_0000_0000_0000_000000000573::weight #line:orange;line.dotted;text:orange : matchedWith +package "Model (after rewrite 1)" { +map "bear1 : Bear" as 00000000_0000_0000_0000_000000000747 { +} +map "george : Man" as 00000000_0000_0000_0000_00000000074e { +weight => 85 +} +map "bill0 : Man" as 00000000_0000_0000_0000_000000000788 { +weight => 100 +} + +00000000_0000_0000_0000_000000000788 -> 00000000_0000_0000_0000_00000000074e : :afraidOf +00000000_0000_0000_0000_00000000074e -> 00000000_0000_0000_0000_000000000747 : :afraidOf +} +00000000_0000_0000_0000_000000000699 ..> 00000000_0000_0000_0000_00000000074e #line:orange;line.dotted;text:orange : matchedWith +00000000_0000_0000_0000_000000000699::RAM_weight ..> 00000000_0000_0000_0000_00000000074e::weight #line:orange;line.dotted;text:orange : matchedWith +00000000_0000_0000_0000_0000000006b6 ..> 00000000_0000_0000_0000_000000000788 #line:orange;line.dotted;text:orange : matchedWith +00000000_0000_0000_0000_000000000747 ..> 00000000_0000_0000_0000_00000000046d #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_00000000074e ..> 00000000_0000_0000_0000_000000000491 #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_00000000074e::weight ..> 00000000_0000_0000_0000_000000000491::weight #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000788 ..> 00000000_0000_0000_0000_000000000491 #line:blue;text:blue : instanceOf +00000000_0000_0000_0000_000000000788::weight ..> 00000000_0000_0000_0000_000000000491::weight #line:blue;text:blue : instanceOf diff --git a/experiments/exp_scd.py b/experiments/exp_woods.py similarity index 64% rename from experiments/exp_scd.py rename to experiments/exp_woods.py index fab83cf..a59a108 100644 --- a/experiments/exp_scd.py +++ b/experiments/exp_woods.py @@ -15,8 +15,6 @@ from services.primitives.integer_type import Integer from concrete_syntax.plantuml import renderer as plantuml from concrete_syntax.textual_od import parser, renderer -import sys - def create_integer_node(state, i: int): node = state.create_node() integer_t = Integer(node, state) @@ -27,119 +25,79 @@ def main(): state = DevState() root = state.read_root() # id: 0 - scd_mm_id = bootstrap_scd(state) + # Meta-meta-model: a class diagram that describes the language of class diagrams + scd_mmm_id = bootstrap_scd(state) int_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "Integer"))) string_mm_id = UUID(state.read_value(state.read_dict(state.read_root(), "String"))) - # conf = Conformance(state, scd_mm_id, scd_mm_id) + # conf = Conformance(state, scd_mmm_id, scd_mmm_id) # print("Conformance SCD_MM -> SCD_MM?", conf.check_nominal(log=True)) # print("--------------------------------------") - # print(renderer.render_od(state, scd_mm_id, scd_mm_id, hide_names=True)) + # print(renderer.render_od(state, scd_mmm_id, scd_mmm_id, hide_names=True)) # print("--------------------------------------") - def create_dsl_mm_api(): - # Create DSL MM with SCD API - dsl_mm_id = state.create_node() - dsl_mm_scd = SCD(dsl_mm_id, state) - dsl_mm_scd.create_class("Animal", abstract=True) - dsl_mm_scd.create_class("Man", min_c=1, max_c=2) - dsl_mm_scd.create_inheritance("Man", "Animal") - dsl_mm_scd.create_model_ref("Integer", int_mm_id) - dsl_mm_scd.create_attribute_link("Man", "Integer", "weight", optional=False) - dsl_mm_scd.create_class("Bear") - dsl_mm_scd.create_inheritance("Bear", "Animal") - dsl_mm_scd.create_association("afraidOf", "Man", "Animal", - # Every Man afraid of at least one Animal: - src_min_c=0, - src_max_c=None, - tgt_min_c=1, - tgt_max_c=None, - ) - dsl_mm_scd.add_constraint("Man", "read_value(element) < 100") - return dsl_mm_id + # Create DSL MM with parser + dsl_mm_cs = """ + # Integer:ModelRef + Bear:Class + Animal:Class { + abstract = True; + } + Man:Class { + lower_cardinality = 1; + upper_cardinality = 2; + constraint = ``` + get_value(get_slot(this, "weight")) > 20 + ```; + } + Man_weight:AttributeLink (Man -> Integer) { + name = "weight"; + optional = False; + constraint = ``` + # this is the same constraint as above, but this time, part of the attributelink itself (and thus shorter) + tgt = get_target(this) + tgt_type = get_type_name(tgt) + get_value(tgt) > 20 + ```; + } + afraidOf:Association (Man -> Animal) { + target_lower_cardinality = 1; + } + :Inheritance (Man -> Animal) + :Inheritance (Bear -> Animal) - def create_dsl_mm_parser(): - # Create DSL MM with parser - dsl_mm_cs = """ - # Integer:ModelRef - Bear:Class - Animal:Class { - abstract = True; - } - Man:Class { - lower_cardinality = 1; - upper_cardinality = 2; - constraint = `get_value(get_slot(element, "weight")) > 20`; - } - Man_weight:AttributeLink (Man -> Integer) { - name = "weight"; - optional = False; - constraint = ``` - # this is the same constraint as above, but this time, part of the attributelink itself (and thus shorter) - node = get_target(element) - get_value(node) > 20 - ```; - } - afraidOf:Association (Man -> Animal) { - target_lower_cardinality = 1; - } - :Inheritance (Man -> Animal) - :Inheritance (Bear -> Animal) - - not_too_fat:GlobalConstraint { - constraint = ``` - # total weight of all men low enough - total_weight = 0 - for man_name, man_id in get_all_instances("Man"): - total_weight += get_value(get_slot(man_id, "weight")) - total_weight < 85 - ```; - } - """ - dsl_mm_id = parser.parse_od(state, dsl_mm_cs, mm=scd_mm_id) - return dsl_mm_id + not_too_fat:GlobalConstraint { + constraint = ``` + # total weight of all men low enough + total_weight = 0 + for man_name, man_id in get_all_instances("Man"): + total_weight += get_value(get_slot(man_id, "weight")) + total_weight < 85 + ```; + } + """ + dsl_mm_id = parser.parse_od(state, dsl_mm_cs, mm=scd_mmm_id) - def create_dsl_m_api(): - # Create DSL M with OD API - dsl_m_id = state.create_node() - dsl_m_od = OD(dsl_mm_id, dsl_m_id, state) - dsl_m_od.create_object("george", "Man") - dsl_m_od.create_slot("weight", "george", - dsl_m_od.create_integer_value("george.weight", 80)) - dsl_m_od.create_object("bear1", "Bear") - dsl_m_od.create_object("bear2", "Bear") - dsl_m_od.create_link("georgeAfraidOfBear1", "afraidOf", "george", "bear1") - dsl_m_od.create_link("georgeAfraidOfBear2", "afraidOf", "george", "bear2") - return dsl_m_id - - def create_dsl_m_parser(): - # Create DSL M with parser - dsl_m_cs = """ - george:Man { - weight = 80; - } - bear1:Bear - bear2:Bear - :afraidOf (george -> bear1) - :afraidOf (george -> bear2) - """ - dsl_m_id = parser.parse_od(state, dsl_m_cs, mm=dsl_mm_id) - return dsl_m_id - - - # dsl_mm_id = create_dsl_mm_api() - dsl_mm_id = create_dsl_mm_parser() + # Create DSL M with parser + dsl_m_cs = """ + george:Man { + weight = 80; + } + bear1:Bear + bear2:Bear + :afraidOf (george -> bear1) + :afraidOf (george -> bear2) + """ + dsl_m_id = parser.parse_od(state, dsl_m_cs, mm=dsl_mm_id) # print("DSL MM:") # print("--------------------------------------") - # print(renderer.render_od(state, dsl_mm_id, scd_mm_id, hide_names=True)) + # print(renderer.render_od(state, dsl_mm_id, scd_mmm_id, hide_names=True)) # print("--------------------------------------") - conf = Conformance(state, dsl_mm_id, scd_mm_id) + conf = Conformance(state, dsl_mm_id, scd_mmm_id) print("Conformance DSL_MM -> SCD_MM?", conf.check_nominal(log=True)) - # dsl_m_id = create_dsl_m_api() - dsl_m_id = create_dsl_m_parser() # print("DSL M:") # print("--------------------------------------") # print(renderer.render_od(state, dsl_m_id, dsl_mm_id, hide_names=True)) diff --git a/framework/conformance.py b/framework/conformance.py index 3d7d27f..8e84671 100644 --- a/framework/conformance.py +++ b/framework/conformance.py @@ -375,21 +375,33 @@ class Conformance: 'read_value': self.state.read_value, 'get_value': lambda el: od.read_primitive_value(self.bottom, el, self.type_model)[0], 'get_target': lambda el: self.bottom.read_edge_target(el), + 'get_source': lambda el: self.bottom.read_edge_source(el), 'get_slot': od.OD(self.type_model, self.model, self.state).get_slot, - 'get_all_instances': self.get_all_instances + 'get_all_instances': self.get_all_instances, + 'get_name': lambda el: [name for name in self.bottom.read_keys(self.model) if self.bottom.read_outgoing_elements(self.model, name)[0] == el][0], + 'get_type_name': self.get_type_name, + 'get_outgoing': self.get_outgoing, + 'get_incoming': self.get_incoming, } # print("evaluating constraint ...", code) - loc = {**kwargs, **funcs} + loc = {**kwargs, } result = exec_then_eval( code, {'__builtins__': {'isinstance': isinstance, 'print': print, - 'int': int, 'float': float, 'bool': bool, 'str': str, 'tuple': tuple, 'len': len} + 'int': int, 'float': float, 'bool': bool, 'str': str, 'tuple': tuple, 'len': len, 'set': set, 'dict': dict}, + **funcs }, # globals loc # locals ) # print('result =', result) return result + def get_type_name(self, element: UUID): + type_node = self.bottom.read_outgoing_elements(element, "Morphism")[0] + for type_name in self.bottom.read_keys(self.type_model): + if self.bottom.read_outgoing_elements(self.type_model, type_name)[0] == type_node: + return type_name + def get_all_instances(self, type_name: str, include_subtypes=True): result = [e_name for e_name, t_name in self.type_mapping.items() if t_name == type_name] if include_subtypes: @@ -399,6 +411,12 @@ class Conformance: result_with_ids = [ (e_name, self.bottom.read_outgoing_elements(self.model, e_name)[0]) for e_name in result] return result_with_ids + def get_outgoing(self, element: UUID, assoc_or_attr_name: str): + return od.find_outgoing_typed_by(self.bottom, src=element, type_node=self.bottom.read_outgoing_elements(self.type_model, assoc_or_attr_name)[0]) + + def get_incoming(self, element: UUID, assoc_or_attr_name: str): + return od.find_incoming_typed_by(self.bottom, tgt=element, type_node=self.bottom.read_outgoing_elements(self.type_model, assoc_or_attr_name)[0]) + def check_constraints(self): """ Check whether all constraints defined for a model are respected @@ -426,7 +444,7 @@ class Conformance: morphisms = self.bottom.read_incoming_elements(tm_element, "Morphism") morphisms = [m for m in morphisms if m in self.model_names] for m_element in morphisms: - result = self.evaluate_constraint(code, element=m_element, type_name=tm_name) + result = self.evaluate_constraint(code, this=m_element) description = f"Local constraint of \"{tm_name}\" in \"{m_name}\"" check_result(result, description) @@ -555,7 +573,7 @@ class Conformance: # eval constraints code = self.read_attribute(attr_tm, "constraint") if code != None: - attr_conforms = self.evaluate_constraint(code, element=attr) + attr_conforms = self.evaluate_constraint(code, this=attr) if attr_conforms: matched += 1 print(" attr_conforms -> matched:", matched) diff --git a/services/od.py b/services/od.py index f312a93..b8c8e43 100644 --- a/services/od.py +++ b/services/od.py @@ -317,6 +317,15 @@ def find_outgoing_typed_by(bottom, src: UUID, type_node: UUID): break return edges +def find_incoming_typed_by(bottom, tgt: UUID, type_node: UUID): + edges = [] + for incoming_edge in bottom.read_incoming_edges(tgt): + for typedBy in bottom.read_outgoing_elements(incoming_edge, "Morphism"): + if typedBy == type_node: + edges.append(incoming_edge) + break + return edges + def navigate_modelref(bottom, node: UUID): uuid = bottom.read_value(node) return UUID(uuid) diff --git a/services/scd.py b/services/scd.py index e2c1daa..8259bd4 100644 --- a/services/scd.py +++ b/services/scd.py @@ -355,6 +355,11 @@ class SCD: name_to_attr[name] = edge return name_to_attr + def get_class_cardinalities(self, class_node): + lower_card = od.find_cardinality(self.bottom, class_node, od.get_scd_mm_class_lowercard_node(self.bottom)) + upper_card = od.find_cardinality(self.bottom, class_node, od.get_scd_mm_class_uppercard_node(self.bottom)) + return lower_card, upper_card + 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))