lutaml 0.10.0 → 0.10.2

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +73 -31
  3. data/Gemfile +1 -5
  4. data/lib/lutaml/cli/enhanced_formatter.rb +4 -2
  5. data/lib/lutaml/cli/tree_view_formatter.rb +1 -1
  6. data/lib/lutaml/cli/uml/diagram_command.rb +2 -6
  7. data/lib/lutaml/converter/xmi_to_uml.rb +11 -7
  8. data/lib/lutaml/ea/diagram/extractor.rb +8 -3
  9. data/lib/lutaml/model_transformations/parsers/base_parser.rb +90 -13
  10. data/lib/lutaml/qea/factory/class_transformer.rb +8 -19
  11. data/lib/lutaml/qea/factory/data_type_transformer.rb +2 -9
  12. data/lib/lutaml/qea/factory/diagram_transformer.rb +0 -3
  13. data/lib/lutaml/qea/factory/enum_transformer.rb +3 -5
  14. data/lib/lutaml/qea/factory/generalization_transformer.rb +1 -1
  15. data/lib/lutaml/qea/factory/package_transformer.rb +45 -19
  16. data/lib/lutaml/qea/validation/base_validator.rb +5 -0
  17. data/lib/lutaml/qea/verification/document_normalizer.rb +4 -12
  18. data/lib/lutaml/qea/verification/document_verifier.rb +25 -15
  19. data/lib/lutaml/uml_repository/index_builder.rb +27 -9
  20. data/lib/lutaml/uml_repository/package_exporter.rb +14 -3
  21. data/lib/lutaml/uml_repository/presenters/diagram_presenter.rb +2 -7
  22. data/lib/lutaml/uml_repository/queries/inheritance_query.rb +66 -0
  23. data/lib/lutaml/uml_repository/queries/search_query.rb +1 -1
  24. data/lib/lutaml/uml_repository/repository.rb +34 -0
  25. data/lib/lutaml/uml_repository/validators/repository_validator.rb +1 -1
  26. data/lib/lutaml/uml_repository/web_ui/app.rb +25 -7
  27. data/lib/lutaml/version.rb +1 -1
  28. data/lib/lutaml/xmi/parsers/xmi_base.rb +67 -150
  29. data/lutaml.gemspec +3 -0
  30. metadata +44 -2
@@ -19,7 +19,7 @@ module Lutaml
19
19
  # @param xml [String]
20
20
  # @return [Lutaml::Model::Serializable]
21
21
  def get_xmi_model(xml)
22
- ::Xmi::Sparx::SparxRoot.parse_xml(File.read(xml))
22
+ ::Xmi::Sparx::Root.parse_xml(File.read(xml))
23
23
  end
24
24
  end
25
25
 
@@ -27,14 +27,23 @@ module Lutaml
27
27
  # @param id_name_mapping [Hash]
28
28
  # @return [Hash]
29
29
  def set_xmi_model(xmi_model, id_name_mapping = nil)
30
- @id_name_mapping ||= id_name_mapping || {}
31
30
  @xmi_root_model ||= xmi_model
32
31
 
33
- if @id_name_mapping.empty?
34
- map_id_name(@id_name_mapping, @xmi_root_model)
32
+ if @xmi_index.nil?
33
+ @xmi_index = @xmi_root_model.index
34
+ @id_name_mapping = id_name_mapping || @xmi_index.id_name_map
35
35
  end
36
36
  end
37
37
 
38
+ # Access the index, auto-initializing from @xmi_root_model if needed
39
+ def xmi_index
40
+ if @xmi_index.nil? && @xmi_root_model
41
+ @xmi_index = @xmi_root_model.index
42
+ @id_name_mapping ||= @xmi_index.id_name_map
43
+ end
44
+ @xmi_index
45
+ end
46
+
38
47
  private
39
48
 
40
49
  # @param xmi_model [Lutaml::Model::Serializable]
@@ -268,46 +277,40 @@ module Lutaml
268
277
  ]
269
278
  end
270
279
 
271
- # Build a cache for packaged elements by ID
272
- def build_packaged_element_cache
273
- @packaged_element_cache = all_packaged_elements.to_h { |e| [e.id, e] }
274
- end
275
-
276
280
  # @param id [String]
277
281
  # @return [Lutaml::Model::Serializable]
278
282
  def find_packaged_element_by_id(id)
279
- build_packaged_element_cache unless @packaged_element_cache
280
- @packaged_element_cache[id]
283
+ xmi_index.find_packaged_element(id)
281
284
  end
282
285
 
283
286
  # @param id [String]
284
287
  # @return [Lutaml::Model::Serializable]
285
288
  def find_upper_level_packaged_element(klass_id)
286
- build_upper_level_cache unless @upper_level_cache
287
- @upper_level_cache[klass_id]
289
+ xmi_index.find_parent(klass_id)
288
290
  end
289
291
 
290
- def find_subtype_of_from_owned_attribute_type(id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
291
- @pkg_elements_owned_attributes ||= all_packaged_elements.map do |e|
292
- {
293
- name: e.name,
294
- idrefs: e&.owned_attribute&.map do |oa|
295
- oa&.uml_type&.idref
296
- end || [],
297
- }
298
- end
299
- result = @pkg_elements_owned_attributes.find do |e|
300
- e[:idrefs].include?(id)
292
+ def find_subtype_of_from_owned_attribute_type(id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
293
+ @pkg_elements_owned_attributes ||= begin
294
+ cache = {}
295
+ all_packaged_elements.each do |e|
296
+ next unless e.owned_attribute
297
+
298
+ e.owned_attribute.each do |oa|
299
+ next unless oa.association && oa.uml_type && oa.uml_type.idref
300
+
301
+ cache[oa.uml_type.idref] = e.name
302
+ end
303
+ end
304
+ cache
301
305
  end
302
306
 
303
- result[:name] if result
307
+ @pkg_elements_owned_attributes[id]
304
308
  end
305
309
 
306
310
  def find_subtype_of_from_generalization(id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
307
- matched_element = @xmi_root_model.extension.elements.element
308
- .find { |e| e.idref == id }
311
+ matched_element = xmi_index.find_element(id)
309
312
 
310
- return if !matched_element || !matched_element.links
313
+ return unless matched_element&.links&.any?
311
314
 
312
315
  matched_generalization = nil
313
316
  matched_element.links.each do |link|
@@ -322,17 +325,6 @@ module Lutaml
322
325
  lookup_entity_name(matched_generalization.end)
323
326
  end
324
327
 
325
- # Build cache once for all packaged elements
326
- def build_upper_level_cache
327
- @upper_level_cache = {}
328
-
329
- all_packaged_elements.each do |e|
330
- e.packaged_element.each do |pe|
331
- @upper_level_cache[pe.id] = e
332
- end
333
- end
334
- end
335
-
336
328
  # @param path [String]
337
329
  # @return [Lutaml::Model::Serializable]
338
330
  def find_klass_packaged_element(path)
@@ -359,10 +351,9 @@ module Lutaml
359
351
  # @param name_array [Array<String>]
360
352
  # @return [Lutaml::Model::Serializable]
361
353
  def iterate_relative_packaged_element(name_array)
362
- # match the first element in the name_array
363
- matched_elements = all_packaged_elements.select do |e|
364
- e.type?("uml:Package") && e.name == name_array[0]
365
- end
354
+ # match the first element in the name_array using index
355
+ matched_elements = xmi_index.packaged_elements_of_type("uml:Package")
356
+ .select { |e| e.name == name_array[0] }
366
357
 
367
358
  # match the rest elements in the name_array
368
359
  result = matched_elements.map do |e|
@@ -395,44 +386,37 @@ module Lutaml
395
386
  # @param name [String]
396
387
  # @return [Lutaml::Model::Serializable]
397
388
  def find_klass_packaged_element_by_name(name)
398
- all_packaged_elements.find do |e|
399
- e.name == name &&
400
- (
401
- e.type?("uml:Class") ||
402
- e.type?("uml:AssociationClass")
403
- )
404
- end
389
+ xmi_index.find_packaged_by_name_and_types(
390
+ name,
391
+ ["uml:Class", "uml:AssociationClass"],
392
+ )
405
393
  end
406
394
 
407
395
  # @param name [String]
408
396
  # @return [Lutaml::Model::Serializable]
409
397
  def find_enum_packaged_element_by_name(name)
410
- all_packaged_elements.find do |e|
411
- e.name == name && e.type?("uml:Enumeration")
412
- end
398
+ xmi_index.packaged_elements_of_type("uml:Enumeration")
399
+ .find { |e| e.name == name }
413
400
  end
414
401
 
415
402
  # @param supplier_id [String]
416
403
  # @return [Lutaml::Model::Serializable]
417
404
  def select_dependencies_by_supplier(supplier_id)
418
- all_packaged_elements.select do |e|
419
- e.supplier == supplier_id &&
420
- e.type?("uml:Dependency")
421
- end
405
+ xmi_index.packaged_elements_of_type("uml:Dependency")
406
+ .select { |e| e.supplier == supplier_id }
422
407
  end
423
408
 
424
409
  # @param supplier_id [String]
425
410
  # @return [Lutaml::Model::Serializable]
426
411
  def select_dependencies_by_client(client_id)
427
- all_packaged_elements.select do |e|
428
- e.client == client_id && e.type?("uml:Dependency")
429
- end
412
+ xmi_index.packaged_elements_of_type("uml:Dependency")
413
+ .select { |e| e.client == client_id }
430
414
  end
431
415
 
432
416
  # @param name [String]
433
417
  # @return [Lutaml::Model::Serializable]
434
418
  def find_packaged_element_by_name(name)
435
- all_packaged_elements.find { |e| e.name == name }
419
+ xmi_index.packaged_elements.find { |e| e.name == name }
436
420
  end
437
421
 
438
422
  # @param package [Lutaml::Model::Serializable]
@@ -526,10 +510,9 @@ module Lutaml
526
510
  # @return [Array<Hash>]
527
511
  # @note xpath %(//element[@xmi:idref="#{xmi_id}"]/links/*)
528
512
  def serialize_model_associations(xmi_id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
529
- matched_element = @xmi_root_model.extension.elements.element
530
- .find { |e| e.idref == xmi_id }
513
+ matched_element = xmi_index.find_element(xmi_id)
531
514
 
532
- return if !matched_element || !matched_element.links
515
+ return unless matched_element&.links&.any?
533
516
 
534
517
  links = []
535
518
  matched_element.links.each do |link|
@@ -571,9 +554,7 @@ module Lutaml
571
554
  # @return [Lutaml::Model::Serializable]
572
555
  # @note xpath %(//connector[@xmi:idref="#{link_id}"])
573
556
  def fetch_connector(link_id)
574
- @xmi_root_model.extension.connectors.connector.find do |con|
575
- con.idref == link_id
576
- end
557
+ xmi_index.find_connector(link_id)
577
558
  end
578
559
 
579
560
  # @param link_id [String]
@@ -645,10 +626,10 @@ module Lutaml
645
626
  # @param link_member_name [String]
646
627
  # @return [String]
647
628
  def serialize_owned_type(owner_xmi_id, link, linke_owner_name)
648
- case link.name
649
- when "NoteLink"
629
+ case link
630
+ when ::Xmi::Sparx::Element::NoteLink
650
631
  return
651
- when "Generalization"
632
+ when ::Xmi::Sparx::Element::Generalization
652
633
  owner_end, _owner_end_type, _owner_xmi_id =
653
634
  generalization_association(owner_xmi_id, link)
654
635
  return owner_end
@@ -692,22 +673,22 @@ module Lutaml
692
673
  ea_type = connector&.properties&.ea_type
693
674
  member_end_type = ea_type&.downcase
694
675
 
695
- member_end = member_end_name(xmi_id, source_or_target, link.name)
676
+ member_end = member_end_name(xmi_id, source_or_target, link)
696
677
  [member_end, member_end_type, xmi_id]
697
678
  end
698
679
 
699
680
  # @param xmi_id [String]
700
681
  # @param source_or_target [Symbol]
701
682
  # @return [String]
702
- def member_end_name(xmi_id, source_or_target, link_name) # rubocop:disable Metrics/MethodLength
683
+ def member_end_name(xmi_id, source_or_target, link) # rubocop:disable Metrics/MethodLength
703
684
  connector_label = connector_labels(xmi_id, source_or_target)
704
685
  entity_name = lookup_entity_name(xmi_id)
705
686
  connector_name = connector_name_by_source_or_target(
706
687
  xmi_id, source_or_target
707
688
  )
708
689
 
709
- case link_name
710
- when "Aggregation"
690
+ case link
691
+ when ::Xmi::Sparx::Element::Aggregation
711
692
  connector_label || entity_name || connector_name
712
693
  else
713
694
  entity_name || connector_name
@@ -722,7 +703,7 @@ module Lutaml
722
703
  member_end, member_end_type, xmi_id =
723
704
  serialize_member_end(owner_xmi_id, link)
724
705
 
725
- if link.name == "Association"
706
+ if link.is_a?(::Xmi::Sparx::Element::Association)
726
707
  connector_type = link_member_name == "start" ? "source" : "target"
727
708
  member_end_cardinality, member_end_attribute_name =
728
709
  fetch_assoc_connector(link.id, connector_type)
@@ -785,7 +766,7 @@ module Lutaml
785
766
  source_or_target = :target
786
767
  end
787
768
 
788
- member_end = member_end_name(xmi_id, source_or_target, link.name)
769
+ member_end = member_end_name(xmi_id, source_or_target, link)
789
770
 
790
771
  # member_end_cardinality, _member_end_attribute_name =
791
772
  # fetch_owned_attribute_node(xmi_id)
@@ -798,13 +779,9 @@ module Lutaml
798
779
  # @return [Array<Hash, String>]
799
780
  # @note xpath
800
781
  # %(//ownedAttribute[@association]/type[@xmi:idref="#{xmi_id}"])
801
- def fetch_owned_attribute_node(xmi_id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
802
- all_elements = all_packaged_elements
803
-
804
- owned_attributes = all_elements.map(&:owned_attribute).flatten
805
- oa = owned_attributes.find do |a|
806
- !!a.association && a.uml_type && a.uml_type.idref == xmi_id
807
- end
782
+ def fetch_owned_attribute_node(xmi_id)
783
+ oa = xmi_index.find_owned_attrs_by_type(xmi_id)
784
+ .find { |a| !!a.association }
808
785
 
809
786
  if oa
810
787
  cardinality = cardinality_min_max_value(
@@ -820,9 +797,7 @@ module Lutaml
820
797
  # @return [Lutaml::Model::Serializable]
821
798
  # @note xpath %(//element[@xmi:idref="#{klass['xmi:id']}"])
822
799
  def fetch_element(klass_id)
823
- @xmi_root_model.extension.elements.element.find do |e|
824
- e.idref == klass_id
825
- end
800
+ xmi_index.find_element(klass_id)
826
801
  end
827
802
 
828
803
  # @param klass [Lutaml::Model::Serializable]
@@ -923,20 +898,7 @@ module Lutaml
923
898
  # @return [Lutaml::Model::Serializable]
924
899
  # @note xpath %(//attribute[@xmi:idref="#{xmi_id}"])
925
900
  def fetch_attribute_node(xmi_id)
926
- @attribute_cache ||= build_attribute_cache
927
- @attribute_cache[xmi_id]
928
- end
929
-
930
- def build_attribute_cache
931
- cache = {}
932
- @xmi_root_model.extension.elements.element.each do |e|
933
- next unless e.attributes&.attribute
934
-
935
- e.attributes.attribute.each do |a|
936
- cache[a.idref] = a # Store in hash for quick lookup
937
- end
938
- end
939
- cache
901
+ xmi_index.find_attribute(xmi_id)
940
902
  end
941
903
 
942
904
  # @param xmi_id [String]
@@ -953,19 +915,16 @@ module Lutaml
953
915
  # @param xmi_id [String]
954
916
  # @return [String]
955
917
  def lookup_element_prop_documentation(xmi_id)
956
- element_node = @xmi_root_model.extension.elements.element.find do |e|
957
- e.idref == xmi_id
958
- end
918
+ element_node = xmi_index.find_element(xmi_id)
959
919
 
960
920
  return unless element_node&.properties
961
921
 
962
- element_node&.properties&.documentation
922
+ element_node.properties.documentation
963
923
  end
964
924
 
965
925
  # @param xmi_id [String]
966
926
  # @return [String]
967
927
  def lookup_entity_name(xmi_id)
968
- model_node_name_by_xmi_id(xmi_id) if @id_name_mapping.empty?
969
928
  @id_name_mapping[xmi_id]
970
929
  end
971
930
 
@@ -1016,31 +975,11 @@ module Lutaml
1016
975
  connector_name_by_source_or_target(xmi_id, :target)
1017
976
  end
1018
977
 
1019
- # @param xmi_id [String]
1020
- # @return [String]
1021
- # @note xpath %(//*[@xmi:id="#{xmi_id}"])
1022
- def model_node_name_by_xmi_id(xmi_id)
1023
- id_name_mapping = Hash.new
1024
- map_id_name(id_name_mapping, @xmi_root_model)
1025
- @id_name_mapping = id_name_mapping
1026
- @id_name_mapping[xmi_id]
1027
- end
978
+ # @note Removed: model_node_name_by_xmi_id - replaced by Xmi::Index
1028
979
 
1029
980
  # @return [Array<::Xmi::Uml::PackagedElement>]
1030
981
  def all_packaged_elements
1031
- return @all_packaged_elements_cache if @all_packaged_elements_cache
1032
-
1033
- all_elements = []
1034
- packaged_element_roots = @xmi_root_model.model.packaged_element +
1035
- @xmi_root_model.extension.primitive_types.packaged_element +
1036
- @xmi_root_model.extension.profiles.profile.map(&:packaged_element)
1037
-
1038
- packaged_element_roots.flatten.each do |e|
1039
- select_all_packaged_elements(all_elements, e, nil)
1040
- end
1041
-
1042
- @all_packaged_elements_cache = all_elements
1043
- all_elements
982
+ xmi_index.packaged_elements
1044
983
  end
1045
984
 
1046
985
  # @param items [Array<Lutaml::Model::Serializable>]
@@ -1078,29 +1017,7 @@ module Lutaml
1078
1017
  end
1079
1018
  end
1080
1019
 
1081
- # @param result [Hash]
1082
- # @param node [Lutaml::Model::Serializable]
1083
- # @note set id as key and name as value into result
1084
- # if id and name are found
1085
- def map_id_name(result, node) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
1086
- return if node.nil?
1087
-
1088
- if node.is_a?(Array)
1089
- node.each do |arr_item|
1090
- map_id_name(result, arr_item)
1091
- end
1092
- elsif node.class.methods.include?(:attributes)
1093
- attrs = node.class.attributes
1094
-
1095
- if attrs.has_key?(:id) && attrs.has_key?(:name)
1096
- result[node.id] = node.name
1097
- end
1098
-
1099
- attrs.each_pair do |k, _v|
1100
- map_id_name(result, node.send(k))
1101
- end
1102
- end
1103
- end
1020
+ # @note Removed: map_id_name - replaced by Xmi::Index
1104
1021
  end
1105
1022
  end
1106
1023
  end
data/lutaml.gemspec CHANGED
@@ -35,12 +35,15 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency "fiddle"
36
36
  spec.add_dependency "htmlentities"
37
37
  spec.add_dependency "liquid"
38
+ spec.add_dependency "listen"
38
39
  spec.add_dependency "lutaml-model", "~> 0.8.0"
39
40
  spec.add_dependency "lutaml-path"
40
41
  spec.add_dependency "lutaml-xsd"
41
42
  spec.add_dependency "nokogiri", "~> 1.18"
42
43
  spec.add_dependency "paint"
43
44
  spec.add_dependency "parslet", "~> 2.0"
45
+ spec.add_dependency "puma"
46
+ spec.add_dependency "rackup"
44
47
  spec.add_dependency "readline"
45
48
  spec.add_dependency "ruby-graphviz"
46
49
  spec.add_dependency "rubyzip", "~> 2.3"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-16 00:00:00.000000000 Z
11
+ date: 2026-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: expressir
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: listen
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: lutaml-model
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +164,34 @@ dependencies:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
166
  version: '2.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: puma
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rackup
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
153
195
  - !ruby/object:Gem::Dependency
154
196
  name: readline
155
197
  requirement: !ruby/object:Gem::Requirement