lutaml-xsd 1.2.93 → 1.2.95
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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a752804ef8cfcbbe98845ffcfcbd8830d2c37020d392d34524795f645351cef9
|
|
4
|
+
data.tar.gz: 467cd19ffcc587cbdc4007e614b02f2f299a26917c684e997138fa5116686a7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 904b2740eaa42225aa419c6a27586cc115fbcba3efaa4e4fe987b979f7139bd00e5e96007f094f65b5631a8383bdd12d0e50eaef82482733662d2dcda8658507
|
|
7
|
+
data.tar.gz: 545c68eb6ce9cda1d4b834d3bf608974d3da82e774fee7465918ba39d5c2d7183892c0a314c3314d15f68e8f20a6af27e264fb752f5023008db7a5cfa84be512
|
data/TODO.conformance.md
CHANGED
|
@@ -120,7 +120,7 @@ pinned commit) is measured via `rake conformance:w3c`:
|
|
|
120
120
|
|
|
121
121
|
| mode | pass-rate |
|
|
122
122
|
|---|---|
|
|
123
|
-
| pipeline |
|
|
123
|
+
| pipeline | 4617/4696 (98.32%) |
|
|
124
124
|
|
|
125
125
|
(2026-11-02, bundled-stack measurement with the escape,
|
|
126
126
|
hint-following, pattern-alternative, identity-crash, engine-set,
|
|
@@ -144,7 +144,7 @@ lockfile refresh) → 4411 (93.9%) → 4430 (94.3%) → 4435 (94.4%) →
|
|
|
144
144
|
→ 4557 (97.0%) → 4565 (97.2%) → 4569 (97.3%) → 4562 (drift raw) →
|
|
145
145
|
4563 (97.17%, ref-namespace round) → 4573 (97.38%, group-content
|
|
146
146
|
round) → 4578 (97.49%, attribute-prohibition round) → 4585 (97.64%,
|
|
147
|
-
multi-document round) → 4588 (97.7%, type-binding round) → 4592 (97.79%, model-161 ride) → 4611 (98.19%, engine-406 ride) → 4613 (98.23%, temporal-enumeration round) → 4614 (98.25%, simple-type-children round), +
|
|
147
|
+
multi-document round) → 4588 (97.7%, type-binding round) → 4592 (97.79%, model-161 ride) → 4611 (98.19%, engine-406 ride) → 4613 (98.23%, temporal-enumeration round) → 4614 (98.25%, simple-type-children round) → 4617 (98.32%, ic-ancestor-scope round), +656 net over the 1.2.43 baseline. The escapes round (PR #197 — \d→\p{Nd},
|
|
148
148
|
\w/\W/\s to their XSD value spaces) collapses Regex 120→69;
|
|
149
149
|
same-step pattern facets now behave as alternatives and
|
|
150
150
|
whitespace-only text reaches the facets intact (PR #200), Regex
|
|
@@ -527,6 +527,47 @@ tuple keys, node-set field violations and absent-field rows handled, cross-eleme
|
|
|
527
527
|
(98.28% restored, zero regressions vs the 406 baseline). The
|
|
528
528
|
407 wins ride again when #47 and #529 land. NET: no score
|
|
529
529
|
change; the pin protects the register.
|
|
530
|
+
- NON-SIMPLE-FIELD ROUND (2026-11-07): +2 IC (idK012, idZ010; suite
|
|
531
|
+
4617->4619 = 98.36%, zero regressions, rspec green). An identity
|
|
532
|
+
field takes a value only from a SIMPLE-typed node: a field hitting
|
|
533
|
+
an element with element children (idK012's pid, complexContent
|
|
534
|
+
restriction of anyType) or one DECLARED non-simple — complex
|
|
535
|
+
content, or xsd:anyType as the typeless-local default (idZ010's
|
|
536
|
+
A3/A22, the cross-namespace import shape) — previously contributed
|
|
537
|
+
its concatenated text as a clean row. The rule consults the field's
|
|
538
|
+
declaration via SchemaDefinitionResolver#field_declaration_simple:
|
|
539
|
+
the IC owner's content model descends the raw selector path, the
|
|
540
|
+
selector element's own type descends the raw field path (base
|
|
541
|
+
chains included; refs resolve to the global declaration). THREE
|
|
542
|
+
guards keep it sound: a resolvable xsi:type override governs the
|
|
543
|
+
INSTANCE value space (idF-class overrides stay simple, idL090
|
|
544
|
+
decimal/string fields untouched); unresolvable descents yield nil
|
|
545
|
+
= no determination (the lenient path is untouched); unresolvable
|
|
546
|
+
named types are simple only when builtin-prefixed. LESSON: the
|
|
547
|
+
tri-state verdicts live in NON-predicate methods — rubocop -A
|
|
548
|
+
rewrites `return nil` guards in `?`-methods to `return false`,
|
|
549
|
+
which silently hardened the lenient path twice before the probes
|
|
550
|
+
caught it. The remaining three IC singletons have DIFFERENT roots
|
|
551
|
+
(next rounds): idZ011 (schema-side xs:ID value-constraint
|
|
552
|
+
prohibition, §3.2.6), idZ012 (IDREFS default "abc" references no
|
|
553
|
+
ID), idZ015 (a skip-wildcard-governed attribute supplies no IC
|
|
554
|
+
field value).
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
- IC ANCESTOR-SCOPE ROUND (2026-11-05): +1 IC (idZ008; suite
|
|
558
|
+
4614->4617 = 98.32% with the prefix-resolution and
|
|
559
|
+
temporal-enumeration riders on main, zero regressions, rspec
|
|
560
|
+
green). A keyref whose `refer` names a key declared on an
|
|
561
|
+
ANCESTOR element resolved against an empty table — the
|
|
562
|
+
collected-keys map reset per element (idZ008: the key KeyAAA on
|
|
563
|
+
A, the keyref QQQ on the descendant A2, selector "."). The map
|
|
564
|
+
now accumulates across the walk; instance document order
|
|
565
|
+
guarantees ancestors process before descendants. The other five
|
|
566
|
+
IC singletons (idK012, idZ010/011/012/015) have DIFFERENT roots
|
|
567
|
+
(cross-namespace keyrefs, IDREF defaults) — next rounds. NOTE:
|
|
568
|
+
the PREFIX-RESOLUTION round (+1, particlesQ032 via the 1131
|
|
569
|
+
xmlns-declarations retention in lutaml-model 0.8.167 — pinned)
|
|
570
|
+
is also on main; the score line carries both.
|
|
530
571
|
|
|
531
572
|
- SIMPLE-TYPE-CHILDREN ROUND (2026-11-05, third attempt — the first
|
|
532
573
|
two regressed 15 at scale): +1 DataTypes (dtZ86723; suite
|
|
@@ -52,12 +52,18 @@ module Lutaml
|
|
|
52
52
|
# schema's target namespace (the suite's single-namespace shapes).
|
|
53
53
|
target_ns = repository&.target_namespace_for(schema_element).to_s
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
# Key tables accumulate across the walk: a keyref whose
|
|
56
|
+
# `refer` names a key declared on an ANCESTOR resolves
|
|
57
|
+
# against the ancestor's rows (idZ008 — the key on A, the
|
|
58
|
+
# keyref on the descendant A2). Instance document order
|
|
59
|
+
# guarantees ancestors are processed before descendants.
|
|
60
|
+
collected = @collected_keys ||= {}
|
|
56
61
|
|
|
57
62
|
Array(schema_element.key).each do |constraint|
|
|
58
63
|
evaluated_constraints << constraint.name.to_s if constraint.name
|
|
59
64
|
values = collect_values(xml_element, constraint, collector,
|
|
60
|
-
repository, target_ns, :key
|
|
65
|
+
repository, target_ns, :key,
|
|
66
|
+
schema_element)
|
|
61
67
|
report_duplicates(collector, xml_element, constraint, values,
|
|
62
68
|
"duplicate_key_violation")
|
|
63
69
|
collected[constraint.name] = values if constraint.name
|
|
@@ -66,7 +72,8 @@ module Lutaml
|
|
|
66
72
|
Array(schema_element.unique).each do |constraint|
|
|
67
73
|
evaluated_constraints << constraint.name.to_s if constraint.name
|
|
68
74
|
values = collect_values(xml_element, constraint, collector,
|
|
69
|
-
repository, target_ns, :unique
|
|
75
|
+
repository, target_ns, :unique,
|
|
76
|
+
schema_element)
|
|
70
77
|
report_duplicates(collector, xml_element, constraint, values,
|
|
71
78
|
"duplicate_unique_violation")
|
|
72
79
|
collected[constraint.name] = values if constraint.name
|
|
@@ -78,7 +85,8 @@ module Lutaml
|
|
|
78
85
|
next if referred.nil?
|
|
79
86
|
|
|
80
87
|
collect_values(xml_element, constraint, collector,
|
|
81
|
-
repository, target_ns, :keyref
|
|
88
|
+
repository, target_ns, :keyref,
|
|
89
|
+
schema_element).each do |value|
|
|
82
90
|
next if referred.include?(value)
|
|
83
91
|
|
|
84
92
|
report_error(
|
|
@@ -102,24 +110,53 @@ module Lutaml
|
|
|
102
110
|
|
|
103
111
|
private
|
|
104
112
|
|
|
113
|
+
# Field values (and violation flags) for ONE selector-matched
|
|
114
|
+
# node: per field, the supply value, the node's text/attr
|
|
115
|
+
# value, or a non-simple verdict.
|
|
116
|
+
#
|
|
117
|
+
# @return [Array(Array, Boolean, Boolean)] [values, ambiguous,
|
|
118
|
+
# non_simple]
|
|
119
|
+
def evaluate_field_row(node, fields, raws, constraint,
|
|
120
|
+
owner_element, repository)
|
|
121
|
+
values = []
|
|
122
|
+
ambiguous = false
|
|
123
|
+
non_simple = false
|
|
124
|
+
fields.each_with_index do |field, index|
|
|
125
|
+
matched = field_nodes(node, field)
|
|
126
|
+
# A field evaluating to a node-set with more than one
|
|
127
|
+
# member violates the constraint (XSD 1.0 §3.11.4).
|
|
128
|
+
ambiguous = true if matched.size > 1
|
|
129
|
+
values << if matched.first.nil?
|
|
130
|
+
field_supply_value(node, field, repository)
|
|
131
|
+
elsif matched.first.is_a?(Moxml::Element) &&
|
|
132
|
+
field_node_non_simple?(
|
|
133
|
+
matched.first, constraint, raws[index],
|
|
134
|
+
owner_element, repository
|
|
135
|
+
)
|
|
136
|
+
# An element field hitting an element with
|
|
137
|
+
# ELEMENT children (complex content), or one
|
|
138
|
+
# DECLARED non-simple (complex content or
|
|
139
|
+
# xsd:anyType — the typeless default), supplies
|
|
140
|
+
# no identity value: the field "does evaluate
|
|
141
|
+
# to a node of non-simple type" (idK012/idZ010).
|
|
142
|
+
non_simple = true
|
|
143
|
+
nil
|
|
144
|
+
else
|
|
145
|
+
field_node_value(matched.first)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
[values, ambiguous, non_simple]
|
|
149
|
+
end
|
|
150
|
+
|
|
105
151
|
def collect_values(xml_element, constraint, collector, repository,
|
|
106
|
-
target_namespace, kind)
|
|
152
|
+
target_namespace, kind, owner_element)
|
|
107
153
|
fields = field_xpaths(constraint, target_namespace)
|
|
154
|
+
raws = raw_fields(constraint)
|
|
108
155
|
selector_xpaths(constraint, target_namespace).flat_map do |selector|
|
|
109
156
|
Array(xml_element.moxml_element.xpath(selector)).map do |node|
|
|
110
|
-
values =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
matched = field_nodes(node, field)
|
|
114
|
-
# A field evaluating to a node-set with more than one
|
|
115
|
-
# member violates the constraint (XSD 1.0 §3.11.4).
|
|
116
|
-
ambiguous = true if matched.size > 1
|
|
117
|
-
values << if matched.first
|
|
118
|
-
field_node_value(matched.first)
|
|
119
|
-
else
|
|
120
|
-
field_supply_value(node, field, repository)
|
|
121
|
-
end
|
|
122
|
-
end
|
|
157
|
+
values, ambiguous, non_simple =
|
|
158
|
+
evaluate_field_row(node, fields, raws, constraint,
|
|
159
|
+
owner_element, repository)
|
|
123
160
|
|
|
124
161
|
if ambiguous
|
|
125
162
|
report_error(
|
|
@@ -133,6 +170,19 @@ module Lutaml
|
|
|
133
170
|
next
|
|
134
171
|
end
|
|
135
172
|
|
|
173
|
+
if non_simple
|
|
174
|
+
report_error(
|
|
175
|
+
collector,
|
|
176
|
+
code: "field_non_simple_type_violation",
|
|
177
|
+
message: "Field #{fields.join(', ')} of #{kind} " \
|
|
178
|
+
"'#{constraint.name}' evaluates to a node of " \
|
|
179
|
+
"non-simple type in '#{node.name}'",
|
|
180
|
+
location: xml_element.xpath,
|
|
181
|
+
context: { constraint: constraint.name },
|
|
182
|
+
)
|
|
183
|
+
next
|
|
184
|
+
end
|
|
185
|
+
|
|
136
186
|
# A qualified node has every field present; an absent
|
|
137
187
|
# field means the node does not contribute a row —
|
|
138
188
|
# except for xs:KEY, where a selected node whose fields
|
|
@@ -287,6 +337,67 @@ module Lutaml
|
|
|
287
337
|
repository.declared_attribute_supply(parent_type, attr_local)
|
|
288
338
|
end
|
|
289
339
|
|
|
340
|
+
# Whether an element field's node supplies no identity value:
|
|
341
|
+
# it has element children, is declared non-simple (complex
|
|
342
|
+
# content or xsd:anyType — the typeless default), or carries a
|
|
343
|
+
# resolvable xsi:type override to a non-simple type — the
|
|
344
|
+
# override governs the INSTANCE value space, so a simple
|
|
345
|
+
# override keeps the field simple no matter what the schema
|
|
346
|
+
# declares (idF-class).
|
|
347
|
+
#
|
|
348
|
+
# @param matched [Moxml::Element]
|
|
349
|
+
# @return [Boolean]
|
|
350
|
+
def field_node_non_simple?(matched, constraint, raw_field,
|
|
351
|
+
owner_element, repository)
|
|
352
|
+
override = override_governs_simple(matched, repository)
|
|
353
|
+
return !override unless override.nil?
|
|
354
|
+
|
|
355
|
+
matched.elements.any? ||
|
|
356
|
+
declared_field_non_simple?(constraint, raw_field,
|
|
357
|
+
owner_element, repository)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# The simple/non-simple verdict of the field node's xsi:type
|
|
361
|
+
# override, or nil when the node has no resolvable override.
|
|
362
|
+
#
|
|
363
|
+
# @return [Boolean, nil]
|
|
364
|
+
def override_governs_simple(node, repository)
|
|
365
|
+
override = node.attributes.find do |candidate|
|
|
366
|
+
candidate.name == "type" &&
|
|
367
|
+
candidate.namespace&.uri == XSI_NAMESPACE
|
|
368
|
+
end
|
|
369
|
+
return unless override
|
|
370
|
+
|
|
371
|
+
parts = override.value.to_s.split(":")
|
|
372
|
+
local = parts.last
|
|
373
|
+
return if local.nil? || local.empty?
|
|
374
|
+
return local != "anyType" if %w[xs xsd].include?(parts.first)
|
|
375
|
+
|
|
376
|
+
case repository&.resolve_named_type(local)
|
|
377
|
+
when Lutaml::Xml::Schema::Xsd::SimpleType then true
|
|
378
|
+
when Lutaml::Xml::Schema::Xsd::ComplexType then false
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Whether the field's element declaration — reached by
|
|
383
|
+
# descending the IC owner's content model through the raw
|
|
384
|
+
# selector path, then the raw field step — is non-simple
|
|
385
|
+
# (complex content or xsd:anyType). Unresolvable paths allow
|
|
386
|
+
# no determination.
|
|
387
|
+
#
|
|
388
|
+
# @return [Boolean]
|
|
389
|
+
def declared_field_non_simple?(constraint, raw_field,
|
|
390
|
+
owner_element, repository)
|
|
391
|
+
return false unless repository && owner_element
|
|
392
|
+
|
|
393
|
+
owner_type = owner_element.complex_type ||
|
|
394
|
+
repository.resolve_type(owner_element)
|
|
395
|
+
raw_selectors(constraint).any? do |selector_raw|
|
|
396
|
+
repository.field_declaration_simple(owner_type, selector_raw,
|
|
397
|
+
raw_field) == false
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
290
401
|
def report_duplicates(collector, xml_element, constraint, values,
|
|
291
402
|
code)
|
|
292
403
|
values.group_by(&:itself).each do |value, group|
|
|
@@ -342,6 +453,20 @@ module Lutaml
|
|
|
342
453
|
end
|
|
343
454
|
end
|
|
344
455
|
|
|
456
|
+
# The selector steps as WRITTEN (unions split the same way the
|
|
457
|
+
# adapted list splits) — the declared-field determination
|
|
458
|
+
# descends the schema through these.
|
|
459
|
+
#
|
|
460
|
+
# @return [Array<String>]
|
|
461
|
+
def raw_selectors(constraint)
|
|
462
|
+
raw = if constraint.selector.is_a?(Lutaml::Xml::Schema::Xsd::Selector)
|
|
463
|
+
constraint.selector.xpath
|
|
464
|
+
else
|
|
465
|
+
constraint.selector
|
|
466
|
+
end
|
|
467
|
+
raw.to_s.split(/\s*\|\s*/).reject(&:empty?)
|
|
468
|
+
end
|
|
469
|
+
|
|
345
470
|
# The field steps as WRITTEN (unions split the same way the
|
|
346
471
|
# adapted list splits) — the absent-row decision reads the
|
|
347
472
|
# step's own namespace governance from these.
|
|
@@ -527,8 +527,228 @@ module Lutaml
|
|
|
527
527
|
particle_for_type(resolve_named_type(local_name))
|
|
528
528
|
end
|
|
529
529
|
|
|
530
|
+
# Whether the element declaration an identity-constraint field
|
|
531
|
+
# binds to is SIMPLE-typed. A field may take an identity value
|
|
532
|
+
# only from a simple-typed node: a complex type with complex
|
|
533
|
+
# content (element children, idK012) or xsd:anyType — the
|
|
534
|
+
# default for a typeless local declaration (idZ010's A3/A22) —
|
|
535
|
+
# supplies no identity value. The declaration is reached by
|
|
536
|
+
# descending the IC owner's content model through the raw
|
|
537
|
+
# selector path, then the selector element's own type through
|
|
538
|
+
# the raw field path.
|
|
539
|
+
#
|
|
540
|
+
# @param owner_type [Lutaml::Xml::Schema::Xsd::ComplexType, nil]
|
|
541
|
+
# @param selector_raw [String] the selector XPath as written
|
|
542
|
+
# @param field_raw [String] the field XPath as written
|
|
543
|
+
# @return [Boolean, nil] nil when unresolvable (no determination)
|
|
544
|
+
def field_declaration_simple(owner_type, selector_raw, field_raw)
|
|
545
|
+
sel_type = path_bound_type(owner_type, selector_raw)
|
|
546
|
+
bound = sel_type && path_bound_with_decl(sel_type, field_raw)
|
|
547
|
+
return unless bound
|
|
548
|
+
|
|
549
|
+
field_type, field_decl, owner_self = bound
|
|
550
|
+
return type_supplies_identity_value?(field_type) if owner_self
|
|
551
|
+
return unless field_decl
|
|
552
|
+
|
|
553
|
+
element_declaration_simple?(field_decl)
|
|
554
|
+
end
|
|
555
|
+
|
|
530
556
|
private
|
|
531
557
|
|
|
558
|
+
# Whether a TYPE carries an identity value for a field bound to
|
|
559
|
+
# the selector node itself (a "." field): a simple type or a
|
|
560
|
+
# complex type with simple content does; complex content does not.
|
|
561
|
+
#
|
|
562
|
+
# @param type [Object]
|
|
563
|
+
# @return [Boolean, nil]
|
|
564
|
+
def type_supplies_identity_value?(type)
|
|
565
|
+
case type
|
|
566
|
+
when Lutaml::Xml::Schema::Xsd::SimpleType then true
|
|
567
|
+
when Lutaml::Xml::Schema::Xsd::ComplexType
|
|
568
|
+
!type.simple_content.nil?
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# The type an identity-constraint XPath (selector or field)
|
|
573
|
+
# binds to, descended through the content model from +type+.
|
|
574
|
+
# A bare "." binds +type+ itself; each named step locates the
|
|
575
|
+
# child element declaration and descends into its type.
|
|
576
|
+
#
|
|
577
|
+
# @param type [Object, nil]
|
|
578
|
+
# @param raw_path [String] the XPath as written
|
|
579
|
+
# @return [Object, nil]
|
|
580
|
+
def path_bound_type(type, raw_path, depth = 0)
|
|
581
|
+
path_bound_with_decl(type, raw_path, depth)&.first
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# The bound type AND the final element declaration of an
|
|
585
|
+
# identity-constraint XPath. The declaration is nil when the
|
|
586
|
+
# path is a bare "." (the bound node IS the starting element).
|
|
587
|
+
#
|
|
588
|
+
# @param type [Object, nil]
|
|
589
|
+
# @param raw_path [String] the XPath as written
|
|
590
|
+
# @return [Array(Object, Object, Boolean), nil] [type, decl,
|
|
591
|
+
# owner_self] — nil when unresolvable
|
|
592
|
+
def path_bound_with_decl(type, raw_path, depth = 0)
|
|
593
|
+
return nil if type.nil? || depth > 8
|
|
594
|
+
|
|
595
|
+
steps = raw_path.to_s.split("/").reject do |step|
|
|
596
|
+
step.empty? || step == "."
|
|
597
|
+
end
|
|
598
|
+
current = type
|
|
599
|
+
decl = nil
|
|
600
|
+
steps.each_with_index do |step, index|
|
|
601
|
+
local = step.split(":").last
|
|
602
|
+
return nil if step.start_with?("@") || local.nil? || local == "*"
|
|
603
|
+
|
|
604
|
+
decl = declared_child_element(current, local, depth)
|
|
605
|
+
return nil unless decl
|
|
606
|
+
|
|
607
|
+
current = declared_element_type(decl, depth)
|
|
608
|
+
# A nil type mid-path (typeless or unresolvable named)
|
|
609
|
+
# cannot descend further; a nil type on the FINAL step
|
|
610
|
+
# still binds its declaration — typeless defaults to
|
|
611
|
+
# xsd:anyType, which the caller's predicate decides.
|
|
612
|
+
return nil if current.nil? && index < steps.length - 1
|
|
613
|
+
end
|
|
614
|
+
[current, decl, steps.empty?]
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# The element declaration for +local_name+ in the content model
|
|
618
|
+
# of +type+: its own particles first, then the derivation base
|
|
619
|
+
# chain (derived types inherit the base's particles).
|
|
620
|
+
#
|
|
621
|
+
# @param type [Object, nil]
|
|
622
|
+
# @param local_name [String]
|
|
623
|
+
# @param depth [Integer] recursion guard
|
|
624
|
+
# @return [Lutaml::Xml::Schema::Xsd::Element, nil]
|
|
625
|
+
def declared_child_element(type, local_name, depth = 0)
|
|
626
|
+
return nil if depth > 8
|
|
627
|
+
return nil unless type.is_a?(Lutaml::Xml::Schema::Xsd::ComplexType)
|
|
628
|
+
|
|
629
|
+
found = find_particle_element(particle_for_type(type), local_name,
|
|
630
|
+
depth)
|
|
631
|
+
return found if found
|
|
632
|
+
|
|
633
|
+
content = type.complex_content
|
|
634
|
+
part = content && (Array(content.extension).first ||
|
|
635
|
+
Array(content.restriction).first)
|
|
636
|
+
return nil unless part
|
|
637
|
+
|
|
638
|
+
declared_child_element(
|
|
639
|
+
resolve_named_type(part.base.to_s.split(":").last),
|
|
640
|
+
local_name, depth + 1
|
|
641
|
+
)
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
# Depth-first search for an Element particle named +local_name+.
|
|
645
|
+
# A ref particle binds the GLOBAL declaration (the descent needs
|
|
646
|
+
# its type); prefix-insensitive, the same trade the adapted
|
|
647
|
+
# XPath evaluation makes.
|
|
648
|
+
#
|
|
649
|
+
# @param node [Object, Array, nil]
|
|
650
|
+
# @param local_name [String]
|
|
651
|
+
# @param depth [Integer] recursion guard
|
|
652
|
+
# @return [Lutaml::Xml::Schema::Xsd::Element, nil]
|
|
653
|
+
def find_particle_element(node, local_name, depth = 0)
|
|
654
|
+
return nil if node.nil? || depth > 24
|
|
655
|
+
|
|
656
|
+
if node.is_a?(Array)
|
|
657
|
+
node.each do |child|
|
|
658
|
+
found = find_particle_element(child, local_name, depth + 1)
|
|
659
|
+
return found if found
|
|
660
|
+
end
|
|
661
|
+
return nil
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
if node.is_a?(Lutaml::Xml::Schema::Xsd::Element)
|
|
665
|
+
return node if node.name.to_s.split(":").last == local_name
|
|
666
|
+
|
|
667
|
+
ref_local = node.ref.to_s.split(":").last
|
|
668
|
+
return nil unless ref_local == local_name
|
|
669
|
+
|
|
670
|
+
return resolve_global(ref_local, nil)
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
nested = CONTAINER_NESTED_KINDS[node.class]
|
|
674
|
+
nested&.each do |kind|
|
|
675
|
+
found = find_particle_element(Array(node.public_send(kind)),
|
|
676
|
+
local_name, depth + 1)
|
|
677
|
+
return found if found
|
|
678
|
+
end
|
|
679
|
+
(CONTAINER_LEAF_KINDS[node.class] || []).each do |kind|
|
|
680
|
+
Array(node.public_send(kind)).each do |leaf|
|
|
681
|
+
next unless leaf
|
|
682
|
+
|
|
683
|
+
if leaf.is_a?(Lutaml::Xml::Schema::Xsd::Group)
|
|
684
|
+
resolved = if leaf.ref
|
|
685
|
+
resolve_group(leaf.ref.to_s
|
|
686
|
+
.split(":").last)
|
|
687
|
+
else
|
|
688
|
+
leaf
|
|
689
|
+
end
|
|
690
|
+
found = find_particle_element(resolved, local_name,
|
|
691
|
+
depth + 1)
|
|
692
|
+
return found if found
|
|
693
|
+
elsif leaf.is_a?(Lutaml::Xml::Schema::Xsd::Element)
|
|
694
|
+
found = find_particle_element(leaf, local_name, depth + 1)
|
|
695
|
+
return found if found
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
nil
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# The TYPE a bound element declaration carries: its inline
|
|
703
|
+
# complexType or simpleType, else the named type its +type+
|
|
704
|
+
# attribute resolves to.
|
|
705
|
+
#
|
|
706
|
+
# @param element_decl [Lutaml::Xml::Schema::Xsd::Element]
|
|
707
|
+
# @param depth [Integer] recursion guard
|
|
708
|
+
# @return [Object, nil]
|
|
709
|
+
def declared_element_type(element_decl, _depth = 0)
|
|
710
|
+
return element_decl.complex_type if element_decl.complex_type
|
|
711
|
+
return element_decl.simple_type if element_decl.simple_type
|
|
712
|
+
|
|
713
|
+
name = element_decl.type.to_s
|
|
714
|
+
return nil if name.empty?
|
|
715
|
+
|
|
716
|
+
resolve_named_type(name.split(":").last)
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
# Whether a bound element declaration's type is SIMPLE: an
|
|
720
|
+
# inline simpleType, a simple-named type, or a complex type
|
|
721
|
+
# with SIMPLE content (text value carries the identity value).
|
|
722
|
+
# Complex content — including xsd:anyType, the default for a
|
|
723
|
+
# typeless declaration — is NOT simple.
|
|
724
|
+
#
|
|
725
|
+
# @param element_decl [Lutaml::Xml::Schema::Xsd::Element]
|
|
726
|
+
# @return [Boolean, nil] nil when the named type is unresolvable
|
|
727
|
+
def element_declaration_simple?(element_decl)
|
|
728
|
+
return true if element_decl.simple_type
|
|
729
|
+
|
|
730
|
+
complex = element_decl.complex_type
|
|
731
|
+
return !complex.simple_content.nil? if complex
|
|
732
|
+
|
|
733
|
+
name = element_decl.type.to_s
|
|
734
|
+
return false if name.empty?
|
|
735
|
+
|
|
736
|
+
parts = name.split(":")
|
|
737
|
+
return false if parts.last == "anyType"
|
|
738
|
+
|
|
739
|
+
resolved = resolve_named_type(parts.last)
|
|
740
|
+
case resolved
|
|
741
|
+
when Lutaml::Xml::Schema::Xsd::SimpleType then true
|
|
742
|
+
when Lutaml::Xml::Schema::Xsd::ComplexType
|
|
743
|
+
!resolved.simple_content.nil?
|
|
744
|
+
else
|
|
745
|
+
# Unresolvable but builtin-prefixed (xs:/xsd: both bind the
|
|
746
|
+
# schema namespace) is simple; unresolvable otherwise allows
|
|
747
|
+
# no determination.
|
|
748
|
+
parts.first.to_s.match?(/\Axs(d)?\z/) || nil
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
532
752
|
def particle_for_type(type)
|
|
533
753
|
case type
|
|
534
754
|
when Lutaml::Xml::Schema::Xsd::ComplexType
|
data/lib/lutaml/xsd/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-xsd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.95
|
|
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-11-
|
|
11
|
+
date: 2026-11-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: liquid
|