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
|
@@ -9,7 +9,7 @@ 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
|
|
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
14
|
|
|
15
15
|
if visited.include?(object_id)
|
|
@@ -23,58 +23,13 @@ module Lutaml
|
|
|
23
23
|
current_obj = find_object_by_id(object_id)
|
|
24
24
|
return nil unless current_obj
|
|
25
25
|
|
|
26
|
-
|
|
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
|
|
26
|
+
generalization = build_generalization(object_id, current_obj)
|
|
36
27
|
return nil unless generalization
|
|
37
28
|
|
|
38
|
-
|
|
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
|
|
29
|
+
populate_generalization_attrs(generalization, object_id)
|
|
57
30
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
generalization.attributes = transform_general_attributes(
|
|
62
|
-
generalization,
|
|
63
|
-
)
|
|
64
|
-
|
|
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
|
|
31
|
+
ea_connector = ea_connector_for(object_id)
|
|
32
|
+
populate_parent_generalization(generalization, ea_connector, visited)
|
|
78
33
|
|
|
79
34
|
if is_leaf && generalization.has_general
|
|
80
35
|
collect_inherited_properties(generalization)
|
|
@@ -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
|
-
|
|
94
|
-
|
|
48
|
+
build_assoc_generalization(ea_connector)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
95
51
|
|
|
96
|
-
|
|
97
|
-
|
|
52
|
+
def convert_to_general_attributes(attributes)
|
|
53
|
+
attributes.map { |attr| to_general_attribute(attr) }
|
|
54
|
+
end
|
|
98
55
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
211
|
+
generalization.general_attributes.map do |attr|
|
|
158
212
|
transformed = attr.dup
|
|
159
|
-
name_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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
34
|
+
Regexp.last_match(1)
|
|
27
35
|
end
|
|
28
|
-
|
|
29
|
-
nil
|
|
30
36
|
end
|
|
31
37
|
end
|
|
32
38
|
end
|
|
@@ -30,6 +30,11 @@ module Lutaml
|
|
|
30
30
|
|
|
31
31
|
# Eagerly build all lazy lookup indexes before freezing
|
|
32
32
|
def build_lookup_indexes
|
|
33
|
+
build_primary_indexes
|
|
34
|
+
build_secondary_indexes
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_primary_indexes
|
|
33
38
|
@objects_by_guid = build_group_index(objects, :ea_guid, single: true)
|
|
34
39
|
@attributes_by_object_id = build_group_index(attributes, :ea_object_id)
|
|
35
40
|
@operations_by_object_id = build_group_index(operations, :ea_object_id)
|
|
@@ -42,7 +47,9 @@ module Lutaml
|
|
|
42
47
|
@diagrams_by_package_id = build_group_index(diagrams, :package_id)
|
|
43
48
|
@diagram_objects_by_id = build_group_index(diagram_objects, :diagram_id)
|
|
44
49
|
@diagram_links_by_id = build_group_index(diagram_links, :diagramid)
|
|
45
|
-
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def build_secondary_indexes
|
|
46
53
|
@packages_by_id = build_group_index(packages, :package_id, single: true)
|
|
47
54
|
@connectors_by_id = build_group_index(connectors, :connector_id,
|
|
48
55
|
single: true)
|
|
@@ -39,7 +39,7 @@ module Lutaml
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
#{build_navigation_links(pkg_path)}
|
|
43
43
|
MARKDOWN
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -67,21 +67,8 @@ module Lutaml
|
|
|
67
67
|
return "" if parent.nil? && children.empty?
|
|
68
68
|
|
|
69
69
|
content = "## Inheritance\n\n"
|
|
70
|
-
|
|
71
|
-
if
|
|
72
|
-
parent_qname = @link_resolver.qualified_name(parent)
|
|
73
|
-
content += "**Extends**: [#{parent.name}](#{@link_resolver.class_link(parent_qname)})\n\n"
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
if children.any?
|
|
77
|
-
content += "**Extended by**:\n\n"
|
|
78
|
-
children.each do |child|
|
|
79
|
-
child_qname = @link_resolver.qualified_name(child)
|
|
80
|
-
content += "- [#{child.name}](#{@link_resolver.class_link(child_qname)})\n"
|
|
81
|
-
end
|
|
82
|
-
content += "\n"
|
|
83
|
-
end
|
|
84
|
-
|
|
70
|
+
content += build_parent_link(parent) if parent
|
|
71
|
+
content += build_children_links(children) if children.any?
|
|
85
72
|
content
|
|
86
73
|
rescue StandardError
|
|
87
74
|
""
|
|
@@ -138,15 +125,7 @@ module Lutaml
|
|
|
138
125
|
end
|
|
139
126
|
|
|
140
127
|
def format_association_row(association, klass)
|
|
141
|
-
|
|
142
|
-
target_end = association.member_end&.last
|
|
143
|
-
|
|
144
|
-
end_obj = if source_end&.type&.xmi_id == klass.xmi_id
|
|
145
|
-
target_end
|
|
146
|
-
else
|
|
147
|
-
source_end
|
|
148
|
-
end
|
|
149
|
-
|
|
128
|
+
end_obj = resolve_target_end(association, klass)
|
|
150
129
|
return "" unless end_obj&.type
|
|
151
130
|
|
|
152
131
|
target_qname = @link_resolver.qualified_name(end_obj.type)
|
|
@@ -172,6 +151,35 @@ module Lutaml
|
|
|
172
151
|
|
|
173
152
|
"#{content}\n"
|
|
174
153
|
end
|
|
154
|
+
|
|
155
|
+
def build_navigation_links(pkg_path)
|
|
156
|
+
"[Back to Package](#{@link_resolver.package_link(pkg_path)}) | [Back to Index](../index.md)"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def build_parent_link(parent)
|
|
160
|
+
parent_qname = @link_resolver.qualified_name(parent)
|
|
161
|
+
"**Extends**: [#{parent.name}](#{@link_resolver.class_link(parent_qname)})\n\n"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def build_children_links(children)
|
|
165
|
+
content = "**Extended by**:\n\n"
|
|
166
|
+
children.each do |child|
|
|
167
|
+
child_qname = @link_resolver.qualified_name(child)
|
|
168
|
+
content += "- [#{child.name}](#{@link_resolver.class_link(child_qname)})\n"
|
|
169
|
+
end
|
|
170
|
+
"#{content}\n"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def resolve_target_end(association, klass)
|
|
174
|
+
source_end = association.member_end&.first
|
|
175
|
+
target_end = association.member_end&.last
|
|
176
|
+
|
|
177
|
+
if source_end&.type&.xmi_id == klass.xmi_id
|
|
178
|
+
target_end
|
|
179
|
+
else
|
|
180
|
+
source_end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
175
183
|
end
|
|
176
184
|
end
|
|
177
185
|
end
|
|
@@ -3,28 +3,35 @@
|
|
|
3
3
|
module Lutaml
|
|
4
4
|
module UmlRepository
|
|
5
5
|
class IndexBuilder
|
|
6
|
-
def build_association_index
|
|
7
|
-
|
|
6
|
+
def build_association_index
|
|
7
|
+
index_document_associations
|
|
8
|
+
index_class_level_associations
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index_document_associations
|
|
8
12
|
@document.associations&.each do |assoc|
|
|
9
13
|
next unless assoc.xmi_id
|
|
10
14
|
|
|
11
15
|
@associations[assoc.xmi_id] = assoc
|
|
12
16
|
end
|
|
17
|
+
end
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
# Note: This requires qualified_names index to be built first
|
|
19
|
+
def index_class_level_associations
|
|
16
20
|
@qualified_names.each_value do |klass|
|
|
17
|
-
next unless
|
|
21
|
+
next unless klassifiable?(klass) && klass.associations
|
|
18
22
|
|
|
19
23
|
klass.associations.each do |assoc|
|
|
20
24
|
next unless assoc.xmi_id
|
|
21
25
|
|
|
22
|
-
# Avoid duplicates - only add if not already present
|
|
23
26
|
@associations[assoc.xmi_id] ||= assoc
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
31
|
+
def klassifiable?(klass)
|
|
32
|
+
klass.is_a?(Lutaml::Uml::Class) || klass.is_a?(Lutaml::Uml::DataType)
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
# Build the inheritance graph index
|
|
29
36
|
#
|
|
30
37
|
# Creates a hash mapping parent qualified names to arrays of
|
|
@@ -49,46 +56,46 @@ module Lutaml
|
|
|
49
56
|
#
|
|
50
57
|
# @param classes [Array<Lutaml::Uml::Class>] Classes to process
|
|
51
58
|
# @param package_path [String] Package path for these classes
|
|
52
|
-
def process_generalizations(classes, package_path)
|
|
59
|
+
def process_generalizations(classes, package_path)
|
|
53
60
|
return unless classes
|
|
54
61
|
|
|
55
62
|
classes.each do |klass|
|
|
56
63
|
next unless klass.name
|
|
57
64
|
|
|
58
65
|
child_qname = "#{package_path}::#{klass.name}"
|
|
66
|
+
index_generalization_edge(child_qname, klass, package_path)
|
|
67
|
+
index_inheritance_assoc_edges(child_qname, klass, package_path)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
59
70
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
parent_name = extract_parent_name(klass.generalization)
|
|
63
|
-
if parent_name
|
|
64
|
-
parent_qname = resolve_qualified_name(parent_name, package_path)
|
|
65
|
-
if parent_qname && child_qname != parent_qname
|
|
66
|
-
@inheritance_graph[parent_qname] ||= []
|
|
67
|
-
@inheritance_graph[parent_qname] << child_qname
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
+
def index_generalization_edge(child_qname, klass, package_path)
|
|
72
|
+
return unless klass.generalization
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
parent_name = extract_parent_name(klass.generalization)
|
|
75
|
+
return unless parent_name
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
next unless assoc.member_end_type == "inheritance"
|
|
77
|
+
parent_qname = resolve_qualified_name(parent_name, package_path)
|
|
78
|
+
return unless parent_qname && child_qname != parent_qname
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
(@inheritance_graph[parent_qname] ||= []) << child_qname
|
|
81
|
+
end
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
def index_inheritance_assoc_edges(child_qname, klass, package_path)
|
|
84
|
+
return unless klass.associations
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
next if child_qname == parent_qname
|
|
86
|
+
klass.associations.each do |assoc|
|
|
87
|
+
next unless assoc.member_end_type == "inheritance"
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
parent_name = assoc.member_end
|
|
90
|
+
next unless parent_name
|
|
91
|
+
|
|
92
|
+
parent_name = parent_name.name if parent_name.is_a?(Lutaml::Uml::Generalization)
|
|
93
|
+
next unless parent_name.is_a?(String) && !parent_name.empty?
|
|
94
|
+
|
|
95
|
+
parent_qname = resolve_qualified_name(parent_name, package_path)
|
|
96
|
+
next unless parent_qname && child_qname != parent_qname
|
|
97
|
+
|
|
98
|
+
(@inheritance_graph[parent_qname] ||= []) << child_qname
|
|
92
99
|
end
|
|
93
100
|
end
|
|
94
101
|
|
|
@@ -97,22 +104,10 @@ module Lutaml
|
|
|
97
104
|
# @param generalization [Lutaml::Uml::Generalization]
|
|
98
105
|
# Generalization object
|
|
99
106
|
# @return [String, nil] Parent class name
|
|
100
|
-
def extract_parent_name(generalization)
|
|
107
|
+
def extract_parent_name(generalization)
|
|
101
108
|
return nil unless generalization
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
if generalization.general
|
|
105
|
-
parent = generalization.general
|
|
106
|
-
return parent.name if parent
|
|
107
|
-
return parent.to_s if parent
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Check for name attribute directly
|
|
111
|
-
if generalization.name
|
|
112
|
-
return generalization.name
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
nil
|
|
110
|
+
name_from_general(generalization) || generalization.name
|
|
116
111
|
end
|
|
117
112
|
|
|
118
113
|
# Resolve a class name to its qualified name
|
|
@@ -124,6 +119,13 @@ module Lutaml
|
|
|
124
119
|
# @param name [String] Class name to resolve
|
|
125
120
|
# @param current_package_path [String] Current package context
|
|
126
121
|
# @return [String, nil] Resolved qualified name
|
|
122
|
+
def name_from_general(generalization)
|
|
123
|
+
parent = generalization.general
|
|
124
|
+
return nil unless parent
|
|
125
|
+
|
|
126
|
+
parent.respond_to?(:name) ? parent.name : parent.to_s
|
|
127
|
+
end
|
|
128
|
+
|
|
127
129
|
def resolve_qualified_name(name, current_package_path)
|
|
128
130
|
# If name contains "::", it might already be qualified
|
|
129
131
|
return name if @qualified_names.key?(name)
|