lutaml-xsd 1.2.3 → 1.2.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/TODO.conformance.md +6 -5
- data/lib/lutaml/xsd/validation/rules/content_model_validation_rule.rb +108 -1
- data/lib/lutaml/xsd/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da77a9a03a7bdbb0a9ea2092de4634b5151a6bafe0781592d58fbf869aacb542
|
|
4
|
+
data.tar.gz: 11c964841aa88464f5ab94c176f18757b1ff5bdc33813030d24cb7429957b828
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 96be4d45cabc55c0e0839a456ce4d6948cbe326bc25d72e1f5f228ead717a4a578871295f3b1bbeb9714657997217345657ba2c0f1b9af8a502211d62e9748cd
|
|
7
|
+
data.tar.gz: 55c6018abace93a782f1d661706568aeb9c48ed3c2c88b31fc64504a6586bb5ae3661fbdcbf312f0ffe75fd3e782e8c11c392387f54548bcbaab19be72892714
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
lutaml-xsd (1.2.
|
|
4
|
+
lutaml-xsd (1.2.3)
|
|
5
5
|
liquid (>= 4.0, < 6.0)
|
|
6
6
|
lutaml-model (~> 0.8.123)
|
|
7
7
|
lutaml-store (~> 0.3)
|
|
@@ -264,7 +264,7 @@ CHECKSUMS
|
|
|
264
264
|
liquid (5.14.0) sha256=e1020c2b2ee58c7a4d3bfdc7a331537bd27a783353748bff0bf27c6ac0c17065
|
|
265
265
|
lutaml-model (0.8.123) sha256=475f391371965f5baee2a9d018a7c9da8361c3774d3802749aa8805ee1253fde
|
|
266
266
|
lutaml-store (0.3.2) sha256=9c455dbab32b4c635c48a27213f6f56fda191731c4574eb6187184ba0588a672
|
|
267
|
-
lutaml-xsd (1.2.
|
|
267
|
+
lutaml-xsd (1.2.3)
|
|
268
268
|
memo_wise (1.13.0) sha256=30220c38c4cef410849bc73553c58664dc2c91c6379e4a1df22aea02358b716b
|
|
269
269
|
moxml (0.5.153) sha256=561c6df843edd4a3b7f4c6785f9cfd6fef61050df7f1113fdcd45d8495704fc2
|
|
270
270
|
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
|
data/TODO.conformance.md
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
Goal: conformance is a number, not a feeling. Vendor the W3C XML Schema
|
|
4
4
|
test-suite categories, build the harness, publish the pass-rate, grow it.
|
|
5
5
|
|
|
6
|
-
## Baseline (2026-10-23,
|
|
6
|
+
## Baseline (2026-10-23, 22 cases / 47 checks, full pass)
|
|
7
7
|
|
|
8
|
-
Corpus:
|
|
8
|
+
Corpus: 22 case classes under `spec/conformance/cases/`, modelled on the
|
|
9
9
|
W3C XML Schema Test Suite category taxonomy — self-authored because the
|
|
10
10
|
canonical 2007 suite distribution is no longer served from w3.org (owner
|
|
11
11
|
can vendor a tarball later; provenance README goes with it).
|
|
12
12
|
|
|
13
13
|
| mode | pass-rate |
|
|
14
14
|
|---|---|
|
|
15
|
-
| engine (Moxml::XSD tier-1, leptris 1.9.380.2) | 38/42 (90.5%,
|
|
16
|
-
| pipeline (ValidationJob + wired rule engine) |
|
|
15
|
+
| engine (Moxml::XSD tier-1, leptris 1.9.380.2) | 38/42 (90.5%, 2 cases unsupported) |
|
|
16
|
+
| pipeline (ValidationJob + wired rule engine) | 47/47 (100.0%) |
|
|
17
17
|
|
|
18
18
|
The pipeline now passes every check in the corpus. The engine trails on
|
|
19
19
|
four: strict wildcard/anyAttribute under-detection, xsi:nil on
|
|
@@ -28,7 +28,8 @@ baseline 34/39 (87.2%); gap rules 37/39 (94.9%); anyAttribute 40/42
|
|
|
28
28
|
(95.2%); keyref activation via lutaml-model 0.8.121 → 41/42 (97.6%);
|
|
29
29
|
lutaml-model 0.8.123 (complexType/extension anyAttribute mappings) +
|
|
30
30
|
leptris 1.9.380.2 → 42/42 (100.0%); xs:group expansion in the content
|
|
31
|
-
and occurrence walks grew the corpus to 45 checks → 45/45 (100.0%)
|
|
31
|
+
and occurrence walks grew the corpus to 45 checks → 45/45 (100.0%);
|
|
32
|
+
abstract-element usage checking → 47/47 (100.0%).
|
|
32
33
|
|
|
33
34
|
Remaining engine-mode gaps (tracked, moxml/leptris-side):
|
|
34
35
|
|
|
@@ -59,7 +59,7 @@ module Lutaml
|
|
|
59
59
|
def validate(xml_element, schema_def, collector, repository: nil)
|
|
60
60
|
return unless schema_def
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
handled = engine_content_check(xml_element, collector) == :handled
|
|
63
63
|
|
|
64
64
|
particles = effective_particles(
|
|
65
65
|
xml_element, schema_def, repository
|
|
@@ -68,6 +68,12 @@ module Lutaml
|
|
|
68
68
|
# rule already reports it, so no content walk runs here.
|
|
69
69
|
return if particles.nil? || particles.empty?
|
|
70
70
|
|
|
71
|
+
# The engine does not check abstract-element usage; scan for
|
|
72
|
+
# it regardless of whether the engine answered.
|
|
73
|
+
scan_abstract_usage(xml_element, particles, collector,
|
|
74
|
+
repository)
|
|
75
|
+
return if handled
|
|
76
|
+
|
|
71
77
|
validate_particle_sequence(xml_element, particles, collector,
|
|
72
78
|
repository)
|
|
73
79
|
end
|
|
@@ -165,6 +171,48 @@ module Lutaml
|
|
|
165
171
|
particles
|
|
166
172
|
end
|
|
167
173
|
|
|
174
|
+
# Engine-accepted content still needs the checks the engine
|
|
175
|
+
# lacks: abstract element declarations cannot appear in
|
|
176
|
+
# instances. Containers flatten to their leaf declarations —
|
|
177
|
+
# the walk keeps them whole, the scan needs the leaves.
|
|
178
|
+
def scan_abstract_usage(xml_element, particles, collector,
|
|
179
|
+
repository)
|
|
180
|
+
leaves = leaf_particles(particles, repository)
|
|
181
|
+
xml_element.children.each do |child|
|
|
182
|
+
leaves.each do |particle|
|
|
183
|
+
next unless particle.is_a?(Lutaml::Xsd::Element)
|
|
184
|
+
next unless particle_matches?(child, particle, repository,
|
|
185
|
+
parent: xml_element)
|
|
186
|
+
next unless element_declaration_abstract?(particle, child,
|
|
187
|
+
repository)
|
|
188
|
+
|
|
189
|
+
report_error(
|
|
190
|
+
collector,
|
|
191
|
+
code: "abstract_element_used",
|
|
192
|
+
message: "Element '#{child.qualified_name}' is " \
|
|
193
|
+
"abstract and cannot appear in the instance",
|
|
194
|
+
location: child.xpath,
|
|
195
|
+
context: { element: child.qualified_name },
|
|
196
|
+
)
|
|
197
|
+
break
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def leaf_particles(particles, repository)
|
|
203
|
+
particles.flat_map do |particle|
|
|
204
|
+
case particle
|
|
205
|
+
when Lutaml::Xml::Schema::Xsd::Group
|
|
206
|
+
leaf_particles(expand_group_container(particle, repository),
|
|
207
|
+
repository)
|
|
208
|
+
when Lutaml::Xsd::Sequence, Lutaml::Xsd::Choice, Lutaml::Xsd::All
|
|
209
|
+
leaf_particles(collect_particles(particle), repository)
|
|
210
|
+
else
|
|
211
|
+
[particle]
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
168
216
|
# Sequential walk over a particle list (min/max per particle,
|
|
169
217
|
# recursive matching, wildcard permission for unmatched tails).
|
|
170
218
|
#
|
|
@@ -202,6 +250,16 @@ module Lutaml
|
|
|
202
250
|
if particle.is_a?(Lutaml::Xsd::Any)
|
|
203
251
|
check_strict_wildcard_child(child, particle, collector,
|
|
204
252
|
repository)
|
|
253
|
+
elsif particle.is_a?(Lutaml::Xsd::Element) &&
|
|
254
|
+
element_declaration_abstract?(particle, child, repository)
|
|
255
|
+
report_error(
|
|
256
|
+
collector,
|
|
257
|
+
code: "abstract_element_used",
|
|
258
|
+
message: "Element '#{child.qualified_name}' is " \
|
|
259
|
+
"abstract and cannot appear in the instance",
|
|
260
|
+
location: child.xpath,
|
|
261
|
+
context: { element: child.qualified_name },
|
|
262
|
+
)
|
|
205
263
|
end
|
|
206
264
|
|
|
207
265
|
matched_count += 1
|
|
@@ -249,6 +307,30 @@ module Lutaml
|
|
|
249
307
|
end
|
|
250
308
|
end
|
|
251
309
|
|
|
310
|
+
# Whether the matched declaration is abstract: abstract element
|
|
311
|
+
# declarations cannot appear directly in instances (substitution
|
|
312
|
+
# members remain legal). Direct declarations carry their own
|
|
313
|
+
# abstract flag; references resolve to the global declaration.
|
|
314
|
+
def element_declaration_abstract?(particle, child, repository)
|
|
315
|
+
declaration = declaration_for(particle, child, repository)
|
|
316
|
+
return false unless declaration
|
|
317
|
+
|
|
318
|
+
truthy?(declaration.abstract) && child.name == declaration.name
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def declaration_for(particle, child, repository)
|
|
322
|
+
return particle if particle.name.to_s.match?(/\S/)
|
|
323
|
+
return nil unless repository
|
|
324
|
+
|
|
325
|
+
local = particle.ref.to_s.split(":").last
|
|
326
|
+
repository.resolve(local, child.namespace_uri) ||
|
|
327
|
+
repository.resolve(local, nil)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def truthy?(value)
|
|
331
|
+
value.to_s == "true"
|
|
332
|
+
end
|
|
333
|
+
|
|
252
334
|
# Report an unsatisfied minimum occurrence: a choice container
|
|
253
335
|
# names its alternatives, anything else reports generically.
|
|
254
336
|
#
|
|
@@ -471,6 +553,13 @@ module Lutaml
|
|
|
471
553
|
# @param repository [SchemaDefinitionResolver, nil]
|
|
472
554
|
# @return [Boolean]
|
|
473
555
|
def element_matches?(xml_element, schema_element, repository = nil)
|
|
556
|
+
local_ref = schema_element.name.to_s.empty? && schema_element.ref
|
|
557
|
+
if local_ref && repository
|
|
558
|
+
return ref_element_matches?(xml_element, schema_element,
|
|
559
|
+
local_ref.to_s.split(":").last,
|
|
560
|
+
repository)
|
|
561
|
+
end
|
|
562
|
+
|
|
474
563
|
if xml_element.name == schema_element.name
|
|
475
564
|
return namespaces_match_instance?(
|
|
476
565
|
xml_element,
|
|
@@ -481,6 +570,24 @@ module Lutaml
|
|
|
481
570
|
substitution_member?(xml_element, schema_element, repository)
|
|
482
571
|
end
|
|
483
572
|
|
|
573
|
+
# An xs:ref particle takes the name and target namespace of the
|
|
574
|
+
# global declaration it references; substitution members of that
|
|
575
|
+
# declaration match too.
|
|
576
|
+
def ref_element_matches?(xml_element, schema_element, local,
|
|
577
|
+
repository)
|
|
578
|
+
declaration = repository.resolve(local,
|
|
579
|
+
xml_element.namespace_uri) ||
|
|
580
|
+
repository.resolve(local, nil)
|
|
581
|
+
return false unless declaration
|
|
582
|
+
|
|
583
|
+
return true if xml_element.name == declaration.name &&
|
|
584
|
+
namespaces_match_instance?(
|
|
585
|
+
xml_element, repository.target_namespace_for(declaration)
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
substitution_member?(xml_element, schema_element, repository)
|
|
589
|
+
end
|
|
590
|
+
|
|
484
591
|
# Whether the instance element's declaration is in the
|
|
485
592
|
# substitution group headed by the particle's name/ref.
|
|
486
593
|
#
|
data/lib/lutaml/xsd/version.rb
CHANGED