lutaml 0.10.4 → 0.10.6

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +10 -0
  4. data/.rubocop_todo.yml +218 -94
  5. data/TODO.cleanups/01-resolve-production-todos.md +65 -0
  6. data/TODO.cleanups/02-reduce-metrics-offenses.md +37 -0
  7. data/TODO.cleanups/03-reduce-rspec-multiple-expectations.md +54 -0
  8. data/TODO.cleanups/04-reduce-rspec-example-length.md +45 -0
  9. data/TODO.cleanups/05-replace-marshal-load.md +37 -0
  10. data/TODO.cleanups/06-replace-eval-in-tests.md +41 -0
  11. data/TODO.cleanups/07-fix-lint-offenses.md +74 -0
  12. data/TODO.cleanups/08-reduce-memoized-helpers-and-nesting.md +43 -0
  13. data/TODO.cleanups/09-reduce-verified-doubles-and-rspec-style.md +57 -0
  14. data/TODO.cleanups/10-split-large-files.md +47 -0
  15. data/bin/console +0 -1
  16. data/exe/lutaml +1 -0
  17. data/lib/lutaml/cli/element_identifier.rb +3 -6
  18. data/lib/lutaml/cli/interactive_shell/bookmark_commands.rb +88 -0
  19. data/lib/lutaml/cli/interactive_shell/command_base.rb +32 -0
  20. data/lib/lutaml/cli/interactive_shell/export_handler.rb +67 -0
  21. data/lib/lutaml/cli/interactive_shell/help_display.rb +114 -0
  22. data/lib/lutaml/cli/interactive_shell/navigation_commands.rb +135 -0
  23. data/lib/lutaml/cli/interactive_shell/query_commands.rb +185 -0
  24. data/lib/lutaml/cli/interactive_shell.rb +116 -802
  25. data/lib/lutaml/cli/uml/build_command.rb +5 -5
  26. data/lib/lutaml/cli/uml/verify_command.rb +0 -1
  27. data/lib/lutaml/converter/xmi_to_uml.rb +3 -153
  28. data/lib/lutaml/converter/xmi_to_uml_generalization.rb +193 -0
  29. data/lib/lutaml/formatter/graphviz.rb +1 -2
  30. data/lib/lutaml/qea/database.rb +1 -47
  31. data/lib/lutaml/qea/factory/association_builder.rb +188 -0
  32. data/lib/lutaml/qea/factory/base_transformer.rb +0 -1
  33. data/lib/lutaml/qea/factory/class_transformer.rb +40 -590
  34. data/lib/lutaml/qea/factory/diagram_transformer.rb +0 -3
  35. data/lib/lutaml/qea/factory/generalization_builder.rb +211 -0
  36. data/lib/lutaml/qea/factory/package_transformer.rb +1 -2
  37. data/lib/lutaml/qea/factory/stereotype_loader.rb +34 -0
  38. data/lib/lutaml/qea/lookup_indexes.rb +54 -0
  39. data/lib/lutaml/qea/models/ea_datatype.rb +0 -2
  40. data/lib/lutaml/qea/validation/validation_engine.rb +0 -2
  41. data/lib/lutaml/uml/has_members.rb +0 -1
  42. data/lib/lutaml/uml/inheritance_walker.rb +92 -0
  43. data/lib/lutaml/uml/model_helpers.rb +129 -0
  44. data/lib/lutaml/uml/node/attribute.rb +3 -1
  45. data/lib/lutaml/uml/node/class_node.rb +3 -3
  46. data/lib/lutaml/uml/operation.rb +2 -0
  47. data/lib/lutaml/uml_repository/class_lookup_index.rb +40 -0
  48. data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +179 -0
  49. data/lib/lutaml/uml_repository/exporters/markdown/formatting.rb +36 -0
  50. data/lib/lutaml/uml_repository/exporters/markdown/index_page_builder.rb +73 -0
  51. data/lib/lutaml/uml_repository/exporters/markdown/link_resolver.rb +40 -0
  52. data/lib/lutaml/uml_repository/exporters/markdown/package_page_builder.rb +107 -0
  53. data/lib/lutaml/uml_repository/exporters/markdown_exporter.rb +26 -538
  54. data/lib/lutaml/uml_repository/index_builder.rb +3 -271
  55. data/lib/lutaml/uml_repository/index_builders/association_index.rb +141 -0
  56. data/lib/lutaml/uml_repository/index_builders/class_index.rb +94 -0
  57. data/lib/lutaml/uml_repository/index_builders/package_index.rb +57 -0
  58. data/lib/lutaml/uml_repository/package_exporter.rb +10 -20
  59. data/lib/lutaml/uml_repository/package_loader.rb +37 -17
  60. data/lib/lutaml/uml_repository/repository/deprecated.rb +39 -0
  61. data/lib/lutaml/uml_repository/repository/loader.rb +112 -0
  62. data/lib/lutaml/uml_repository/repository.rb +7 -57
  63. data/lib/lutaml/uml_repository/static_site/association_serialization.rb +142 -0
  64. data/lib/lutaml/uml_repository/static_site/configuration.rb +0 -2
  65. data/lib/lutaml/uml_repository/static_site/data_transformer.rb +52 -873
  66. data/lib/lutaml/uml_repository/static_site/generator.rb +29 -8
  67. data/lib/lutaml/uml_repository/static_site/search_index_builder.rb +1 -4
  68. data/lib/lutaml/uml_repository/static_site/serializers/attribute_serializer.rb +78 -0
  69. data/lib/lutaml/uml_repository/static_site/serializers/class_serializer.rb +124 -0
  70. data/lib/lutaml/uml_repository/static_site/serializers/diagram_serializer.rb +60 -0
  71. data/lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb +258 -0
  72. data/lib/lutaml/uml_repository/static_site/serializers/metadata_builder.rb +48 -0
  73. data/lib/lutaml/uml_repository/static_site/serializers/operation_serializer.rb +57 -0
  74. data/lib/lutaml/uml_repository/static_site/serializers/package_serializer.rb +94 -0
  75. data/lib/lutaml/uml_repository/static_site/serializers/package_tree_builder.rb +93 -0
  76. data/lib/lutaml/version.rb +1 -1
  77. data/lib/lutaml/xmi/liquid_drops/association_drop.rb +13 -35
  78. data/lib/lutaml/xmi/liquid_drops/attribute_drop.rb +12 -18
  79. data/lib/lutaml/xmi/liquid_drops/cardinality_drop.rb +14 -6
  80. data/lib/lutaml/xmi/liquid_drops/connector_drop.rb +0 -3
  81. data/lib/lutaml/xmi/liquid_drops/constraint_drop.rb +1 -3
  82. data/lib/lutaml/xmi/liquid_drops/data_type_drop.rb +13 -70
  83. data/lib/lutaml/xmi/liquid_drops/dependency_drop.rb +2 -5
  84. data/lib/lutaml/xmi/liquid_drops/diagram_drop.rb +5 -11
  85. data/lib/lutaml/xmi/liquid_drops/enum_drop.rb +8 -16
  86. data/lib/lutaml/xmi/liquid_drops/enum_owned_literal_drop.rb +3 -9
  87. data/lib/lutaml/xmi/liquid_drops/generalization_attribute_drop.rb +11 -13
  88. data/lib/lutaml/xmi/liquid_drops/generalization_drop.rb +27 -85
  89. data/lib/lutaml/xmi/liquid_drops/klass_drop.rb +39 -91
  90. data/lib/lutaml/xmi/liquid_drops/operation_drop.rb +3 -9
  91. data/lib/lutaml/xmi/liquid_drops/package_drop.rb +16 -44
  92. data/lib/lutaml/xmi/liquid_drops/root_drop.rb +3 -11
  93. data/lib/lutaml/xmi/liquid_drops/source_target_drop.rb +2 -5
  94. data/lib/lutaml/xmi/parsers/xmi_base.rb +2 -749
  95. data/lib/lutaml/xmi/parsers/xmi_class_members.rb +45 -0
  96. data/lib/lutaml/xmi/parsers/xmi_connector.rb +251 -0
  97. data/lib/lutaml/xmi/parsers/xml.rb +7 -120
  98. data/lib/lutaml/xmi/xmi_lookup_service.rb +42 -0
  99. data/lib/lutaml.rb +0 -1
  100. metadata +48 -21
  101. data/lib/lutaml/cli/commands/base_command.rb +0 -118
  102. data/lib/lutaml/command_line.rb +0 -272
  103. data/lib/lutaml/sysml/allocate.rb +0 -9
  104. data/lib/lutaml/sysml/allocated.rb +0 -9
  105. data/lib/lutaml/sysml/binding_connector.rb +0 -9
  106. data/lib/lutaml/sysml/block.rb +0 -32
  107. data/lib/lutaml/sysml/constraint_block.rb +0 -14
  108. data/lib/lutaml/sysml/copy.rb +0 -8
  109. data/lib/lutaml/sysml/derive_requirement.rb +0 -9
  110. data/lib/lutaml/sysml/nested_connector_end.rb +0 -13
  111. data/lib/lutaml/sysml/refine.rb +0 -9
  112. data/lib/lutaml/sysml/requirement.rb +0 -44
  113. data/lib/lutaml/sysml/requirement_related.rb +0 -9
  114. data/lib/lutaml/sysml/satisfy.rb +0 -9
  115. data/lib/lutaml/sysml/test_case.rb +0 -25
  116. data/lib/lutaml/sysml/trace.rb +0 -9
  117. data/lib/lutaml/sysml/verify.rb +0 -8
  118. data/lib/lutaml/sysml/xmi_file.rb +0 -486
  119. data/lib/lutaml/sysml.rb +0 -11
@@ -69,8 +69,8 @@ module Lutaml
69
69
  desc: "Load metadata from YAML file"
70
70
 
71
71
  # Build options
72
- thor_class.option :format, type: :string, default: "marshal",
73
- desc: "Serialization format (marshal|yaml)"
72
+ thor_class.option :format, type: :string, default: "yaml",
73
+ desc: "Serialization format (yaml)"
74
74
  thor_class.option :validate, type: :boolean, default: true,
75
75
  desc: "Validate before building"
76
76
  thor_class.option :strict, type: :boolean, default: false,
@@ -166,7 +166,7 @@ module Lutaml
166
166
  # Export to package with metadata
167
167
  export_options = {
168
168
  serialization_format: (
169
- options[:format] || options["format"] || "marshal"
169
+ options[:format] || options["format"] || "yaml"
170
170
  ).to_sym,
171
171
  metadata: metadata,
172
172
  }
@@ -213,7 +213,7 @@ module Lutaml
213
213
  name: options[:name] || File.basename(options[:output] || "model",
214
214
  ".lur"),
215
215
  version: options[:version] || "1.0",
216
- serialization_format: (options[:format] || "marshal").to_s,
216
+ serialization_format: (options[:format] || "yaml").to_s,
217
217
  }
218
218
 
219
219
  # Add optional fields if provided
@@ -273,7 +273,7 @@ module Lutaml
273
273
 
274
274
  # Ensure serialization_format is set
275
275
  metadata_hash["serialization_format"] ||= (
276
- options[:format] || "marshal"
276
+ options[:format] || "yaml"
277
277
  ).to_s
278
278
 
279
279
  Lutaml::UmlRepository::PackageMetadata.from_hash(metadata_hash)
@@ -92,7 +92,6 @@ module Lutaml
92
92
  report_data = result.to_report
93
93
  ext = File.extname(report_path).downcase
94
94
  report_format = case ext
95
- when ".json" then :json
96
95
  when ".yaml", ".yml" then :yaml
97
96
  else :json
98
97
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "xmi_to_uml_generalization"
4
+
3
5
  module Lutaml
4
6
  module Converter
5
7
  module XmiToUml
@@ -199,159 +201,6 @@ module Lutaml
199
201
  end
200
202
  end
201
203
 
202
- def create_uml_attributes(uml_general_obj) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
203
- upper_klass = uml_general_obj.general_upper_klass
204
- gen_attrs = uml_general_obj.general_attributes
205
- gen_name = uml_general_obj.general_name
206
-
207
- gen_attrs&.each do |i|
208
- name_ns = case i.type_ns
209
- when "core", "gml"
210
- upper_klass
211
- else
212
- i.type_ns
213
- end
214
- name_ns = upper_klass if name_ns.nil?
215
-
216
- i.name_ns = name_ns
217
- i.gen_name = gen_name
218
- i.name = "" if i.name.nil?
219
- end
220
-
221
- gen_attrs
222
- end
223
-
224
- def create_uml_generalization(klass) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
225
- uml_general_obj, next_general_node_id = get_uml_general(klass.id)
226
- return uml_general_obj unless next_general_node_id
227
-
228
- if uml_general_obj.general
229
- inherited_props = []
230
- inherited_assoc_props = []
231
- level = 0
232
-
233
- loop_general_item(
234
- uml_general_obj.general,
235
- level,
236
- inherited_props,
237
- inherited_assoc_props,
238
- )
239
- uml_general_obj.inherited_props = inherited_props.reverse
240
- uml_general_obj.inherited_assoc_props = inherited_assoc_props.reverse
241
- end
242
-
243
- uml_general_obj
244
- end
245
-
246
- def get_uml_general(general_id) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
247
- general_node = find_packaged_element_by_id(general_id)
248
- return [] unless general_node
249
-
250
- general_node_attrs = get_uml_general_attributes(general_node)
251
- general_upper_klass = find_upper_level_packaged_element(general_id)
252
- next_general_node_id = get_next_general_node_id(general_node)
253
-
254
- uml_general = ::Lutaml::Uml::Generalization.new.tap do |gen|
255
- gen.general_id = general_id
256
- gen.general_name = general_node.name
257
- gen.general_attributes = general_node_attrs
258
- gen.general_upper_klass = general_upper_klass&.name
259
- gen.name = general_node.name
260
- gen.type = general_node.type
261
- gen.definition = lookup_element_prop_documentation(general_id)
262
- gen_st = doc_node_attribute_value(general_id, "stereotype")
263
- gen.stereotype = [gen_st] if gen_st
264
-
265
- if next_general_node_id
266
- gen.general = set_uml_generalization(
267
- next_general_node_id,
268
- )
269
- gen.has_general = true
270
- gen.general_id = general_node.id
271
- gen.general_name = general_node.name
272
- end
273
- end
274
-
275
- uml_general.attributes = create_uml_attributes(uml_general)
276
- uml_general.owned_props = uml_general.attributes.select do |attr|
277
- attr.association.nil?
278
- end
279
- uml_general.assoc_props = uml_general.attributes.select(&:association)
280
-
281
- [uml_general, next_general_node_id]
282
- end
283
-
284
- def get_uml_general_attributes(general_node) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
285
- attrs = create_uml_class_attributes(general_node)
286
-
287
- attrs.sort_by { |a| [a.name.to_s, a.id] }.map do |attr|
288
- ::Lutaml::Uml::GeneralAttribute.new.tap do |gen_attr|
289
- gen_attr.id = attr.id
290
- gen_attr.name = attr.name
291
- gen_attr.type = attr.type
292
- gen_attr.xmi_id = attr.xmi_id
293
- gen_attr.is_derived = !!attr.is_derived
294
- gen_attr.cardinality = attr.cardinality
295
- gen_attr.definition = attr.definition&.strip
296
- gen_attr.association = attr.association
297
- gen_attr.has_association = !!attr.association
298
- gen_attr.type_ns = attr.type_ns
299
- end
300
- end
301
- end
302
-
303
- def set_uml_generalization(general_id)
304
- uml_general_obj, next_general_node_id = get_uml_general(general_id)
305
-
306
- if next_general_node_id
307
- uml_general_obj.general = set_uml_generalization(
308
- next_general_node_id,
309
- )
310
- uml_general_obj.has_general = true
311
- end
312
-
313
- uml_general_obj
314
- end
315
-
316
- def loop_general_item( # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
317
- general_item, level, inherited_props, inherited_assoc_props
318
- )
319
- gen_upper_klass = general_item.general_upper_klass
320
- gen_name = general_item.general_name
321
-
322
- # reverse the order to show super class first
323
- general_item.attributes.reverse_each do |attr|
324
- attr.upper_klass = gen_upper_klass
325
- attr.gen_name = gen_name
326
- attr.level = level
327
-
328
- if attr.association
329
- inherited_assoc_props << attr
330
- else
331
- inherited_props << attr
332
- end
333
- end
334
-
335
- if general_item&.has_general && general_item.general
336
- level += 1
337
- loop_general_item(
338
- general_item.general, level, inherited_props, inherited_assoc_props
339
- )
340
- end
341
- end
342
-
343
- def create_uml_assoc_generalizations(klass) # rubocop:disable Metrics/AbcSize
344
- return [] if klass.generalization.nil? || klass.generalization.empty?
345
-
346
- klass.generalization.map do |gen|
347
- ::Lutaml::Uml::AssociationGeneralization.new.tap do |assoc_gen|
348
- assoc_gen.id = gen.id
349
- assoc_gen.type = gen.type
350
- assoc_gen.general = gen.general
351
- end
352
- end
353
- end
354
-
355
204
  def create_uml_associations(xmi_id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
356
205
  matched_element = xmi_index&.find_element(xmi_id)
357
206
 
@@ -424,6 +273,7 @@ module Lutaml
424
273
 
425
274
  if !operation.respond_to?(:association) || operation.association.nil?
426
275
  ::Lutaml::Uml::Operation.new.tap do |op|
276
+ op.id = operation.id
427
277
  op.xmi_id = uml_type_idref
428
278
  op.name = operation.name
429
279
  op.definition = lookup_attribute_documentation(operation.id)
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Converter
5
+ module XmiToUml
6
+ # Generalization-related conversion methods for XMI → UML
7
+ #
8
+ # These methods handle the recursive generalization hierarchy:
9
+ # creating UML generalization objects, walking the chain of
10
+ # parent classes, and collecting inherited properties.
11
+
12
+ def create_uml_attributes(uml_general_obj) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
13
+ upper_klass = uml_general_obj.general_upper_klass
14
+ gen_attrs = uml_general_obj.general_attributes
15
+ gen_name = uml_general_obj.general_name
16
+
17
+ gen_attrs&.each do |i|
18
+ name_ns = case i.type_ns
19
+ when "core", "gml"
20
+ upper_klass
21
+ else
22
+ i.type_ns
23
+ end
24
+ name_ns = upper_klass if name_ns.nil?
25
+
26
+ i.name_ns = name_ns
27
+ i.gen_name = gen_name
28
+ i.name = "" if i.name.nil?
29
+ end
30
+
31
+ gen_attrs
32
+ end
33
+
34
+ def create_uml_generalization(klass) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
35
+ uml_general_obj, next_general_node_id = get_uml_general(klass.id)
36
+ return uml_general_obj unless next_general_node_id
37
+
38
+ if uml_general_obj.general
39
+ inherited_props = []
40
+ inherited_assoc_props = []
41
+ level = 0
42
+
43
+ loop_general_item(
44
+ uml_general_obj.general,
45
+ level,
46
+ inherited_props,
47
+ inherited_assoc_props,
48
+ )
49
+ uml_general_obj.inherited_props = inherited_props.reverse
50
+ uml_general_obj.inherited_assoc_props = inherited_assoc_props.reverse
51
+ end
52
+
53
+ uml_general_obj
54
+ end
55
+
56
+ def get_next_general_node_id(general_node)
57
+ general_node.generalization.first&.general
58
+ end
59
+
60
+ def get_uml_general(general_id) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
61
+ general_node = find_packaged_element_by_id(general_id)
62
+ return [] unless general_node
63
+
64
+ general_node_attrs = get_uml_general_attributes(general_node)
65
+ general_upper_klass = find_upper_level_packaged_element(general_id)
66
+ next_general_node_id = get_next_general_node_id(general_node)
67
+
68
+ uml_general = build_uml_general_node(
69
+ general_id, general_node, general_node_attrs,
70
+ general_upper_klass, next_general_node_id
71
+ )
72
+
73
+ assign_general_properties(uml_general)
74
+
75
+ [uml_general, next_general_node_id]
76
+ end
77
+
78
+ def build_uml_general_node(general_id, general_node, attrs, upper_klass,
79
+ next_id)
80
+ gen = ::Lutaml::Uml::Generalization.new
81
+ assign_general_basic_props(gen, general_id, general_node, attrs,
82
+ upper_klass)
83
+ assign_stereotype(gen, general_id)
84
+ assign_parent_generalization(gen, general_node, next_id)
85
+ gen
86
+ end
87
+
88
+ def assign_general_basic_props(gen, general_id, general_node, attrs,
89
+ upper_klass)
90
+ gen.general_id = general_id
91
+ gen.general_name = general_node.name
92
+ gen.general_attributes = attrs
93
+ gen.general_upper_klass = upper_klass ? get_package_name(upper_klass) : nil
94
+ gen.name = general_node.name
95
+ gen.type = general_node.type
96
+ gen.definition = lookup_element_prop_documentation(general_id)
97
+ end
98
+
99
+ def assign_stereotype(gen, general_id)
100
+ gen_st = doc_node_attribute_value(general_id, "stereotype")
101
+ gen.stereotype = [gen_st] if gen_st
102
+ end
103
+
104
+ def assign_parent_generalization(gen, general_node, next_id)
105
+ return unless next_id
106
+
107
+ gen.general = set_uml_generalization(next_id)
108
+ gen.has_general = true
109
+ gen.general_id = general_node.id
110
+ gen.general_name = general_node.name
111
+ end
112
+
113
+ def assign_general_properties(uml_general)
114
+ uml_general.attributes = create_uml_attributes(uml_general)
115
+ uml_general.owned_props = uml_general.attributes.select do |attr|
116
+ attr.association.nil?
117
+ end
118
+ uml_general.assoc_props = uml_general.attributes.select(&:association)
119
+ end
120
+
121
+ def get_uml_general_attributes(general_node) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
122
+ attrs = create_uml_class_attributes(general_node)
123
+
124
+ attrs.map do |attr|
125
+ ::Lutaml::Uml::GeneralAttribute.new.tap do |gen_attr|
126
+ gen_attr.id = attr.id
127
+ gen_attr.name = attr.name
128
+ gen_attr.type = attr.type
129
+ gen_attr.xmi_id = attr.xmi_id
130
+ gen_attr.is_derived = !!attr.is_derived
131
+ gen_attr.cardinality = attr.cardinality
132
+ gen_attr.definition = attr.definition&.strip
133
+ gen_attr.association = attr.association
134
+ gen_attr.has_association = !!attr.association
135
+ gen_attr.type_ns = attr.type_ns
136
+ end
137
+ end
138
+ end
139
+
140
+ def set_uml_generalization(general_id)
141
+ uml_general_obj, next_general_node_id = get_uml_general(general_id)
142
+
143
+ if next_general_node_id
144
+ uml_general_obj.general = set_uml_generalization(
145
+ next_general_node_id,
146
+ )
147
+ uml_general_obj.has_general = true
148
+ end
149
+
150
+ uml_general_obj
151
+ end
152
+
153
+ def loop_general_item( # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity
154
+ general_item, level, inherited_props, inherited_assoc_props
155
+ )
156
+ gen_upper_klass = general_item.general_upper_klass
157
+ gen_name = general_item.general_name
158
+
159
+ # reverse the order to show super class first
160
+ general_item.attributes.reverse_each do |attr|
161
+ attr.upper_klass = gen_upper_klass
162
+ attr.gen_name = gen_name
163
+ attr.level = level
164
+
165
+ if attr.association
166
+ inherited_assoc_props << attr
167
+ else
168
+ inherited_props << attr
169
+ end
170
+ end
171
+
172
+ if general_item&.has_general && general_item.general
173
+ level += 1
174
+ loop_general_item(
175
+ general_item.general, level, inherited_props, inherited_assoc_props
176
+ )
177
+ end
178
+ end
179
+
180
+ def create_uml_assoc_generalizations(klass) # rubocop:disable Metrics/AbcSize
181
+ return [] if klass.generalization.nil? || klass.generalization.empty?
182
+
183
+ klass.generalization.map do |gen|
184
+ ::Lutaml::Uml::AssociationGeneralization.new.tap do |assoc_gen|
185
+ assoc_gen.id = gen.id
186
+ assoc_gen.type = gen.type
187
+ assoc_gen.general = gen.general
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -53,8 +53,7 @@ module Lutaml
53
53
  @graph["ranksep"] = "1.2.equally"
54
54
  # Padding between nodes
55
55
  @graph["nodesep"] = "1.2.equally"
56
- # TODO: set rankdir
57
- # @graph['rankdir'] = 'BT'
56
+ @graph["rankdir"] = "BT"
58
57
 
59
58
  @edge = Attributes.new
60
59
  @edge["color"] = "gray50"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "repositories/object_repository"
4
+ require_relative "lookup_indexes"
4
5
 
5
6
  module Lutaml
6
7
  module Qea
@@ -425,53 +426,6 @@ module Lutaml
425
426
  @collections.freeze
426
427
  super
427
428
  end
428
-
429
- private
430
-
431
- # Build a group index from a collection by a given attribute.
432
- # BaseRepository overrides group_by to take a symbol (not a block),
433
- # so we use each_with_object instead.
434
- #
435
- # @param collection [Array] Collection to index
436
- # @param method [Symbol] Attribute method to group by
437
- # @param single [Boolean] If true, return single object (last match)
438
- # instead of array
439
- # @return [Hash] Group index
440
- def build_group_index(collection, method, single: false)
441
- if single
442
- collection.each_with_object({}) do |item, hash|
443
- key = item.send(method)
444
- hash[key] = item if key
445
- end
446
- else
447
- collection.each_with_object({}) do |item, hash|
448
- key = item.send(method)
449
- (hash[key] ||= []) << item if key
450
- end
451
- end
452
- end
453
-
454
- # Eagerly build all lazy lookup indexes before freezing
455
- def build_lookup_indexes
456
- @objects_by_guid = build_group_index(objects, :ea_guid, single: true)
457
- @attributes_by_object_id = build_group_index(attributes, :ea_object_id)
458
- @operations_by_object_id = build_group_index(operations, :ea_object_id)
459
- @operation_params_by_id = build_group_index(operation_params,
460
- :operationid)
461
- @connectors_by_start = build_group_index(connectors, :start_object_id)
462
- @connectors_by_end = build_group_index(connectors, :end_object_id)
463
- @packages_by_parent = build_group_index(packages, :parent_id)
464
- @objects_by_package_id = build_group_index(objects, :package_id)
465
- @diagrams_by_package_id = build_group_index(diagrams, :package_id)
466
- @diagram_objects_by_id = build_group_index(diagram_objects, :diagram_id)
467
- @diagram_links_by_id = build_group_index(diagram_links, :diagramid)
468
- # Also build hash indexes for find_* methods
469
- @packages_by_id = build_group_index(packages, :package_id, single: true)
470
- @connectors_by_id = build_group_index(connectors, :connector_id,
471
- single: true)
472
- @diagrams_by_id = build_group_index(diagrams, :diagram_id, single: true)
473
- @attributes_by_id = build_group_index(attributes, :id, single: true)
474
- end
475
429
  end
476
430
  end
477
431
  end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_transformer"
4
+
5
+ module Lutaml
6
+ module Qea
7
+ module Factory
8
+ class AssociationBuilder < BaseTransformer
9
+ ASSOC_TYPES = ["Association", "Aggregation", "Composition"].freeze
10
+
11
+ def load_class_associations(object_id, object_guid)
12
+ return [] if object_id.nil?
13
+
14
+ associations = []
15
+ normalized_owner_xmi_id = normalize_guid_to_xmi_format(object_guid,
16
+ "EAID")
17
+
18
+ assoc_connectors = database.connectors_for_object(object_id)
19
+ .select { |c| ASSOC_TYPES.include?(c.connector_type) }
20
+
21
+ assoc_connectors.each do |ea_connector|
22
+ is_start = ea_connector.start_object_id == object_id
23
+
24
+ owner_end_attribute_name = if is_start
25
+ ea_connector.destrole
26
+ else
27
+ ea_connector.sourcerole
28
+ end
29
+
30
+ if owner_end_attribute_name.nil? || owner_end_attribute_name.empty?
31
+ next
32
+ end
33
+
34
+ member_obj = if is_start
35
+ find_object_by_id(ea_connector.end_object_id)
36
+ else
37
+ find_object_by_id(ea_connector.start_object_id)
38
+ end
39
+ next unless member_obj
40
+
41
+ member_end_attribute_name = if is_start
42
+ ea_connector.sourcerole
43
+ else
44
+ ea_connector.destrole
45
+ end
46
+ if member_end_attribute_name.nil? ||
47
+ member_end_attribute_name.empty?
48
+ member_end_attribute_name = member_obj.name
49
+ end
50
+
51
+ member_cardinality_str = if is_start
52
+ ea_connector.destcard
53
+ else
54
+ ea_connector.sourcecard
55
+ end
56
+
57
+ associations << Lutaml::Uml::Association.new.tap do |assoc|
58
+ assoc.xmi_id = normalize_guid_to_xmi_format(ea_connector.ea_guid,
59
+ "EAID")
60
+ assoc.name = ea_connector.name unless ea_connector.name.nil? || ea_connector.name.empty?
61
+
62
+ assoc.owner_end = find_object_by_id(object_id)&.name
63
+ assoc.owner_end_xmi_id = normalized_owner_xmi_id
64
+ assoc.owner_end_attribute_name = owner_end_attribute_name
65
+
66
+ assoc.member_end = member_obj.name
67
+ assoc.member_end_xmi_id = normalize_guid_to_xmi_format(
68
+ member_obj.ea_guid, "EAID"
69
+ )
70
+ assoc.member_end_attribute_name = member_end_attribute_name
71
+
72
+ assoc.member_end_type = ea_connector.connector_type&.downcase
73
+
74
+ if member_cardinality_str && !member_cardinality_str.empty?
75
+ parsed = parse_cardinality(member_cardinality_str)
76
+ if parsed[:min] || parsed[:max]
77
+ assoc.member_end_cardinality = Lutaml::Uml::Cardinality.new
78
+ .tap do |card|
79
+ card.min = parsed[:min]
80
+ card.max = parsed[:max]
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ associations.compact
88
+ end
89
+
90
+ def load_association_attributes(object_id)
91
+ return [] if object_id.nil?
92
+
93
+ attributes = []
94
+ assoc_connectors = database.connectors_for_object(object_id)
95
+ .select { |c| ASSOC_TYPES.include?(c.connector_type) }
96
+ obj = find_object_by_id(object_id)
97
+ obj_pkg_name = find_package_name(obj&.package_id)
98
+
99
+ assoc_connectors.each do |ea_connector|
100
+ if ea_connector.start_object_id == object_id
101
+ next if ea_connector.destrole.nil? || ea_connector.destrole.empty?
102
+
103
+ target_obj = find_object_by_id(ea_connector.end_object_id)
104
+ next unless target_obj
105
+
106
+ target_obj_pkg_name = find_package_name(target_obj.package_id)
107
+
108
+ attributes << create_association_attribute(
109
+ name: ea_connector.destrole,
110
+ type: target_obj.name,
111
+ type_xmi_id: target_obj.ea_guid,
112
+ association_xmi_id: ea_connector.ea_guid,
113
+ cardinality: ea_connector.destcard,
114
+ definition: ea_connector.notes,
115
+ gen_name: obj.name,
116
+ name_ns: obj_pkg_name,
117
+ type_ns: target_obj_pkg_name,
118
+ is_src: false,
119
+ )
120
+ elsif ea_connector.end_object_id == object_id
121
+ next if ea_connector.sourcerole.nil? || ea_connector.sourcerole.empty?
122
+
123
+ source_obj = find_object_by_id(ea_connector.start_object_id)
124
+ next unless source_obj
125
+
126
+ source_obj_pkg_name = find_package_name(source_obj.package_id)
127
+
128
+ attributes << create_association_attribute(
129
+ name: ea_connector.sourcerole,
130
+ type: source_obj.name,
131
+ type_xmi_id: source_obj.ea_guid,
132
+ association_xmi_id: ea_connector.ea_guid,
133
+ cardinality: ea_connector.sourcecard,
134
+ definition: ea_connector.notes,
135
+ gen_name: obj.name,
136
+ name_ns: obj_pkg_name,
137
+ type_ns: source_obj_pkg_name,
138
+ )
139
+ end
140
+ end
141
+
142
+ attributes.compact
143
+ end
144
+
145
+ def create_association_attribute( # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/ParameterLists
146
+ name:, type:, type_xmi_id:,
147
+ association_xmi_id:, cardinality:, definition:,
148
+ gen_name:, name_ns:, type_ns:, is_src: true
149
+ )
150
+ Lutaml::Uml::GeneralAttribute.new.tap do |attr|
151
+ attr.name = name
152
+ attr.type = type
153
+ attr.gen_name = gen_name
154
+ attr.definition = definition
155
+ attr.xmi_id = normalize_guid_to_xmi_format(type_xmi_id, "EAID")
156
+ attr.association = normalize_guid_to_xmi_format(
157
+ association_xmi_id, "EAID"
158
+ )
159
+ attr.has_association = true
160
+ attr.id = normalize_guid_to_xmi_src_dst_format(
161
+ association_xmi_id, "EAID", is_src
162
+ )
163
+ attr.name_ns = name_ns
164
+ attr.type_ns = type_ns
165
+
166
+ if cardinality && !cardinality.empty?
167
+ parsed = parse_cardinality(cardinality)
168
+ if parsed[:min] || parsed[:max]
169
+ attr.cardinality = Lutaml::Uml::Cardinality.new.tap do |card|
170
+ card.min = parsed[:min]
171
+ card.max = parsed[:max]
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ def find_package_name(package_id)
181
+ return nil if package_id.nil?
182
+
183
+ database.find_package(package_id)&.name
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -41,7 +41,6 @@ module Lutaml
41
41
  return "public" if ea_visibility.nil? || ea_visibility.empty?
42
42
 
43
43
  case ea_visibility.downcase
44
- when "public" then "public"
45
44
  when "private" then "private"
46
45
  when "protected" then "protected"
47
46
  when "package" then "package"