lutaml-model 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) 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 +396 -23
  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 +4 -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 +24 -8
  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/version.rb +1 -1
  19. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
  20. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
  21. data/lib/lutaml/model/xml_mapping.rb +4 -2
  22. data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
  23. data/lib/lutaml/model.rb +1 -0
  24. data/spec/fixtures/xml/invalid_math_document.xml +4 -0
  25. data/spec/fixtures/xml/math_document_schema.xsd +56 -0
  26. data/spec/fixtures/xml/test_schema.xsd +53 -0
  27. data/spec/fixtures/xml/valid_math_document.xml +4 -0
  28. data/spec/lutaml/model/cdata_spec.rb +2 -2
  29. data/spec/lutaml/model/custom_model_spec.rb +7 -20
  30. data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
  31. data/spec/lutaml/model/map_all_spec.rb +188 -0
  32. data/spec/lutaml/model/mixed_content_spec.rb +15 -15
  33. data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
  34. data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
  35. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
  36. data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
  37. 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