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
82 lines
3.9 KiB
Swift
82 lines
3.9 KiB
Swift
public extension PureXML.Validation {
|
|
/// The compiled-schema context the XSD validations read: the named-type table
|
|
/// a content model resolves against, the identity constraints, and the root
|
|
/// element's declared type.
|
|
struct XSDContext {
|
|
public let types: [String: PureXML.Schema.ElementType]
|
|
public let constraints: [String: [PureXML.Schema.IdentityConstraint]]
|
|
public let rootDeclaration: PureXML.Schema.ElementType?
|
|
public let nillableElements: Set<String>
|
|
public let elementConstraints: [String: PureXML.Schema.ValueConstraint]
|
|
public let abstractTypes: Set<String>
|
|
public let typeBlock: [String: Set<PureXML.Schema.DerivationMethod>]
|
|
public let typeDerivation: [String: PureXML.Schema.TypeDerivation]
|
|
|
|
public init(
|
|
types: [String: PureXML.Schema.ElementType],
|
|
constraints: [String: [PureXML.Schema.IdentityConstraint]],
|
|
rootDeclaration: PureXML.Schema.ElementType?,
|
|
nillableElements: Set<String> = [],
|
|
elementConstraints: [String: PureXML.Schema.ValueConstraint] = [:],
|
|
abstractTypes: Set<String> = [],
|
|
typeBlock: [String: Set<PureXML.Schema.DerivationMethod>] = [:],
|
|
typeDerivation: [String: PureXML.Schema.TypeDerivation] = [:],
|
|
) {
|
|
self.types = types
|
|
self.constraints = constraints
|
|
self.rootDeclaration = rootDeclaration
|
|
self.nillableElements = nillableElements
|
|
self.elementConstraints = elementConstraints
|
|
self.abstractTypes = abstractTypes
|
|
self.typeBlock = typeBlock
|
|
self.typeDerivation = typeDerivation
|
|
}
|
|
}
|
|
|
|
/// XSD validation expressed as composable ``Validation`` values over an
|
|
/// ``XSDContext``. The content-model check is inherently recursive (a child's
|
|
/// type depends on its position), so each rule delegates to the recursive
|
|
/// validators while contributing located ``ValidationError`` results to one
|
|
/// collection.
|
|
enum XSD {
|
|
/// The document element is valid against its declared XSD type.
|
|
static var contentValidity: Validation<PureXML.Model.Node, XSDContext> {
|
|
.init(
|
|
description: "The document element is valid against its XSD type",
|
|
check: { context in
|
|
guard case let .element(root) = context.subject, let declaration = context.document.rootDeclaration else {
|
|
return []
|
|
}
|
|
return PureXML.Schema.ComplexValidator(
|
|
types: context.document.types,
|
|
nillableElements: context.document.nillableElements,
|
|
elementConstraints: context.document.elementConstraints,
|
|
abstractTypes: context.document.abstractTypes,
|
|
typeBlock: context.document.typeBlock,
|
|
typeDerivation: context.document.typeDerivation,
|
|
)
|
|
.validate(root, as: declaration, at: [.element(root.name.description)])
|
|
},
|
|
when: { $0.codingPath.isEmpty },
|
|
)
|
|
}
|
|
|
|
/// The document's XSD identity constraints (`unique`, `key`, `keyref`) hold.
|
|
static var identityConstraints: Validation<PureXML.Model.Node, XSDContext> {
|
|
.init(
|
|
description: "XSD identity constraints hold",
|
|
check: { context in
|
|
PureXML.Schema.IdentityValidator(constraints: context.document.constraints)
|
|
.validate(PureXML.Model.TreeNode(context.subject))
|
|
},
|
|
when: { $0.codingPath.isEmpty },
|
|
)
|
|
}
|
|
|
|
/// A validator combining the content-model and identity-constraint rules.
|
|
static func validator() -> Validator<XSDContext> {
|
|
Validator<XSDContext>.blank.validating(contentValidity, identityConstraints)
|
|
}
|
|
}
|
|
}
|