Added rendering of implementations
This commit is contained in:
parent
c53b3b7669
commit
5ba24cafaf
@ -64,9 +64,8 @@ class Application(ApplicationABC):
|
|||||||
Console.write_line(f'Found {classes.count()} classes')
|
Console.write_line(f'Found {classes.count()} classes')
|
||||||
implementations: List[ClassImplementation] = self._parser.parse_implementations(classes)
|
implementations: List[ClassImplementation] = self._parser.parse_implementations(classes)
|
||||||
Console.write_line(f'Found {implementations.count()} implementations')
|
Console.write_line(f'Found {implementations.count()} implementations')
|
||||||
Console.write_line(implementations)
|
|
||||||
# self._console_output(classes)
|
# self._console_output(classes)
|
||||||
xml = self._umlet_creator.generate_xml(classes)
|
xml = self._umlet_creator.generate_xml(classes, implementations)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Console.error('Parsing failed', f'{e} -> {traceback.format_exc()}')
|
Console.error('Parsing failed', f'{e} -> {traceback.format_exc()}')
|
||||||
exit()
|
exit()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from py_to_uxf_core.model.class_implementation import ClassImplementation
|
||||||
from py_to_uxf_core.model.python_class import PythonClass
|
from py_to_uxf_core.model.python_class import PythonClass
|
||||||
|
|
||||||
|
|
||||||
@ -9,4 +10,4 @@ class UmletCreatorABC(ABC):
|
|||||||
def __init__(self): pass
|
def __init__(self): pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def generate_xml(self, classes: list[PythonClass]) -> str: pass
|
def generate_xml(self, classes: list[PythonClass], implementations: list[ClassImplementation]) -> str: pass
|
||||||
|
@ -19,3 +19,9 @@ class Dimension:
|
|||||||
@height.setter
|
@height.setter
|
||||||
def height(self, value: int):
|
def height(self, value: int):
|
||||||
self._height = value
|
self._height = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'(w:{self._width}, h:{self._height})'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'(w:{self._width}, h:{self._height})'
|
||||||
|
@ -19,3 +19,9 @@ class Position:
|
|||||||
@y.setter
|
@y.setter
|
||||||
def y(self, value: int):
|
def y(self, value: int):
|
||||||
self._y = value
|
self._y = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'(x:{self._x}, y:{self._y})'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'(x:{self._x}, y:{self._y})'
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import math
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from cpl_core.console import Console
|
from cpl_core.console import Console
|
||||||
@ -19,6 +20,9 @@ class UMLClass:
|
|||||||
self._position = pos
|
self._position = pos
|
||||||
self._dimension = dim
|
self._dimension = dim
|
||||||
|
|
||||||
|
self._attributes = ''
|
||||||
|
self._functions = ''
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cls(self) -> PythonClass:
|
def cls(self) -> PythonClass:
|
||||||
return self._cls
|
return self._cls
|
||||||
@ -39,21 +43,54 @@ class UMLClass:
|
|||||||
def dimension(self, value: int):
|
def dimension(self, value: int):
|
||||||
self._dimension = value
|
self._dimension = value
|
||||||
|
|
||||||
def as_xml(self) -> str:
|
def implementation_as_xml(self, base_class: 'UMLClass') -> str:
|
||||||
|
base_point = Position(
|
||||||
|
base_class.position.x + int(base_class.dimension.width / 2),
|
||||||
|
base_class.position.y + base_class.dimension.height
|
||||||
|
)
|
||||||
|
|
||||||
|
sub_point = Position(
|
||||||
|
self._position.x + int(self._dimension.width / 2),
|
||||||
|
self._position.y
|
||||||
|
)
|
||||||
|
|
||||||
|
path_dimension = Dimension(
|
||||||
|
self._position.x - base_class.position.x,
|
||||||
|
sub_point.y - base_point.y
|
||||||
|
)
|
||||||
|
path_position = Position(
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
return f"""\
|
||||||
|
<element>
|
||||||
|
<id>Relation</id>
|
||||||
|
<coordinates>
|
||||||
|
<x>{base_point.x}</x>
|
||||||
|
<y>{base_point.y}</y>
|
||||||
|
<w>0</w>
|
||||||
|
<h>0</h>
|
||||||
|
</coordinates>
|
||||||
|
<panel_attributes>lt=<<-
|
||||||
|
</panel_attributes>
|
||||||
|
<additional_attributes>{float(path_position.x)};{float(path_position.y)};{float(path_dimension.width)};{float(path_dimension.height)}</additional_attributes>
|
||||||
|
</element>
|
||||||
|
""".replace(' ', '').replace('\t', '')
|
||||||
|
|
||||||
|
def create_xml_body(self):
|
||||||
px_per_line = 16
|
px_per_line = 16
|
||||||
px_per_char = 2.9
|
px_per_char = 2.9
|
||||||
self._dimension.height += (self._cls.attributes.count() + self._cls.functions.count()) * px_per_line
|
self._dimension.height += int(
|
||||||
|
round((self._cls.attributes.count() + self._cls.functions.count()) * px_per_line, -1))
|
||||||
longest_line_length = self._dimension.width / px_per_char
|
longest_line_length = self._dimension.width / px_per_char
|
||||||
|
|
||||||
attributes = ''
|
|
||||||
functions = ''
|
|
||||||
|
|
||||||
if len(self._cls.attributes) > 0:
|
if len(self._cls.attributes) > 0:
|
||||||
for atr in self._cls.attributes:
|
for atr in self._cls.attributes:
|
||||||
attribute = f'{atr.access_modifier.value}{atr.name}: {atr.type}\n'
|
attribute = f'{atr.access_modifier.value}{atr.name}: {atr.type}\n'
|
||||||
if len(attribute) > longest_line_length:
|
if len(attribute) > longest_line_length:
|
||||||
longest_line_length = len(attribute)
|
longest_line_length = len(attribute)
|
||||||
attributes += attribute
|
self._attributes += attribute
|
||||||
|
|
||||||
if len(self._cls.functions) > 0:
|
if len(self._cls.functions) > 0:
|
||||||
for func in self._cls.functions:
|
for func in self._cls.functions:
|
||||||
@ -61,10 +98,11 @@ class UMLClass:
|
|||||||
function = f'{func.access_modifier.value}{func.name}({args}): {func.return_type}\n'
|
function = f'{func.access_modifier.value}{func.name}({args}): {func.return_type}\n'
|
||||||
if len(function) > longest_line_length:
|
if len(function) > longest_line_length:
|
||||||
longest_line_length = len(function)
|
longest_line_length = len(function)
|
||||||
functions += function
|
self._functions += function
|
||||||
|
|
||||||
self._dimension.width = round(longest_line_length * px_per_char * px_per_char)
|
self._dimension.width = int(round(longest_line_length * px_per_char * px_per_char, -1))
|
||||||
|
|
||||||
|
def as_xml(self) -> str:
|
||||||
return f"""\
|
return f"""\
|
||||||
<element>
|
<element>
|
||||||
<id>UMLClass</id>
|
<id>UMLClass</id>
|
||||||
@ -77,9 +115,9 @@ class UMLClass:
|
|||||||
<panel_attributes>
|
<panel_attributes>
|
||||||
{self._cls.name}
|
{self._cls.name}
|
||||||
--
|
--
|
||||||
{attributes}
|
{self._attributes}
|
||||||
--
|
--
|
||||||
{functions}
|
{self._functions}
|
||||||
</panel_attributes>
|
</panel_attributes>
|
||||||
<additional_attributes></additional_attributes>
|
<additional_attributes></additional_attributes>
|
||||||
</element>
|
</element>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import textwrap
|
import classes as classes
|
||||||
|
from cpl_query.extension import List
|
||||||
from cpl_core.console import Console
|
|
||||||
|
|
||||||
from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC
|
from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC
|
||||||
|
from py_to_uxf_core.model.class_implementation import ClassImplementation
|
||||||
from py_to_uxf_core.model.dimension import Dimension
|
from py_to_uxf_core.model.dimension import Dimension
|
||||||
from py_to_uxf_core.model.position import Position
|
from py_to_uxf_core.model.position import Position
|
||||||
from py_to_uxf_core.model.python_class import PythonClass
|
from py_to_uxf_core.model.python_class import PythonClass
|
||||||
@ -14,22 +14,81 @@ class UmletCreatorService(UmletCreatorABC):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
UmletCreatorABC.__init__(self)
|
UmletCreatorABC.__init__(self)
|
||||||
|
|
||||||
def generate_xml(self, classes: list[PythonClass]) -> str:
|
self._padding = 30
|
||||||
xml_cls = ''
|
|
||||||
|
def _sort_by_implementation(self, uml_classes: List[UMLClass], implementations: List[ClassImplementation]):
|
||||||
|
base_sub_map: dict[UMLClass, list[UMLClass]] = {}
|
||||||
|
moved_classes: list[UMLClass] = []
|
||||||
|
for implementation in implementations:
|
||||||
|
sub_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.subclass).first()
|
||||||
|
base_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.base).first()
|
||||||
|
|
||||||
|
if base_class not in base_sub_map:
|
||||||
|
base_sub_map[base_class] = []
|
||||||
|
|
||||||
|
base_sub_map[base_class].append(sub_class)
|
||||||
|
|
||||||
|
last_base = Position(self._padding, self._padding)
|
||||||
|
for base in base_sub_map:
|
||||||
|
subclasses = base_sub_map[base]
|
||||||
|
|
||||||
|
base.position.x = last_base.x
|
||||||
|
base.position.y = self._padding
|
||||||
|
|
||||||
|
last_sub = Position(base.position.x, base.position.y + base.dimension.height)
|
||||||
|
for sub_class in subclasses:
|
||||||
|
sub_class.position.x = base.position.x
|
||||||
|
sub_class.position.y = base.position.y
|
||||||
|
|
||||||
|
if sub_class.position.x <= last_sub.x:
|
||||||
|
sub_class.position.x = last_sub.x
|
||||||
|
if sub_class.position.y <= last_sub.y:
|
||||||
|
sub_class.position.y = last_sub.y + self._padding * 2
|
||||||
|
|
||||||
|
last_sub.x = sub_class.position.x + sub_class.dimension.width + self._padding
|
||||||
|
moved_classes.append(sub_class)
|
||||||
|
|
||||||
|
last_base = last_sub
|
||||||
|
last_base.x += self._padding * 2
|
||||||
|
moved_classes.append(base)
|
||||||
|
|
||||||
|
for cls in uml_classes:
|
||||||
|
if cls in moved_classes:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cls.position.x = last_base.x + self._padding
|
||||||
|
last_base.x = cls.position.x + cls.dimension.width
|
||||||
|
|
||||||
|
def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str:
|
||||||
|
uml_classes = List(UMLClass)
|
||||||
|
xml_classes = ''
|
||||||
|
xml_relations = ''
|
||||||
default_width = 80
|
default_width = 80
|
||||||
default_height = 80
|
default_height = 80
|
||||||
next_x = 10
|
next_x = self._padding
|
||||||
|
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
uml_cls = UMLClass(cls, Position(next_x, 10), Dimension(default_width, default_height))
|
uml_cls = UMLClass(cls, Position(next_x, self._padding), Dimension(default_width, default_height))
|
||||||
|
# trigger xml generation to render coordinates
|
||||||
|
uml_cls.create_xml_body()
|
||||||
|
next_x += round(uml_cls.dimension.width, -1) + self._padding
|
||||||
|
uml_classes.append(uml_cls)
|
||||||
|
|
||||||
|
self._sort_by_implementation(uml_classes, implementations)
|
||||||
|
for implementation in implementations:
|
||||||
|
sub_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.subclass).first()
|
||||||
|
base_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.base).first()
|
||||||
|
xml_relations += sub_class.implementation_as_xml(base_class)
|
||||||
|
|
||||||
|
for uml_cls in uml_classes:
|
||||||
# save class xml
|
# save class xml
|
||||||
xml_cls += uml_cls.as_xml()
|
xml_classes += uml_cls.as_xml()
|
||||||
next_x += round(uml_cls.dimension.width, -1) + 10
|
|
||||||
|
|
||||||
return f"""\
|
return f"""\
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<diagram program="umlet" version="14.1.1">
|
<diagram program="umlet" version="14.1.1">
|
||||||
<zoom_level>10</zoom_level>
|
<zoom_level>10</zoom_level>
|
||||||
{xml_cls}
|
{xml_classes}
|
||||||
|
{xml_relations}
|
||||||
</diagram>
|
</diagram>
|
||||||
""".replace(' ', '').replace('\t', '')
|
""".replace(' ', '').replace('\t', '')
|
||||||
|
Loading…
Reference in New Issue
Block a user