lutaml 0.10.13 → 0.10.14
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 +58 -18
- data/lib/lutaml/cli/interactive_shell/export_handler.rb +9 -7
- data/lib/lutaml/cli/interactive_shell/help_display.rb +39 -45
- data/lib/lutaml/cli/interactive_shell/navigation_commands.rb +36 -21
- data/lib/lutaml/cli/interactive_shell/query_commands.rb +63 -47
- data/lib/lutaml/cli/interactive_shell.rb +43 -25
- data/lib/lutaml/qea/factory/association_builder.rb +114 -125
- data/lib/lutaml/qea/factory/class_transformer.rb +67 -43
- data/lib/lutaml/qea/factory/enum_transformer.rb +22 -23
- data/lib/lutaml/qea/factory/generalization_builder.rb +146 -116
- data/lib/lutaml/qea/factory/stereotype_loader.rb +13 -7
- data/lib/lutaml/qea/lookup_indexes.rb +8 -1
- data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +33 -25
- data/lib/lutaml/uml_repository/index_builders/association_index.rb +49 -47
- data/lib/lutaml/uml_repository/index_builders/class_index.rb +30 -23
- data/lib/lutaml/uml_repository/queries/class_query.rb +74 -48
- data/lib/lutaml/uml_repository/queries/inheritance_query.rb +42 -32
- data/lib/lutaml/uml_repository/static_site/data_transformer.rb +55 -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 +27 -17
- data/lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb +92 -68
- data/lib/lutaml/uml_repository/static_site/serializers/package_tree_builder.rb +32 -23
- data/lib/lutaml/version.rb +1 -1
- metadata +1 -1
|
@@ -8,8 +8,12 @@ module Lutaml
|
|
|
8
8
|
# Creates a hash mapping qualified names to Class/DataType/Enum objects:
|
|
9
9
|
# "ModelRoot::i-UR::urf::Building" => Class{}
|
|
10
10
|
# @api public
|
|
11
|
-
def build_qualified_name_index
|
|
12
|
-
|
|
11
|
+
def build_qualified_name_index
|
|
12
|
+
index_document_classifiers
|
|
13
|
+
index_package_classifiers
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def index_document_classifiers
|
|
13
17
|
if @document.classes
|
|
14
18
|
index_classifiers(@document.classes,
|
|
15
19
|
ROOT_PACKAGE_NAME)
|
|
@@ -19,8 +23,9 @@ module Lutaml
|
|
|
19
23
|
ROOT_PACKAGE_NAME)
|
|
20
24
|
end
|
|
21
25
|
index_classifiers(@document.enums, ROOT_PACKAGE_NAME) if @document.enums
|
|
26
|
+
end
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
def index_package_classifiers
|
|
24
29
|
traverse_packages(@document.packages,
|
|
25
30
|
parent_path: ROOT_PACKAGE_NAME) do |package, path|
|
|
26
31
|
index_classifiers(package.classes, path) if package.classes
|
|
@@ -35,13 +40,18 @@ module Lutaml
|
|
|
35
40
|
# "featureType" => [Class{}, Class{}],
|
|
36
41
|
# "dataType" => [Class{}]
|
|
37
42
|
# @api public
|
|
38
|
-
def build_stereotype_index
|
|
39
|
-
|
|
43
|
+
def build_stereotype_index
|
|
44
|
+
index_document_stereotypes
|
|
45
|
+
index_package_stereotypes
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def index_document_stereotypes
|
|
40
49
|
index_by_stereotype(@document.classes) if @document.classes
|
|
41
50
|
index_by_stereotype(@document.data_types) if @document.data_types
|
|
42
51
|
index_by_stereotype(@document.enums) if @document.enums
|
|
52
|
+
end
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
def index_package_stereotypes
|
|
45
55
|
traverse_packages(@document.packages) do |package, _path|
|
|
46
56
|
index_by_stereotype(package.classes) if package.classes
|
|
47
57
|
index_by_stereotype(package.data_types) if package.data_types
|
|
@@ -53,39 +63,36 @@ module Lutaml
|
|
|
53
63
|
#
|
|
54
64
|
# @param classifiers [Array] Array of classifier objects
|
|
55
65
|
# @param package_path [String] Package path for these classifiers
|
|
56
|
-
def index_classifiers(classifiers, package_path)
|
|
66
|
+
def index_classifiers(classifiers, package_path)
|
|
57
67
|
return unless classifiers
|
|
58
68
|
|
|
59
69
|
classifiers.each do |classifier|
|
|
60
70
|
next unless classifier.name
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
@qualified_names[qualified_name] = classifier
|
|
64
|
-
if classifier.xmi_id
|
|
65
|
-
@class_to_qname[classifier.xmi_id] =
|
|
66
|
-
qualified_name
|
|
67
|
-
end
|
|
68
|
-
@classes[classifier.xmi_id] = classifier if classifier.xmi_id
|
|
69
|
-
@simple_name_to_qnames[classifier.name] ||= []
|
|
70
|
-
@simple_name_to_qnames[classifier.name] << qualified_name
|
|
71
|
-
(@package_to_classes[package_path] ||= []) << classifier
|
|
72
|
+
index_classifier(classifier, package_path)
|
|
72
73
|
end
|
|
73
74
|
end
|
|
74
75
|
|
|
76
|
+
def index_classifier(classifier, package_path)
|
|
77
|
+
qualified_name = "#{package_path}::#{classifier.name}"
|
|
78
|
+
@qualified_names[qualified_name] = classifier
|
|
79
|
+
@class_to_qname[classifier.xmi_id] = qualified_name if classifier.xmi_id
|
|
80
|
+
@classes[classifier.xmi_id] = classifier if classifier.xmi_id
|
|
81
|
+
(@simple_name_to_qnames[classifier.name] ||= []) << qualified_name
|
|
82
|
+
(@package_to_classes[package_path] ||= []) << classifier
|
|
83
|
+
end
|
|
84
|
+
|
|
75
85
|
# Index classifiers by their stereotypes
|
|
76
86
|
#
|
|
77
87
|
# @param classifiers [Array] Array of classifier objects
|
|
78
|
-
def index_by_stereotype(classifiers)
|
|
88
|
+
def index_by_stereotype(classifiers)
|
|
79
89
|
return unless classifiers
|
|
80
90
|
|
|
81
91
|
classifiers.each do |classifier|
|
|
82
92
|
next unless classifier.stereotype && !classifier.stereotype.empty?
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
stereotypes.each do |stereotype|
|
|
87
|
-
@stereotypes[stereotype] ||= []
|
|
88
|
-
@stereotypes[stereotype] << classifier
|
|
94
|
+
Array(classifier.stereotype).each do |stereotype|
|
|
95
|
+
(@stereotypes[stereotype] ||= []) << classifier
|
|
89
96
|
end
|
|
90
97
|
end
|
|
91
98
|
end
|
|
@@ -66,7 +66,7 @@ module Lutaml
|
|
|
66
66
|
# @example Recursive query
|
|
67
67
|
# classes = query.in_package("ModelRoot::i-UR", recursive: true)
|
|
68
68
|
# # Returns classes in i-UR and all nested packages
|
|
69
|
-
def in_package(package_path_string, recursive: false)
|
|
69
|
+
def in_package(package_path_string, recursive: false)
|
|
70
70
|
return [] if package_path_string.nil? || package_path_string.empty?
|
|
71
71
|
|
|
72
72
|
pkg_to_classes = indexes[:package_to_classes]
|
|
@@ -81,69 +81,95 @@ module Lutaml
|
|
|
81
81
|
private
|
|
82
82
|
|
|
83
83
|
# O(1) indexed lookup for in_package
|
|
84
|
-
def in_package_indexed(package_path_string, pkg_to_classes, recursive:)
|
|
84
|
+
def in_package_indexed(package_path_string, pkg_to_classes, recursive:)
|
|
85
85
|
is_absolute = package_path_string.start_with?("::")
|
|
86
86
|
search_segs = package_path_string.split("::").reject(&:empty?)
|
|
87
87
|
|
|
88
88
|
results = []
|
|
89
89
|
pkg_to_classes.each do |path, classes|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
path == package_path_string ||
|
|
94
|
-
path.start_with?("#{package_path_string}::")
|
|
95
|
-
else
|
|
96
|
-
path == package_path_string
|
|
97
|
-
end
|
|
98
|
-
elsif recursive
|
|
99
|
-
# Relative: match when path ends with search segments
|
|
100
|
-
(0..(path_segs.size - search_segs.size)).any? do |i|
|
|
101
|
-
path_segs[i, search_segs.size] == search_segs
|
|
102
|
-
end
|
|
103
|
-
else
|
|
104
|
-
path_segs.size >= search_segs.size &&
|
|
105
|
-
path_segs[-search_segs.size..] == search_segs
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
results.concat(classes) if matched
|
|
90
|
+
results.concat(classes) if indexed_path_matches?(
|
|
91
|
+
path, package_path_string, is_absolute, search_segs, recursive
|
|
92
|
+
)
|
|
109
93
|
end
|
|
110
94
|
results
|
|
111
95
|
end
|
|
112
96
|
|
|
97
|
+
def indexed_path_matches?(path, package_path_string, is_absolute,
|
|
98
|
+
search_segs, recursive)
|
|
99
|
+
if is_absolute
|
|
100
|
+
indexed_absolute_match?(path, package_path_string, recursive)
|
|
101
|
+
else
|
|
102
|
+
indexed_relative_match?(path.split("::"), search_segs, recursive)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def indexed_absolute_match?(path, package_path_string, recursive)
|
|
107
|
+
if recursive
|
|
108
|
+
path == package_path_string ||
|
|
109
|
+
path.start_with?("#{package_path_string}::")
|
|
110
|
+
else
|
|
111
|
+
path == package_path_string
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def indexed_relative_match?(path_segs, search_segs, recursive)
|
|
116
|
+
if recursive
|
|
117
|
+
(0..(path_segs.size - search_segs.size)).any? do |i|
|
|
118
|
+
path_segs[i, search_segs.size] == search_segs
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
path_segs.size >= search_segs.size &&
|
|
122
|
+
path_segs[-search_segs.size..] == search_segs
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
113
126
|
# Fallback: original O(n) scan
|
|
114
127
|
def in_package_scan(package_path_string, recursive:)
|
|
115
128
|
package_path = Lutaml::Uml::PackagePath.new(package_path_string)
|
|
116
|
-
results = []
|
|
117
129
|
is_absolute = package_path.absolute?
|
|
118
130
|
|
|
119
|
-
indexes[:qualified_names].
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
matched = if is_absolute
|
|
123
|
-
if recursive
|
|
124
|
-
qname.package_path.starts_with?(package_path)
|
|
125
|
-
else
|
|
126
|
-
qname.package_path == package_path
|
|
127
|
-
end
|
|
128
|
-
else
|
|
129
|
-
class_pkg_segs = qname.package_path.segments
|
|
130
|
-
search_segs = package_path.segments
|
|
131
|
-
|
|
132
|
-
if recursive
|
|
133
|
-
(0..(class_pkg_segs.size - search_segs.size))
|
|
134
|
-
.any? do |i|
|
|
135
|
-
class_pkg_segs[i, search_segs.size] == search_segs
|
|
136
|
-
end
|
|
137
|
-
else
|
|
138
|
-
class_pkg_segs.size >= search_segs.size &&
|
|
139
|
-
class_pkg_segs[-search_segs.size..] == search_segs
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
results << klass if matched
|
|
131
|
+
indexes[:qualified_names].each_value.select do |klass|
|
|
132
|
+
scan_matches_package?(klass, package_path, is_absolute, recursive)
|
|
144
133
|
end
|
|
134
|
+
end
|
|
145
135
|
|
|
146
|
-
|
|
136
|
+
def scan_matches_package?(klass, package_path, is_absolute, recursive)
|
|
137
|
+
qname = resolve_qname_for(klass)
|
|
138
|
+
return false unless qname
|
|
139
|
+
|
|
140
|
+
if is_absolute
|
|
141
|
+
match_absolute_path?(qname, package_path, recursive)
|
|
142
|
+
else
|
|
143
|
+
match_relative_path?(qname, package_path, recursive)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def resolve_qname_for(klass)
|
|
148
|
+
indexes[:qualified_names].find { |_, v| v == klass }&.first
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def match_absolute_path?(qname, package_path, recursive)
|
|
152
|
+
qname = Lutaml::Uml::QualifiedName.new(qname)
|
|
153
|
+
if recursive
|
|
154
|
+
qname.package_path.starts_with?(package_path)
|
|
155
|
+
else
|
|
156
|
+
qname.package_path == package_path
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def match_relative_path?(qname_string, package_path, recursive)
|
|
161
|
+
qname = Lutaml::Uml::QualifiedName.new(qname_string)
|
|
162
|
+
class_pkg_segs = qname.package_path.segments
|
|
163
|
+
search_segs = package_path.segments
|
|
164
|
+
|
|
165
|
+
if recursive
|
|
166
|
+
(0..(class_pkg_segs.size - search_segs.size)).any? do |i|
|
|
167
|
+
class_pkg_segs[i, search_segs.size] == search_segs
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
class_pkg_segs.size >= search_segs.size &&
|
|
171
|
+
class_pkg_segs[-search_segs.size..] == search_segs
|
|
172
|
+
end
|
|
147
173
|
end
|
|
148
174
|
end
|
|
149
175
|
end
|
|
@@ -33,27 +33,15 @@ module Lutaml
|
|
|
33
33
|
# parent = query.supertype("ModelRoot::Child")
|
|
34
34
|
# # Or
|
|
35
35
|
# parent = query.supertype(child_class)
|
|
36
|
-
def supertype(class_or_qname)
|
|
36
|
+
def supertype(class_or_qname)
|
|
37
37
|
klass = resolve_class(class_or_qname)
|
|
38
|
-
return nil unless klass
|
|
39
|
-
return nil unless klass.is_a?(Lutaml::Uml::Class)
|
|
40
|
-
return nil unless klass.generalization
|
|
38
|
+
return nil unless valid_supertype_target?(klass)
|
|
41
39
|
|
|
42
40
|
parent_name = extract_parent_name(klass.generalization)
|
|
43
41
|
return nil unless parent_name
|
|
44
|
-
# avoid self-references
|
|
45
42
|
return nil if parent_name == klass.name
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
qname_string = resolve_qname(class_or_qname)
|
|
49
|
-
return nil unless qname_string
|
|
50
|
-
|
|
51
|
-
qname = Lutaml::Uml::QualifiedName.new(qname_string)
|
|
52
|
-
package_path = qname.package_path.to_s
|
|
53
|
-
|
|
54
|
-
# Try to resolve parent qualified name
|
|
55
|
-
parent_qname = resolve_parent_qualified_name(parent_name,
|
|
56
|
-
package_path)
|
|
44
|
+
parent_qname = resolve_parent_qname(class_or_qname, parent_name)
|
|
57
45
|
return nil unless parent_qname
|
|
58
46
|
|
|
59
47
|
indexes[:qualified_names][parent_qname]
|
|
@@ -200,14 +188,8 @@ module Lutaml
|
|
|
200
188
|
# qualified name, or xmi_id
|
|
201
189
|
# @return [Boolean] true if circular inheritance detected
|
|
202
190
|
def has_circular_inheritance?(class_or_id, visited: Set.new)
|
|
203
|
-
qname =
|
|
204
|
-
indexes[:qualified_names].key?(class_or_id)
|
|
205
|
-
class_or_id
|
|
206
|
-
else
|
|
207
|
-
resolve_qname(class_or_id)
|
|
208
|
-
end
|
|
191
|
+
qname = resolve_to_qname(class_or_id)
|
|
209
192
|
return false unless qname
|
|
210
|
-
|
|
211
193
|
return true if visited.include?(qname)
|
|
212
194
|
|
|
213
195
|
visited.add(qname)
|
|
@@ -219,6 +201,21 @@ module Lutaml
|
|
|
219
201
|
|
|
220
202
|
private
|
|
221
203
|
|
|
204
|
+
def valid_supertype_target?(klass)
|
|
205
|
+
return false unless klass
|
|
206
|
+
return false unless klass.is_a?(Lutaml::Uml::Class)
|
|
207
|
+
|
|
208
|
+
klass.generalization ? true : false
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def resolve_parent_qname(class_or_qname, parent_name)
|
|
212
|
+
qname_string = resolve_qname(class_or_qname)
|
|
213
|
+
return nil unless qname_string
|
|
214
|
+
|
|
215
|
+
qname = Lutaml::Uml::QualifiedName.new(qname_string)
|
|
216
|
+
resolve_parent_qualified_name(parent_name, qname.package_path.to_s)
|
|
217
|
+
end
|
|
218
|
+
|
|
222
219
|
# Resolve a class by xmi_id or qualified name
|
|
223
220
|
#
|
|
224
221
|
# @param class_or_id [String] Qualified name or xmi_id
|
|
@@ -257,22 +254,24 @@ module Lutaml
|
|
|
257
254
|
# @param max_depth [Integer, nil] Maximum depth to traverse
|
|
258
255
|
# @param current_depth [Integer] Current depth
|
|
259
256
|
# @return [Array] Array of descendant class objects
|
|
260
|
-
def collect_descendants(qname_string, max_depth, current_depth)
|
|
257
|
+
def collect_descendants(qname_string, max_depth, current_depth)
|
|
261
258
|
return [] if max_depth && current_depth >= max_depth
|
|
262
259
|
|
|
263
260
|
children = direct_subtypes(qname_string)
|
|
264
261
|
result = children.dup
|
|
262
|
+
collect_child_descendants(children, max_depth, current_depth, result)
|
|
263
|
+
result
|
|
264
|
+
end
|
|
265
265
|
|
|
266
|
+
def collect_child_descendants(children, max_depth, current_depth,
|
|
267
|
+
result)
|
|
266
268
|
children.each do |child|
|
|
267
269
|
child_qname = resolve_qname(child)
|
|
268
270
|
next unless child_qname
|
|
269
271
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
result.concat(grandchildren)
|
|
272
|
+
result.concat(collect_descendants(child_qname, max_depth,
|
|
273
|
+
current_depth + 1))
|
|
273
274
|
end
|
|
274
|
-
|
|
275
|
-
result
|
|
276
275
|
end
|
|
277
276
|
|
|
278
277
|
# Extract parent name from generalization object
|
|
@@ -285,13 +284,24 @@ module Lutaml
|
|
|
285
284
|
return nil unless generalization.is_a?(Lutaml::Uml::Generalization)
|
|
286
285
|
|
|
287
286
|
parent = generalization.general
|
|
288
|
-
if parent
|
|
289
|
-
|
|
287
|
+
return extract_name_from_parent(parent) if parent
|
|
288
|
+
|
|
289
|
+
generalization.name if generalization.name
|
|
290
|
+
end
|
|
290
291
|
|
|
291
|
-
|
|
292
|
+
def resolve_to_qname(class_or_id)
|
|
293
|
+
if class_or_id.is_a?(String) &&
|
|
294
|
+
indexes[:qualified_names].key?(class_or_id)
|
|
295
|
+
class_or_id
|
|
296
|
+
else
|
|
297
|
+
resolve_qname(class_or_id)
|
|
292
298
|
end
|
|
299
|
+
end
|
|
293
300
|
|
|
294
|
-
|
|
301
|
+
def extract_name_from_parent(parent)
|
|
302
|
+
return parent.name if parent.is_a?(Lutaml::Uml::Generalization) && parent.name
|
|
303
|
+
|
|
304
|
+
parent.to_s
|
|
295
305
|
end
|
|
296
306
|
|
|
297
307
|
# Resolve a class name to its qualified name
|
|
@@ -33,24 +33,13 @@ module Lutaml
|
|
|
33
33
|
def transform
|
|
34
34
|
Models::SpaDocument.new(
|
|
35
35
|
metadata: Serializers::MetadataBuilder.new(repository).build,
|
|
36
|
-
package_tree:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
classes: Serializers::ClassSerializer.new(repository, id_generator,
|
|
41
|
-
options, inheritance_resolver).build_map,
|
|
42
|
-
attributes: Serializers::AttributeSerializer.new(repository,
|
|
43
|
-
id_generator, options).build_map,
|
|
36
|
+
package_tree: build_package_tree,
|
|
37
|
+
packages: build_packages_map,
|
|
38
|
+
classes: build_classes_map,
|
|
39
|
+
attributes: build_attributes_map,
|
|
44
40
|
associations: build_associations_map,
|
|
45
|
-
operations:
|
|
46
|
-
|
|
47
|
-
diagrams: if options[:include_diagrams]
|
|
48
|
-
Serializers::DiagramSerializer.new(
|
|
49
|
-
repository, id_generator, options
|
|
50
|
-
).build_map
|
|
51
|
-
else
|
|
52
|
-
{}
|
|
53
|
-
end,
|
|
41
|
+
operations: build_operations_map,
|
|
42
|
+
diagrams: build_diagrams_map,
|
|
54
43
|
)
|
|
55
44
|
end
|
|
56
45
|
|
|
@@ -74,24 +63,7 @@ module Lutaml
|
|
|
74
63
|
map = Hash.new { |h, k| h[k] = [] }
|
|
75
64
|
|
|
76
65
|
repository.classes_index.each do |klass|
|
|
77
|
-
|
|
78
|
-
unless klass.association_generalization && !klass.association_generalization.empty?
|
|
79
|
-
next
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
klass.association_generalization.each do |assoc_gen|
|
|
83
|
-
parent_object_id = assoc_gen.parent_object_id
|
|
84
|
-
next unless parent_object_id
|
|
85
|
-
|
|
86
|
-
parent_class = class_lookup.by_object_id(parent_object_id)
|
|
87
|
-
if parent_class&.xmi_id
|
|
88
|
-
next if parent_class.xmi_id == klass.xmi_id
|
|
89
|
-
|
|
90
|
-
unless map[klass.xmi_id].include?(parent_class.xmi_id)
|
|
91
|
-
map[klass.xmi_id] << parent_class.xmi_id
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
66
|
+
add_generalization_entries(map, klass)
|
|
95
67
|
end
|
|
96
68
|
|
|
97
69
|
map
|
|
@@ -101,6 +73,54 @@ module Lutaml
|
|
|
101
73
|
@class_lookup ||= ClassLookupIndex.new(repository.classes_index)
|
|
102
74
|
end
|
|
103
75
|
|
|
76
|
+
def build_package_tree
|
|
77
|
+
Serializers::PackageTreeBuilder.new(repository, id_generator).build
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def build_packages_map
|
|
81
|
+
Serializers::PackageSerializer.new(repository, id_generator,
|
|
82
|
+
options).build_map
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def build_classes_map
|
|
86
|
+
Serializers::ClassSerializer.new(repository, id_generator,
|
|
87
|
+
options, inheritance_resolver).build_map
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def build_attributes_map
|
|
91
|
+
Serializers::AttributeSerializer.new(repository, id_generator,
|
|
92
|
+
options).build_map
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def build_operations_map
|
|
96
|
+
Serializers::OperationSerializer.new(repository,
|
|
97
|
+
id_generator).build_map
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def build_diagrams_map
|
|
101
|
+
return {} unless options[:include_diagrams]
|
|
102
|
+
|
|
103
|
+
Serializers::DiagramSerializer.new(repository, id_generator,
|
|
104
|
+
options).build_map
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def add_generalization_entries(map, klass)
|
|
108
|
+
return unless klass.association_generalization
|
|
109
|
+
return if klass.association_generalization.empty?
|
|
110
|
+
|
|
111
|
+
klass.association_generalization.each do |assoc_gen|
|
|
112
|
+
parent_object_id = assoc_gen.parent_object_id
|
|
113
|
+
next unless parent_object_id
|
|
114
|
+
|
|
115
|
+
parent_class = class_lookup.by_object_id(parent_object_id)
|
|
116
|
+
next unless parent_class&.xmi_id
|
|
117
|
+
next if parent_class.xmi_id == klass.xmi_id
|
|
118
|
+
next if map[klass.xmi_id].include?(parent_class.xmi_id)
|
|
119
|
+
|
|
120
|
+
map[klass.xmi_id] << parent_class.xmi_id
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
104
124
|
def format_definition(definition)
|
|
105
125
|
return nil if definition.nil? || definition.empty?
|
|
106
126
|
|
|
@@ -51,23 +51,9 @@ module Lutaml
|
|
|
51
51
|
|
|
52
52
|
def build_document_store
|
|
53
53
|
documents = []
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
klass.attributes&.each do |attr|
|
|
59
|
-
documents << build_attribute_document(attr, klass)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
repository.associations_index.each do |assoc|
|
|
64
|
-
documents << build_association_document(assoc)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
repository.packages_index.each do |package|
|
|
68
|
-
documents << build_package_document(package)
|
|
69
|
-
end
|
|
70
|
-
|
|
54
|
+
documents.concat(build_class_documents)
|
|
55
|
+
documents.concat(build_association_documents)
|
|
56
|
+
documents.concat(build_package_documents)
|
|
71
57
|
documents
|
|
72
58
|
end
|
|
73
59
|
|
|
@@ -135,8 +121,8 @@ module Lutaml
|
|
|
135
121
|
class_type(klass),
|
|
136
122
|
Array(klass.stereotype).join(" "),
|
|
137
123
|
klass.definition,
|
|
138
|
-
klass.attributes
|
|
139
|
-
klass.operations
|
|
124
|
+
collect_names(klass.attributes),
|
|
125
|
+
collect_names(klass.operations),
|
|
140
126
|
].compact
|
|
141
127
|
|
|
142
128
|
normalize_content(parts.join(" "))
|
|
@@ -186,6 +172,33 @@ module Lutaml
|
|
|
186
172
|
all_content.join(" ").gsub(/\s+/, " ").strip
|
|
187
173
|
end
|
|
188
174
|
|
|
175
|
+
def build_class_documents
|
|
176
|
+
docs = []
|
|
177
|
+
repository.classes_index.each do |klass|
|
|
178
|
+
docs << build_class_document(klass)
|
|
179
|
+
klass.attributes&.each do |attr|
|
|
180
|
+
docs << build_attribute_document(attr, klass)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
docs
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def build_association_documents
|
|
187
|
+
repository.associations_index.map do |assoc|
|
|
188
|
+
build_association_document(assoc)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def build_package_documents
|
|
193
|
+
repository.packages_index.map do |package|
|
|
194
|
+
build_package_document(package)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def collect_names(items)
|
|
199
|
+
items&.map(&:name)&.join(" ")
|
|
200
|
+
end
|
|
201
|
+
|
|
189
202
|
def class_type(klass)
|
|
190
203
|
klass.class.name.split("::").last
|
|
191
204
|
end
|
|
@@ -30,10 +30,7 @@ inheritance_resolver)
|
|
|
30
30
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
|
-
def serialize(klass, id)
|
|
34
|
-
class_associations = find_class_associations(klass)
|
|
35
|
-
sorted_associations = sort_associations(class_associations, klass)
|
|
36
|
-
|
|
33
|
+
def serialize(klass, id)
|
|
37
34
|
Models::SpaClass.new(
|
|
38
35
|
id: id,
|
|
39
36
|
xmi_id: klass.xmi_id,
|
|
@@ -43,10 +40,10 @@ inheritance_resolver)
|
|
|
43
40
|
package: package_id_for_class(klass),
|
|
44
41
|
stereotypes: normalize_stereotypes(klass.stereotype),
|
|
45
42
|
definition: format_definition(klass.definition),
|
|
46
|
-
attributes: (klass
|
|
47
|
-
.map { |attr| @id_generator.attribute_id(attr, klass) },
|
|
43
|
+
attributes: serialize_attribute_ids(klass),
|
|
48
44
|
operations: serialize_class_operations(klass),
|
|
49
|
-
associations:
|
|
45
|
+
associations: sort_associations(find_class_associations(klass),
|
|
46
|
+
klass),
|
|
50
47
|
generalizations: @inheritance_resolver.find_generalizations(klass),
|
|
51
48
|
specializations: @inheritance_resolver.find_specializations(klass),
|
|
52
49
|
is_abstract: klass.is_abstract,
|
|
@@ -56,6 +53,11 @@ inheritance_resolver)
|
|
|
56
53
|
)
|
|
57
54
|
end
|
|
58
55
|
|
|
56
|
+
def serialize_attribute_ids(klass)
|
|
57
|
+
(klass.attributes || []).sort_by { |a| a.name || "" }
|
|
58
|
+
.map { |attr| @id_generator.attribute_id(attr, klass) }
|
|
59
|
+
end
|
|
60
|
+
|
|
59
61
|
def find_class_associations(klass)
|
|
60
62
|
associations = @repository.associations_of(klass)
|
|
61
63
|
associations.map { |assoc| @id_generator.association_id(assoc) }
|
|
@@ -65,18 +67,26 @@ inheritance_resolver)
|
|
|
65
67
|
|
|
66
68
|
def sort_associations(assoc_ids, klass)
|
|
67
69
|
assoc_ids.sort_by do |assoc_id|
|
|
68
|
-
assoc =
|
|
69
|
-
@id_generator.association_id(a) == assoc_id
|
|
70
|
-
end
|
|
70
|
+
assoc = find_assoc_by_id(assoc_id)
|
|
71
71
|
next "" unless assoc
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
resolve_assoc_role(assoc, klass)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def find_assoc_by_id(assoc_id)
|
|
78
|
+
@repository.associations_index.find do |a|
|
|
79
|
+
@id_generator.association_id(a) == assoc_id
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resolve_assoc_role(assoc, klass)
|
|
84
|
+
if assoc.owner_end_xmi_id == klass.xmi_id
|
|
85
|
+
assoc.owner_end_attribute_name || assoc.owner_end || ""
|
|
86
|
+
elsif assoc.member_end_xmi_id == klass.xmi_id
|
|
87
|
+
assoc.member_end_attribute_name || assoc.member_end || ""
|
|
88
|
+
else
|
|
89
|
+
""
|
|
80
90
|
end
|
|
81
91
|
end
|
|
82
92
|
|