Resolve a built-in datatype named by xsi:type (xsd:int, xsd:decimal,
xsd:string), which previously resolved only against schema-declared types and
so reported "unknown" and wrongly rejected the element. The built-in is now
resolved when it validly substitutes for the declared type per Schema-Validity
Assessment: the ur-type (anyType/anySimpleType) admits any type, and a bare
built-in declared type admits any built-in derived from it through the XSD
Part 2 datatype lattice (new BuiltinType.derives(from:)). An invalid built-in
substitution is a located error, not a silent acceptance.
Resolving the override correctly exposed two pre-existing gaps, both closed so
the change carries no invalid-instances-accepted regression:
- A simple-typed element rejects non-schema-instance attributes. A simple type
declares no attribute uses, so an element of simple type (including via an
xsi:type override, and including an xsi:nil element) may carry only xsi:* and
namespace attributes. Checked before the nil short-circuit.
- Identity-constraint fields compare in their value space when the selected
node carries an xsi:type. unique/key/keyref tuples compared lexically, so two
xsi:type="xsd:decimal" fields 3.0 and 3 were treated as distinct and a unique
violation was missed. Each field value now carries the type read from the
node's xsi:type and compares via SimpleType.valueMatches when both share a
built-in base, otherwise lexically.
XSTS: valid instances rejected 248 to 233; invalid instances accepted held at
165. Identity tests corrected to spec-valid schemas (an element carrying an
attribute must be a complex type, not type="xs:string").
textContent trimmed leading/trailing whitespace unconditionally before the
type's whiteSpace facet ran, so an xs:string (whiteSpace="preserve") value that
was whitespace-only or had significant edges was emptied or shortened: a tab
value did not match a \t pattern, a length facet undercounted, and so on.
Simple-content validation now reads the raw character content (rawTextContent)
and lets the facet normalize it (collapse types still trim/collapse through
process). The element-only and empty "no significant text" check keeps using
the trimmed form, so indentation whitespace between child elements is still
ignored.
XSTS: valid instances rejected 288 to 248. Baseline updated; regression tests
cover a preserved tab matching \t, a length facet counting whitespace, and
element-only content still ignoring indentation.
Refs #147, #129.
xs:ID, xs:IDREF, and xs:IDREFS were validated only lexically (as NCNames); the
document-wide rules that every ID is unique and every IDREF/IDREFS item matches
some ID were not enforced, so duplicate IDs and dangling references passed. The
tree validator now records ID and reference values during its typed walk over
elements and attributes (a new IDTracker), and reports duplicate IDs and
unresolved references after the whole document is seen, so a forward reference
resolves against an ID declared later.
XSTS: invalid instances accepted 179 to 166. Baseline updated; regression tests
cover unique IDs, a forward reference, a duplicate ID, an unresolved IDREF, and
IDREFS item resolution. The ID/IDREF helpers live in a ComplexValidator
extension to keep the type within the file/body length limits.
Refs #147, #129.
An element with a default or fixed value takes that value when it appears empty,
so the empty content is valid even where the type would reject an empty string.
The constraint value, not the empty string, is validated against the type,
including any xsi:type override, so a fixed value invalid against the overriding
type is still rejected.
Validating the constraint value under xsi:type exposed a second defect: a
restriction's enumeration was unioned with the inherited enumeration, so a
derived type wrongly accepted the base's values. Enumeration now replaces on
restriction (the new set is the value space); pattern still accumulates and is
ANDed across derivation steps.
XSTS: valid instances rejected 395 to 369, invalid instances accepted 240 to
238. Baseline updated; regression tests cover empty default/fixed, present
content still validated, and the constraint value checked against an xsi:type.
Refs #147, #129.
The block on an element declaration (distinct from block on its type) was
parsed into the derivation tables but never threaded to the instance validator,
so an xsi:type that substitutes a type derived by a blocked method was accepted.
elementBlock now flows through XSDCompiled, SchemaDocument, the validation
context, and both ComplexValidator construction sites to blockedSubstitutionError,
which checks the element's own block alongside the type's. It also resolves the
element-ref type indirection (a ref'd child carries an `element:foo` key that
resolves through `types` to the concrete declared type) so the derivation chain
and block checks fire for referenced children, not only the document root.
XSTS: invalid instances accepted 261 to 240. Baseline updated; regression tests
cover element block rejecting a restriction xsi:type, the no-block control, and
an unrelated block not over-rejecting.
Refs #147, #129.
Running the full W3C XSTS archive to completion exposed three crash and hang
classes, now fixed with regression tests, which unblocked the #129 first
measurement.
- Content-model and regex NFA builders now cap total state count (2^20),
degrading further occurrence/quantifier repetition to star, so nested
high-maxOccurs schemas no longer OOM (was 8 GB on the MS Particles set).
- Content-model validation advances one active state-set per child instead of
re-walking the prefix each time, removing the O(children^2) spin.
- Regex class subtraction [abc-[b]] no longer traps on a reversed range, and a
reversed range such as [z-a] is rejected cleanly.
XSTS first measurement baselined: 75 valid schemas rejected, 2467 invalid
schemas accepted, 604 valid instances rejected, 582 invalid instances accepted
across 14383 groups. The suite runs green in 8s, opt-in via XSTS_ROOT.
Refs #129, #122.
An xsi:type naming an undeclared type is now a located error in both the
tree validator (validateChild) and the streaming driver, instead of a silent
fallback to the declared type. typeReference chains resolve through one
shared resolver (resolveReference, with a visited set), replacing the inline
recursion, the 64-hop cap, and the completions guard: a circular chain now
reports 'circular type reference' instead of silently truncating, and the
streaming shallow check no longer recurses without bound on a cyclic table
(a latent crash). Tests cover unknown xsi:type (tree + streaming agree),
the circular chain (tree + shallow), the resolver's three outcomes, and a
conformance-harness case pair.
Closes#110
Add Schema.Document.validate(streaming:) and ComplexValidator.validateShallow:
resolve each element's type from its parent's content model (and xsi:type /
typeReference) as it opens, then check its attributes and content-model
structure shallowly as it closes, against a shallow element synthesized from
the streamed child names. A child's deeper validity is checked when it closes,
so only the open-element stack is held. A differential test asserts streaming
agrees with the tree validator across valid and invalid documents (missing
attributes, bad simple values, missing/extra/misordered children). Wildcard
processContents and substitution-group resolution in the streaming form stay
on the tree validator. The streaming methods moved to ComplexValidatorStreaming
to keep ComplexValidator under the file/type-body caps.
Closes#107
A content-model violation is now placed at the offending child with a
recovery hint naming what was expected, instead of one opaque "content
does not match the content model" per element. The sequence/choice
automaton is walked child by child via its follow-set to flag the first
child the model rejects, or the incomplete content at the end; an xs:all
group recovers past each stray child and reports every missing required
member. Well-placed children are still validated for their own content,
so an editor sees every problem at once. Brings XSD to the same located,
recovering standard as the RELAX NG, DTD, and Schematron validators.
Make XSD validation namespace-aware and enforce the form defaults and
xs:all structural constraints that were previously ignored:
- targetNamespace is tracked: a schema's global elements are in its
target namespace, and an instance's root element must be in it.
- elementFormDefault / attributeFormDefault (and per-declaration form)
qualify local element and attribute declarations. The content-model
matcher already compares namespace URIs, so qualified declarations
now match only same-namespace instance nodes; attribute matching
became namespace-aware too.
- xs:all groups are checked at compile time: every member must be an
element occurring at most once, the group may occur at most once, and
nested model groups inside an all are rejected (a SchemaError).
When a schema has no target namespace the stamping is a no-op, so
unqualified schemas validate exactly as before.
Cross-namespace type references through xs:import resolve by local name
only; full multi-namespace type tables are not modeled.
Closes#78
Read and enforce the XSD 1.0 derivation controls that were previously
ignored:
- abstract elements may not appear in an instance directly, only through
a substitution member; an element of an abstract complex type must
supply an xsi:type naming a concrete derived type.
- block on a type forbids the disallowed xsi:type substitutions; block on
an element drops substitution-group members that derive by a blocked
method.
- final on a type rejects, at compile time, any type that derives from it
by a final method.
- xs:redefine requires the redefined type to derive from itself.
Track each named complex type's base and derivation method as the
backbone these checks walk. Also compose complexContent extension and
restriction content models, which were previously dropped: an extension
now inherits its base's attributes and content (base first, then the
extension, in sequence).
Complex-type restriction is not verified to be a structurally valid
subset of its base (Particle Valid (Restriction)); that limit is
documented at its sites.
Closes#76
Add xs:any and xs:anyAttribute wildcards with namespace and
processContents constraints (skip/lax/strict), and resolve the
list built-in datatypes (IDREFS, ENTITIES, NMTOKENS) to their
item types so each list item validates.
WildcardNamespace models ##any/##other/enumerated URI sets;
Wildcard.admits gates undeclared attributes and wildcard-matched
children. ContentNFA.follow exposes the follow set for completions.
Closes#77
Closes#75, on the validation framework. XSD instance-level features:
- nillable elements: an xsi:nil="true" element is accepted (its content model is
not checked) only when the element is declared nillable and carries no content;
xsi:nil on a non-nillable element, or a nilled element with content, is a
located error.
- fixed value constraints: a present fixed attribute must equal its fixed value,
and a fixed element's text must match.
- default/fixed are parsed onto AttributeUse (per use) and collected per element
name (nillable set + value constraints), threaded through XSDContext to the
ComplexValidator.
All findings are located ValidationErrors. Collection and helper methods move to
same-file extensions to stay within type-body limits.
Slice 4 of #92. The XSD complex-type and identity-constraint validators move
onto the validation framework, producing located results.
- ComplexValidator threads a coding path through its (inherently recursive)
descent, so every content-model, attribute, and simple-type violation is now
a ValidationError located by path ("... at path: order/qty"). Child steps
carry a sibling index only when a name repeats.
- IdentityValidator returns ValidationError instead of Issue.
- A new XSD rules enum wraps both as Validation<Node, XSDContext> values gated to
the document root, combined by Validator<XSDContext>. XSDContext carries the
type table, the identity constraints, and the root declaration.
- Schema.Document.validate builds the context and runs the validator, returning
[ValidationError].
XSD content-model validation is recursive by nature (a child's type depends on
its position), so the rules delegate to the recursion while contributing to one
error collection. Schematron is the last validator on Issue and migrates next.
Compose schemas across documents and resolve types at the instance:
- xs:include, xs:import, and xs:redefine pull a transitive closure of schema
documents through an injected loader (resolved by schemaLocation), base
schemas first so the including schema wins; a redefine's nested definitions
override the schema it includes.
- substitution groups: an xs:element ref to a head also admits every element
that substitutes for it, expanded at parse time into a choice over the head
and its transitive members.
- xsi:type overrides an element's declared type with the named type at the
instance; XML Schema instance attributes are exempt from attribute checks.
The schema parser splits across XSDContext (shared tree helpers, document
collection, parsing context), XSDSimpleParser (simple types and facets), and
XSDParser (elements, complex types, model groups) to keep each within bounds.
Closes#66
Add Schema.Document compiling an xs:schema into global element declarations
and a named-type table, and validate(_:) checking instance documents end to
end. The parser supports global and local element declarations, named and
inline simple and complex types, sequence/choice/all groups with occurrence,
attribute uses, simple content (restriction/extension), the full facet set,
and recursive types via named references resolved during validation. Extends
ComplexType with a typeReference case and ComplexValidator with a type table
so recursive schemas are representable.
Closes#10
Add ComplexValidator validating an element against a ComplexType: attribute
uses (required, typed, unknown-attribute rejection) and the content model
(empty, simpleContent, elementOnly, mixed). Particles compile to a Thompson
NFA over element names honoring minOccurs/maxOccurs with sequence and choice
compositors; all groups are validated order-independently by counting. Child
elements are validated recursively against their declared types.
Closes#9