lutaml 0.10.13 → 0.10.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +29 -33
  3. data/lib/lutaml/cli/interactive_shell/export_handler.rb +25 -17
  4. data/lib/lutaml/cli/interactive_shell/help_display.rb +39 -45
  5. data/lib/lutaml/cli/interactive_shell/navigation_commands.rb +45 -26
  6. data/lib/lutaml/cli/interactive_shell/query_commands.rb +73 -47
  7. data/lib/lutaml/cli/interactive_shell.rb +53 -27
  8. data/lib/lutaml/cli/tree_view_formatter.rb +11 -3
  9. data/lib/lutaml/converter/xmi_to_uml.rb +11 -6
  10. data/lib/lutaml/formatter/graphviz.rb +65 -35
  11. data/lib/lutaml/model_transformations/parsers/base_parser.rb +27 -29
  12. data/lib/lutaml/qea/factory/association_builder.rb +144 -127
  13. data/lib/lutaml/qea/factory/class_transformer.rb +91 -53
  14. data/lib/lutaml/qea/factory/ea_to_uml_factory.rb +11 -22
  15. data/lib/lutaml/qea/factory/enum_transformer.rb +41 -31
  16. data/lib/lutaml/qea/factory/generalization_builder.rb +155 -125
  17. data/lib/lutaml/qea/factory/stereotype_loader.rb +13 -7
  18. data/lib/lutaml/qea/lookup_indexes.rb +31 -13
  19. data/lib/lutaml/uml/inheritance_walker.rb +11 -7
  20. data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +33 -25
  21. data/lib/lutaml/uml_repository/exporters/markdown/index_page_builder.rb +17 -9
  22. data/lib/lutaml/uml_repository/exporters/markdown_exporter.rb +27 -20
  23. data/lib/lutaml/uml_repository/index_builders/association_index.rb +60 -48
  24. data/lib/lutaml/uml_repository/index_builders/class_index.rb +35 -24
  25. data/lib/lutaml/uml_repository/queries/class_query.rb +79 -48
  26. data/lib/lutaml/uml_repository/queries/inheritance_query.rb +42 -32
  27. data/lib/lutaml/uml_repository/queries/search_query.rb +93 -85
  28. data/lib/lutaml/uml_repository/query_dsl/conditions/package_condition.rb +9 -2
  29. data/lib/lutaml/uml_repository/repository/loader.rb +14 -7
  30. data/lib/lutaml/uml_repository/static_site/data_transformer.rb +64 -35
  31. data/lib/lutaml/uml_repository/static_site/search_index_builder.rb +32 -19
  32. data/lib/lutaml/uml_repository/static_site/serializers/class_serializer.rb +36 -20
  33. data/lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb +131 -105
  34. data/lib/lutaml/uml_repository/static_site/serializers/package_serializer.rb +15 -9
  35. data/lib/lutaml/uml_repository/static_site/serializers/package_tree_builder.rb +38 -24
  36. data/lib/lutaml/version.rb +1 -1
  37. data/lib/lutaml/xmi/liquid_drops/klass_drop.rb +34 -18
  38. data/lib/lutaml/xmi/parsers/xmi_connector.rb +35 -23
  39. metadata +2 -9
  40. data/TODO.cleanups/01-resolve-production-todos.md +0 -65
  41. data/TODO.cleanups/02-reduce-metrics-offenses.md +0 -37
  42. data/TODO.cleanups/03-reduce-rspec-multiple-expectations.md +0 -54
  43. data/TODO.cleanups/04-reduce-rspec-example-length.md +0 -45
  44. data/TODO.cleanups/07-fix-lint-offenses.md +0 -74
  45. data/TODO.cleanups/08-reduce-memoized-helpers-and-nesting.md +0 -43
  46. data/TODO.cleanups/09-reduce-verified-doubles-and-rspec-style.md +0 -57
@@ -17,73 +17,111 @@ module Lutaml
17
17
  class ClassTransformer < BaseTransformer
18
18
  def transform(ea_object)
19
19
  return nil if ea_object.nil?
20
-
21
- is_class_type = ea_object.uml_class? || ea_object.interface?
22
- is_proxy = ea_object.object_type == "ProxyConnector"
23
- is_text_class = ea_object.object_type == "Text"
24
- return nil unless is_class_type || is_proxy || is_text_class
20
+ return nil unless transformable?(ea_object)
25
21
 
26
22
  Lutaml::Uml::Class.new.tap do |klass|
27
- klass.name = ea_object.name
28
- klass.xmi_id = normalize_guid_to_xmi_format(ea_object.ea_guid,
29
- "EAID")
30
- klass.is_abstract = ea_object.abstract?
31
- klass.type = "Class"
32
- klass.visibility = map_visibility(ea_object.visibility)
33
-
34
- stereotypes = build_stereotypes(ea_object)
35
- klass.stereotype = stereotypes unless stereotypes.empty?
36
-
37
- klass.definition = normalize_line_endings(ea_object.note) unless
38
- ea_object.note.nil? || ea_object.note.empty?
39
-
40
- gen_builder = GeneralizationBuilder.new(database)
41
- assoc_builder = AssociationBuilder.new(database)
42
-
43
- attrs = load_attributes(ea_object.ea_object_id)
44
- assoc_attrs = gen_builder.convert_to_top_element_attributes(
45
- assoc_builder.load_association_attributes(ea_object.ea_object_id),
46
- )
47
- klass.attributes = attrs + assoc_attrs
48
-
49
- klass.operations = load_operations(ea_object.ea_object_id)
50
- klass.constraints = load_constraints(ea_object.ea_object_id)
51
- klass.tagged_values = load_tagged_values(ea_object.ea_guid)
52
- klass.tagged_values.concat(
53
- load_object_properties(ea_object.ea_object_id),
54
- )
55
-
56
- klass.generalization = gen_builder.load_generalization(
57
- ea_object.ea_object_id,
58
- )
59
- klass.association_generalization = gen_builder
60
- .load_association_generalizations(ea_object.ea_object_id)
61
-
62
- klass.associations = assoc_builder.load_class_associations(
63
- ea_object.ea_object_id, ea_object.ea_guid
64
- )
23
+ assign_basic_properties(klass, ea_object)
24
+ assign_features(klass, ea_object)
25
+ assign_relationships(klass, ea_object)
65
26
  end
66
27
  end
67
28
 
68
29
  private
69
30
 
31
+ def transformable?(ea_object)
32
+ ea_object.uml_class? || ea_object.interface? ||
33
+ ea_object.object_type == "ProxyConnector" ||
34
+ ea_object.object_type == "Text"
35
+ end
36
+
37
+ def assign_basic_properties(klass, ea_object)
38
+ klass.name = ea_object.name
39
+ klass.xmi_id = normalize_guid_to_xmi_format(ea_object.ea_guid, "EAID")
40
+ klass.is_abstract = ea_object.abstract?
41
+ klass.type = "Class"
42
+ klass.visibility = map_visibility(ea_object.visibility)
43
+ assign_stereotypes(klass, ea_object)
44
+ assign_definition(klass, ea_object)
45
+ end
46
+
47
+ def assign_stereotypes(klass, ea_object)
48
+ stereotypes = build_stereotypes(ea_object)
49
+ klass.stereotype = stereotypes unless stereotypes.empty?
50
+ end
51
+
52
+ def assign_definition(klass, ea_object)
53
+ return if ea_object.note.nil? || ea_object.note.empty?
54
+
55
+ klass.definition = normalize_line_endings(ea_object.note)
56
+ end
57
+
58
+ def assign_features(klass, ea_object)
59
+ klass.attributes = load_all_attributes(ea_object)
60
+ assign_feature_collections(klass, ea_object)
61
+ end
62
+
63
+ def assign_feature_collections(klass, ea_object)
64
+ klass.operations = load_operations(ea_object.ea_object_id)
65
+ klass.constraints = load_constraints(ea_object.ea_object_id)
66
+ klass.tagged_values = load_tagged_values(ea_object.ea_guid)
67
+ klass.tagged_values.concat(
68
+ load_object_properties(ea_object.ea_object_id),
69
+ )
70
+ end
71
+
72
+ def assign_relationships(klass, ea_object)
73
+ gen_builder = GeneralizationBuilder.new(database)
74
+ assoc_builder = AssociationBuilder.new(database)
75
+
76
+ klass.generalization = gen_builder.load_generalization(
77
+ ea_object.ea_object_id,
78
+ )
79
+ klass.association_generalization = gen_builder
80
+ .load_association_generalizations(ea_object.ea_object_id)
81
+
82
+ klass.associations = assoc_builder.load_class_associations(
83
+ ea_object.ea_object_id, ea_object.ea_guid
84
+ )
85
+ end
86
+
87
+ def load_all_attributes(ea_object)
88
+ gen_builder = GeneralizationBuilder.new(database)
89
+ assoc_builder = AssociationBuilder.new(database)
90
+
91
+ attrs = load_attributes(ea_object.ea_object_id)
92
+ assoc_attrs = gen_builder.convert_to_top_element_attributes(
93
+ assoc_builder.load_association_attributes(ea_object.ea_object_id),
94
+ )
95
+ attrs + assoc_attrs
96
+ end
97
+
70
98
  def build_stereotypes(ea_object)
71
99
  stereotypes = []
72
- if ea_object.stereotype && !ea_object.stereotype.empty?
73
- stereotypes << ea_object.stereotype
74
- end
100
+ add_direct_stereotype(stereotypes, ea_object)
101
+ add_xref_stereotype(stereotypes, ea_object)
102
+ add_interface_stereotype(stereotypes, ea_object)
103
+
104
+ stereotypes
105
+ end
75
106
 
107
+ def add_direct_stereotype(stereotypes, ea_object)
108
+ return unless ea_object.stereotype && !ea_object.stereotype.empty?
109
+
110
+ stereotypes << ea_object.stereotype
111
+ end
112
+
113
+ def add_xref_stereotype(stereotypes, ea_object)
76
114
  xref_stereotype = StereotypeLoader.new(database)
77
115
  .load_from_xref(ea_object.ea_guid)
78
- if xref_stereotype && !stereotypes.include?(xref_stereotype)
79
- stereotypes << xref_stereotype
80
- end
116
+ return unless xref_stereotype && !stereotypes.include?(xref_stereotype)
81
117
 
82
- if ea_object.interface? && !stereotypes.include?("interface")
83
- stereotypes << "interface"
84
- end
118
+ stereotypes << xref_stereotype
119
+ end
85
120
 
86
- stereotypes
121
+ def add_interface_stereotype(stereotypes, ea_object)
122
+ return unless ea_object.interface? && !stereotypes.include?("interface")
123
+
124
+ stereotypes << "interface"
87
125
  end
88
126
 
89
127
  def load_attributes(object_id)
@@ -183,35 +183,24 @@ module Lutaml
183
183
  # Register package and all its descendants in resolver
184
184
  # @param package [Lutaml::Uml::Package] Package to register
185
185
  # @return [void]
186
- def register_package_hierarchy(package) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
186
+ def register_package_hierarchy(package)
187
187
  return if package.nil?
188
188
 
189
- # Register the package itself
190
189
  register_element(package)
190
+ register_package_members(package)
191
191
 
192
- # Register all classes in the package
193
- package.classes&.each do |klass|
194
- register_element(klass)
195
- end
196
-
197
- # Register all enums in the package
198
- package.enums&.each do |enum|
199
- register_element(enum)
200
- end
201
-
202
- # Register all data types in the package
203
- package.data_types&.each do |data_type|
204
- register_element(data_type)
192
+ package.packages&.each do |child_package|
193
+ register_package_hierarchy(child_package)
205
194
  end
195
+ end
206
196
 
207
- # Register all instances in the package
208
- package.instances&.each do |instance|
209
- register_element(instance)
210
- end
197
+ MEMBER_COLLECTIONS = %i[classes enums data_types instances].freeze
211
198
 
212
- # Recursively register child packages
213
- package.packages&.each do |child_package|
214
- register_package_hierarchy(child_package)
199
+ def register_package_members(package)
200
+ MEMBER_COLLECTIONS.each do |collection|
201
+ package.public_send(collection)&.each do |elem|
202
+ register_element(elem)
203
+ end
215
204
  end
216
205
  end
217
206
 
@@ -13,53 +13,63 @@ module Lutaml
13
13
  # Transform EA object to UML enum
14
14
  # @param ea_object [EaObject] EA object model
15
15
  # @return [Lutaml::Uml::Enum] UML enum
16
- def transform(ea_object) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
16
+ def transform(ea_object)
17
17
  return nil if ea_object.nil?
18
- return nil unless ea_object.enumeration? ||
19
- (ea_object.stereotype && ea_object.stereotype.downcase == "enumeration")
18
+ return nil unless enum?(ea_object)
20
19
 
21
20
  Lutaml::Uml::Enum.new.tap do |enum|
22
- # Map basic properties
23
- enum.name = ea_object.name
24
- enum.xmi_id = normalize_guid_to_xmi_format(ea_object.ea_guid,
25
- "EAID")
26
- enum.visibility = map_visibility(ea_object.visibility)
27
-
28
- # Map stereotype
29
- if ea_object.stereotype && !ea_object.stereotype.empty?
30
- enum.stereotype = [ea_object.stereotype]
31
- end
32
-
33
- # Map definition/notes
34
- enum.definition = ea_object.note unless
35
- ea_object.note.nil? || ea_object.note.empty?
36
-
37
- # Load enum values (stored as attributes in EA)
38
- enum.values = load_enum_values(ea_object.ea_object_id)
39
-
40
- # Load and transform tagged values
41
- enum.tagged_values = load_tagged_values(ea_object.ea_guid)
21
+ assign_enum_basic(enum, ea_object)
22
+ assign_enum_features(enum, ea_object)
42
23
  end
43
24
  end
44
25
 
26
+ def assign_enum_basic(enum, ea_object)
27
+ enum.name = ea_object.name
28
+ enum.xmi_id = normalize_guid_to_xmi_format(ea_object.ea_guid,
29
+ "EAID")
30
+ enum.visibility = map_visibility(ea_object.visibility)
31
+ enum.stereotype = [ea_object.stereotype] if valid_stereotype?(ea_object)
32
+ enum.definition = ea_object.note if note_present?(ea_object)
33
+ end
34
+
35
+ def note_present?(ea_object)
36
+ ea_object.note && !ea_object.note.empty?
37
+ end
38
+
39
+ def assign_enum_features(enum, ea_object)
40
+ enum.values = load_enum_values(ea_object.ea_object_id)
41
+ enum.tagged_values = load_tagged_values(ea_object.ea_guid)
42
+ end
43
+
45
44
  private
46
45
 
46
+ def enum?(ea_object)
47
+ ea_object.enumeration? ||
48
+ (ea_object.stereotype && ea_object.stereotype.downcase == "enumeration")
49
+ end
50
+
51
+ def valid_stereotype?(ea_object)
52
+ ea_object.stereotype && !ea_object.stereotype.empty?
53
+ end
54
+
47
55
  # Load enum values (literals) from attributes
48
56
  # @param object_id [Integer] Object ID
49
57
  # @return [Array<Lutaml::Uml::Value>] Enum values
50
- def load_enum_values(object_id) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
58
+ def load_enum_values(object_id)
51
59
  return [] if object_id.nil?
52
60
 
53
61
  ea_attrs = database.attributes_for_object(object_id)
54
62
  .sort_by { |a| a.pos || 0 }
55
63
 
56
- ea_attrs.filter_map do |ea_attr|
57
- Lutaml::Uml::Value.new.tap do |value|
58
- value.name = ea_attr.name
59
- value.id = normalize_guid_to_xmi_format(ea_attr.ea_guid, "EAID")
60
- value.definition = ea_attr.notes unless
61
- ea_attr.notes.nil? || ea_attr.notes.empty?
62
- end
64
+ ea_attrs.filter_map { |ea_attr| build_enum_value(ea_attr) }
65
+ end
66
+
67
+ def build_enum_value(ea_attr)
68
+ Lutaml::Uml::Value.new.tap do |value|
69
+ value.name = ea_attr.name
70
+ value.id = normalize_guid_to_xmi_format(ea_attr.ea_guid, "EAID")
71
+ value.definition = ea_attr.notes unless
72
+ ea_attr.notes.nil? || ea_attr.notes.empty?
63
73
  end
64
74
  end
65
75
 
@@ -9,78 +9,33 @@ module Lutaml
9
9
  module Qea
10
10
  module Factory
11
11
  class GeneralizationBuilder < BaseTransformer
12
- def load_generalization(object_id, visited = Set.new, is_leaf = true) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity,Style/OptionalBooleanParameter
12
+ def load_generalization(object_id, visited = Set.new, is_leaf = true) # rubocop:disable Style/OptionalBooleanParameter
13
13
  return nil if object_id.nil?
14
-
15
- if visited.include?(object_id)
16
- warn "Circular inheritance detected for object_id #{object_id}, " \
17
- "stopping recursion"
18
- return nil
19
- end
14
+ return nil if circular_inheritance?(object_id, visited)
20
15
 
21
16
  visited = visited.dup.add(object_id)
22
17
 
23
18
  current_obj = find_object_by_id(object_id)
24
19
  return nil unless current_obj
25
20
 
26
- ea_connector = database.connectors_for_object(object_id)
27
- .find { |c| c.generalization? && c.start_object_id == object_id }
28
-
29
- gen_transformer = GeneralizationTransformer.new(database)
30
- generalization = if ea_connector.nil?
31
- gen_transformer.transform(nil, current_obj)
32
- else
33
- gen_transformer.transform(ea_connector,
34
- current_obj)
35
- end
21
+ generalization = build_generalization(object_id, current_obj)
36
22
  return nil unless generalization
37
23
 
38
- current_attrs = load_attributes(object_id)
39
- current_assoc_attrs = AssociationBuilder.new(database)
40
- .load_association_attributes(object_id)
41
- general_attrs = convert_to_general_attributes(
42
- current_attrs + current_assoc_attrs,
43
- )
44
-
45
- upper_klass = generalization.general_upper_klass
46
- gen_name = generalization.general_name
47
- general_attrs.each do |attr|
48
- attr.gen_name = gen_name
49
- name_ns = case attr.type_ns
50
- when "core", "gml"
51
- upper_klass
52
- else
53
- attr.type_ns
54
- end
55
- attr.name_ns = name_ns || upper_klass
56
- end
57
-
58
- generalization.general_attributes = general_attrs
59
- .sort_by { |a| [a.name.to_s, a.id] }
24
+ populate_generalization_attrs(generalization, object_id)
25
+ populate_parent_generalization(generalization,
26
+ ea_connector_for(object_id), visited)
60
27
 
61
- generalization.attributes = transform_general_attributes(
62
- generalization,
63
- )
28
+ collect_inherited_properties(generalization) if is_leaf && generalization.has_general
64
29
 
65
- generalization.owned_props = generalization.attributes
66
- .reject(&:has_association)
67
- generalization.assoc_props = generalization.attributes
68
- .select(&:has_association)
69
-
70
- parent_object_id = ea_connector&.end_object_id
71
- if parent_object_id
72
- parent_gen = load_generalization(parent_object_id, visited, false)
73
- if parent_gen
74
- generalization.general = parent_gen
75
- generalization.has_general = true
76
- end
77
- end
30
+ generalization
31
+ end
78
32
 
79
- if is_leaf && generalization.has_general
80
- collect_inherited_properties(generalization)
81
- end
33
+ def circular_inheritance?(object_id, visited)
34
+ return false unless visited.include?(object_id)
82
35
 
83
- generalization
36
+ warn "Circular inheritance detected for object_id #{object_id}, " \
37
+ "stopping recursion"
38
+ true
84
39
  end
85
40
 
86
41
  def load_association_generalizations(object_id)
@@ -90,55 +45,155 @@ module Lutaml
90
45
  .select { |c| c.generalization? && c.start_object_id == object_id }
91
46
 
92
47
  gen_connectors.filter_map do |ea_connector|
93
- guid = ea_connector.ea_guid
94
- parent_object_id = ea_connector.end_object_id
48
+ build_assoc_generalization(ea_connector)
49
+ end
50
+ end
95
51
 
96
- parent_obj = find_object_by_id(parent_object_id)
97
- next unless parent_obj
52
+ def convert_to_general_attributes(attributes)
53
+ attributes.map { |attr| to_general_attribute(attr) }
54
+ end
98
55
 
99
- Lutaml::Uml::AssociationGeneralization.new.tap do |ag|
100
- ag.id = normalize_guid_to_xmi_format(guid, "EAID")
101
- ag.type = "uml:Generalization"
102
- ag.general = normalize_guid_to_xmi_format(parent_obj.ea_guid,
103
- "EAID")
104
- end
56
+ def convert_to_top_element_attributes(attributes)
57
+ attributes.map { |attr| to_top_element_attribute(attr) }
58
+ end
59
+
60
+ def to_general_attribute(attr)
61
+ base = base_attr_hash(attr)
62
+ Lutaml::Uml::GeneralAttribute.new.tap do |gen_attr|
63
+ base.each { |k, v| gen_attr.public_send(:"#{k}=", v) }
64
+ gen_attr.is_derived = !!attr.is_derived
65
+ gen_attr.has_association = !!attr.association
105
66
  end
106
67
  end
107
68
 
108
- def convert_to_general_attributes(attributes)
109
- attributes.map do |attr|
110
- Lutaml::Uml::GeneralAttribute.new.tap do |gen_attr|
111
- gen_attr.id = attr.id
112
- gen_attr.name = attr.name
113
- gen_attr.type = attr.type
114
- gen_attr.xmi_id = attr.xmi_id
115
- gen_attr.is_derived = !!attr.is_derived
116
- gen_attr.cardinality = attr.cardinality
117
- gen_attr.definition = attr.definition&.strip
118
- gen_attr.association = attr.association
119
- gen_attr.has_association = !!attr.association
120
- gen_attr.type_ns = attr.type_ns
69
+ def to_top_element_attribute(attr)
70
+ base = base_attr_hash(attr)
71
+ Lutaml::Uml::TopElementAttribute.new.tap do |top_attr|
72
+ base.each { |k, v| top_attr.public_send(:"#{k}=", v) }
73
+ top_attr.is_derived = !!attr.is_derived
74
+ end
75
+ end
76
+
77
+ def base_attr_hash(attr)
78
+ {
79
+ id: attr.id,
80
+ name: attr.name,
81
+ type: attr.type,
82
+ xmi_id: attr.xmi_id,
83
+ cardinality: attr.cardinality,
84
+ definition: attr.definition&.strip,
85
+ association: attr.association,
86
+ type_ns: attr.type_ns,
87
+ }
88
+ end
89
+
90
+ private
91
+
92
+ def tag_ancestor_attributes(gen, level)
93
+ [gen.general_attributes, gen.attributes].each do |attr_list|
94
+ attr_list&.each do |attr|
95
+ attr.upper_klass = gen.general_upper_klass
96
+ attr.level = level
121
97
  end
122
98
  end
123
99
  end
124
100
 
125
- def convert_to_top_element_attributes(attributes)
126
- attributes.map do |attr|
127
- Lutaml::Uml::TopElementAttribute.new.tap do |top_attr|
128
- top_attr.id = attr.id
129
- top_attr.name = attr.name
130
- top_attr.type = attr.type
131
- top_attr.xmi_id = attr.xmi_id
132
- top_attr.cardinality = attr.cardinality
133
- top_attr.definition = attr.definition&.strip
134
- top_attr.association = attr.association
135
- top_attr.type_ns = attr.type_ns
136
- top_attr.is_derived = !!attr.is_derived
101
+ def collect_ancestor_attrs(gen, level, inherited_props,
102
+ inherited_assoc_props)
103
+ gen.attributes.reverse_each do |attr|
104
+ inherited_attr = attr.dup
105
+ inherited_attr.upper_klass = gen.general_upper_klass
106
+ inherited_attr.gen_name = gen.general_name
107
+ inherited_attr.level = level
108
+
109
+ if attr.has_association
110
+ inherited_assoc_props << inherited_attr
111
+ else
112
+ inherited_props << inherited_attr
137
113
  end
138
114
  end
139
115
  end
140
116
 
141
- private
117
+ def build_assoc_generalization(ea_connector)
118
+ parent_obj = find_object_by_id(ea_connector.end_object_id)
119
+ return nil unless parent_obj
120
+
121
+ Lutaml::Uml::AssociationGeneralization.new.tap do |ag|
122
+ ag.id = normalize_guid_to_xmi_format(ea_connector.ea_guid, "EAID")
123
+ ag.type = "uml:Generalization"
124
+ ag.general = normalize_guid_to_xmi_format(parent_obj.ea_guid,
125
+ "EAID")
126
+ end
127
+ end
128
+
129
+ def resolve_name_ns(type_ns, upper_klass)
130
+ ns = case type_ns
131
+ when "core", "gml"
132
+ upper_klass
133
+ else
134
+ type_ns
135
+ end
136
+ ns || upper_klass
137
+ end
138
+
139
+ def build_generalization(object_id, current_obj)
140
+ ea_connector = ea_connector_for(object_id)
141
+ gen_transformer = GeneralizationTransformer.new(database)
142
+ if ea_connector.nil?
143
+ gen_transformer.transform(nil, current_obj)
144
+ else
145
+ gen_transformer.transform(ea_connector, current_obj)
146
+ end
147
+ end
148
+
149
+ def ea_connector_for(object_id)
150
+ database.connectors_for_object(object_id)
151
+ .find { |c| c.generalization? && c.start_object_id == object_id }
152
+ end
153
+
154
+ def populate_generalization_attrs(generalization, object_id)
155
+ general_attrs = build_general_attrs(object_id)
156
+ apply_namespace_to_attrs(general_attrs, generalization)
157
+
158
+ generalization.general_attributes = general_attrs
159
+ .sort_by { |a| [a.name.to_s, a.id] }
160
+
161
+ generalization.attributes = transform_general_attributes(
162
+ generalization,
163
+ )
164
+
165
+ generalization.owned_props = generalization.attributes
166
+ .reject(&:has_association)
167
+ generalization.assoc_props = generalization.attributes
168
+ .select(&:has_association)
169
+ end
170
+
171
+ def build_general_attrs(object_id)
172
+ current_attrs = load_attributes(object_id)
173
+ current_assoc_attrs = AssociationBuilder.new(database)
174
+ .load_association_attributes(object_id)
175
+ convert_to_general_attributes(current_attrs + current_assoc_attrs)
176
+ end
177
+
178
+ def apply_namespace_to_attrs(general_attrs, generalization)
179
+ upper_klass = generalization.general_upper_klass
180
+ general_attrs.each do |attr|
181
+ attr.gen_name = generalization.general_name
182
+ attr.name_ns = resolve_name_ns(attr.type_ns, upper_klass)
183
+ end
184
+ end
185
+
186
+ def populate_parent_generalization(generalization, ea_connector,
187
+ visited)
188
+ parent_object_id = ea_connector&.end_object_id
189
+ return unless parent_object_id
190
+
191
+ parent_gen = load_generalization(parent_object_id, visited, false)
192
+ return unless parent_gen
193
+
194
+ generalization.general = parent_gen
195
+ generalization.has_general = true
196
+ end
142
197
 
143
198
  def load_attributes(object_id)
144
199
  return [] if object_id.nil?
@@ -152,18 +207,10 @@ module Lutaml
152
207
  def transform_general_attributes(generalization)
153
208
  upper_klass = generalization.general_upper_klass
154
209
  gen_name = generalization.general_name
155
- gen_attrs = generalization.general_attributes
156
210
 
157
- gen_attrs.map do |attr|
211
+ generalization.general_attributes.map do |attr|
158
212
  transformed = attr.dup
159
- name_ns = case attr.type_ns
160
- when "core", "gml"
161
- upper_klass
162
- else
163
- attr.type_ns
164
- end
165
- name_ns = upper_klass if name_ns.nil?
166
- transformed.name_ns = name_ns
213
+ transformed.name_ns = resolve_name_ns(attr.type_ns, upper_klass)
167
214
  transformed.gen_name = gen_name
168
215
  transformed.name = "" if transformed.name.nil?
169
216
  transformed
@@ -177,26 +224,9 @@ module Lutaml
177
224
 
178
225
  current_gen = generalization.general
179
226
  while current_gen
180
- [current_gen.general_attributes,
181
- current_gen.attributes].each do |attr_list|
182
- attr_list&.each do |attr|
183
- attr.upper_klass = current_gen.general_upper_klass
184
- attr.level = level
185
- end
186
- end
187
-
188
- current_gen.attributes.reverse_each do |attr|
189
- inherited_attr = attr.dup
190
- inherited_attr.upper_klass = current_gen.general_upper_klass
191
- inherited_attr.gen_name = current_gen.general_name
192
- inherited_attr.level = level
193
-
194
- if attr.has_association
195
- inherited_assoc_props << inherited_attr
196
- else
197
- inherited_props << inherited_attr
198
- end
199
- end
227
+ tag_ancestor_attributes(current_gen, level)
228
+ collect_ancestor_attrs(current_gen, level, inherited_props,
229
+ inherited_assoc_props)
200
230
 
201
231
  level += 1
202
232
  current_gen = current_gen.general
@@ -12,21 +12,27 @@ module Lutaml
12
12
  return nil if ea_guid.nil?
13
13
  return nil unless @database.xrefs
14
14
 
15
- xref = @database.xrefs.find do |x|
15
+ xref = find_stereotype_xref(ea_guid)
16
+ return nil unless xref
17
+
18
+ extract_stereotype_name(xref.description)
19
+ end
20
+
21
+ private
22
+
23
+ def find_stereotype_xref(ea_guid)
24
+ @database.xrefs.find do |x|
16
25
  x.client == ea_guid && x.name == "Stereotypes" &&
17
26
  x.type == "element property"
18
27
  end
28
+ end
19
29
 
20
- return nil unless xref
21
-
22
- description = xref.description
30
+ def extract_stereotype_name(description)
23
31
  return nil if description.nil? || description.empty?
24
32
 
25
33
  if description =~ /@STEREO;Name=([^;]+);/
26
- return Regexp.last_match(1)
34
+ Regexp.last_match(1)
27
35
  end
28
-
29
- nil
30
36
  end
31
37
  end
32
38
  end