lutaml 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -6
  3. data/.rubocop.yml +1 -0
  4. data/LUTAML.adoc +372 -0
  5. data/Makefile +2 -0
  6. data/bin/console +5 -0
  7. data/bin/folder_yaml2lutaml.sh +6 -0
  8. data/bin/plantuml2lutaml +59 -0
  9. data/bin/yaml2lutaml +144 -0
  10. data/exe/lutaml-sysml +20 -0
  11. data/exe/lutaml-wsd2uml +59 -0
  12. data/exe/lutaml-yaml2uml +144 -0
  13. data/lib/lutaml/express/README.adoc +55 -0
  14. data/lib/lutaml/express/parsers/exp.rb +21 -0
  15. data/lib/lutaml/express/version.rb +7 -0
  16. data/lib/lutaml/express.rb +9 -0
  17. data/lib/lutaml/parser.rb +7 -0
  18. data/lib/lutaml/sysml/README.md +40 -0
  19. data/lib/lutaml/sysml/allocate.rb +8 -0
  20. data/lib/lutaml/sysml/allocated.rb +7 -0
  21. data/lib/lutaml/sysml/binding_connector.rb +7 -0
  22. data/lib/lutaml/sysml/block.rb +27 -0
  23. data/lib/lutaml/sysml/constraint_block.rb +12 -0
  24. data/lib/lutaml/sysml/copy.rb +6 -0
  25. data/lib/lutaml/sysml/derive_requirement.rb +7 -0
  26. data/lib/lutaml/sysml/nested_connector_end.rb +11 -0
  27. data/lib/lutaml/sysml/refine.rb +7 -0
  28. data/lib/lutaml/sysml/requirement.rb +34 -0
  29. data/lib/lutaml/sysml/requirement_related.rb +7 -0
  30. data/lib/lutaml/sysml/satisfy.rb +7 -0
  31. data/lib/lutaml/sysml/test_case.rb +22 -0
  32. data/lib/lutaml/sysml/trace.rb +7 -0
  33. data/lib/lutaml/sysml/verify.rb +6 -0
  34. data/lib/lutaml/sysml/version.rb +5 -0
  35. data/lib/lutaml/sysml/xmi_file.rb +417 -0
  36. data/lib/lutaml/sysml.rb +10 -0
  37. data/lib/lutaml/uml/README.adoc +44 -0
  38. data/lib/lutaml/uml/abstraction.rb +11 -0
  39. data/lib/lutaml/uml/activity.rb +11 -0
  40. data/lib/lutaml/uml/actor.rb +19 -0
  41. data/lib/lutaml/uml/association.rb +43 -0
  42. data/lib/lutaml/uml/behavior.rb +11 -0
  43. data/lib/lutaml/uml/class.rb +83 -0
  44. data/lib/lutaml/uml/classifier.rb +11 -0
  45. data/lib/lutaml/uml/connector.rb +21 -0
  46. data/lib/lutaml/uml/constraint.rb +12 -0
  47. data/lib/lutaml/uml/constructor_end.rb +16 -0
  48. data/lib/lutaml/uml/data_type.rb +75 -0
  49. data/lib/lutaml/uml/dependency.rb +21 -0
  50. data/lib/lutaml/uml/diagram.rb +8 -0
  51. data/lib/lutaml/uml/document.rb +81 -0
  52. data/lib/lutaml/uml/enum.rb +45 -0
  53. data/lib/lutaml/uml/event.rb +12 -0
  54. data/lib/lutaml/uml/final_state.rb +11 -0
  55. data/lib/lutaml/uml/formatter/base.rb +67 -0
  56. data/lib/lutaml/uml/formatter/graphviz.rb +334 -0
  57. data/lib/lutaml/uml/formatter.rb +21 -0
  58. data/lib/lutaml/uml/has_attributes.rb +14 -0
  59. data/lib/lutaml/uml/has_members.rb +30 -0
  60. data/lib/lutaml/uml/instance.rb +17 -0
  61. data/lib/lutaml/uml/model.rb +13 -0
  62. data/lib/lutaml/uml/node/base.rb +21 -0
  63. data/lib/lutaml/uml/node/class_node.rb +57 -0
  64. data/lib/lutaml/uml/node/class_relationship.rb +14 -0
  65. data/lib/lutaml/uml/node/document.rb +18 -0
  66. data/lib/lutaml/uml/node/field.rb +34 -0
  67. data/lib/lutaml/uml/node/has_name.rb +15 -0
  68. data/lib/lutaml/uml/node/has_type.rb +15 -0
  69. data/lib/lutaml/uml/node/method.rb +29 -0
  70. data/lib/lutaml/uml/node/method_argument.rb +16 -0
  71. data/lib/lutaml/uml/node/relationship.rb +28 -0
  72. data/lib/lutaml/uml/opaque_behavior.rb +11 -0
  73. data/lib/lutaml/uml/operation.rb +31 -0
  74. data/lib/lutaml/uml/package.rb +53 -0
  75. data/lib/lutaml/uml/parsers/attribute.rb +70 -0
  76. data/lib/lutaml/uml/parsers/dsl.rb +413 -0
  77. data/lib/lutaml/uml/parsers/dsl_preprocessor.rb +59 -0
  78. data/lib/lutaml/uml/parsers/dsl_transform.rb +27 -0
  79. data/lib/lutaml/uml/parsers/yaml.rb +46 -0
  80. data/lib/lutaml/uml/port.rb +8 -0
  81. data/lib/lutaml/uml/primitive_type.rb +14 -0
  82. data/lib/lutaml/uml/property.rb +27 -0
  83. data/lib/lutaml/uml/pseudostate.rb +11 -0
  84. data/lib/lutaml/uml/realization.rb +11 -0
  85. data/lib/lutaml/uml/region.rb +12 -0
  86. data/lib/lutaml/uml/serializers/association.rb +58 -0
  87. data/lib/lutaml/uml/serializers/base.rb +16 -0
  88. data/lib/lutaml/uml/serializers/class.rb +29 -0
  89. data/lib/lutaml/uml/serializers/top_element_attribute.rb +14 -0
  90. data/lib/lutaml/uml/serializers/yaml_view.rb +18 -0
  91. data/lib/lutaml/uml/state.rb +12 -0
  92. data/lib/lutaml/uml/state_machine.rb +12 -0
  93. data/lib/lutaml/uml/top_element.rb +58 -0
  94. data/lib/lutaml/uml/top_element_attribute.rb +39 -0
  95. data/lib/lutaml/uml/transition.rb +12 -0
  96. data/lib/lutaml/uml/trigger.rb +12 -0
  97. data/lib/lutaml/uml/value.rb +31 -0
  98. data/lib/lutaml/uml/version.rb +7 -0
  99. data/lib/lutaml/uml/vertex.rb +11 -0
  100. data/lib/lutaml/uml.rb +13 -0
  101. data/lib/lutaml/version.rb +1 -1
  102. data/lib/lutaml/xmi/README.adoc +24 -0
  103. data/lib/lutaml/xmi/parsers/xml.rb +600 -0
  104. data/lib/lutaml/xmi/version.rb +5 -0
  105. data/lib/lutaml/xmi.rb +7 -0
  106. data/lib/lutaml/xml/lutaml_path/document_wrapper.rb +45 -0
  107. data/lib/lutaml/xml/mapper.rb +448 -0
  108. data/lib/lutaml/xml/parsers/xml.rb +57 -0
  109. data/lib/lutaml/xml.rb +9 -0
  110. data/lutaml.gemspec +8 -3
  111. metadata +192 -16
@@ -0,0 +1,600 @@
1
+ require "nokogiri"
2
+ require "htmlentities"
3
+ require "lutaml/uml/has_attributes"
4
+ require "lutaml/uml/document"
5
+ require "lutaml/xmi"
6
+ require "xmi"
7
+
8
+ module Lutaml
9
+ module XMI
10
+ module Parsers
11
+ # Class for parsing .xmi schema files into ::Lutaml::Uml::Document
12
+ class XML
13
+ LOWER_VALUE_MAPPINGS = {
14
+ "0" => "C",
15
+ "1" => "M",
16
+ }.freeze
17
+ attr_reader :xmi_cache, :xmi_root_model
18
+
19
+ # @param xml [String] path to xml
20
+ # @param options [Hash] options for parsing
21
+ # @return [Lutaml::Uml::Document]
22
+ def self.parse(xml, _options = {})
23
+ xml_content = File.read(xml)
24
+ xmi_model = Xmi::Sparx::SparxRoot2013.from_xml(xml_content)
25
+ new.parse(xmi_model)
26
+ end
27
+
28
+ # @param xmi_model [Shale::Mapper]
29
+ # @return [Lutaml::Uml::Document]
30
+ def parse(xmi_model)
31
+ @xmi_cache = {}
32
+ @xmi_root_model = xmi_model
33
+ ::Lutaml::Uml::Document.new(serialize_to_hash(xmi_model))
34
+ end
35
+
36
+ private
37
+
38
+ # @param xmi_model [Shale::Mapper]
39
+ # @return [Hash]
40
+ # @note xpath: //uml:Model[@xmi:type="uml:Model"]
41
+ def serialize_to_hash(xmi_model)
42
+ model = xmi_model.model
43
+ {
44
+ name: model.name,
45
+ packages: serialize_model_packages(model)
46
+ }
47
+ end
48
+
49
+ # @param model [Shale::Mapper]
50
+ # @return [Array<Hash>]
51
+ # @note xpath ./packagedElement[@xmi:type="uml:Package"]
52
+ def serialize_model_packages(model)
53
+ model.packaged_element.map do |package|
54
+ {
55
+ xmi_id: package.id,
56
+ name: package.name,
57
+ classes: serialize_model_classes(package, model),
58
+ enums: serialize_model_enums(package),
59
+ data_types: serialize_model_data_types(package),
60
+ diagrams: serialize_model_diagrams(package.id),
61
+ packages: serialize_model_packages(package),
62
+ definition: doc_node_attribute_value(package.id, "documentation"),
63
+ stereotype: doc_node_attribute_value(package.id, "stereotype")
64
+ }
65
+ end
66
+ end
67
+
68
+ # @param package [Shale::Mapper]
69
+ # @param model [Shale::Mapper]
70
+ # @return [Array<Hash>]
71
+ # @note xpath ./packagedElement[@xmi:type="uml:Class" or
72
+ # @xmi:type="uml:AssociationClass"]
73
+ def serialize_model_classes(package, model)
74
+ package.packaged_element.select { |e|
75
+ e.type?("uml:Class") || e.type?("uml:AssociationClass")
76
+ }.map do |klass|
77
+ {
78
+ xmi_id: klass.id,
79
+ name: klass.name,
80
+ package: model,
81
+ attributes: serialize_class_attributes(klass),
82
+ associations: serialize_model_associations(klass.id),
83
+ operations: serialize_class_operations(klass),
84
+ constraints: serialize_class_constraints(klass.id),
85
+ is_abstract: doc_node_attribute_value(klass.id, "isAbstract"),
86
+ definition: doc_node_attribute_value(klass.id, "documentation"),
87
+ stereotype: doc_node_attribute_value(klass.id, "stereotype")
88
+ }
89
+ end
90
+ end
91
+
92
+ # @param package [Shale::Mapper]
93
+ # @return [Array<Hash>]
94
+ # @note xpath ./packagedElement[@xmi:type="uml:Enumeration"]
95
+ def serialize_model_enums(package)
96
+ package.packaged_element
97
+ .select { |e| e.type?("uml:Enumeration") }.map do |enum|
98
+ # xpath .//ownedLiteral[@xmi:type="uml:EnumerationLiteral"]
99
+ owned_literals = enum.owned_literal.map do |owned_literal|
100
+ owned_literal.to_hash.transform_keys(&:to_sym)
101
+ end
102
+
103
+ {
104
+ xmi_id: enum.id,
105
+ name: enum.name,
106
+ values: owned_literals,
107
+ definition: doc_node_attribute_value(enum.id, "documentation"),
108
+ stereotype: doc_node_attribute_value(enum.id, "stereotype"),
109
+ }
110
+ end
111
+ end
112
+
113
+ # @param model [Shale::Mapper]
114
+ # @return [Array<Hash>]
115
+ # @note xpath ./packagedElement[@xmi:type="uml:DataType"]
116
+ def serialize_model_data_types(model)
117
+ all_data_type_elements = []
118
+ select_all_packaged_elements(all_data_type_elements, model,
119
+ "uml:DataType")
120
+ all_data_type_elements.map do |klass|
121
+ {
122
+ xmi_id: klass.id,
123
+ name: klass.name,
124
+ attributes: serialize_class_attributes(klass),
125
+ operations: serialize_class_operations(klass),
126
+ associations: serialize_model_associations(klass.id),
127
+ constraints: serialize_class_constraints(klass.id),
128
+ is_abstract: doc_node_attribute_value(klass.id, "isAbstract"),
129
+ definition: doc_node_attribute_value(klass.id, "documentation"),
130
+ stereotype: doc_node_attribute_value(klass.id, "stereotype"),
131
+ }
132
+ end
133
+ end
134
+
135
+ # @param node_id [String]
136
+ # @return [Array<Hash>]
137
+ # @note xpath %(//diagrams/diagram/model[@package="#{node['xmi:id']}"])
138
+ def serialize_model_diagrams(node_id)
139
+ diagrams = @xmi_root_model.extension.diagrams.diagram.select do |d|
140
+ d.model.package == node_id
141
+ end
142
+
143
+ diagrams.map do |diagram|
144
+ {
145
+ xmi_id: diagram.id,
146
+ name: diagram.properties.name,
147
+ definition: diagram.properties.documentation
148
+ }
149
+ end
150
+ end
151
+
152
+ # @param xmi_id [String]
153
+ # @return [Array<Hash>]
154
+ # @note xpath %(//element[@xmi:idref="#{xmi_id}"]/links/*)
155
+ def serialize_model_associations(xmi_id)
156
+ matched_element = @xmi_root_model.extension.elements.element
157
+ .find { |e| e.idref == xmi_id }
158
+
159
+ return if !matched_element.links ||
160
+ matched_element.links.association.empty?
161
+
162
+ matched_element.links.association.map do |assoc|
163
+ link_member_name = assoc.start == xmi_id ? "end" : "start"
164
+ linke_owner_name = link_member_name == "start" ? "end" : "start"
165
+
166
+ member_end, member_end_type, member_end_cardinality,
167
+ member_end_attribute_name, member_end_xmi_id =
168
+ serialize_member_type(xmi_id, assoc, link_member_name)
169
+
170
+ owner_end = serialize_owned_type(xmi_id, assoc, linke_owner_name)
171
+
172
+ if member_end && ((member_end_type != 'aggregation') ||
173
+ (member_end_type == 'aggregation' && member_end_attribute_name))
174
+
175
+ doc_node_name = (link_member_name == "start" ?
176
+ "source" : "target")
177
+ definition = fetch_definition_node_value(assoc.id,
178
+ doc_node_name)
179
+
180
+ {
181
+ xmi_id: assoc.id,
182
+ member_end: member_end,
183
+ member_end_type: member_end_type,
184
+ member_end_cardinality: member_end_cardinality,
185
+ member_end_attribute_name: member_end_attribute_name,
186
+ member_end_xmi_id: member_end_xmi_id,
187
+ owner_end: owner_end,
188
+ owner_end_xmi_id: xmi_id,
189
+ definition: definition
190
+ }
191
+ end
192
+ end
193
+ end
194
+
195
+ # @param link_id [String]
196
+ # @return [Shale::Mapper]
197
+ # @note xpath %(//connector[@xmi:idref="#{link_id}"])
198
+ def fetch_connector(link_id)
199
+ @xmi_root_model.extension.connectors.connector.select do |con|
200
+ con.idref == link_id
201
+ end.first
202
+ end
203
+
204
+ # @param link_id [String]
205
+ # @param node_name [String] source or target
206
+ # @return [String]
207
+ # @note xpath
208
+ # %(//connector[@xmi:idref="#{link_id}"]/#{node_name}/documentation)
209
+ def fetch_definition_node_value(link_id, node_name)
210
+ connector_node = fetch_connector(link_id)
211
+ connector_node.send(node_name.to_sym).documentation
212
+ end
213
+
214
+ # @param klass [Shale::Mapper]
215
+ # @return [Array<Hash>]
216
+ # @note xpath .//ownedOperation
217
+ def serialize_class_operations(klass)
218
+ klass.owned_operation.map do |operation|
219
+ uml_type = operation.uml_type.first
220
+ uml_type_idref = uml_type.idref if uml_type
221
+
222
+ if operation.association.nil?
223
+ {
224
+ id: operation.id,
225
+ xmi_id: uml_type_idref,
226
+ name: operation.name,
227
+ definition: lookup_attribute_documentation(operation.id),
228
+ }
229
+ end
230
+ end.compact
231
+ end
232
+
233
+ # @param klass_id [String]
234
+ # @return [Array<Hash>]
235
+ # @note xpath ./constraints/constraint
236
+ def serialize_class_constraints(klass_id)
237
+ connector_node = fetch_connector(klass_id)
238
+
239
+ if connector_node
240
+ # In ea-xmi-2.5.1, constraints are moved to source/target under
241
+ # connectors
242
+ constraints = [:source, :target].map do |st|
243
+ connector_node.send(st).constraints.constraint
244
+ end.flatten
245
+
246
+ constraints.map do |constraint|
247
+ {
248
+ name: HTMLEntities.new.decode(constraint.name),
249
+ type: constraint.type,
250
+ weight: constraint.weight,
251
+ status: constraint.status,
252
+ }
253
+ end
254
+ end
255
+ end
256
+
257
+ # @param owner_xmi_id [String]
258
+ # @param link [Shale::Mapper]
259
+ # @param link_member_name [String]
260
+ # @return [String]
261
+ def serialize_owned_type(owner_xmi_id, link, linke_owner_name)
262
+ case link.name
263
+ when "NoteLink"
264
+ return
265
+ when "Generalization"
266
+ return generalization_association(owner_xmi_id, link)
267
+ end
268
+
269
+ xmi_id = link.send(linke_owner_name.to_sym)
270
+ owner_end = lookup_entity_name(xmi_id) ||
271
+ connector_source_name(xmi_id)
272
+
273
+ # not necessary
274
+ # if link.name == "Association"
275
+ # owned_cardinality, owned_attribute_name =
276
+ # fetch_assoc_connector(link.id, "source")
277
+ # else
278
+ # owned_cardinality, owned_attribute_name =
279
+ # fetch_owned_attribute_node(xmi_id)
280
+ # end
281
+ # [owner_end, owned_cardinality, owned_attribute_name]
282
+ owner_end
283
+ end
284
+
285
+ # @param owner_xmi_id [String]
286
+ # @param link [Shale::Mapper]
287
+ # @param link_member_name [String]
288
+ # @return [Array<String, String>]
289
+ def serialize_member_end(owner_xmi_id, link, link_member_name)
290
+ case link.name
291
+ when "NoteLink"
292
+ return
293
+ when "Generalization"
294
+ return generalization_association(owner_xmi_id, link)
295
+ end
296
+
297
+ xmi_id = link.start
298
+ source_or_target = :source
299
+
300
+ if link.start == owner_xmi_id
301
+ xmi_id = link.end
302
+ source_or_target = :target
303
+ end
304
+
305
+ member_end = lookup_entity_name(xmi_id) ||
306
+ connector_name_by_source_or_target(xmi_id, source_or_target)
307
+
308
+ [member_end, xmi_id]
309
+ end
310
+
311
+ # @param owner_xmi_id [String]
312
+ # @param link [Shale::Mapper]
313
+ # @param link_member_name [String]
314
+ # @return [Array<String, String, Hash, String, String>]
315
+ def serialize_member_type(owner_xmi_id, link, link_member_name)
316
+ member_end, xmi_id = serialize_member_end(
317
+ owner_xmi_id, link, link_member_name)
318
+
319
+ if link.name == "Association"
320
+ connector_type = link_member_name == "start" ? "source" : "target"
321
+ member_end_cardinality, member_end_attribute_name =
322
+ fetch_assoc_connector(link.id, connector_type)
323
+ else
324
+ member_end_cardinality, member_end_attribute_name =
325
+ fetch_owned_attribute_node(xmi_id)
326
+ end
327
+
328
+ [member_end, "aggregation", member_end_cardinality,
329
+ member_end_attribute_name, xmi_id]
330
+ end
331
+
332
+ # @param link_id [String]
333
+ # @param connector_type [String]
334
+ # @return [Array<Hash, String>]
335
+ # @note xpath %(//connector[@xmi:idref="#{link_id}"]/#{connector_type})
336
+ def fetch_assoc_connector(link_id, connector_type)
337
+ assoc_connector = fetch_connector(link_id).send(connector_type.to_sym)
338
+
339
+ if assoc_connector
340
+ assoc_connector_type = assoc_connector.type
341
+ if assoc_connector_type && assoc_connector_type.multiplicity
342
+ cardinality = assoc_connector_type.multiplicity.split('..')
343
+ cardinality.unshift('1') if cardinality.length == 1
344
+ min, max = cardinality
345
+ end
346
+ assoc_connector_role = assoc_connector.role
347
+ # Does role has name attribute? Or get name from model?
348
+ # attribute_name = assoc_connector_role.name if assoc_connector_role
349
+ attribute_name = assoc_connector.model.name if assoc_connector_role
350
+ cardinality = cardinality_min_max_value(min, max)
351
+ end
352
+
353
+ [cardinality, attribute_name]
354
+ end
355
+
356
+ # @param owner_xmi_id [String]
357
+ # @param link [Shale::Mapper]
358
+ # @return [Array<String, String, Hash, String, String>]
359
+ # @note match return value of serialize_member_type
360
+ def generalization_association(owner_xmi_id, link)
361
+ member_end_type = "generalization"
362
+ xmi_id = link.start
363
+ source_or_target = :source
364
+
365
+ if link.start == owner_xmi_id
366
+ member_end_type = "inheritance"
367
+ xmi_id = link.end
368
+ source_or_target = :target
369
+ end
370
+
371
+ member_end = lookup_entity_name(xmi_id) ||
372
+ connector_name_by_source_or_target(xmi_id, source_or_target)
373
+
374
+ member_end_cardinality, _member_end_attribute_name =
375
+ fetch_owned_attribute_node(xmi_id)
376
+
377
+ [member_end, member_end_type, member_end_cardinality, nil, xmi_id]
378
+ end
379
+
380
+ # Multiple items if search type is idref. Should search association?
381
+ # @param xmi_id [String]
382
+ # @return [Array<Hash, String>]
383
+ # @note xpath
384
+ # %(//ownedAttribute[@association]/type[@xmi:idref="#{xmi_id}"])
385
+ def fetch_owned_attribute_node(xmi_id)
386
+ all_elements = all_packaged_elements
387
+
388
+ owned_attributes = all_elements.map { |e| e.owned_attribute }.flatten
389
+ oa = owned_attributes.select do |a|
390
+ !!a.association && a.uml_type && a.uml_type.idref == xmi_id
391
+ end.first
392
+
393
+ if oa
394
+ cardinality = cardinality_min_max_value(
395
+ oa.lower_value&.value,
396
+ oa.upper_value&.value
397
+ )
398
+ oa_name = oa.name
399
+ end
400
+
401
+ [cardinality, oa_name]
402
+ end
403
+
404
+ # @param klass_id [String]
405
+ # @return [Shale::Mapper]
406
+ # @note xpath %(//element[@xmi:idref="#{klass['xmi:id']}"])
407
+ def fetch_element(klass_id)
408
+ @xmi_root_model.extension.elements.element.select do |e|
409
+ e.idref == klass_id
410
+ end.first
411
+ end
412
+
413
+ # @param klass [Shale::Mapper]
414
+ # @return [Array<Hash>]
415
+ # @note xpath .//ownedAttribute[@xmi:type="uml:Property"]
416
+ def serialize_class_attributes(klass)
417
+ klass.owned_attribute.select { |attr| attr.type?("uml:Property") }
418
+ .map do |oa|
419
+ uml_type = oa.uml_type
420
+ uml_type_idref = uml_type.idref if uml_type
421
+
422
+ if oa.association.nil?
423
+ {
424
+ id: oa.id,
425
+ name: oa.name,
426
+ type: lookup_entity_name(uml_type_idref) || uml_type_idref,
427
+ xmi_id: uml_type_idref,
428
+ is_derived: oa.is_derived,
429
+ cardinality: cardinality_min_max_value(
430
+ oa.lower_value&.value,
431
+ oa.upper_value&.value),
432
+ definition: lookup_attribute_documentation(oa.id),
433
+ }
434
+ end
435
+ end.compact
436
+ end
437
+
438
+ # @param min [String]
439
+ # @param max [String]
440
+ # @return [Hash]
441
+ def cardinality_min_max_value(min, max)
442
+ {
443
+ "min" => cardinality_value(min, true),
444
+ "max" => cardinality_value(max, false)
445
+ }
446
+ end
447
+
448
+ # @param value [String]
449
+ # @param is_min [Boolean]
450
+ # @return [String]
451
+ def cardinality_value(value, is_min = false)
452
+ return unless value
453
+
454
+ is_min ? LOWER_VALUE_MAPPINGS[value] : value
455
+ end
456
+
457
+ # @node [Shale::Mapper]
458
+ # @attr_name [String]
459
+ # @return [String]
460
+ # @note xpath %(//element[@xmi:idref="#{xmi_id}"]/properties)
461
+ def doc_node_attribute_value(node_id, attr_name)
462
+ doc_node = fetch_element(node_id)
463
+ return unless doc_node
464
+
465
+ doc_node.properties.send(Shale::Utils.snake_case(attr_name).to_sym)
466
+ end
467
+
468
+ # @param xmi_id [String]
469
+ # @return [String]
470
+ # @note xpath %(//attribute[@xmi:idref="#{xmi_id}"]/documentation)
471
+ def lookup_attribute_documentation(xmi_id)
472
+ doc_node = fetch_element(xmi_id)
473
+ return unless doc_node
474
+
475
+ doc_node.documentation
476
+ end
477
+
478
+ # @param xmi_id [String]
479
+ # @return [String]
480
+ def lookup_entity_name(xmi_id)
481
+ model_node_name_by_xmi_id(xmi_id) if @xmi_cache.empty?
482
+ @xmi_cache[xmi_id]
483
+ end
484
+
485
+ # @param xmi_id [String]
486
+ # @param source_or_target [String]
487
+ # @return [String]
488
+ def connector_name_by_source_or_target(xmi_id, source_or_target)
489
+ node = @xmi_root_model.extension.connectors.connector.select do |con|
490
+ con.send(source_or_target.to_sym).idref == xmi_id
491
+ end
492
+ return if node.empty? ||
493
+ node.first.send(source_or_target.to_sym).nil? ||
494
+ node.first.send(source_or_target.to_sym).model.nil?
495
+
496
+ node.first.send(source_or_target.to_sym).model.name
497
+ end
498
+
499
+ # @param xmi_id [String]
500
+ # @return [String]
501
+ # @note xpath %(//source[@xmi:idref="#{xmi_id}"]/model)
502
+ def connector_source_name(xmi_id)
503
+ connector_name_by_source_or_target(xmi_id, :source)
504
+ end
505
+
506
+ # @param xmi_id [String]
507
+ # @return [String]
508
+ # @note xpath %(//target[@xmi:idref="#{xmi_id}"]/model)
509
+ def connector_target_name(xmi_id)
510
+ connector_name_by_source_or_target(xmi_id, :target)
511
+ end
512
+
513
+ # @param xmi_id [String]
514
+ # @return [String]
515
+ # @note xpath %(//*[@xmi:id="#{xmi_id}"])
516
+ def model_node_name_by_xmi_id(xmi_id)
517
+ id_name_mapping = Hash.new
518
+ map_id_name(id_name_mapping, @xmi_root_model)
519
+ @xmi_cache = id_name_mapping
520
+ @xmi_cache[xmi_id]
521
+ end
522
+
523
+ # @return [Array<Xmi::Uml::PackagedElement>]
524
+ def all_packaged_elements
525
+ all_elements = []
526
+ [
527
+ @xmi_root_model.model.packaged_element +
528
+ @xmi_root_model.extension.primitive_types.packaged_element +
529
+ @xmi_root_model.extension.profiles.profile
530
+ .map {|p| p.packaged_element }
531
+ ].flatten.each do |e|
532
+ select_all_packaged_elements(all_elements, e, nil)
533
+ end
534
+
535
+ all_elements
536
+ end
537
+
538
+ # @param items [Array<Shale::Mapper>]
539
+ # @param model [Shale::Mapper]
540
+ # @param type [String] nil for any
541
+ def select_all_items(items, model, type, method)
542
+ iterate_tree(items, model, type, method.to_sym)
543
+ end
544
+
545
+ # @param all_elements [Array<Shale::Mapper>]
546
+ # @param model [Shale::Mapper]
547
+ # @param type [String] nil for any
548
+ # @note xpath ./packagedElement[@xmi:type="#{type}"]
549
+ def select_all_packaged_elements(all_elements, model, type)
550
+ select_all_items(all_elements, model, type, :packaged_element)
551
+ all_elements.delete_if do |e|
552
+ !e.is_a?(Xmi::Uml::PackagedElement) &&
553
+ !e.is_a?(Xmi::Uml::PackagedElement2013)
554
+ end
555
+ end
556
+
557
+ # @param result [Array<Shale::Mapper>]
558
+ # @param node [Shale::Mapper]
559
+ # @param type [String] nil for any
560
+ # @param children_method [String] method to determine children exist
561
+ def iterate_tree(result, node, type, children_method)
562
+ result << node if type.nil? || node.type == type
563
+ return unless node.send(children_method.to_sym)
564
+
565
+ node.send(children_method.to_sym).each do |sub_node|
566
+ if sub_node.send(children_method.to_sym)
567
+ iterate_tree(result, sub_node, type, children_method)
568
+ elsif type.nil? || sub_node.type == type
569
+ result << sub_node
570
+ end
571
+ end
572
+ end
573
+
574
+ # @param result [Hash]
575
+ # @param node [Shale::Mapper]
576
+ # @note set id as key and name as value into result
577
+ # if id and name are found
578
+ def map_id_name(result, node)
579
+ return if node.nil?
580
+
581
+ if node.is_a?(Array)
582
+ node.each do |arr_item|
583
+ map_id_name(result, arr_item)
584
+ end
585
+ elsif node.class.methods.include?(:attributes)
586
+ attrs = node.class.attributes
587
+
588
+ if attrs.has_key?(:id) && attrs.has_key?(:name)
589
+ result[node.id] = node.name
590
+ end
591
+
592
+ attrs.each_pair do |k, v|
593
+ map_id_name(result, node.send(k))
594
+ end
595
+ end
596
+ end
597
+ end
598
+ end
599
+ end
600
+ end
@@ -0,0 +1,5 @@
1
+ module Lutaml
2
+ module XMI
3
+ VERSION = "0.2.3"
4
+ end
5
+ end
data/lib/lutaml/xmi.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "lutaml/xmi/version"
2
+ require "lutaml/xmi/parsers/xml"
3
+
4
+ module Lutaml
5
+ module XMI
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ require "lutaml/lutaml_path/document_wrapper"
2
+
3
+ module Lutaml
4
+ module Xml
5
+ module LutamlPath
6
+ class DocumentWrapper < ::Lutaml::LutamlPath::DocumentWrapper
7
+ protected
8
+
9
+ def serialize_document(document)
10
+ serialize_to_hash(document)
11
+ end
12
+
13
+ def serialize_to_hash(object)
14
+ hash = {}
15
+ attribute_name = nil
16
+
17
+ object.all_content.each do |mapping, content|
18
+ if mapping == "content"
19
+ attribute_name = object.class.xml_mapping.content.attribute.to_s
20
+ hash[attribute_name] ||= []
21
+ hash[attribute_name] << content.strip unless content.strip&.empty?
22
+ elsif content.is_a?(String)
23
+ if object.class.attributes[mapping.attribute].collection?
24
+ hash[mapping.name] ||= []
25
+ hash[mapping.name] << content.strip
26
+ else
27
+ hash[mapping.name] = content.strip
28
+ end
29
+ else
30
+ if object.class.attributes[mapping.attribute].collection?
31
+ hash[mapping.name] ||= []
32
+ hash[mapping.name] << serialize_to_hash(content)
33
+ else
34
+ hash[mapping.name] = serialize_to_hash(content)
35
+ end
36
+ end
37
+ end
38
+
39
+ hash[attribute_name] = hash[attribute_name].compact if attribute_name
40
+ hash
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end