diff --git a/gaphor/core/modeling/diagram.py b/gaphor/core/modeling/diagram.py index b0d005192..3b829be83 100644 --- a/gaphor/core/modeling/diagram.py +++ b/gaphor/core/modeling/diagram.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Iterator, Optional, Sequence, Union import gaphas -from gaphor.core.modeling.coremodel import PackageableElement +from gaphor.core.modeling.coremodel import Element, PackageableElement from gaphor.core.modeling.element import Id, RepositoryProtocol from gaphor.core.modeling.event import DiagramItemCreated from gaphor.core.modeling.presentation import Presentation @@ -79,6 +79,37 @@ def removesuffix(self: str, suffix: str, /) -> str: return self[:] +def rgetattr(obj, attrs, default=None): + a, *tail = attrs + v = getattr(obj, a, None) + print("") + if isinstance(v, (list, tuple)): + if tail: + for m in v: + yield from rgetattr(m, tail) + else: + yield from v + else: + if tail: + yield from rgetattr(v, tail) + elif v is not None: + yield v + + +def attrstr(obj): + """Returns lower-case string representation of an attribute.""" + if isinstance(obj, str): + return obj.lower() + elif isinstance(obj, (bool, int)): + return "true" if obj else "" + elif isinstance(obj, Element): + return obj.__class__.__name__.lower() + log.warn( + f'Can not make a string out of {obj}, returning "". Please raise an issue.' + ) + return "" + + class StyledDiagram: def __init__(self, diagram: Diagram, view: Optional[gaphas.View] = None): self.diagram = diagram @@ -95,7 +126,8 @@ class StyledDiagram: return (StyledItem(item, view) for item in self.diagram.canvas.get_root_items()) def attribute(self, name: str) -> str: - return "" + fields = name.split(".") + return " ".join(map(attrstr, rgetattr(self.diagram, fields))).strip() def state(self): return () @@ -132,7 +164,11 @@ class StyledItem: return (StyledItem(child, view) for child in children) def attribute(self, name: str) -> str: - return "" + fields = name.split(".") + a = " ".join(map(attrstr, rgetattr(self.item, fields))).strip() + if (not a) and self.item.subject: + a = " ".join(map(attrstr, rgetattr(self.item.subject, fields))).strip() + return a def state(self) -> Sequence[str]: view = self.view diff --git a/gaphor/core/modeling/tests/conftest.py b/gaphor/core/modeling/tests/conftest.py new file mode 100644 index 000000000..4cd29a406 --- /dev/null +++ b/gaphor/core/modeling/tests/conftest.py @@ -0,0 +1 @@ +from gaphor.diagram.tests.fixtures import diagram, element_factory, event_manager diff --git a/gaphor/core/modeling/tests/test_style_attributes.py b/gaphor/core/modeling/tests/test_style_attributes.py new file mode 100644 index 000000000..7296e6cc0 --- /dev/null +++ b/gaphor/core/modeling/tests/test_style_attributes.py @@ -0,0 +1,73 @@ +from gaphor import UML +from gaphor.core.modeling.diagram import StyledItem +from gaphor.UML.classes import ClassItem + + +def test_attribute_of_item(diagram): + classitem = diagram.create(ClassItem) + + node = StyledItem(classitem) + + assert node.attribute("show_attributes") == "true" + + +def test_nonexistant_attribute_of_item(diagram): + classitem = diagram.create(ClassItem) + + node = StyledItem(classitem) + + assert node.attribute("foobar") == "" + + +def test_should_get_type_of_element_attribute(diagram, element_factory): + class_ = element_factory.create(UML.Class) + classitem = diagram.create(ClassItem, subject=class_) + + node = StyledItem(classitem) + + assert node.attribute("subject") == "class" + + +def test_should_read_attribute_of_subject(diagram, element_factory): + class_ = element_factory.create(UML.Class) + classitem = diagram.create(ClassItem, subject=class_) + + class_.name = "myname" + node = StyledItem(classitem) + + assert node.attribute("name") == "myname" + + +# test attributes that can be any of a list +def test_nested_attribute_of_subject(diagram, element_factory): + class_ = element_factory.create(UML.Class) + attr = element_factory.create(UML.Property) + class_.ownedAttribute = attr + classitem = diagram.create(ClassItem, subject=class_) + + attr.name = "myname" + attr.isService = 1 + node = StyledItem(classitem) + + assert node.attribute("attribute") == "property" + assert node.attribute("attribute.name") == "myname" + assert node.attribute("attribute.isBehavior") == "" + assert node.attribute("attribute.isService") == "true" + assert node.attribute("attribute.notAnAttribute") == "" + + +def test_multiple_nested_attribute_of_subject(diagram, element_factory): + class_ = element_factory.create(UML.Class) + attr1 = element_factory.create(UML.Property) + attr2 = element_factory.create(UML.Property) + class_.ownedAttribute = attr1 + class_.ownedAttribute = attr2 + classitem = diagram.create(ClassItem, subject=class_) + + attr1.name = "first" + attr2.name = "second" + node = StyledItem(classitem) + + assert node.attribute("attribute") == "property property" + assert node.attribute("attribute.name") in ("first second", "second first") + assert node.attribute("attribute.notAnAttribute") == ""