lutaml 0.9.27 → 0.9.29

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +36 -35
  3. data/bin/plantuml2lutaml +11 -7
  4. data/bin/yaml2lutaml +1 -1
  5. data/exe/lutaml-sysml +4 -2
  6. data/exe/lutaml-wsd2uml +11 -7
  7. data/exe/lutaml-yaml2uml +1 -1
  8. data/lib/lutaml/express/parsers/exp.rb +4 -4
  9. data/lib/lutaml/formatter/graphviz.rb +7 -7
  10. data/lib/lutaml/parser.rb +3 -31
  11. data/lib/lutaml/sysml/allocate.rb +6 -7
  12. data/lib/lutaml/sysml/allocated.rb +6 -6
  13. data/lib/lutaml/sysml/binding_connector.rb +6 -6
  14. data/lib/lutaml/sysml/block.rb +28 -25
  15. data/lib/lutaml/sysml/constraint_block.rb +11 -11
  16. data/lib/lutaml/sysml/copy.rb +5 -5
  17. data/lib/lutaml/sysml/derive_requirement.rb +6 -6
  18. data/lib/lutaml/sysml/nested_connector_end.rb +9 -9
  19. data/lib/lutaml/sysml/refine.rb +6 -6
  20. data/lib/lutaml/sysml/requirement_related.rb +6 -6
  21. data/lib/lutaml/sysml/satisfy.rb +6 -6
  22. data/lib/lutaml/sysml/test_case.rb +20 -19
  23. data/lib/lutaml/sysml/trace.rb +6 -6
  24. data/lib/lutaml/sysml/verify.rb +5 -5
  25. data/lib/lutaml/sysml/version.rb +1 -1
  26. data/lib/lutaml/sysml/xmi_file.rb +455 -415
  27. data/lib/lutaml/sysml.rb +1 -1
  28. data/lib/lutaml/uml/association.rb +4 -3
  29. data/lib/lutaml/uml/data_type.rb +1 -0
  30. data/lib/lutaml/uml/document.rb +4 -1
  31. data/lib/lutaml/uml/formatter/graphviz.rb +11 -13
  32. data/lib/lutaml/uml/has_attributes.rb +2 -2
  33. data/lib/lutaml/uml/has_members.rb +4 -3
  34. data/lib/lutaml/uml/node/class_node.rb +5 -7
  35. data/lib/lutaml/uml/node/field.rb +1 -3
  36. data/lib/lutaml/uml/node/method.rb +1 -3
  37. data/lib/lutaml/uml/node/relationship.rb +1 -3
  38. data/lib/lutaml/uml/operation.rb +6 -6
  39. data/lib/lutaml/uml/package.rb +3 -1
  40. data/lib/lutaml/uml/parsers/attribute.rb +1 -3
  41. data/lib/lutaml/uml/parsers/dsl.rb +11 -10
  42. data/lib/lutaml/uml/parsers/dsl_preprocessor.rb +7 -6
  43. data/lib/lutaml/uml/parsers/yaml.rb +2 -2
  44. data/lib/lutaml/uml/serializers/class.rb +1 -1
  45. data/lib/lutaml/uml/top_element.rb +9 -9
  46. data/lib/lutaml/uml/top_element_attribute.rb +6 -6
  47. data/lib/lutaml/uml/value.rb +6 -6
  48. data/lib/lutaml/version.rb +1 -1
  49. data/lib/lutaml/xmi/liquid_drops/association_drop.rb +31 -11
  50. data/lib/lutaml/xmi/liquid_drops/attribute_drop.rb +29 -11
  51. data/lib/lutaml/xmi/liquid_drops/cardinality_drop.rb +8 -2
  52. data/lib/lutaml/xmi/liquid_drops/constraint_drop.rb +6 -4
  53. data/lib/lutaml/xmi/liquid_drops/data_type_drop.rb +76 -18
  54. data/lib/lutaml/xmi/liquid_drops/diagram_drop.rb +13 -6
  55. data/lib/lutaml/xmi/liquid_drops/enum_drop.rb +16 -7
  56. data/lib/lutaml/xmi/liquid_drops/enum_owned_literal_drop.rb +10 -4
  57. data/lib/lutaml/xmi/liquid_drops/generalization_attribute_drop.rb +2 -0
  58. data/lib/lutaml/xmi/liquid_drops/generalization_drop.rb +10 -3
  59. data/lib/lutaml/xmi/liquid_drops/klass_drop.rb +98 -24
  60. data/lib/lutaml/xmi/liquid_drops/operation_drop.rb +11 -5
  61. data/lib/lutaml/xmi/liquid_drops/package_drop.rb +61 -18
  62. data/lib/lutaml/xmi/liquid_drops/root_drop.rb +14 -4
  63. data/lib/lutaml/xmi/parsers/xmi_base.rb +1031 -0
  64. data/lib/lutaml/xmi/parsers/xml.rb +23 -1018
  65. data/lib/lutaml/xml/parsers/xml.rb +6 -19
  66. data/lib/lutaml/xml.rb +0 -8
  67. data/lutaml.gemspec +2 -1
  68. metadata +32 -23
  69. data/lib/lutaml/express/lutaml_path/document_wrapper.rb +0 -22
  70. data/lib/lutaml/express/lutaml_path/formatter.rb +0 -14
  71. data/lib/lutaml/lutaml_path/document_wrapper.rb +0 -51
  72. data/lib/lutaml/uml/lutaml_path/document_wrapper.rb +0 -15
  73. data/lib/lutaml/xml/lutaml_path/document_wrapper.rb +0 -45
  74. data/lib/lutaml/xml/mapper.rb +0 -448
@@ -4,6 +4,7 @@ require "lutaml/uml/has_attributes"
4
4
  require "lutaml/uml/document"
5
5
  require "lutaml/xmi"
6
6
  require "xmi"
7
+ require "lutaml/xmi/parsers/xmi_base"
7
8
 
8
9
  module Lutaml
9
10
  module XMI
@@ -12,6 +13,8 @@ module Lutaml
12
13
  class XML
13
14
  attr_reader :xmi_cache, :xmi_root_model
14
15
 
16
+ include XMIBase
17
+
15
18
  class << self
16
19
  # @param xml [String] path to xml
17
20
  # @param options [Hash] options for parsing
@@ -44,23 +47,9 @@ module Lutaml
44
47
  xmi_model = get_xmi_model(xml)
45
48
  new.serialize_generalization_by_name(xmi_model, name, guidance)
46
49
  end
47
-
48
- private
49
-
50
- # @param xml [String]
51
- # @return [Shale::Mapper]
52
- def get_xmi_model(xml)
53
- Xmi::Sparx::SparxRoot.parse_xml(File.read(xml))
54
- end
55
- end
56
-
57
- # @param xmi_model [Shale::Mapper]
58
- def set_xmi_model(xmi_model)
59
- @xmi_cache = {}
60
- @xmi_root_model = xmi_model
61
50
  end
62
51
 
63
- # @param xmi_model [Shale::Mapper]
52
+ # @param xmi_model [Lutaml::Model::Serializable]
64
53
  # @return [Lutaml::Uml::Document]
65
54
  def parse(xmi_model)
66
55
  set_xmi_model(xmi_model)
@@ -69,7 +58,7 @@ module Lutaml
69
58
  ::Lutaml::Uml::Document.new(serialized_hash)
70
59
  end
71
60
 
72
- # @param xmi_model [Shale::Mapper]
61
+ # @param xmi_model [Lutaml::Model::Serializable]
73
62
  # @param with_gen: [Boolean]
74
63
  # @param with_absolute_path: [Boolean]
75
64
  # return [Hash]
@@ -82,1026 +71,42 @@ module Lutaml
82
71
  )
83
72
  end
84
73
 
85
- # @param xmi_model [Shale::Mapper]
74
+ # @param xmi_model [Lutaml::Model::Serializable]
86
75
  # @param guidance_yaml [String]
87
76
  # return [Liquid::Drop]
88
77
  def serialize_xmi_to_liquid(xmi_model, guidance_yaml = nil)
89
78
  set_xmi_model(xmi_model)
90
- serialized_hash = serialize_xmi(
91
- xmi_model,
79
+ model = xmi_model.model
80
+ options = {
81
+ xmi_root_model: @xmi_root_model,
82
+ xmi_cache: @xmi_cache,
92
83
  with_gen: true,
93
84
  with_absolute_path: true,
94
- )
85
+ }
95
86
  guidance = get_guidance(guidance_yaml)
96
- ::Lutaml::XMI::RootDrop.new(serialized_hash, guidance)
87
+ ::Lutaml::XMI::RootDrop.new(model, guidance, options)
97
88
  end
98
89
 
99
- # @param yaml [String]
100
- # @return [Hash]
101
- def get_guidance(yaml)
102
- return unless yaml
103
-
104
- YAML.safe_load(File.read(yaml, encoding: "UTF-8"))
105
- end
106
-
107
- # @param xmi_model [Shale::Mapper]
90
+ # @param xmi_model [Lutaml::Model::Serializable]
108
91
  # @param name [String]
109
92
  # @param guidance_yaml [String]
110
93
  # @return [Hash]
111
- def serialize_generalization_by_name(xmi_model, name,
94
+ def serialize_generalization_by_name(xmi_model, name, # rubocop:disable Metrics/MethodLength
112
95
  guidance_yaml = nil)
113
96
  set_xmi_model(xmi_model)
114
- model = xmi_model.model
115
97
  klass = find_klass_packaged_element(name)
116
- serialized_hash = build_klass_hash(klass, model, with_gen: true)
117
98
  guidance = get_guidance(guidance_yaml)
118
- ::Lutaml::XMI::KlassDrop.new(serialized_hash, guidance)
119
- end
120
-
121
- private
122
-
123
- # @param xmi_model [Shale::Mapper]
124
- # @param with_gen: [Boolean]
125
- # @param with_absolute_path: [Boolean]
126
- # @return [Hash]
127
- # @note xpath: //uml:Model[@xmi:type="uml:Model"]
128
- def serialize_to_hash(xmi_model,
129
- with_gen: false, with_absolute_path: false)
130
- model = xmi_model.model
131
- {
132
- name: model.name,
133
- packages: serialize_model_packages(
134
- model,
135
- with_gen: with_gen,
136
- with_absolute_path: with_absolute_path,
137
- ),
138
- }
139
- end
140
-
141
- # @param model [Shale::Mapper]
142
- # @param with_gen: [Boolean]
143
- # @param with_absolute_path: [Boolean]
144
- # @param absolute_path: [String]
145
- # @return [Array<Hash>]
146
- # @note xpath ./packagedElement[@xmi:type="uml:Package"]
147
- def serialize_model_packages(model, # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
148
- with_gen: false, with_absolute_path: false, absolute_path: "")
149
- packages = model.packaged_element.select do |e|
150
- e.type?("uml:Package")
151
- end
152
-
153
- if with_absolute_path
154
- absolute_path = "#{absolute_path}::#{model.name}"
155
- end
156
-
157
- packages.map do |package| # rubocop:disable Metrics/BlockLength
158
- h = {
159
- xmi_id: package.id,
160
- name: get_package_name(package),
161
- classes: serialize_model_classes(
162
- package, model,
163
- with_gen: with_gen,
164
- with_absolute_path: with_absolute_path,
165
- absolute_path: "#{absolute_path}::#{package.name}"
166
- ),
167
- enums: serialize_model_enums(package),
168
- data_types: serialize_model_data_types(package),
169
- diagrams: serialize_model_diagrams(
170
- package.id,
171
- with_package: with_gen,
172
- ),
173
- packages: serialize_model_packages(
174
- package,
175
- with_gen: with_gen,
176
- with_absolute_path: with_absolute_path,
177
- absolute_path: absolute_path,
178
- ),
179
- definition: doc_node_attribute_value(package.id, "documentation"),
180
- stereotype: doc_node_attribute_value(package.id, "stereotype"),
181
- }
182
-
183
- if with_absolute_path
184
- h[:absolute_path] = "#{absolute_path}::#{package.name}"
185
- end
186
-
187
- h
188
- end
189
- end
190
-
191
- # @param package [Shale::Mapper]
192
- # @return [String]
193
- def get_package_name(package) # rubocop:disable Metrics/AbcSize
194
- return package.name unless package.name.nil?
195
-
196
- connector = fetch_connector(package.id)
197
- if connector.target&.model && connector.target.model&.name
198
- return "#{connector.target.model.name} " \
199
- "(#{package.type.split(':').last})"
200
- end
201
-
202
- "unnamed"
203
- end
204
-
205
- # @param package [Shale::Mapper]
206
- # @param model [Shale::Mapper]
207
- # @param with_gen: [Boolean]
208
- # @param with_absolute_path: [Boolean]
209
- # @return [Array<Hash>]
210
- # @note xpath ./packagedElement[@xmi:type="uml:Class" or
211
- # @xmi:type="uml:AssociationClass"]
212
- def serialize_model_classes(package, model, # rubocop:disable Metrics/MethodLength
213
- with_gen: false, with_absolute_path: false, absolute_path: "")
214
- klasses = package.packaged_element.select do |e|
215
- e.type?("uml:Class") || e.type?("uml:AssociationClass") ||
216
- e.type?("uml:Interface")
217
- end
218
-
219
- klasses.map do |klass|
220
- h = build_klass_hash(
221
- klass, model,
222
- with_gen: with_gen
223
- )
224
-
225
- h[:absolute_path] = absolute_path if with_absolute_path
226
-
227
- h
228
- end
229
- end
230
-
231
- # @param klass [Shale::Mapper]
232
- # @param model [Shale::Mapper]
233
- # @param with_gen: [Boolean]
234
- # @param with_absolute_path: [Boolean]
235
- # @return [Hash]
236
- def build_klass_hash(klass, model, # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
237
- with_gen: false, with_absolute_path: false, absolute_path: "")
238
- klass_hash = {
239
- xmi_id: klass.id,
240
- name: klass.name,
241
- package: model,
242
- type: klass.type.split(":").last,
243
- attributes: serialize_class_attributes(klass),
244
- associations: serialize_model_associations(klass.id),
245
- operations: serialize_class_operations(klass),
246
- constraints: serialize_class_constraints(klass.id),
247
- is_abstract: doc_node_attribute_value(klass.id, "isAbstract"),
248
- definition: doc_node_attribute_value(klass.id, "documentation"),
249
- stereotype: doc_node_attribute_value(klass.id, "stereotype"),
99
+ options = {
100
+ xmi_root_model: @xmi_root_model,
101
+ xmi_cache: @xmi_cache,
102
+ with_gen: true,
103
+ with_absolute_path: true,
250
104
  }
251
-
252
- klass_hash[:absolute_path] = absolute_path if with_absolute_path
253
-
254
- if with_gen && klass.type?("uml:Class")
255
- klass_hash[:generalization] = serialize_generalization(klass)
256
- end
257
-
258
- klass_hash
259
- end
260
-
261
- # @param klass [Shale::Mapper]
262
- # # @return [Hash]
263
- def serialize_generalization(klass)
264
- general_hash, next_general_node_id = get_top_level_general_hash(klass)
265
- return general_hash unless next_general_node_id
266
-
267
- general_hash[:general] = serialize_generalization_attributes(
268
- next_general_node_id,
269
- )
270
-
271
- general_hash
272
- end
273
-
274
- # @param klass [Shale::Mapper]
275
- # @return [Array<Hash>]
276
- def get_top_level_general_hash(klass) # rubocop:disable Metrics/AbcSize
277
- general_hash, next_general_node_id = get_general_hash(klass.id)
278
- general_hash[:name] = klass.name
279
- general_hash[:type] = klass.type
280
- general_hash[:definition] = lookup_general_documentation(klass.id)
281
- general_hash[:stereotype] = doc_node_attribute_value(
282
- klass.id, "stereotype"
105
+ ::Lutaml::XMI::KlassDrop.new(
106
+ klass,
107
+ guidance,
108
+ options,
283
109
  )
284
-
285
- # update_inherited_attributes(general_hash)
286
- # update_gen_attributes(general_hash)
287
-
288
- [general_hash, next_general_node_id]
289
- end
290
-
291
- def lookup_general_documentation(klass_id)
292
- # lookup_attribute_documentation(klass_id) ||
293
- # lookup_element_prop_documentation(klass_id)
294
-
295
- lookup_element_prop_documentation(klass_id)
296
- end
297
-
298
- def update_gen_attributes(general_hash)
299
- general_hash[:gen_attributes] = serialize_gen_attributes
300
- end
301
-
302
- def update_inherited_attributes(general_hash)
303
- general_hash[:gml_attributes] = serialize_gml_attributes
304
- general_hash[:core_attributes] = serialize_core_attributes
305
- end
306
-
307
- # @param xmi_id [String]
308
- # @param model [Shale::Mapper]
309
- # @return [Array<Hash>]
310
- # @note get generalization node and its owned attributes
311
- def serialize_generalization_attributes(general_id)
312
- general_hash, next_general_node_id = get_general_hash(general_id)
313
-
314
- if next_general_node_id
315
- general_hash[:general] = serialize_generalization_attributes(
316
- next_general_node_id,
317
- )
318
- end
319
-
320
- general_hash
321
- end
322
-
323
- # @param xmi_id [String]
324
- # @return [Shale::Mapper]
325
- def get_general_node(xmi_id)
326
- find_packaged_element_by_id(xmi_id)
327
- end
328
-
329
- # @param general_node [Shale::Mapper]
330
- # @return [Hash]
331
- def get_general_attributes(general_node)
332
- serialize_class_attributes(general_node, with_assoc: true)
333
- end
334
-
335
- # @param general_node [Shale::Mapper]
336
- # @return [String]
337
- def get_next_general_node_id(general_node)
338
- general_node.generalization.first&.general
339
- end
340
-
341
- # @param general_id [String]
342
- # @return [Array<Hash>]
343
- def get_general_hash(general_id)
344
- general_node = get_general_node(general_id)
345
- return [] unless general_node
346
-
347
- general_node_attrs = get_general_attributes(general_node)
348
- general_upper_klass = find_upper_level_packaged_element(general_id)
349
- next_general_node_id = get_next_general_node_id(general_node)
350
-
351
- [
352
- {
353
- general_id: general_id,
354
- general_name: general_node.name,
355
- general_attributes: general_node_attrs,
356
- general_upper_klass: general_upper_klass,
357
- general: {},
358
- },
359
- next_general_node_id,
360
- ]
361
- end
362
-
363
- # @param id [String]
364
- # @return [Shale::Mapper]
365
- def find_packaged_element_by_id(id)
366
- all_packaged_elements.find { |e| e.id == id }
367
- end
368
-
369
- # @param id [String]
370
- # @return [Shale::Mapper]
371
- def find_upper_level_packaged_element(klass_id)
372
- upper_klass = all_packaged_elements.find do |e|
373
- e.packaged_element.find { |pe| pe.id == klass_id }
374
- end
375
- upper_klass&.name
376
- end
377
-
378
- # @param path [String]
379
- # @return [Shale::Mapper]
380
- def find_klass_packaged_element(path)
381
- lutaml_path = Lutaml::Path.parse(path)
382
- if lutaml_path.segments.count == 1
383
- return find_klass_packaged_element_by_name(path)
384
- end
385
-
386
- find_klass_packaged_element_by_path(lutaml_path)
387
- end
388
-
389
- # @param path [Lutaml::Path::ElementPath]
390
- # @return [Shale::Mapper]
391
- def find_klass_packaged_element_by_path(path)
392
- if path.absolute?
393
- iterate_packaged_element(
394
- @xmi_root_model.model, path.segments.map(&:name)
395
- )
396
- else
397
- iterate_relative_packaged_element(path.segments.map(&:name))
398
- end
399
- end
400
-
401
- # @param name_array [Array<String>]
402
- # @return [Shale::Mapper]
403
- def iterate_relative_packaged_element(name_array)
404
- # match the first element in the name_array
405
- matched_elements = all_packaged_elements.select do |e|
406
- e.type?("uml:Package") && e.name == name_array[0]
407
- end
408
-
409
- # match the rest elements in the name_array
410
- result = matched_elements.map do |e|
411
- iterate_packaged_element(e, name_array, type: "uml:Class")
412
- end
413
-
414
- result.compact.first
415
- end
416
-
417
- # @param model [Shale::Mapper]
418
- # @param name_array [Array<String>]
419
- # @param index: [Integer]
420
- # @param type: [String]
421
- # @return [Shale::Mapper]
422
- def iterate_packaged_element(model, name_array,
423
- index: 1, type: "uml:Package")
424
- return model if index == name_array.count
425
-
426
- model = model.packaged_element.find do |p|
427
- p.name == name_array[index] && p.type?(type)
428
- end
429
-
430
- return nil if model.nil?
431
-
432
- index += 1
433
- type = index == name_array.count - 1 ? "uml:Class" : "uml:Package"
434
- iterate_packaged_element(model, name_array, index: index, type: type)
435
- end
436
-
437
- # @param name [String]
438
- # @return [Shale::Mapper]
439
- def find_klass_packaged_element_by_name(name)
440
- all_packaged_elements.find do |e|
441
- e.type?("uml:Class") && e.name == name
442
- end
443
- end
444
-
445
- # @param name [String]
446
- # @return [Shale::Mapper]
447
- def find_packaged_element_by_name(name)
448
- all_packaged_elements.find do |e|
449
- e.name == name
450
- end
451
- end
452
-
453
- # @param package [Shale::Mapper]
454
- # @return [Array<Hash>]
455
- # @note xpath ./packagedElement[@xmi:type="uml:Enumeration"]
456
- def serialize_model_enums(package)
457
- package.packaged_element.select { |e| e.type?("uml:Enumeration") }
458
- .map do |enum|
459
- {
460
- xmi_id: enum.id,
461
- name: enum.name,
462
- values: serialize_enum_owned_literal(enum),
463
- definition: doc_node_attribute_value(enum.id, "documentation"),
464
- stereotype: doc_node_attribute_value(enum.id, "stereotype"),
465
- }
466
- end
467
- end
468
-
469
- # @param model [Shale::Mapper]
470
- # @return [Hash]
471
- # @note xpath .//ownedLiteral[@xmi:type="uml:EnumerationLiteral"]
472
- def serialize_enum_owned_literal(enum)
473
- owned_literals = enum.owned_literal.select do |owned_literal|
474
- owned_literal.type? "uml:EnumerationLiteral"
475
- end
476
-
477
- owned_literals.map do |owned_literal|
478
- # xpath .//type
479
- uml_type_id = owned_literal&.uml_type&.idref
480
-
481
- {
482
- name: owned_literal.name,
483
- type: lookup_entity_name(uml_type_id) || uml_type_id,
484
- definition: lookup_attribute_documentation(owned_literal.id),
485
- }
486
- end
487
- end
488
-
489
- # @param model [Shale::Mapper]
490
- # @return [Array<Hash>]
491
- # @note xpath ./packagedElement[@xmi:type="uml:DataType"]
492
- def serialize_model_data_types(model)
493
- all_data_type_elements = []
494
- select_all_packaged_elements(all_data_type_elements, model,
495
- "uml:DataType")
496
- all_data_type_elements.map do |klass|
497
- {
498
- xmi_id: klass.id,
499
- name: klass.name,
500
- attributes: serialize_class_attributes(klass),
501
- operations: serialize_class_operations(klass),
502
- associations: serialize_model_associations(klass.id),
503
- constraints: serialize_class_constraints(klass.id),
504
- is_abstract: doc_node_attribute_value(klass.id, "isAbstract"),
505
- definition: doc_node_attribute_value(klass.id, "documentation"),
506
- stereotype: doc_node_attribute_value(klass.id, "stereotype"),
507
- }
508
- end
509
- end
510
-
511
- # @param node_id [String]
512
- # @return [Array<Hash>]
513
- # @note xpath %(//diagrams/diagram/model[@package="#{node['xmi:id']}"])
514
- def serialize_model_diagrams(node_id, with_package: false) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
515
- diagrams = @xmi_root_model.extension.diagrams.diagram.select do |d|
516
- d.model.package == node_id
517
- end
518
-
519
- diagrams.map do |diagram|
520
- h = {
521
- xmi_id: diagram.id,
522
- name: diagram.properties.name,
523
- definition: diagram.properties.documentation,
524
- }
525
-
526
- if with_package
527
- package_id = diagram.model.package
528
- h[:package_id] = package_id
529
- h[:package_name] = find_packaged_element_by_id(package_id)&.name
530
- end
531
-
532
- h
533
- end
534
- end
535
-
536
- # @param xmi_id [String]
537
- # @return [Array<Hash>]
538
- # @note xpath %(//element[@xmi:idref="#{xmi_id}"]/links/*)
539
- def serialize_model_associations(xmi_id)
540
- matched_element = @xmi_root_model.extension.elements.element
541
- .find { |e| e.idref == xmi_id }
542
-
543
- return if !matched_element ||
544
- !matched_element.links ||
545
- matched_element.links.association.empty?
546
-
547
- matched_element.links.association.map do |assoc|
548
- link_member = assoc.start == xmi_id ? "end" : "start"
549
- linke_owner_name = link_member == "start" ? "end" : "start"
550
-
551
- member_end, member_end_type, member_end_cardinality,
552
- member_end_attribute_name, member_end_xmi_id =
553
- serialize_member_type(xmi_id, assoc, link_member)
554
-
555
- owner_end = serialize_owned_type(xmi_id, assoc, linke_owner_name)
556
-
557
- if member_end && ((member_end_type != "aggregation") ||
558
- (member_end_type == "aggregation" && member_end_attribute_name))
559
-
560
- doc_node_name = (link_member == "start" ? "source" : "target")
561
- definition = fetch_definition_node_value(assoc.id, doc_node_name)
562
-
563
- {
564
- xmi_id: assoc.id,
565
- member_end: member_end,
566
- member_end_type: member_end_type,
567
- member_end_cardinality: member_end_cardinality,
568
- member_end_attribute_name: member_end_attribute_name,
569
- member_end_xmi_id: member_end_xmi_id,
570
- owner_end: owner_end,
571
- owner_end_xmi_id: xmi_id,
572
- definition: definition,
573
- }
574
- end
575
- end
576
- end
577
-
578
- # @param link_id [String]
579
- # @return [Shale::Mapper]
580
- # @note xpath %(//connector[@xmi:idref="#{link_id}"])
581
- def fetch_connector(link_id)
582
- @xmi_root_model.extension.connectors.connector.find do |con|
583
- con.idref == link_id
584
- end
585
- end
586
-
587
- # @param link_id [String]
588
- # @param node_name [String] source or target
589
- # @return [String]
590
- # @note xpath
591
- # %(//connector[@xmi:idref="#{link_id}"]/#{node_name}/documentation)
592
- def fetch_definition_node_value(link_id, node_name)
593
- connector_node = fetch_connector(link_id)
594
- connector_node.send(node_name.to_sym).documentation
595
- end
596
-
597
- # @param klass [Shale::Mapper]
598
- # @return [Array<Hash>]
599
- # @note xpath .//ownedOperation
600
- def serialize_class_operations(klass)
601
- klass.owned_operation.map do |operation|
602
- uml_type = operation.uml_type.first
603
- uml_type_idref = uml_type.idref if uml_type
604
-
605
- if operation.association.nil?
606
- {
607
- id: operation.id,
608
- xmi_id: uml_type_idref,
609
- name: operation.name,
610
- definition: lookup_attribute_documentation(operation.id),
611
- }
612
- end
613
- end.compact
614
- end
615
-
616
- # @param klass_id [String]
617
- # @return [Array<Hash>]
618
- # @note xpath ./constraints/constraint
619
- def serialize_class_constraints(klass_id)
620
- connector_node = fetch_connector(klass_id)
621
-
622
- if connector_node
623
- # In ea-xmi-2.5.1, constraints are moved to source/target under
624
- # connectors
625
- constraints = %i[source target].map do |st|
626
- connector_node.send(st).constraints.constraint
627
- end.flatten
628
-
629
- constraints.map do |constraint|
630
- {
631
- name: HTMLEntities.new.decode(constraint.name),
632
- type: constraint.type,
633
- weight: constraint.weight,
634
- status: constraint.status,
635
- }
636
- end
637
- end
638
- end
639
-
640
- # @param owner_xmi_id [String]
641
- # @param link [Shale::Mapper]
642
- # @param link_member_name [String]
643
- # @return [String]
644
- def serialize_owned_type(owner_xmi_id, link, linke_owner_name)
645
- case link.name
646
- when "NoteLink"
647
- return
648
- when "Generalization"
649
- return generalization_association(owner_xmi_id, link)
650
- end
651
-
652
- xmi_id = link.send(linke_owner_name.to_sym)
653
- lookup_entity_name(xmi_id) || connector_source_name(xmi_id)
654
-
655
- # not necessary
656
- # if link.name == "Association"
657
- # owned_cardinality, owned_attribute_name =
658
- # fetch_assoc_connector(link.id, "source")
659
- # else
660
- # owned_cardinality, owned_attribute_name =
661
- # fetch_owned_attribute_node(xmi_id)
662
- # end
663
- # [owner_end, owned_cardinality, owned_attribute_name]
664
- # owner_end
665
- end
666
-
667
- # @param owner_xmi_id [String]
668
- # @param link [Shale::Mapper]
669
- # @return [Array<String, String>]
670
- def serialize_member_end(owner_xmi_id, link)
671
- case link.name
672
- when "NoteLink"
673
- return
674
- when "Generalization"
675
- return generalization_association(owner_xmi_id, link)
676
- end
677
-
678
- xmi_id = link.start
679
- source_or_target = :source
680
-
681
- if link.start == owner_xmi_id
682
- xmi_id = link.end
683
- source_or_target = :target
684
- end
685
-
686
- member_end = member_end_name(xmi_id, source_or_target, link.name)
687
- [member_end, xmi_id]
688
- end
689
-
690
- # @param xmi_id [String]
691
- # @param source_or_target [Symbol]
692
- # @return [String]
693
- def member_end_name(xmi_id, source_or_target, link_name)
694
- connector_label = connector_labels(xmi_id, source_or_target)
695
- entity_name = lookup_entity_name(xmi_id)
696
- connector_name = connector_name_by_source_or_target(
697
- xmi_id, source_or_target
698
- )
699
-
700
- case link_name
701
- when "Aggregation"
702
- connector_label || entity_name || connector_name
703
- else
704
- entity_name || connector_name
705
- end
706
- end
707
-
708
- # @param owner_xmi_id [String]
709
- # @param link [Shale::Mapper]
710
- # @param link_member_name [String]
711
- # @return [Array<String, String, Hash, String, String>]
712
- def serialize_member_type(owner_xmi_id, link, link_member_name)
713
- member_end, xmi_id = serialize_member_end(owner_xmi_id, link)
714
-
715
- if link.name == "Association"
716
- connector_type = link_member_name == "start" ? "source" : "target"
717
- member_end_cardinality, member_end_attribute_name =
718
- fetch_assoc_connector(link.id, connector_type)
719
- else
720
- member_end_cardinality, member_end_attribute_name =
721
- fetch_owned_attribute_node(xmi_id)
722
- end
723
-
724
- [member_end, "aggregation", member_end_cardinality,
725
- member_end_attribute_name, xmi_id]
726
- end
727
-
728
- # @param link_id [String]
729
- # @param connector_type [String]
730
- # @return [Array<Hash, String>]
731
- # @note xpath %(//connector[@xmi:idref="#{link_id}"]/#{connector_type})
732
- def fetch_assoc_connector(link_id, connector_type)
733
- assoc_connector = fetch_connector(link_id).send(connector_type.to_sym)
734
-
735
- if assoc_connector
736
- assoc_connector_type = assoc_connector.type
737
- if assoc_connector_type&.multiplicity
738
- cardinality = assoc_connector_type.multiplicity.split("..")
739
- cardinality.unshift("1") if cardinality.length == 1
740
- min, max = cardinality
741
- end
742
- assoc_connector_role = assoc_connector.role
743
- # Does role has name attribute? Or get name from model?
744
- # attribute_name = assoc_connector_role.name if assoc_connector_role
745
- attribute_name = assoc_connector.model.name if assoc_connector_role
746
- cardinality = cardinality_min_max_value(min, max)
747
- end
748
-
749
- [cardinality, attribute_name]
750
- end
751
-
752
- # @param owner_xmi_id [String]
753
- # @param link [Shale::Mapper]
754
- # @return [Array<String, String, Hash, String, String>]
755
- # @note match return value of serialize_member_type
756
- def generalization_association(owner_xmi_id, link)
757
- member_end_type = "generalization"
758
- xmi_id = link.start
759
- source_or_target = :source
760
-
761
- if link.start == owner_xmi_id
762
- member_end_type = "inheritance"
763
- xmi_id = link.end
764
- source_or_target = :target
765
- end
766
-
767
- member_end = member_end_name(xmi_id, source_or_target, link.name)
768
-
769
- member_end_cardinality, _member_end_attribute_name =
770
- fetch_owned_attribute_node(xmi_id)
771
-
772
- [member_end, member_end_type, member_end_cardinality, nil, xmi_id]
773
- end
774
-
775
- # Multiple items if search type is idref. Should search association?
776
- # @param xmi_id [String]
777
- # @return [Array<Hash, String>]
778
- # @note xpath
779
- # %(//ownedAttribute[@association]/type[@xmi:idref="#{xmi_id}"])
780
- def fetch_owned_attribute_node(xmi_id)
781
- all_elements = all_packaged_elements
782
-
783
- owned_attributes = all_elements.map(&:owned_attribute).flatten
784
- oa = owned_attributes.find do |a|
785
- !!a.association && a.uml_type && a.uml_type.idref == xmi_id
786
- end
787
-
788
- if oa
789
- cardinality = cardinality_min_max_value(
790
- oa.lower_value&.value, oa.upper_value&.value
791
- )
792
- oa_name = oa.name
793
- end
794
-
795
- [cardinality, oa_name]
796
- end
797
-
798
- # @param klass_id [String]
799
- # @return [Shale::Mapper]
800
- # @note xpath %(//element[@xmi:idref="#{klass['xmi:id']}"])
801
- def fetch_element(klass_id)
802
- @xmi_root_model.extension.elements.element.find do |e|
803
- e.idref == klass_id
804
- end
805
- end
806
-
807
- # @param klass [Shale::Mapper]
808
- # @param with_assoc [Boolean]
809
- # @return [Array<Hash>]
810
- # @note xpath .//ownedAttribute[@xmi:type="uml:Property"]
811
- def serialize_class_attributes(klass, with_assoc: false)
812
- klass.owned_attribute.select { |attr| attr.type?("uml:Property") }
813
- .map do |oa|
814
- if with_assoc || oa.association.nil?
815
- attrs = build_class_attributes(oa)
816
-
817
- if with_assoc && oa.association
818
- attrs[:association] = oa.association
819
- attrs[:definition] = loopup_assoc_def(oa.association)
820
- attrs[:type_ns] = get_ns_by_xmi_id(attrs[:xmi_id])
821
- end
822
-
823
- attrs
824
- end
825
- end.compact
826
- end
827
-
828
- def loopup_assoc_def(association)
829
- connector = fetch_connector(association)
830
- connector&.documentation&.value
831
- end
832
-
833
- # @return [Array<Hash>]
834
- def serialize_gml_attributes
835
- element = find_packaged_element_by_name("_Feature")
836
- attrs = serialize_class_attributes(element, with_assoc: true)
837
- attrs.each { |attr| attr[:upper_klass] = "gml" }
838
- end
839
-
840
- # @return [Array<Hash>]
841
- def serialize_core_attributes
842
- element = find_packaged_element_by_name("_CityObject")
843
- attrs = serialize_class_attributes(element, with_assoc: false)
844
- attrs.each { |attr| attr[:upper_klass] = "core" }
845
- end
846
-
847
- # @return [Array<Hash>]
848
- def select_gen_attributes
849
- element = find_packaged_element_by_name("gen")
850
- gen_attr_element = find_packaged_element_by_name("_genericAttribute")
851
-
852
- element.packaged_element.select do |e|
853
- e.type?("uml:Class") &&
854
- e.generalization&.first&.general == gen_attr_element.id
855
- end
856
- end
857
-
858
- # @return [Array<Hash>]
859
- def serialize_gen_attributes
860
- klasses = select_gen_attributes
861
-
862
- klasses.map do |klass|
863
- attr = serialize_class_attributes(klass, with_assoc: false)
864
- attr.first[:name] = klass.name
865
- attr.first[:type] = "gen:#{klass.name}"
866
- attr.first[:upper_klass] = "gen"
867
- attr
868
- end.flatten!
869
- end
870
-
871
- # @param type [String]
872
- # @return [String]
873
- def get_ns_by_type(type)
874
- return unless type
875
-
876
- p = find_klass_packaged_element_by_name(type)
877
- return unless p
878
-
879
- find_upper_level_packaged_element(p.id)
880
- end
881
-
882
- # @param xmi_id [String]
883
- # @return [String]
884
- def get_ns_by_xmi_id(xmi_id)
885
- return unless xmi_id
886
-
887
- p = find_packaged_element_by_id(xmi_id)
888
- return unless p
889
-
890
- find_upper_level_packaged_element(p.id)
891
- end
892
-
893
- # @param klass_id [String]
894
- # @return [Array<Hash>]
895
- def build_class_attributes(owned_attr) # rubocop:disable Metrics/MethodLength
896
- uml_type = owned_attr.uml_type
897
- uml_type_idref = uml_type.idref if uml_type
898
-
899
- {
900
- id: owned_attr.id,
901
- name: owned_attr.name,
902
- type: lookup_entity_name(uml_type_idref) || uml_type_idref,
903
- xmi_id: uml_type_idref,
904
- is_derived: owned_attr.is_derived,
905
- cardinality: cardinality_min_max_value(
906
- owned_attr.lower_value&.value,
907
- owned_attr.upper_value&.value,
908
- ),
909
- definition: lookup_attribute_documentation(owned_attr.id),
910
- }
911
- end
912
-
913
- # @param min [String]
914
- # @param max [String]
915
- # @return [Hash]
916
- def cardinality_min_max_value(min, max)
917
- {
918
- min: min,
919
- max: max,
920
- }
921
- end
922
-
923
- # @node [Shale::Mapper]
924
- # @attr_name [String]
925
- # @return [String]
926
- # @note xpath %(//element[@xmi:idref="#{xmi_id}"]/properties)
927
- def doc_node_attribute_value(node_id, attr_name)
928
- doc_node = fetch_element(node_id)
929
- return unless doc_node
930
-
931
- doc_node.properties&.send(Shale::Utils.snake_case(attr_name).to_sym)
932
- end
933
-
934
- # @param xmi_id [String]
935
- # @return [Shale::Mapper]
936
- # @note xpath %(//attribute[@xmi:idref="#{xmi_id}"])
937
- def fetch_attribute_node(xmi_id)
938
- attribute_node = nil
939
- @xmi_root_model.extension.elements.element.each do |e|
940
- if e.attributes&.attribute
941
- e.attributes.attribute.each do |a|
942
- attribute_node = a if a.idref == xmi_id
943
- end
944
- end
945
- end
946
- attribute_node
947
- end
948
-
949
- # @param xmi_id [String]
950
- # @return [String]
951
- # @note xpath %(//attribute[@xmi:idref="#{xmi_id}"]/documentation)
952
- def lookup_attribute_documentation(xmi_id)
953
- attribute_node = fetch_attribute_node(xmi_id)
954
-
955
- return unless attribute_node&.documentation
956
-
957
- attribute_node&.documentation&.value
958
- end
959
-
960
- # @param xmi_id [String]
961
- # @return [String]
962
- def lookup_element_prop_documentation(xmi_id)
963
- element_node = @xmi_root_model.extension.elements.element.find do |e|
964
- e.idref == xmi_id
965
- end
966
-
967
- return unless element_node&.properties
968
-
969
- element_node&.properties&.documentation
970
- end
971
-
972
- # @param xmi_id [String]
973
- # @return [String]
974
- def lookup_entity_name(xmi_id)
975
- model_node_name_by_xmi_id(xmi_id) if @xmi_cache.empty?
976
- @xmi_cache[xmi_id]
977
- end
978
-
979
- # @param xmi_id [String]
980
- # @param source_or_target [String]
981
- # @return [String]
982
- def connector_node_by_id(xmi_id, source_or_target)
983
- @xmi_root_model.extension.connectors.connector.find do |con|
984
- con.send(source_or_target.to_sym).idref == xmi_id
985
- end
986
- end
987
-
988
- # @param xmi_id [String]
989
- # @param source_or_target [String]
990
- # @return [String]
991
- def connector_name_by_source_or_target(xmi_id, source_or_target)
992
- node = connector_node_by_id(xmi_id, source_or_target)
993
- return if node.nil? ||
994
- node.send(source_or_target.to_sym).nil? ||
995
- node.send(source_or_target.to_sym).model.nil?
996
-
997
- node.send(source_or_target.to_sym).model.name
998
- end
999
-
1000
- # @param xmi_id [String]
1001
- # @param source_or_target [String]
1002
- # @return [String]
1003
- def connector_labels(xmi_id, source_or_target)
1004
- node = connector_node_by_id(xmi_id, source_or_target)
1005
- return if node.nil?
1006
-
1007
- node.labels&.rt || node.labels&.lt
1008
- end
1009
-
1010
- # @param xmi_id [String]
1011
- # @return [String]
1012
- # @note xpath %(//source[@xmi:idref="#{xmi_id}"]/model)
1013
- def connector_source_name(xmi_id)
1014
- connector_name_by_source_or_target(xmi_id, :source)
1015
- end
1016
-
1017
- # @param xmi_id [String]
1018
- # @return [String]
1019
- # @note xpath %(//target[@xmi:idref="#{xmi_id}"]/model)
1020
- def connector_target_name(xmi_id)
1021
- connector_name_by_source_or_target(xmi_id, :target)
1022
- end
1023
-
1024
- # @param xmi_id [String]
1025
- # @return [String]
1026
- # @note xpath %(//*[@xmi:id="#{xmi_id}"])
1027
- def model_node_name_by_xmi_id(xmi_id)
1028
- id_name_mapping = Hash.new
1029
- map_id_name(id_name_mapping, @xmi_root_model)
1030
- @xmi_cache = id_name_mapping
1031
- @xmi_cache[xmi_id]
1032
- end
1033
-
1034
- # @return [Array<Xmi::Uml::PackagedElement>]
1035
- def all_packaged_elements
1036
- all_elements = []
1037
- packaged_element_roots = @xmi_root_model.model.packaged_element +
1038
- @xmi_root_model.extension.primitive_types.packaged_element +
1039
- @xmi_root_model.extension.profiles.profile.map(&:packaged_element)
1040
-
1041
- packaged_element_roots.flatten.each do |e|
1042
- select_all_packaged_elements(all_elements, e, nil)
1043
- end
1044
-
1045
- all_elements
1046
- end
1047
-
1048
- # @param items [Array<Shale::Mapper>]
1049
- # @param model [Shale::Mapper]
1050
- # @param type [String] nil for any
1051
- def select_all_items(items, model, type, method)
1052
- iterate_tree(items, model, type, method.to_sym)
1053
- end
1054
-
1055
- # @param all_elements [Array<Shale::Mapper>]
1056
- # @param model [Shale::Mapper]
1057
- # @param type [String] nil for any
1058
- # @note xpath ./packagedElement[@xmi:type="#{type}"]
1059
- def select_all_packaged_elements(all_elements, model, type)
1060
- select_all_items(all_elements, model, type, :packaged_element)
1061
- all_elements.delete_if do |e|
1062
- !e.is_a?(Xmi::Uml::PackagedElement)
1063
- end
1064
- end
1065
-
1066
- # @param result [Array<Shale::Mapper>]
1067
- # @param node [Shale::Mapper]
1068
- # @param type [String] nil for any
1069
- # @param children_method [String] method to determine children exist
1070
- def iterate_tree(result, node, type, children_method)
1071
- result << node if type.nil? || node.type == type
1072
- return unless node.send(children_method.to_sym)
1073
-
1074
- node.send(children_method.to_sym).each do |sub_node|
1075
- if sub_node.send(children_method.to_sym)
1076
- iterate_tree(result, sub_node, type, children_method)
1077
- elsif type.nil? || sub_node.type == type
1078
- result << sub_node
1079
- end
1080
- end
1081
- end
1082
-
1083
- # @param result [Hash]
1084
- # @param node [Shale::Mapper]
1085
- # @note set id as key and name as value into result
1086
- # if id and name are found
1087
- def map_id_name(result, node)
1088
- return if node.nil?
1089
-
1090
- if node.is_a?(Array)
1091
- node.each do |arr_item|
1092
- map_id_name(result, arr_item)
1093
- end
1094
- elsif node.class.methods.include?(:attributes)
1095
- attrs = node.class.attributes
1096
-
1097
- if attrs.has_key?(:id) && attrs.has_key?(:name)
1098
- result[node.id] = node.name
1099
- end
1100
-
1101
- attrs.each_pair do |k, _v|
1102
- map_id_name(result, node.send(k))
1103
- end
1104
- end
1105
110
  end
1106
111
  end
1107
112
  end