diff --git a/src/py_to_uxf_core/model/uml_class.py b/src/py_to_uxf_core/model/uml_class.py index 2dca5f6..d763729 100644 --- a/src/py_to_uxf_core/model/uml_class.py +++ b/src/py_to_uxf_core/model/uml_class.py @@ -17,6 +17,9 @@ class UMLClass: self._position = pos self._dimension = dim + self._base_classes = List(type(self)) + self._sub_classes = List(type(self)) + self._attributes = '' self._functions = '' @@ -40,6 +43,22 @@ class UMLClass: def dimension(self, value: int): self._dimension = value + @property + def base_classes(self) -> List['UMLClass']: + return self._base_classes + + @base_classes.setter + def base_classes(self, value: List['UMLClass']): + self._base_classes = value + + @property + def sub_classes(self) -> List['UMLClass']: + return self._sub_classes + + @sub_classes.setter + def sub_classes(self, value: List['UMLClass']): + self._sub_classes = value + def __str__(self): return f'<{type(self).__name__} {self._cls}: {self._position} {self._dimension}>' diff --git a/src/py_to_uxf_core/service/umlet_creator_service.py b/src/py_to_uxf_core/service/umlet_creator_service.py index afc64e2..9ca1eba 100644 --- a/src/py_to_uxf_core/service/umlet_creator_service.py +++ b/src/py_to_uxf_core/service/umlet_creator_service.py @@ -26,176 +26,53 @@ class UmletCreatorService(UmletCreatorABC): self._moved_classes: list[UMLClass] = [] self._padding = 30 - def _sort_by_implementation(self, uml_classes: List[UMLClass], implementations: List[ClassImplementation]): - base_sub_map: dict[UMLClass, list[UMLClass]] = {} + @staticmethod + def _resolve_implementations(classes: List[UMLClass], implementations: List[ClassImplementation]) -> List[UMLClass]: + new_classes = List(UMLClass) + sub_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() + implementation: ClassImplementation = implementation + base: UMLClass = classes.where(lambda c: c.cls == implementation.base).first() + sub: UMLClass = classes.where(lambda c: c.cls == implementation.subclass).first() - if base_class not in base_sub_map: - base_sub_map[base_class] = [] + base.sub_classes.append(sub) + sub.base_classes.append(base) + if base not in new_classes: + new_classes.append(base) + sub_classes.append(sub) - if sub_class in base_sub_map[base_class]: - continue - base_sub_map[base_class].append(sub_class) + for cls in classes: + if cls not in new_classes and cls not in sub_classes: + new_classes.append(cls) - last_base = Position(self._padding, self._padding) - highest_y = self._padding + return new_classes - for base in base_sub_map: - subclasses = base_sub_map[base] - - base.position.x = last_base.x - base.position.y = last_base.y - - last_sub = Position(base.position.x, base.position.y + base.dimension.height) - for sub_class in subclasses: - if sub_class not in self._moved_classes: - 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 - if sub_class.position.y + sub_class.dimension.height > highest_y: - highest_y = sub_class.position.y + sub_class.dimension.height - - self._moved_classes.append(sub_class) - - last_base.x = last_sub.x - if last_base.x <= self._settings.max_pixel_x: - last_base.x += self._padding * 2 - else: - last_base.x = self._padding - last_base.y = highest_y + self._padding * 2 - - self._moved_classes.append(base) - - for cls in uml_classes: - if cls in self._moved_classes: + def _move_classes(self, classes: List[UMLClass], moved_classes: List[UMLClass], next_pos: Position, highest_y: int): + for cls in classes: + if cls.cls.name in self._parser_settings.ignore_class_names: continue - new_x = last_base.x - if new_x <= self._settings.max_pixel_x: - cls.position.x = new_x - last_base.x = new_x + cls.dimension.width + self._padding - cls.position.y = last_base.y - else: - cls.position.x = self._padding - cls.position.y = highest_y + self._padding * 2 - last_base = cls.position + if cls not in moved_classes: + Console.write_line(f'MOVE {cls.cls.name} to {next_pos} - {cls.dimension}') - if cls.position.y + cls.dimension.height > highest_y: - highest_y = cls.position.y + cls.dimension.height + if next_pos.x > self._settings.max_pixel_x: + next_pos.x = self._padding + next_pos.y = highest_y + self._padding * 2 - 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.x = next_pos.x 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 + cls.dimension.width + self._padding + + if cls.position.y + cls.dimension.height > highest_y: + highest_y = cls.position.y + cls.dimension.height + + moved_classes.append(cls) + + if cls.sub_classes.count() > 0: 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 + next_pos.y = cls.position.y + cls.dimension.height + self._padding + self._move_classes(cls.sub_classes, moved_classes, next_pos, highest_y) def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str: uml_classes = List(UMLClass) @@ -208,21 +85,32 @@ class UmletCreatorService(UmletCreatorABC): uml_cls.create_xml_body() uml_classes.append(uml_cls) - # 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): + sorted_classes = self._resolve_implementations(uml_classes, implementations) + moved_classes = List(UMLClass) + self._move_classes(sorted_classes, moved_classes, Position(self._padding, self._padding), 0) + + for uml_cls in moved_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: - 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.cls.name in self._parser_settings.ignore_class_names: - continue - xml_relations += sub_class.implementation_as_xml(base_class) + # 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: + # 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.cls.name in self._parser_settings.ignore_class_names: + # continue + # xml_relations += sub_class.implementation_as_xml(base_class) return f"""\