diff --git a/capellambse_context_diagrams/__init__.py b/capellambse_context_diagrams/__init__.py
index 3d496816..130af425 100644
--- a/capellambse_context_diagrams/__init__.py
+++ b/capellambse_context_diagrams/__init__.py
@@ -47,7 +47,7 @@ def init() -> None:
"""Initialize the extension."""
register_classes()
register_interface_context()
- register_class_tree()
+ register_tree_view()
# register_functional_context() XXX: Future
@@ -151,10 +151,10 @@ def register_functional_context() -> None:
)
-def register_class_tree() -> None:
- """Add the `tree_diagram` attribute to ``Class``es."""
+def register_tree_view() -> None:
+ """Add the `tree_view` attribute to ``Class``es."""
common.set_accessor(
information.Class,
- "tree_diagram",
+ "tree_view",
context.ClassTreeAccessor(DiagramType.CDB.value),
)
diff --git a/capellambse_context_diagrams/collectors/class_tree.py b/capellambse_context_diagrams/collectors/tree_view.py
similarity index 81%
rename from capellambse_context_diagrams/collectors/class_tree.py
rename to capellambse_context_diagrams/collectors/tree_view.py
index e19c1010..29470f05 100644
--- a/capellambse_context_diagrams/collectors/class_tree.py
+++ b/capellambse_context_diagrams/collectors/tree_view.py
@@ -41,28 +41,30 @@ def __init__(
self.all_associations = all_associations
def process_class(self, cls, params):
- self._process_box(cls.target, cls.partition, params)
self._process_box(cls.source, cls.partition, params)
- edges = [
- assoc
- for assoc in self.all_associations
- if cls.prop in assoc.navigable_members
- ]
- assert len(edges) == 1
- if (edge_id := edges[0].uuid) not in self.made_edges:
- self.made_edges.add(edge_id)
- text = cls.prop.name
- start, end = cls.multiplicity
- if start != "1" or end != "1":
- text = f"[{start}..{end}] {text}"
- self.data["edges"].append(
- {
- "id": edge_id,
- "sources": [cls.source.uuid],
- "targets": [cls.target.uuid],
- "labels": [makers.make_label(text)],
- }
- )
+
+ if not cls.primitive:
+ self._process_box(cls.target, cls.partition, params)
+ edges = [
+ assoc
+ for assoc in self.all_associations
+ if cls.prop in assoc.navigable_members
+ ]
+ assert len(edges) == 1
+ if (edge_id := edges[0].uuid) not in self.made_edges:
+ self.made_edges.add(edge_id)
+ text = cls.prop.name
+ start, end = cls.multiplicity
+ if start != "1" or end != "1":
+ text = f"[{start}..{end}] {text}"
+ self.data["edges"].append(
+ {
+ "id": edge_id,
+ "sources": [cls.source.uuid],
+ "targets": [cls.target.uuid],
+ "labels": [makers.make_label(text)],
+ }
+ )
if cls.generalizes:
self._process_box(cls.generalizes, cls.partition, params)
@@ -153,6 +155,7 @@ class ClassInfo:
partition: int
multiplicity: tuple[str, str]
generalizes: information.Class | None = None
+ primitive: bool = False
def get_all_classes(
@@ -170,6 +173,9 @@ def get_all_classes(
)
continue
+ if prop.type.is_primitive:
+ continue
+
edge_id = f"{root.uuid} {prop.uuid} {prop.type.uuid}"
if edge_id not in classes:
classes[edge_id] = _make_class_info(root, prop, partition)
@@ -185,6 +191,9 @@ def get_all_classes(
)
continue
+ if prop.type.is_primitive:
+ continue
+
edge_id = f"{root.uuid} {prop.uuid} {prop.type.uuid}"
if edge_id not in classes:
classes[edge_id] = _make_class_info(
@@ -212,6 +221,7 @@ def _make_class_info(
partition=partition,
multiplicity=(start, end),
generalizes=generalizes,
+ primitive=source.is_primitive,
)
@@ -222,7 +232,11 @@ def _get_all_non_edge_properties(
properties: list[_elkjs.ELKInputLabel] = []
legends: list[_elkjs.ELKInputChild] = []
for prop in obj.properties:
- if isinstance(prop.type, (information.Class, type(None))):
+ if prop.type is None:
+ continue
+
+ is_class = isinstance(prop.type, information.Class)
+ if is_class and not prop.type.is_primitive:
continue
text = f"{prop.name}: {prop.type.name}" # type: ignore[unreachable]
@@ -233,8 +247,10 @@ def _get_all_non_edge_properties(
continue
data_types.add(prop.type.uuid)
- if not isinstance(prop.type, information.datatype.Enumeration):
+ is_enum = isinstance(prop.type, information.datatype.Enumeration)
+ if not is_enum and not (is_class and prop.type.is_primitive):
continue
+
legend = makers.make_box(prop.type, label_getter=_get_legend_labels)
legend["layoutOptions"] = {}
legend["layoutOptions"]["nodeSize.constraints"] = "NODE_LABELS"
@@ -243,15 +259,19 @@ def _get_all_non_edge_properties(
def _get_legend_labels(
- obj: information.datatype.Enumeration,
+ obj: information.datatype.Enumeration | information.Class,
) -> cabc.Iterator[makers._LabelBuilder]:
yield {"text": obj.name, "icon": (0, 0), "layout_options": {}}
- if not isinstance(obj, information.datatype.Enumeration):
+ if isinstance(obj, information.datatype.Enumeration):
+ labels = [literal.name for literal in obj.literals]
+ elif isinstance(obj, information.Class):
+ labels = [prop.name for prop in obj.owned_properties]
+ else:
return
layout_options = DATA_TYPE_LABEL_LAYOUT_OPTIONS
- for lit in obj.literals:
+ for label in labels:
yield {
- "text": lit.name,
+ "text": label,
"icon": (0, 0),
"layout_options": layout_options,
}
diff --git a/capellambse_context_diagrams/context.py b/capellambse_context_diagrams/context.py
index e12ffbea..7355151a 100644
--- a/capellambse_context_diagrams/context.py
+++ b/capellambse_context_diagrams/context.py
@@ -16,7 +16,7 @@
from capellambse.model import common, diagram, modeltypes
from . import _elkjs, filters, serializers, styling
-from .collectors import class_tree, exchanges, get_elkdata
+from .collectors import exchanges, get_elkdata, tree_view
logger = logging.getLogger(__name__)
@@ -131,7 +131,7 @@ def __get__( # type: ignore
class ClassTreeAccessor(ContextAccessor):
- """Provides access to the class tree diagrams."""
+ """Provides access to the tree view diagrams."""
# pylint: disable=super-init-not-called
def __init__(self, diagclass: str) -> None:
@@ -340,12 +340,12 @@ def __init__(self, class_: str, obj: common.GenericElement, **kw) -> None:
@property
def uuid(self) -> str: # type: ignore
"""Returns the UUID of the diagram."""
- return f"{self.target.uuid}_class-tree"
+ return f"{self.target.uuid}_tree_view"
@property
def name(self) -> str: # type: ignore
"""Returns the name of the diagram."""
- return f"Class Tree of {self.target.name}"
+ return f"Tree view of {self.target.name}"
def _create_diagram(self, params: dict[str, t.Any]) -> cdiagram.Diagram:
params.setdefault("algorithm", params.get("algorithm", "layered"))
@@ -362,7 +362,7 @@ def _create_diagram(self, params: dict[str, t.Any]) -> cdiagram.Diagram:
"layered.edgeLabels.sideSelection",
params.pop("edgeLabelsSide", "SMART_DOWN"),
)
- data, legend = class_tree.collector(self, params)
+ data, legend = tree_view.collector(self, params)
params["elkdata"] = data
class_diagram = super()._create_diagram(params)
width, height = class_diagram.viewport.size
diff --git a/capellambse_context_diagrams/serializers.py b/capellambse_context_diagrams/serializers.py
index 538cd944..d9f14e34 100644
--- a/capellambse_context_diagrams/serializers.py
+++ b/capellambse_context_diagrams/serializers.py
@@ -67,7 +67,8 @@ def make_diagram(self, data: _elkjs.ELKOutputData) -> diagram.Diagram:
from the input data.
"""
self.diagram = diagram.Diagram(
- self._diagram.name, styleclass=self._diagram.styleclass
+ self._diagram.name.replace("/", "\\"),
+ styleclass=self._diagram.styleclass,
)
for child in data["children"]:
self.deserialize_child(child, diagram.Vector2D(), None)
diff --git a/docs/gen_images.py b/docs/gen_images.py
index f9004f57..6a54b009 100644
--- a/docs/gen_images.py
+++ b/docs/gen_images.py
@@ -94,7 +94,7 @@ def generate_hierarchy_image() -> None:
def generate_class_tree_images() -> None:
obj = model.by_uuid(class_tree_uuid)
- diag = obj.tree_diagram
+ diag = obj.tree_view
with mkdocs_gen_files.open(f"{str(dest / diag.name)}.svg", "w") as fd:
print(diag.render("svg"), file=fd)
with mkdocs_gen_files.open(
diff --git a/docs/class-tree.md b/docs/tree_view.md
similarity index 77%
rename from docs/class-tree.md
rename to docs/tree_view.md
index 40709290..f3147ed7 100644
--- a/docs/class-tree.md
+++ b/docs/tree_view.md
@@ -3,25 +3,25 @@
~ SPDX-License-Identifier: Apache-2.0
-->
-# Class Tree Diagram
+# Tree View Diagram
With release [`v0.5.35`](https://github.com/DSD-DBS/py-capellambse/releases/tag/v0.5.35) of [py-capellambse](https://github.com/DSD-DBS/py-capellambse) you can access the
-`.tree_diagram` on [`Class`][capellambse.model.crosslayer.information.Class]
-objects. A class tree diagram shows a tree made from all properties of the
+`.tree_view` on [`Class`][capellambse.model.crosslayer.information.Class]
+objects. A tree view diagram shows a tree made from all properties of the
parent class.
-??? example "Class Tree of Root"
+??? example "Tree view of Root"
``` py
import capellambse
model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
- diag = model.by_uuid("b7c7f442-377f-492c-90bf-331e66988bda").tree_diagram
+ diag = model.by_uuid("b7c7f442-377f-492c-90bf-331e66988bda").tree_view
diag.render("svgdiagram").save_drawing(pretty=True)
```
Additional rendering parameters enable the control over the layout computed by
@@ -55,13 +55,13 @@ classes is its own partition.
Here is an example that shows how convenient these parameters can be passed
before rendering:
-??? example "Class Tree of Root"
+??? example "Tree View of Root"
``` py
import capellambse
model = capellambse.MelodyModel("tests/data/ContextDiagram.aird")
- diag = model.by_uuid("b7c7f442-377f-492c-90bf-331e66988bda").tree_diagram
+ diag = model.by_uuid("b7c7f442-377f-492c-90bf-331e66988bda").tree_view
diag.render(
"svgdiagram",
edgeRouting="ORTHOGONAL",
@@ -71,8 +71,8 @@ before rendering:
).save_drawing(pretty=True)
```
They are optional and don't need to be set all together.
@@ -80,4 +80,4 @@ They are optional and don't need to be set all together.
## Check out the code
To understand the collection have a look into the
-[`class_tree`][capellambse_context_diagrams.collectors.class_tree] module.
+[`class_tree`][capellambse_context_diagrams.collectors.tree_view] module.
diff --git a/mkdocs.yml b/mkdocs.yml
index de5e508a..a0463c35 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -89,8 +89,8 @@ nav:
- Extras:
- Filters: extras/filters.md
- Styling: extras/styling.md
- - Class Tree:
- - Overview: class-tree.md
+ - Tree View:
+ - Overview: tree_view.md
- Code Reference: reference/
extra_css:
diff --git a/tests/test_class_tree_diagrams.py b/tests/test_tree_views.py
similarity index 86%
rename from tests/test_class_tree_diagrams.py
rename to tests/test_tree_views.py
index 66e20866..04526076 100644
--- a/tests/test_class_tree_diagrams.py
+++ b/tests/test_tree_views.py
@@ -8,12 +8,12 @@
@pytest.mark.parametrize("fmt", ["svgdiagram", "svg", None])
-def test_tree_diagram_gets_rendered_successfully(
+def test_tree_view_gets_rendered_successfully(
model: capellambse.MelodyModel, fmt: str
) -> None:
obj = model.by_uuid(CLASS_UUID)
- diag = obj.tree_diagram
+ diag = obj.tree_view
assert diag.render(fmt)
@@ -24,7 +24,7 @@ def test_tree_diagram_gets_rendered_successfully(
@pytest.mark.parametrize(
"edgeLabelsSide", ["ALWAYS_DOWN", "DIRECTION_DOWN", "SMART_DOWN"]
)
-def test_tree_diagram_renders_with_additional_params(
+def test_tree_view_renders_with_additional_params(
model: capellambse.MelodyModel,
edgeRouting: str,
direction: str,
@@ -33,7 +33,7 @@ def test_tree_diagram_renders_with_additional_params(
) -> None:
obj = model.by_uuid(CLASS_UUID)
- diag = obj.tree_diagram
+ diag = obj.tree_view
assert diag.render(
"svgdiagram",