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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +29 -33
- data/lib/lutaml/cli/interactive_shell/export_handler.rb +25 -17
- data/lib/lutaml/cli/interactive_shell/help_display.rb +39 -45
- data/lib/lutaml/cli/interactive_shell/navigation_commands.rb +45 -26
- data/lib/lutaml/cli/interactive_shell/query_commands.rb +73 -47
- data/lib/lutaml/cli/interactive_shell.rb +53 -27
- data/lib/lutaml/cli/tree_view_formatter.rb +11 -3
- data/lib/lutaml/converter/xmi_to_uml.rb +11 -6
- data/lib/lutaml/formatter/graphviz.rb +65 -35
- data/lib/lutaml/model_transformations/parsers/base_parser.rb +27 -29
- data/lib/lutaml/qea/factory/association_builder.rb +144 -127
- data/lib/lutaml/qea/factory/class_transformer.rb +91 -53
- data/lib/lutaml/qea/factory/ea_to_uml_factory.rb +11 -22
- data/lib/lutaml/qea/factory/enum_transformer.rb +41 -31
- data/lib/lutaml/qea/factory/generalization_builder.rb +155 -125
- data/lib/lutaml/qea/factory/stereotype_loader.rb +13 -7
- data/lib/lutaml/qea/lookup_indexes.rb +31 -13
- data/lib/lutaml/uml/inheritance_walker.rb +11 -7
- data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +33 -25
- data/lib/lutaml/uml_repository/exporters/markdown/index_page_builder.rb +17 -9
- data/lib/lutaml/uml_repository/exporters/markdown_exporter.rb +27 -20
- data/lib/lutaml/uml_repository/index_builders/association_index.rb +60 -48
- data/lib/lutaml/uml_repository/index_builders/class_index.rb +35 -24
- data/lib/lutaml/uml_repository/queries/class_query.rb +79 -48
- data/lib/lutaml/uml_repository/queries/inheritance_query.rb +42 -32
- data/lib/lutaml/uml_repository/queries/search_query.rb +93 -85
- data/lib/lutaml/uml_repository/query_dsl/conditions/package_condition.rb +9 -2
- data/lib/lutaml/uml_repository/repository/loader.rb +14 -7
- data/lib/lutaml/uml_repository/static_site/data_transformer.rb +64 -35
- data/lib/lutaml/uml_repository/static_site/search_index_builder.rb +32 -19
- data/lib/lutaml/uml_repository/static_site/serializers/class_serializer.rb +36 -20
- data/lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb +131 -105
- data/lib/lutaml/uml_repository/static_site/serializers/package_serializer.rb +15 -9
- data/lib/lutaml/uml_repository/static_site/serializers/package_tree_builder.rb +38 -24
- data/lib/lutaml/version.rb +1 -1
- data/lib/lutaml/xmi/liquid_drops/klass_drop.rb +34 -18
- data/lib/lutaml/xmi/parsers/xmi_connector.rb +35 -23
- metadata +2 -9
- data/TODO.cleanups/01-resolve-production-todos.md +0 -65
- data/TODO.cleanups/02-reduce-metrics-offenses.md +0 -37
- data/TODO.cleanups/03-reduce-rspec-multiple-expectations.md +0 -54
- data/TODO.cleanups/04-reduce-rspec-example-length.md +0 -45
- data/TODO.cleanups/07-fix-lint-offenses.md +0 -74
- data/TODO.cleanups/08-reduce-memoized-helpers-and-nesting.md +0 -43
- data/TODO.cleanups/09-reduce-verified-doubles-and-rspec-style.md +0 -57
|
@@ -275,13 +275,21 @@ module Lutaml
|
|
|
275
275
|
|
|
276
276
|
# Determine class type (class, interface, enumeration)
|
|
277
277
|
def determine_class_type(klass)
|
|
278
|
-
return :enumeration if
|
|
279
|
-
return :interface if
|
|
280
|
-
Array(klass.stereotype).any? { |s| s&.downcase == "interface" }
|
|
278
|
+
return :enumeration if enumeration?(klass)
|
|
279
|
+
return :interface if interface?(klass)
|
|
281
280
|
|
|
282
281
|
:class
|
|
283
282
|
end
|
|
284
283
|
|
|
284
|
+
def enumeration?(klass)
|
|
285
|
+
klass.class.name&.include?("Enum")
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def interface?(klass)
|
|
289
|
+
klass.is_a?(Lutaml::Uml::TopElement) &&
|
|
290
|
+
Array(klass.stereotype).any? { |s| s&.downcase == "interface" }
|
|
291
|
+
end
|
|
292
|
+
|
|
285
293
|
# Find package path for a package object
|
|
286
294
|
def find_package_path(repository, package)
|
|
287
295
|
repository.indexes[:package_paths].each do |path, pkg|
|
|
@@ -148,12 +148,17 @@ module Lutaml
|
|
|
148
148
|
# Lazy-built hash index for O(1) diagram lookups by package
|
|
149
149
|
# @return [Hash] Mapping of package_id => [diagrams]
|
|
150
150
|
def diagram_lookup
|
|
151
|
-
@diagram_lookup ||=
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
@diagram_lookup ||= build_diagram_index
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def build_diagram_index
|
|
155
|
+
idx = Hash.new { |h, k| h[k] = [] }
|
|
156
|
+
xmi_diagrams.each { |d| idx[d.model.package] << d if d.model&.package }
|
|
157
|
+
idx
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def xmi_diagrams
|
|
161
|
+
@xmi_root_model.extension&.diagrams&.diagram || []
|
|
157
162
|
end
|
|
158
163
|
|
|
159
164
|
def create_uml_class_attributes(klass) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
@@ -227,50 +227,65 @@ module Lutaml
|
|
|
227
227
|
field_table
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
-
def format_class(node, hide_members) # rubocop:disable Metrics/
|
|
230
|
+
def format_class(node, hide_members) # rubocop:disable Metrics/MethodLength
|
|
231
231
|
name = ["<B>#{node.name}</B>"]
|
|
232
232
|
name.unshift("«#{node.keyword}»") if node.keyword
|
|
233
|
-
name_html =
|
|
234
|
-
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
|
|
235
|
-
#{name.map { |n| %(<TR><TD ALIGN="CENTER">#{n}</TD></TR>) }.join('\n')}
|
|
236
|
-
</TABLE>
|
|
237
|
-
HEREDOC
|
|
233
|
+
name_html = build_name_table(name)
|
|
238
234
|
|
|
239
235
|
field_table = format_member_rows(node.attributes, hide_members)
|
|
240
236
|
method_table = if node.operations&.any?
|
|
241
237
|
format_member_rows(node.operations, hide_members)
|
|
242
238
|
end
|
|
243
|
-
table_body =
|
|
244
|
-
|
|
239
|
+
table_body = build_table_body(name_html, field_table, method_table)
|
|
240
|
+
|
|
241
|
+
<<~HEREDOC.chomp
|
|
242
|
+
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="10">
|
|
243
|
+
#{table_body}
|
|
244
|
+
</TABLE>
|
|
245
|
+
HEREDOC
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def build_name_table(name_parts)
|
|
249
|
+
<<~HEREDOC
|
|
250
|
+
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
|
|
251
|
+
#{name_parts.map { |n| %(<TR><TD ALIGN="CENTER">#{n}</TD></TR>) }.join('\n')}
|
|
252
|
+
</TABLE>
|
|
253
|
+
HEREDOC
|
|
254
|
+
end
|
|
245
255
|
|
|
256
|
+
def build_table_body(name_html, field_table, method_table)
|
|
257
|
+
[name_html, field_table, method_table].compact.filter_map do |type|
|
|
246
258
|
<<~TEXT
|
|
247
259
|
<TR>
|
|
248
260
|
<TD>#{type}</TD>
|
|
249
261
|
</TR>
|
|
250
262
|
TEXT
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
<<~HEREDOC.chomp
|
|
254
|
-
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="10">
|
|
255
|
-
#{table_body.compact.join("\n")}
|
|
256
|
-
</TABLE>
|
|
257
|
-
HEREDOC
|
|
263
|
+
end.join("\n")
|
|
258
264
|
end
|
|
259
265
|
|
|
260
|
-
def format_document(node)
|
|
266
|
+
def format_document(node)
|
|
261
267
|
@fontname = node.fontname || DEFAULT_CLASS_FONT
|
|
262
268
|
@node["fontname"] = "#{@fontname}-bold"
|
|
263
269
|
|
|
270
|
+
hide_members, hide_other_classes = extract_fidelity_options(node)
|
|
271
|
+
classes = format_all_classes(node, hide_members)
|
|
272
|
+
associations = build_associations(node, hide_other_classes)
|
|
273
|
+
|
|
274
|
+
build_digraph(classes, associations)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def extract_fidelity_options(node)
|
|
264
278
|
if node.fidelity
|
|
265
|
-
|
|
266
|
-
|
|
279
|
+
[node.fidelity.hideMembers, node.fidelity.hideOtherClasses]
|
|
280
|
+
else
|
|
281
|
+
[nil, nil]
|
|
267
282
|
end
|
|
268
|
-
|
|
269
|
-
node.enums +
|
|
270
|
-
node.data_types +
|
|
271
|
-
node.primitives).map do |class_node|
|
|
272
|
-
graph_node_name = generate_graph_name(class_node.name)
|
|
283
|
+
end
|
|
273
284
|
|
|
285
|
+
def format_all_classes(node, hide_members)
|
|
286
|
+
all_classes = node.classes + node.enums + node.data_types + node.primitives
|
|
287
|
+
all_classes.map do |class_node|
|
|
288
|
+
graph_node_name = generate_graph_name(class_node.name)
|
|
274
289
|
<<~HEREDOC
|
|
275
290
|
#{graph_node_name} [
|
|
276
291
|
shape="plain"
|
|
@@ -278,25 +293,36 @@ module Lutaml
|
|
|
278
293
|
label=<#{format_class(class_node, hide_members)}>]
|
|
279
294
|
HEREDOC
|
|
280
295
|
end.join("\n")
|
|
281
|
-
|
|
282
|
-
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def build_associations(node, hide_other_classes)
|
|
299
|
+
associations = collect_all_associations(node)
|
|
283
300
|
if node.groups
|
|
284
301
|
associations = sort_by_document_grouping(node.groups,
|
|
285
302
|
associations)
|
|
286
303
|
end
|
|
304
|
+
|
|
287
305
|
classes_names = node.classes.map(&:name)
|
|
288
|
-
associations
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
306
|
+
format_filtered_associations(associations, classes_names,
|
|
307
|
+
hide_other_classes)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def collect_all_associations(node)
|
|
311
|
+
node.classes.filter_map(&:associations).flatten + node.associations
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def format_filtered_associations(associations, classes_names,
|
|
315
|
+
hide_other_classes)
|
|
316
|
+
associations.filter_map do |assoc_node|
|
|
317
|
+
next if hide_other_classes && !classes_names.include?(assoc_node.member_end)
|
|
293
318
|
|
|
294
319
|
format_relationship(assoc_node)
|
|
295
320
|
end.join("\n")
|
|
321
|
+
end
|
|
296
322
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
323
|
+
def build_digraph(classes, associations)
|
|
324
|
+
indented_classes = indent_lines(classes)
|
|
325
|
+
indented_assocs = indent_lines(associations)
|
|
300
326
|
|
|
301
327
|
<<~HEREDOC
|
|
302
328
|
digraph G {
|
|
@@ -304,13 +330,17 @@ module Lutaml
|
|
|
304
330
|
edge [#{@edge}]
|
|
305
331
|
node [#{@node}]
|
|
306
332
|
|
|
307
|
-
#{
|
|
333
|
+
#{indented_classes}
|
|
308
334
|
|
|
309
|
-
#{
|
|
335
|
+
#{indented_assocs}
|
|
310
336
|
}
|
|
311
337
|
HEREDOC
|
|
312
338
|
end
|
|
313
339
|
|
|
340
|
+
def indent_lines(text)
|
|
341
|
+
text.lines.map { |line| " #{line}" }.join.chomp
|
|
342
|
+
end
|
|
343
|
+
|
|
314
344
|
protected
|
|
315
345
|
|
|
316
346
|
def sort_by_document_grouping(groups, associations) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
@@ -55,7 +55,7 @@ module Lutaml
|
|
|
55
55
|
# @param file_path [String] Path to the model file
|
|
56
56
|
# @return [Lutaml::Uml::Document] Parsed UML document
|
|
57
57
|
# @raise [ParseError] if parsing fails
|
|
58
|
-
def parse(file_path)
|
|
58
|
+
def parse(file_path)
|
|
59
59
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
60
60
|
@stats_mutex.synchronize { @parse_stats[:total_parses] += 1 }
|
|
61
61
|
parse_succeeded = false
|
|
@@ -65,37 +65,35 @@ module Lutaml
|
|
|
65
65
|
validate_file!(file_path) if should_validate_input?
|
|
66
66
|
clear_errors_and_warnings
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
parse_succeeded, result = execute_parse(file_path)
|
|
69
|
+
parse_handled = !parse_succeeded
|
|
70
|
+
result
|
|
71
|
+
ensure
|
|
72
|
+
record_parse_stats(parse_succeeded, parse_handled, start_time)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
def execute_parse(file_path)
|
|
77
|
+
before_parse(file_path)
|
|
78
|
+
document = parse_internal(file_path)
|
|
79
|
+
document = after_parse(document, file_path)
|
|
80
|
+
validate_output!(document) if should_validate_output?
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
@stats_mutex.synchronize { @parse_stats[:successful_parses] += 1 }
|
|
83
|
+
[true, document]
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
[false, handle_parsing_error(e, file_path)]
|
|
86
|
+
end
|
|
80
87
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
unless parse_succeeded || parse_handled
|
|
91
|
-
@stats_mutex.synchronize { @parse_stats[:failed_parses] += 1 }
|
|
92
|
-
end
|
|
93
|
-
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
94
|
-
@last_duration = duration
|
|
95
|
-
@stats_mutex.synchronize do
|
|
96
|
-
@parse_stats[:total_duration] += duration
|
|
97
|
-
@parse_stats[:durations] << duration
|
|
98
|
-
end
|
|
88
|
+
def record_parse_stats(succeeded, handled, start_time)
|
|
89
|
+
unless succeeded || handled
|
|
90
|
+
@stats_mutex.synchronize { @parse_stats[:failed_parses] += 1 }
|
|
91
|
+
end
|
|
92
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
93
|
+
@last_duration = duration
|
|
94
|
+
@stats_mutex.synchronize do
|
|
95
|
+
@parse_stats[:total_duration] += duration
|
|
96
|
+
@parse_stats[:durations] << duration
|
|
99
97
|
end
|
|
100
98
|
end
|
|
101
99
|
|
|
@@ -11,147 +11,48 @@ module Lutaml
|
|
|
11
11
|
def load_class_associations(object_id, object_guid)
|
|
12
12
|
return [] if object_id.nil?
|
|
13
13
|
|
|
14
|
-
associations = []
|
|
15
14
|
normalized_owner_xmi_id = normalize_guid_to_xmi_format(object_guid,
|
|
16
15
|
"EAID")
|
|
17
16
|
|
|
18
17
|
assoc_connectors = database.connectors_for_object(object_id)
|
|
19
18
|
.select { |c| ASSOC_TYPES.include?(c.connector_type) }
|
|
20
19
|
|
|
21
|
-
assoc_connectors.
|
|
22
|
-
|
|
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
|
|
20
|
+
assoc_connectors.filter_map do |ea_connector|
|
|
21
|
+
build_association(ea_connector, object_id, normalized_owner_xmi_id)
|
|
85
22
|
end
|
|
86
|
-
|
|
87
|
-
associations.compact
|
|
88
23
|
end
|
|
89
24
|
|
|
90
25
|
def load_association_attributes(object_id)
|
|
91
26
|
return [] if object_id.nil?
|
|
92
27
|
|
|
93
|
-
attributes = []
|
|
94
28
|
assoc_connectors = database.connectors_for_object(object_id)
|
|
95
29
|
.select { |c| ASSOC_TYPES.include?(c.connector_type) }
|
|
96
30
|
obj = find_object_by_id(object_id)
|
|
97
31
|
obj_pkg_name = find_package_name(obj&.package_id)
|
|
98
32
|
|
|
99
|
-
assoc_connectors.
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
33
|
+
assoc_connectors.filter_map do |ea_connector|
|
|
34
|
+
build_connector_attribute(ea_connector, object_id, obj,
|
|
35
|
+
obj_pkg_name)
|
|
140
36
|
end
|
|
37
|
+
end
|
|
141
38
|
|
|
142
|
-
|
|
39
|
+
def build_connector_attribute(ea_connector, object_id, obj,
|
|
40
|
+
obj_pkg_name)
|
|
41
|
+
if ea_connector.start_object_id == object_id
|
|
42
|
+
build_end_attribute(ea_connector, obj, obj_pkg_name)
|
|
43
|
+
elsif ea_connector.end_object_id == object_id
|
|
44
|
+
build_start_attribute(ea_connector, obj, obj_pkg_name)
|
|
45
|
+
end
|
|
143
46
|
end
|
|
144
47
|
|
|
145
|
-
def create_association_attribute( # rubocop:disable Metrics/
|
|
48
|
+
def create_association_attribute( # rubocop:disable Metrics/ParameterLists
|
|
146
49
|
name:, type:, type_xmi_id:,
|
|
147
50
|
association_xmi_id:, cardinality:, definition:,
|
|
148
51
|
gen_name:, name_ns:, type_ns:, is_src: true
|
|
149
52
|
)
|
|
150
53
|
Lutaml::Uml::GeneralAttribute.new.tap do |attr|
|
|
151
|
-
attr
|
|
152
|
-
|
|
153
|
-
attr.gen_name = gen_name
|
|
154
|
-
attr.definition = definition
|
|
54
|
+
assign_assoc_attr_basic(attr, name, type, gen_name, definition,
|
|
55
|
+
name_ns, type_ns)
|
|
155
56
|
attr.xmi_id = normalize_guid_to_xmi_format(type_xmi_id, "EAID")
|
|
156
57
|
attr.association = normalize_guid_to_xmi_format(
|
|
157
58
|
association_xmi_id, "EAID"
|
|
@@ -160,23 +61,139 @@ module Lutaml
|
|
|
160
61
|
attr.id = normalize_guid_to_xmi_src_dst_format(
|
|
161
62
|
association_xmi_id, "EAID", is_src
|
|
162
63
|
)
|
|
163
|
-
attr.
|
|
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
|
|
64
|
+
attr.cardinality = build_cardinality(cardinality)
|
|
175
65
|
end
|
|
176
66
|
end
|
|
177
67
|
|
|
178
68
|
private
|
|
179
69
|
|
|
70
|
+
def assign_assoc_attr_basic(attr, name, type, gen_name,
|
|
71
|
+
definition, name_ns, type_ns)
|
|
72
|
+
attr.name = name
|
|
73
|
+
attr.type = type
|
|
74
|
+
attr.gen_name = gen_name
|
|
75
|
+
attr.definition = definition
|
|
76
|
+
attr.name_ns = name_ns
|
|
77
|
+
attr.type_ns = type_ns
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def build_association(ea_connector, object_id, normalized_owner_xmi_id)
|
|
81
|
+
is_start = ea_connector.start_object_id == object_id
|
|
82
|
+
owner_role = is_start ? ea_connector.destrole : ea_connector.sourcerole
|
|
83
|
+
return nil if owner_role.nil? || owner_role.empty?
|
|
84
|
+
|
|
85
|
+
member_obj = resolve_member_object(ea_connector, is_start)
|
|
86
|
+
return nil unless member_obj
|
|
87
|
+
|
|
88
|
+
member_role = resolve_member_role(ea_connector, is_start, member_obj)
|
|
89
|
+
|
|
90
|
+
build_association_record(ea_connector, object_id, normalized_owner_xmi_id,
|
|
91
|
+
owner_role, member_obj, member_role, is_start)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def build_association_record(ea_connector, object_id, owner_xmi_id,
|
|
95
|
+
owner_role, member_obj, member_role, is_start)
|
|
96
|
+
cardinality_str = is_start ? ea_connector.destcard : ea_connector.sourcecard
|
|
97
|
+
|
|
98
|
+
Lutaml::Uml::Association.new.tap do |assoc|
|
|
99
|
+
assoc.xmi_id = normalize_guid_to_xmi_format(ea_connector.ea_guid,
|
|
100
|
+
"EAID")
|
|
101
|
+
assign_assoc_name(assoc, ea_connector)
|
|
102
|
+
assign_association_ends(assoc, object_id, owner_xmi_id,
|
|
103
|
+
owner_role, member_obj, member_role)
|
|
104
|
+
assoc.member_end_type = ea_connector.connector_type&.downcase
|
|
105
|
+
assoc.member_end_cardinality = build_cardinality(cardinality_str)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def assign_assoc_name(assoc, ea_connector)
|
|
110
|
+
return if ea_connector.name.nil? || ea_connector.name.empty?
|
|
111
|
+
|
|
112
|
+
assoc.name = ea_connector.name
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def assign_association_ends(assoc, object_id, owner_xmi_id,
|
|
116
|
+
owner_role, member_obj, member_role)
|
|
117
|
+
assoc.owner_end = find_object_by_id(object_id)&.name
|
|
118
|
+
assoc.owner_end_xmi_id = owner_xmi_id
|
|
119
|
+
assoc.owner_end_attribute_name = owner_role
|
|
120
|
+
assoc.member_end = member_obj.name
|
|
121
|
+
assoc.member_end_xmi_id = normalize_guid_to_xmi_format(
|
|
122
|
+
member_obj.ea_guid, "EAID"
|
|
123
|
+
)
|
|
124
|
+
assoc.member_end_attribute_name = member_role
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def resolve_member_object(ea_connector, is_start)
|
|
128
|
+
peer_id = is_start ? ea_connector.end_object_id : ea_connector.start_object_id
|
|
129
|
+
find_object_by_id(peer_id)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def resolve_member_role(ea_connector, is_start, member_obj)
|
|
133
|
+
role = is_start ? ea_connector.sourcerole : ea_connector.destrole
|
|
134
|
+
role.nil? || role.empty? ? member_obj.name : role
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def build_end_attribute(ea_connector, obj, obj_pkg_name)
|
|
138
|
+
return nil if ea_connector.destrole.nil? || ea_connector.destrole.empty?
|
|
139
|
+
|
|
140
|
+
target_obj = find_object_by_id(ea_connector.end_object_id)
|
|
141
|
+
return nil unless target_obj
|
|
142
|
+
|
|
143
|
+
build_directional_attribute(
|
|
144
|
+
role: ea_connector.destrole,
|
|
145
|
+
peer_obj: target_obj,
|
|
146
|
+
ea_connector: ea_connector,
|
|
147
|
+
cardinality: ea_connector.destcard,
|
|
148
|
+
obj: obj, obj_pkg_name: obj_pkg_name,
|
|
149
|
+
is_src: false
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def build_start_attribute(ea_connector, obj, obj_pkg_name)
|
|
154
|
+
return nil if ea_connector.sourcerole.nil? || ea_connector.sourcerole.empty?
|
|
155
|
+
|
|
156
|
+
source_obj = find_object_by_id(ea_connector.start_object_id)
|
|
157
|
+
return nil unless source_obj
|
|
158
|
+
|
|
159
|
+
build_directional_attribute(
|
|
160
|
+
role: ea_connector.sourcerole,
|
|
161
|
+
peer_obj: source_obj,
|
|
162
|
+
ea_connector: ea_connector,
|
|
163
|
+
cardinality: ea_connector.sourcecard,
|
|
164
|
+
obj: obj, obj_pkg_name: obj_pkg_name
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def build_directional_attribute(role:, peer_obj:, ea_connector:,
|
|
169
|
+
cardinality:, obj:, obj_pkg_name:,
|
|
170
|
+
is_src: true)
|
|
171
|
+
create_association_attribute(
|
|
172
|
+
name: role,
|
|
173
|
+
type: peer_obj.name,
|
|
174
|
+
type_xmi_id: peer_obj.ea_guid,
|
|
175
|
+
association_xmi_id: ea_connector.ea_guid,
|
|
176
|
+
cardinality: cardinality,
|
|
177
|
+
definition: ea_connector.notes,
|
|
178
|
+
gen_name: obj.name,
|
|
179
|
+
name_ns: obj_pkg_name,
|
|
180
|
+
type_ns: find_package_name(peer_obj.package_id),
|
|
181
|
+
is_src: is_src,
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def build_cardinality(cardinality_str)
|
|
186
|
+
return nil unless cardinality_str && !cardinality_str.empty?
|
|
187
|
+
|
|
188
|
+
parsed = parse_cardinality(cardinality_str)
|
|
189
|
+
return nil unless parsed[:min] || parsed[:max]
|
|
190
|
+
|
|
191
|
+
Lutaml::Uml::Cardinality.new.tap do |card|
|
|
192
|
+
card.min = parsed[:min]
|
|
193
|
+
card.max = parsed[:max]
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
180
197
|
def find_package_name(package_id)
|
|
181
198
|
return nil if package_id.nil?
|
|
182
199
|
|