[WIP] Changed logic to move nodes
This commit is contained in:
parent
cea3e35800
commit
0dbd5f57e6
@ -2,6 +2,7 @@
|
|||||||
"Parser": {
|
"Parser": {
|
||||||
"IgnoreClassNames": [
|
"IgnoreClassNames": [
|
||||||
"ABC",
|
"ABC",
|
||||||
|
"ABCMeta",
|
||||||
"Enum"
|
"Enum"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,7 @@ class ClassImplementation:
|
|||||||
def __init__(self, base: PythonClass, subclass: PythonClass):
|
def __init__(self, base: PythonClass, subclass: PythonClass):
|
||||||
self._base = base
|
self._base = base
|
||||||
self._subclass = subclass
|
self._subclass = subclass
|
||||||
self.is_first = True
|
self._is_first = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def base(self) -> PythonClass:
|
def base(self) -> PythonClass:
|
||||||
@ -16,8 +16,16 @@ class ClassImplementation:
|
|||||||
def subclass(self) -> PythonClass:
|
def subclass(self) -> PythonClass:
|
||||||
return self._subclass
|
return self._subclass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_first(self) -> bool:
|
||||||
|
return self._is_first
|
||||||
|
|
||||||
|
@is_first.setter
|
||||||
|
def is_first(self, value: bool):
|
||||||
|
self._is_first = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'<{type(self).__name__} {self.is_first} {self._subclass.name} -> {self._base.name}>'
|
return f'<{type(self).__name__} {self._is_first} {self._subclass.name} -> {self._base.name}>'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'<{type(self).__name__} {self.is_first} {self._subclass.name} -> {self._base.name}>'
|
return f'<{type(self).__name__} {self._is_first} {self._subclass.name} -> {self._base.name}>'
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import math
|
from cpl_query.extension import List
|
||||||
import textwrap
|
|
||||||
|
|
||||||
from cpl_core.console import Console
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -16,11 +16,9 @@ class ImplementationScannerService(ImplementationScannerABC):
|
|||||||
ImplementationScannerABC.__init__(self)
|
ImplementationScannerABC.__init__(self)
|
||||||
self._parser_settings: ParserSettings = configuration.get_configuration(ParserSettings)
|
self._parser_settings: ParserSettings = configuration.get_configuration(ParserSettings)
|
||||||
|
|
||||||
def _get_implementation(self, base_name: str, sub_class: PythonClass, classes: List[PythonClass]) -> Optional[ClassImplementation]:
|
@staticmethod
|
||||||
|
def _get_implementation(base_name: str, sub_class: PythonClass, classes: List[PythonClass]) -> Optional[ClassImplementation]:
|
||||||
base: Optional[PythonClass] = classes.where(lambda c: c.name == base_name).first_or_default()
|
base: Optional[PythonClass] = classes.where(lambda c: c.name == base_name).first_or_default()
|
||||||
if base_name in self._parser_settings.ignore_class_names:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if base is None:
|
if base is None:
|
||||||
new_base = PythonClass(base_name)
|
new_base = PythonClass(base_name)
|
||||||
classes.append(new_base)
|
classes.append(new_base)
|
||||||
@ -44,13 +42,20 @@ class ImplementationScannerService(ImplementationScannerABC):
|
|||||||
implementations.append(implementation)
|
implementations.append(implementation)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for name in base_name.split(','):
|
split_base_name = base_name.split(',')
|
||||||
|
found_first_base = False
|
||||||
|
for i in range(len(split_base_name)):
|
||||||
|
name = split_base_name[i]
|
||||||
if '=' in name:
|
if '=' in name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
implementation = self._get_implementation(name, sub, classes)
|
implementation = self._get_implementation(name, sub, classes)
|
||||||
if implementation is not None:
|
if implementation is not None:
|
||||||
|
if i > 0 and found_first_base:
|
||||||
implementation.is_first = False
|
implementation.is_first = False
|
||||||
|
|
||||||
|
if not found_first_base:
|
||||||
|
found_first_base = name not in self._parser_settings.ignore_class_names
|
||||||
implementations.append(implementation)
|
implementations.append(implementation)
|
||||||
return implementations
|
return implementations
|
||||||
return None
|
return None
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
from cpl_core.configuration import ConfigurationABC
|
from cpl_core.configuration import ConfigurationABC
|
||||||
from cpl_core.console import Console
|
from cpl_core.console import Console
|
||||||
from cpl_query.extension import List
|
from cpl_query.extension import List, IterableABC
|
||||||
|
|
||||||
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.configuration.parser_settings import ParserSettings
|
||||||
from py_to_uxf_core.configuration.uml_creator_settings import UMLCreatorSettings
|
from py_to_uxf_core.configuration.uml_creator_settings import UMLCreatorSettings
|
||||||
from py_to_uxf_core.model.class_implementation import ClassImplementation
|
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
|
||||||
@ -10,6 +13,8 @@ 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
|
||||||
from py_to_uxf_core.model.uml_class import UMLClass
|
from py_to_uxf_core.model.uml_class import UMLClass
|
||||||
|
|
||||||
|
OrderedImplementation = dict[ClassImplementation, IterableABC]
|
||||||
|
|
||||||
|
|
||||||
class UmletCreatorService(UmletCreatorABC):
|
class UmletCreatorService(UmletCreatorABC):
|
||||||
|
|
||||||
@ -17,6 +22,7 @@ class UmletCreatorService(UmletCreatorABC):
|
|||||||
UmletCreatorABC.__init__(self)
|
UmletCreatorABC.__init__(self)
|
||||||
|
|
||||||
self._settings: UMLCreatorSettings = config.get_configuration(UMLCreatorSettings)
|
self._settings: UMLCreatorSettings = config.get_configuration(UMLCreatorSettings)
|
||||||
|
self._parser_settings: ParserSettings = config.get_configuration(ParserSettings)
|
||||||
self._moved_classes: list[UMLClass] = []
|
self._moved_classes: list[UMLClass] = []
|
||||||
self._padding = 30
|
self._padding = 30
|
||||||
|
|
||||||
@ -85,6 +91,112 @@ class UmletCreatorService(UmletCreatorABC):
|
|||||||
if cls.position.y + cls.dimension.height > highest_y:
|
if cls.position.y + cls.dimension.height > highest_y:
|
||||||
highest_y = cls.position.y + cls.dimension.height
|
highest_y = cls.position.y + cls.dimension.height
|
||||||
|
|
||||||
|
def _sort_implementations_by_base_order(self, implementations: List[ClassImplementation]) -> OrderedImplementation:
|
||||||
|
"""
|
||||||
|
Sort given implementations by order of base classes
|
||||||
|
SubClass(FirstBaseClass, SecondBaseClass, ThirdBaseClass)
|
||||||
|
"""
|
||||||
|
ordered_implementations: OrderedImplementation = {}
|
||||||
|
for implementation in list(implementations):
|
||||||
|
if not implementation.is_first or implementation.base.name in self._parser_settings.ignore_class_names:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ordered_implementations[implementation] = implementations.where(lambda i: i.subclass == implementation.subclass and not i.is_first)
|
||||||
|
|
||||||
|
return ordered_implementations
|
||||||
|
|
||||||
|
def _move_by_implementation(self, implementations: OrderedImplementation, uml_classes: List[UMLClass]) -> List[UMLClass]:
|
||||||
|
base_sub_map: dict[UMLClass, 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] = []
|
||||||
|
|
||||||
|
if sub_class in base_sub_map[base_class]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
base_sub_map[base_class].append(sub_class)
|
||||||
|
for sub_impl in implementations[implementation]:
|
||||||
|
si_sub_class: UMLClass = uml_classes.where(lambda u: u.cls == sub_impl.subclass).first()
|
||||||
|
si_base_class: UMLClass = uml_classes.where(lambda u: u.cls == sub_impl.base).first()
|
||||||
|
|
||||||
|
if si_base_class not in base_sub_map:
|
||||||
|
base_sub_map[si_base_class] = []
|
||||||
|
|
||||||
|
if si_sub_class in base_sub_map[si_base_class]:
|
||||||
|
continue
|
||||||
|
base_sub_map[si_base_class].append(si_sub_class)
|
||||||
|
|
||||||
|
moved_classes = List(UMLClass)
|
||||||
|
next_pos = Position(self._padding, self._padding)
|
||||||
|
highest_y = 0
|
||||||
|
for base in base_sub_map:
|
||||||
|
subclasses = base_sub_map[base]
|
||||||
|
if base in moved_classes:
|
||||||
|
Console.write_line('b_SKIP', base)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if next_pos.x >= self._settings.max_pixel_x:
|
||||||
|
next_pos.x = self._padding
|
||||||
|
next_pos.y = highest_y + self._padding
|
||||||
|
|
||||||
|
base.position.x = next_pos.x
|
||||||
|
base.position.y = next_pos.y
|
||||||
|
if base.position.y + base.dimension.height > highest_y:
|
||||||
|
highest_y = base.position.y + base.dimension.height
|
||||||
|
|
||||||
|
next_pos.y = base.position.y + base.dimension.height + self._padding
|
||||||
|
|
||||||
|
Console.write_line('moved b', base)
|
||||||
|
moved_classes.append(base)
|
||||||
|
|
||||||
|
moved_subclasses = []
|
||||||
|
for sub in subclasses:
|
||||||
|
if sub in moved_classes:
|
||||||
|
Console.write_line('s_SKIP', sub)
|
||||||
|
continue
|
||||||
|
|
||||||
|
sub.position.x = next_pos.x
|
||||||
|
sub.position.y = next_pos.y
|
||||||
|
next_pos.x += sub.dimension.width + self._padding
|
||||||
|
if sub.position.y + sub.dimension.height > highest_y:
|
||||||
|
highest_y = sub.position.y + sub.dimension.height
|
||||||
|
|
||||||
|
Console.write_line('moved s', sub)
|
||||||
|
moved_classes.append(sub)
|
||||||
|
moved_subclasses.append(sub)
|
||||||
|
|
||||||
|
delta_x = self._padding
|
||||||
|
if len(moved_subclasses) == 0:
|
||||||
|
delta_x += base.dimension.width
|
||||||
|
next_pos.x += delta_x
|
||||||
|
next_pos.y = base.position.y
|
||||||
|
|
||||||
|
for cls in uml_classes:
|
||||||
|
if cls in moved_classes:
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_x = next_pos.x
|
||||||
|
if new_x <= self._settings.max_pixel_x:
|
||||||
|
cls.position.x = new_x
|
||||||
|
next_pos.x = new_x + cls.dimension.width + self._padding
|
||||||
|
cls.position.y = next_pos.y
|
||||||
|
else:
|
||||||
|
cls.position.x = self._padding
|
||||||
|
cls.position.y = highest_y + self._padding * 2
|
||||||
|
next_pos.x = cls.position.x
|
||||||
|
next_pos.y = cls.position.y
|
||||||
|
|
||||||
|
if cls.position.y + cls.dimension.height > highest_y:
|
||||||
|
highest_y = cls.position.y + cls.dimension.height
|
||||||
|
|
||||||
|
moved_classes.append(cls)
|
||||||
|
# Console.write_line('moved c', cls)
|
||||||
|
|
||||||
|
return moved_classes
|
||||||
|
|
||||||
def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str:
|
def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str:
|
||||||
uml_classes = List(UMLClass)
|
uml_classes = List(UMLClass)
|
||||||
xml_classes = ''
|
xml_classes = ''
|
||||||
@ -96,16 +208,22 @@ class UmletCreatorService(UmletCreatorABC):
|
|||||||
uml_cls.create_xml_body()
|
uml_cls.create_xml_body()
|
||||||
uml_classes.append(uml_cls)
|
uml_classes.append(uml_cls)
|
||||||
|
|
||||||
self._sort_by_implementation(uml_classes, implementations)
|
# self._sort_by_implementation(uml_classes, implementations)
|
||||||
|
oi = self._sort_implementations_by_base_order(implementations)
|
||||||
|
for uml_cls in self._move_by_implementation(oi, uml_classes):
|
||||||
|
# save class xml
|
||||||
|
if uml_cls.cls.name in self._parser_settings.ignore_class_names:
|
||||||
|
continue
|
||||||
|
|
||||||
|
xml_classes += uml_cls.as_xml()
|
||||||
|
|
||||||
for implementation in implementations:
|
for implementation in implementations:
|
||||||
sub_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.subclass).first()
|
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()
|
base_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.base).first()
|
||||||
|
if base_class.cls.name in self._parser_settings.ignore_class_names:
|
||||||
|
continue
|
||||||
xml_relations += sub_class.implementation_as_xml(base_class)
|
xml_relations += sub_class.implementation_as_xml(base_class)
|
||||||
|
|
||||||
for uml_cls in uml_classes:
|
|
||||||
# save class xml
|
|
||||||
xml_classes += uml_cls.as_xml()
|
|
||||||
|
|
||||||
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">
|
||||||
|
Loading…
Reference in New Issue
Block a user