lutaml-model 0.5.2 → 0.5.4

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +2 -0
  3. data/.rubocop_todo.yml +39 -13
  4. data/Gemfile +1 -0
  5. data/README.adoc +430 -52
  6. data/lib/lutaml/model/constants.rb +7 -0
  7. data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
  8. data/lib/lutaml/model/error.rb +1 -0
  9. data/lib/lutaml/model/key_value_mapping.rb +31 -2
  10. data/lib/lutaml/model/mapping_hash.rb +8 -0
  11. data/lib/lutaml/model/mapping_rule.rb +8 -0
  12. data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
  13. data/lib/lutaml/model/schema/xml_compiler.rb +720 -0
  14. data/lib/lutaml/model/schema.rb +5 -0
  15. data/lib/lutaml/model/serialize.rb +33 -13
  16. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
  17. data/lib/lutaml/model/type/hash.rb +11 -11
  18. data/lib/lutaml/model/utils.rb +7 -0
  19. data/lib/lutaml/model/version.rb +1 -1
  20. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
  21. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
  22. data/lib/lutaml/model/xml_mapping.rb +4 -2
  23. data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
  24. data/lib/lutaml/model.rb +1 -0
  25. data/spec/fixtures/xml/invalid_math_document.xml +4 -0
  26. data/spec/fixtures/xml/math_document_schema.xsd +56 -0
  27. data/spec/fixtures/xml/test_schema.xsd +53 -0
  28. data/spec/fixtures/xml/valid_math_document.xml +4 -0
  29. data/spec/lutaml/model/cdata_spec.rb +2 -2
  30. data/spec/lutaml/model/custom_model_spec.rb +7 -20
  31. data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
  32. data/spec/lutaml/model/map_all_spec.rb +188 -0
  33. data/spec/lutaml/model/mixed_content_spec.rb +15 -15
  34. data/spec/lutaml/model/render_nil_spec.rb +29 -0
  35. data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
  36. data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
  37. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
  38. data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
  39. metadata +12 -2
@@ -0,0 +1,720 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "lutaml/xsd"
5
+ require_relative "templates/simple_type"
6
+
7
+ module Lutaml
8
+ module Model
9
+ module Schema
10
+ module XmlCompiler
11
+ extend self
12
+
13
+ DEFAULT_CLASSES = %w[string integer int boolean].freeze
14
+ ELEMENT_ORDER_IGNORABLE = %w[import include].freeze
15
+
16
+ MODEL_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
17
+ # frozen_string_literal: true
18
+ require "lutaml/model"
19
+ <%=
20
+ requiring_files = resolve_required_files(content)
21
+ if requiring_files.any?
22
+ requiring_files.map { |file| "require_relative \\\"\#{file}\\\"" }.join("\n") + "\n"
23
+ end
24
+ -%>
25
+
26
+ class <%= Utils.camel_case(name) %> < <%= resolve_parent_class(content) %>
27
+ <%=
28
+ if content&.key_exist?(:attributes)
29
+ output = content.attributes.map do |attribute|
30
+ attribute = @attributes[attribute.ref_class.split(":").last] if attribute.key?(:ref_class)
31
+ " attribute :\#{Utils.snake_case(attribute.name)}, \#{resolve_attribute_class(attribute)}\#{resolve_attribute_default(attribute) if attribute.key_exist?(:default)}"
32
+ end.join("\n")
33
+ output + "\n" if output && !output&.empty?
34
+ end
35
+ -%>
36
+ <%=
37
+ if content&.key_exist?(:sequence) || content&.key_exist?(:choice) || content&.key_exist?(:group)
38
+ output = resolve_content(content).map do |element_name, element|
39
+ element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
40
+ " attribute :\#{Utils.snake_case(element_name)}, \#{Utils.camel_case(element.type_name.split(":").last)}\#{resolve_occurs(element.arguments) if element.key_exist?(:arguments)}"
41
+ end.join("\n")
42
+ output + "\n" if output && !output&.empty?
43
+ end
44
+ -%>
45
+ <%=
46
+ if content&.key_exist?(:complex_content)
47
+ resolve_complex_content(content.complex_content).map do |element_name, element|
48
+ if element_name == :attributes
49
+ element.map { |attribute| " attribute :\#{Utils.snake_case(attribute.name)}, \#{resolve_attribute_class(attribute)}\#{resolve_attribute_default(attribute.default) if attribute.key_exist?(:default)}" }.join("\n")
50
+ else
51
+ element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
52
+ " attribute :\#{Utils.snake_case(element_name)}, \#{Utils.camel_case(element.type_name.split(":").last)}\#{resolve_occurs(element.arguments) if element.key_exist?(:arguments)}"
53
+ end
54
+ end.join("\n")
55
+ output + "\n" if output && !output&.empty?
56
+ end
57
+ -%>
58
+ <%= " attribute :content, \#{content.simple_content.extension_base}" if content_exist = content.key_exist?(:simple_content) && content.simple_content.key_exist?(:extension_base) -%>
59
+
60
+ xml do
61
+ root "<%= name %>", mixed: true
62
+ <%= resolve_namespace(options) %>
63
+ <%= " map_content to: :content\n" if content_exist -%>
64
+ <%=
65
+ if content&.key_exist?(:attributes)
66
+ output = content.attributes.map do |attribute|
67
+ attribute = @attributes[attribute.ref_class.split(":").last] if attribute.key?(:ref_class)
68
+ " map_attribute :\#{Utils.snake_case(attribute.name)}, to: :\#{Utils.snake_case(attribute.name)}"
69
+ end.join("\n")
70
+ output + "\n" if output && !output&.empty?
71
+ end
72
+ -%>
73
+ <%=
74
+ if content&.key_exist?(:sequence) || content&.key_exist?(:choice) || content&.key_exist?(:group)
75
+ output = resolve_content(content).map do |element_name, element|
76
+ element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
77
+ " map_element :\#{element_name}, to: :\#{Utils.snake_case(element_name)}"
78
+ end.join("\n")
79
+ output + "\n" if output && !output&.empty?
80
+ end
81
+ -%>
82
+ <%=
83
+ if content&.key_exist?(:complex_content)
84
+ output = resolve_complex_content(content.complex_content).map do |element_name, element|
85
+ if element_name == :attributes
86
+ element.map { |attribute| " map_attribute :\#{Utils.snake_case(attribute.name)}, to: :\#{Utils.snake_case(attribute.name)}" }.join("\n")
87
+ else
88
+ element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
89
+ " map_element :\#{element_name}, to: :\#{Utils.snake_case(element_name)}"
90
+ end
91
+ end.join("\n")
92
+ output + "\n" if output && !output&.empty?
93
+ end
94
+ -%>
95
+ end
96
+ end
97
+
98
+ TEMPLATE
99
+
100
+ XML_ADAPTER_NOT_SET_MESSAGE = <<~MSG
101
+ Nokogiri is not set as XML Adapter.
102
+ Make sure Nokogiri is installed and set as XML Adapter eg.
103
+ execute: gem install nokogiri
104
+ require 'lutaml/model/adapter/nokogiri'
105
+ Lutaml::Model.xml_adapter = Lutaml::Model::Adapter::Nokogiri
106
+ MSG
107
+
108
+ def to_models(schema, options = {})
109
+ as_models(schema, options: options)
110
+ dir = options.fetch(:output_dir, "lutaml_models_#{Time.now.to_i}")
111
+ FileUtils.mkdir_p(dir)
112
+
113
+ @data_types_classes = Templates::SimpleType.create_simple_types(@simple_types)
114
+ @data_types_classes.each { |name, content| create_file(name, content, dir) }
115
+ @complex_types.each { |name, content| create_file(name, MODEL_TEMPLATE.result(binding), dir) }
116
+ nil
117
+ end
118
+
119
+ private
120
+
121
+ def create_file(name, content, dir)
122
+ File.write("#{dir}/#{Utils.snake_case(name)}.rb", content)
123
+ end
124
+
125
+ # START: STRUCTURE SETUP METHODS
126
+
127
+ def as_models(schema, options: {})
128
+ raise Error, XML_ADAPTER_NOT_SET_MESSAGE unless Config.xml_adapter.name.end_with?("NokogiriAdapter")
129
+
130
+ parsed_schema = Xsd.parse(schema, location: options[:location])
131
+
132
+ @elements = MappingHash.new
133
+ @attributes = MappingHash.new
134
+ @group_types = MappingHash.new
135
+ @simple_types = MappingHash.new
136
+ @complex_types = MappingHash.new
137
+ @attribute_groups = MappingHash.new
138
+
139
+ schema_to_models([parsed_schema])
140
+ end
141
+
142
+ def schema_to_models(schemas)
143
+ return if schemas.empty?
144
+
145
+ schemas.each do |schema|
146
+ schema_to_models(schema.include) if schema.include.any?
147
+ schema_to_models(schema.import) if schema.import.any?
148
+ resolved_element_order(schema).each do |order_item|
149
+ item_name = order_item&.name
150
+ case order_item
151
+ when Xsd::SimpleType
152
+ @simple_types[item_name] = setup_simple_type(order_item)
153
+ when Xsd::Group
154
+ @group_types[item_name] = setup_group_type(order_item)
155
+ when Xsd::ComplexType
156
+ @complex_types[item_name] = setup_complex_type(order_item)
157
+ when Xsd::Element
158
+ @elements[item_name] = setup_element(order_item)
159
+ when Xsd::Attribute
160
+ @attributes[item_name] = setup_attribute(order_item)
161
+ when Xsd::AttributeGroup
162
+ @attribute_groups[item_name] = setup_attribute_groups(order_item)
163
+ end
164
+ end
165
+ end
166
+ nil
167
+ end
168
+
169
+ def setup_simple_type(simple_type)
170
+ MappingHash.new.tap do |hash|
171
+ setup_restriction(simple_type.restriction, hash) if simple_type&.restriction
172
+ hash[:union] = setup_union(simple_type.union) if simple_type.union
173
+ end
174
+ end
175
+
176
+ def restriction_content(hash, restriction)
177
+ return hash unless restriction.respond_to?(:max_length)
178
+
179
+ hash[:max_length] = restriction.max_length.map(&:value).min if restriction.max_length&.any?
180
+ hash[:min_length] = restriction.min_length.map(&:value).max if restriction.min_length&.any?
181
+ hash[:min_inclusive] = restriction.min_inclusive.map(&:value).max if restriction.min_inclusive&.any?
182
+ hash[:max_inclusive] = restriction.max_inclusive.map(&:value).min if restriction.max_inclusive&.any?
183
+ hash[:length] = restriction_length(restriction.length) if restriction.length.any?
184
+ end
185
+
186
+ def restriction_length(lengths)
187
+ lengths.map do |length|
188
+ MappingHash.new.tap do |hash|
189
+ hash[:value] = length.value
190
+ hash[:fixed] = length.fixed if length.fixed
191
+ end
192
+ end
193
+ end
194
+
195
+ def setup_complex_type(complex_type)
196
+ MappingHash.new.tap do |hash|
197
+ hash[:attributes] = [] if complex_type.attribute.any?
198
+ hash[:attribute_groups] = [] if complex_type.attribute_group.any?
199
+ resolved_element_order(complex_type).each do |element|
200
+ case element
201
+ when Xsd::Attribute
202
+ hash[:attributes] << setup_attribute(element)
203
+ when Xsd::Sequence
204
+ hash[:sequence] = setup_sequence(element)
205
+ when Xsd::Choice
206
+ hash[:choice] = setup_choice(element)
207
+ when Xsd::ComplexContent
208
+ hash[:complex_content] = setup_complex_content(element)
209
+ when Xsd::AttributeGroup
210
+ hash[:attribute_groups] << setup_attribute_groups(element)
211
+ when Xsd::Group
212
+ hash[:group] = setup_group_type(element)
213
+ when Xsd::SimpleContent
214
+ hash[:simple_content] = setup_simple_content(element)
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def setup_simple_content(simple_content)
221
+ if simple_content.extension
222
+ setup_extension(simple_content.extension)
223
+ elsif simple_content.restriction
224
+ setup_restriction(simple_content.restriction, {})
225
+ end
226
+ end
227
+
228
+ def setup_sequence(sequence)
229
+ MappingHash.new.tap do |hash|
230
+ hash[:sequences] = [] if sequence.sequence.any?
231
+ hash[:elements] = [] if sequence.element.any?
232
+ hash[:choice] = [] if sequence.choice.any?
233
+ hash[:groups] = [] if sequence.group.any?
234
+ resolved_element_order(sequence).each do |instance|
235
+ case instance
236
+ when Xsd::Sequence
237
+ hash[:sequences] << setup_sequence(instance)
238
+ when Xsd::Element
239
+ hash[:elements] << if instance.name
240
+ setup_element(instance)
241
+ else
242
+ create_mapping_hash(instance.ref, hash_key: :ref_class)
243
+ end
244
+ when Xsd::Choice
245
+ hash[:choice] << setup_choice(instance)
246
+ when Xsd::Group
247
+ hash[:groups] << if instance.name
248
+ setup_group_type(instance)
249
+ else
250
+ create_mapping_hash(instance.ref, hash_key: :ref_class)
251
+ end
252
+ when Xsd::Any
253
+ # No implementation yet!
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ def setup_group_type(group)
260
+ MappingHash.new.tap do |hash|
261
+ if group.ref
262
+ hash[:ref_class] = group.ref
263
+ else
264
+ resolved_element_order(group).map do |instance|
265
+ case instance
266
+ when Xsd::Sequence
267
+ hash[:sequence] = setup_sequence(instance)
268
+ when Xsd::Choice
269
+ hash[:choice] = setup_choice(instance)
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ def setup_choice(choice)
277
+ MappingHash.new.tap do |hash|
278
+ resolved_element_order(choice).each do |element|
279
+ case element
280
+ when Xsd::Element
281
+ element_name = element.name || @elements[element.ref.split(":").last]&.element_name
282
+ hash[element_name] = setup_element(element)
283
+ when Xsd::Sequence
284
+ hash[:sequence] = setup_sequence(element)
285
+ when Xsd::Group
286
+ hash[:group] = setup_group_type(element)
287
+ when Xsd::Choice
288
+ hash[:choice] = setup_choice(element)
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ def setup_union(union)
295
+ union.member_types.split.map do |member_type|
296
+ @simple_types[member_type]
297
+ end.flatten
298
+ end
299
+
300
+ def setup_attribute(attribute)
301
+ MappingHash.new.tap do |attr_hash|
302
+ if attribute.ref
303
+ attr_hash[:ref_class] = attribute.ref
304
+ else
305
+ attr_hash[:name] = attribute.name
306
+ attr_hash[:base_class] = attribute.type
307
+ attr_hash[:default] = attribute.default if attribute.default
308
+ end
309
+ end
310
+ end
311
+
312
+ def setup_attribute_groups(attribute_group)
313
+ MappingHash.new.tap do |hash|
314
+ if attribute_group.ref
315
+ hash[:ref_class] = attribute_group.ref
316
+ else
317
+ hash[:attributes] = [] if attribute_group.attribute.any?
318
+ hash[:attribute_groups] = [] if attribute_group.attribute_group.any?
319
+ resolved_element_order(attribute_group).each do |instance|
320
+ case instance
321
+ when Xsd::Attribute
322
+ hash[:attributes] << setup_attribute(instance)
323
+ when Xsd::AttributeGroup
324
+ hash[:attribute_groups] << setup_attribute_groups(instance)
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+
331
+ def create_mapping_hash(value, hash_key: :class_name)
332
+ MappingHash.new.tap do |hash|
333
+ hash[hash_key] = value
334
+ end
335
+ end
336
+
337
+ def setup_element(element)
338
+ MappingHash.new.tap do |hash|
339
+ if element.ref
340
+ hash[:ref_class] = element.ref
341
+ else
342
+ hash[:type_name] = element.type
343
+ hash[:element_name] = element.name
344
+ element_arguments(element, hash)
345
+ return hash unless complex_type = element.complex_type
346
+
347
+ hash[:complex_type] = setup_complex_type(complex_type)
348
+ @complex_types[complex_type.name] = hash[:complex_type]
349
+ end
350
+ end
351
+ end
352
+
353
+ def setup_restriction(restriction, hash)
354
+ hash[:base_class] = restriction.base
355
+ restriction_patterns(restriction.pattern, hash) if restriction.respond_to?(:pattern)
356
+ restriction_content(hash, restriction)
357
+ return hash unless restriction.respond_to?(:enumeration) && restriction.enumeration.any?
358
+
359
+ hash[:values] = restriction.enumeration.map(&:value)
360
+ hash
361
+ end
362
+
363
+ def restriction_patterns(patterns, hash)
364
+ return if patterns.empty?
365
+
366
+ hash[:pattern] = patterns.map { |pattern| "(#{pattern.value})" }.join("|")
367
+ hash
368
+ end
369
+
370
+ def setup_complex_content(complex_content)
371
+ MappingHash.new.tap do |hash|
372
+ hash[:mixed] = true if complex_content.mixed
373
+ if complex_content.extension
374
+ hash[:extension] = setup_extension(complex_content.extension)
375
+ elsif restriction = complex_content.restriction
376
+ setup_restriction(restriction, hash)
377
+ end
378
+ end
379
+ end
380
+
381
+ def setup_extension(extension)
382
+ MappingHash.new.tap do |hash|
383
+ hash[:extension_base] = extension.base
384
+ hash[:attribute_groups] = [] if extension&.attribute_group&.any?
385
+ hash[:attributes] = [] if extension&.attribute&.any?
386
+ resolved_element_order(extension).each do |element|
387
+ case element
388
+ when Xsd::Attribute
389
+ hash[:attributes] << setup_attribute(element)
390
+ when Xsd::Sequence
391
+ hash[:sequence] = setup_sequence(element)
392
+ when Xsd::Choice
393
+ hash[:choice] = setup_choice(element)
394
+ end
395
+ end
396
+ end
397
+ end
398
+
399
+ def element_arguments(element, element_hash)
400
+ MappingHash.new.tap do |hash|
401
+ hash[:min_occurs] = element.min_occurs if element.min_occurs
402
+ hash[:max_occurs] = element.max_occurs if element.max_occurs
403
+ element_hash[:arguments] = hash if hash.any?
404
+ end
405
+ end
406
+
407
+ def resolved_element_order(object, ignore_text: true)
408
+ return [] if object.element_order.nil?
409
+
410
+ object.element_order.each_with_object(object.element_order.dup) do |name, array|
411
+ next array.delete(name) if name == "text" && (ignore_text || !object.respond_to?(:text))
412
+ next array.delete(name) if ELEMENT_ORDER_IGNORABLE.include?(name)
413
+
414
+ index = 0
415
+ array.each_with_index do |element, i|
416
+ next unless element == name
417
+
418
+ array[i] = Array(object.send(Utils.snake_case(name)))[index]
419
+ index += 1
420
+ end
421
+ end
422
+ end
423
+ # END: STRUCTURE SETUP METHODS
424
+
425
+ # START: TEMPLATE RESOLVER METHODS
426
+ def resolve_parent_class(content)
427
+ return "Lutaml::Model::Serializable" unless content.dig(:complex_content, :extension)
428
+
429
+ Utils.camel_case(content.dig(:complex_content, :extension, :extension_base))
430
+ end
431
+
432
+ def resolve_attribute_class(attribute)
433
+ attr_class = attribute.base_class.split(":")&.last
434
+ case attr_class
435
+ when *DEFAULT_CLASSES
436
+ ":#{attr_class}"
437
+ else
438
+ Utils.camel_case(attr_class)
439
+ end
440
+ end
441
+
442
+ def resolve_occurs(arguments)
443
+ min_occurs = arguments[:min_occurs]
444
+ max_occurs = arguments[:max_occurs]
445
+ max_occurs = max_occurs.to_s&.match?(/[A-Za-z]+/) ? nil : max_occurs.to_i if max_occurs
446
+ ", collection: #{max_occurs ? min_occurs.to_i..max_occurs : true}"
447
+ end
448
+
449
+ def resolve_content(content, hash = MappingHash.new)
450
+ content.each do |key, value|
451
+ case key
452
+ when :sequence
453
+ resolve_sequence(value, hash)
454
+ when :choice
455
+ resolve_choice(value, hash)
456
+ when :group
457
+ resolve_group(value, hash)
458
+ end
459
+ end
460
+ hash
461
+ end
462
+
463
+ def resolve_elements(elements, hash = MappingHash.new)
464
+ elements.each do |element|
465
+ if element.key?(:ref_class)
466
+ new_element = @elements[element.ref_class.split(":").last]
467
+ hash[new_element.element_name] = new_element
468
+ else
469
+ hash[element.element_name] = element
470
+ end
471
+ end
472
+ hash
473
+ end
474
+
475
+ def resolve_sequence(sequence, hash = MappingHash.new)
476
+ sequence.each do |key, value|
477
+ case key
478
+ when :sequence
479
+ resolve_sequence(value, hash)
480
+ when :elements
481
+ resolve_elements(value, hash)
482
+ when :groups
483
+ value.each { |group| resolve_group(group, hash) }
484
+ when :choice
485
+ value.each { |choice| resolve_choice(choice, hash) }
486
+ end
487
+ end
488
+ hash
489
+ end
490
+
491
+ def resolve_choice(choice, hash = MappingHash.new)
492
+ choice.each do |key, value|
493
+ case key
494
+ when :element
495
+ [resolve_elements(value, hash)]
496
+ when :group
497
+ resolve_group(value, hash)
498
+ when String
499
+ hash[key] = value
500
+ when :sequence
501
+ resolve_sequence(value, hash)
502
+ end
503
+ end
504
+ hash
505
+ end
506
+
507
+ def resolve_group(group, hash = MappingHash.new)
508
+ group.each do |key, value|
509
+ case key
510
+ when :ref_class
511
+ resolve_group(@group_types[value.split(":").last], hash)
512
+ when :choice
513
+ resolve_choice(value, hash)
514
+ when :group
515
+ resolve_group(value, hash)
516
+ when :sequence
517
+ resolve_sequence(value, hash)
518
+ end
519
+ end
520
+ hash
521
+ end
522
+
523
+ def resolve_complex_content(complex_content, hash = MappingHash.new)
524
+ complex_content.each do |key, value|
525
+ case key
526
+ when :extension
527
+ resolve_extension(value, hash)
528
+ when :restriction
529
+ # TODO: No implementation yet!
530
+ hash
531
+ end
532
+ end
533
+ hash
534
+ end
535
+
536
+ def resolve_extension(extension, hash = MappingHash.new)
537
+ hash[:attributes] = extension.attributes if extension.key?(:attributes)
538
+ resolve_sequence(extension.sequence, hash) if extension.key?(:sequence)
539
+ resolve_choice(extension.choice, hash) if extension.key?(:choice)
540
+ hash
541
+ end
542
+
543
+ def resolve_attribute_default(attribute)
544
+ klass = attribute.base_class.split(":").last
545
+ default = attribute[:default]
546
+ ", default: #{resolve_attribute_default_value(klass, default)}"
547
+ end
548
+
549
+ def resolve_attribute_default_value(klass, default)
550
+ return default.inspect unless DEFAULT_CLASSES.include?(klass)
551
+
552
+ klass = "integer" if klass == "int"
553
+ type_klass = Lutaml::Model::Type.const_get(klass.capitalize)
554
+ type_klass.cast(default)
555
+ end
556
+
557
+ def resolve_namespace(options)
558
+ namespace_str = "namespace \"#{options[:namespace]}\"" if options.key?(:namespace)
559
+ namespace_str += ", \"#{options[:prefix]}\"" if options.key?(:prefix) && options.key?(:namespace)
560
+ namespace_str += "\n" if namespace_str
561
+ namespace_str
562
+ end
563
+
564
+ def resolve_required_files(content)
565
+ @required_files = []
566
+ content.each do |key, value|
567
+ case key
568
+ when :sequence
569
+ required_files_sequence(value)
570
+ when :choice
571
+ required_files_choice(value)
572
+ when :group
573
+ required_files_group(value)
574
+ when :attributes
575
+ required_files_attribute(value)
576
+ when :attribute_groups
577
+ value.each { |attribute_group| required_files_attribute_groups(attribute_group) }
578
+ when :complex_content
579
+ required_files_complex_content(value)
580
+ when :simple_content
581
+ required_files_simple_content(value)
582
+ end
583
+ end
584
+ @required_files.uniq.sort_by(&:length)
585
+ end
586
+ # END: TEMPLATE RESOLVER METHODS
587
+
588
+ # START: REQUIRED FILES LIST COMPILER METHODS
589
+ def required_files_simple_content(simple_content)
590
+ simple_content.each do |key, value|
591
+ case key
592
+ when :extension_base
593
+ # Do nothing.
594
+ when :attributes
595
+ required_files_attribute(value)
596
+ when :extension
597
+ required_files_extension(value)
598
+ when :restriction
599
+ required_files_restriction(value)
600
+ end
601
+ end
602
+ end
603
+
604
+ def required_files_complex_content(complex_content)
605
+ complex_content.each do |key, value|
606
+ case key
607
+ when :extension
608
+ required_files_extension(value)
609
+ when :restriction
610
+ required_files_restriction(value)
611
+ end
612
+ end
613
+ end
614
+
615
+ def required_files_extension(extension)
616
+ extension.each do |key, value|
617
+ case key
618
+ when :attribute_group
619
+ required_files_attribute_groups(value)
620
+ when :attribute, :attributes
621
+ required_files_attribute(value)
622
+ when :extension_base
623
+ # Do nothing.
624
+ when :sequence
625
+ required_files_sequence(value)
626
+ when :choice
627
+ required_files_choice(value)
628
+ end
629
+ end
630
+ end
631
+
632
+ def required_files_restriction(restriction)
633
+ restriction.each do |key, value|
634
+ case key
635
+ when :base
636
+ @required_files << Utils.snake_case(value.split(":").last)
637
+ end
638
+ end
639
+ end
640
+
641
+ def required_files_attribute_groups(attr_groups)
642
+ attr_groups.each do |key, value|
643
+ case key
644
+ when :ref_class
645
+ required_files_attribute_groups(@attribute_groups[value.split(":").last])
646
+ when :attribute, :attributes
647
+ required_files_attribute(value)
648
+ end
649
+ end
650
+ end
651
+
652
+ def required_files_attribute(attributes)
653
+ attributes.each do |attribute|
654
+ next if attribute[:ref_class]&.start_with?("xml") || attribute[:base_class]&.start_with?("xml")
655
+
656
+ attribute = @attributes[attribute.ref_class.split(":").last] if attribute.key_exist?(:ref_class)
657
+ attr_class = attribute.base_class.split(":")&.last
658
+ next if DEFAULT_CLASSES.include?(attr_class)
659
+
660
+ @required_files << Utils.snake_case(attr_class)
661
+ end
662
+ end
663
+
664
+ def required_files_choice(choice)
665
+ choice.each do |key, value|
666
+ case key
667
+ when String
668
+ value = @elements[value.ref_class.split(":").last] if value.key?(:ref_class)
669
+ @required_files << Utils.snake_case(value.type_name.split(":").last)
670
+ when :element
671
+ required_files_elements(value)
672
+ when :group
673
+ required_files_group(value)
674
+ when :choice
675
+ required_files_choice(value)
676
+ when :sequence
677
+ required_files_sequence(value)
678
+ end
679
+ end
680
+ end
681
+
682
+ def required_files_group(group)
683
+ group.each do |key, value|
684
+ case key
685
+ when :ref_class
686
+ required_files_group(@group_types[value.split(":").last])
687
+ when :choice
688
+ required_files_choice(value)
689
+ when :sequence
690
+ required_files_sequence(value)
691
+ end
692
+ end
693
+ end
694
+
695
+ def required_files_sequence(sequence)
696
+ sequence.each do |key, value|
697
+ case key
698
+ when :elements
699
+ required_files_elements(value)
700
+ when :sequence
701
+ required_files_sequence(value)
702
+ when :groups
703
+ value.each { |group| required_files_group(group) }
704
+ when :choice
705
+ value.each { |choice| required_files_choice(choice) }
706
+ end
707
+ end
708
+ end
709
+
710
+ def required_files_elements(elements)
711
+ elements.each do |element|
712
+ element = @elements[element.ref_class.split(":").last] if element.key_exist?(:ref_class)
713
+ @required_files << Utils.snake_case(element.type_name.split(":").last)
714
+ end
715
+ end
716
+ # END: REQUIRED FILES LIST COMPILER METHODS
717
+ end
718
+ end
719
+ end
720
+ end