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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 689df8d8930b23eb8ca6b52c4d435c0e52ea9d654bf64cc51b546fbfe2fcb69e
4
- data.tar.gz: 0e58e44689241fdd080621539f438a3f64d7dad94ca55ff72f06740b93320be9
3
+ metadata.gz: da77a9a03a7bdbb0a9ea2092de4634b5151a6bafe0781592d58fbf869aacb542
4
+ data.tar.gz: 11c964841aa88464f5ab94c176f18757b1ff5bdc33813030d24cb7429957b828
5
5
  SHA512:
6
- metadata.gz: 77edb222b3737cc289729cdafe818c083bf12f3aed2f0b3a8251657a92dec54fb9dcd6f0a2464d4da20fb58a39dc9884ca68bf3e5abeeb9ff6122cb3dc269d6c
7
- data.tar.gz: 96ccd234460bdfee103628897e869e7c999e510d6d76ddc0a989f990757743dfeea0f7848c9bada3212db900096931fdeaf3e832f4b4f31b6e0a62e6072a4b92
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.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.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, 21 cases / 45 checks, full pass)
6
+ ## Baseline (2026-10-23, 22 cases / 47 checks, full pass)
7
7
 
8
- Corpus: 21 case classes under `spec/conformance/cases/`, modelled on the
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%, 1 case unsupported) |
16
- | pipeline (ValidationJob + wired rule engine) | 45/45 (100.0%) |
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
- return if engine_content_check(xml_element, collector) == :handled
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
  #
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Xsd
5
- VERSION = "1.2.3"
5
+ VERSION = "1.2.4"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-xsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.