Complete the element-vs-element type clause of Particle Valid (Restriction): the restricting element's type must be, or derive by restriction from, the base element's type. Previously only names were compared, so an unrelated, wider, or ur-type element type was accepted under restriction. Enrich the content-model particle: Term.element carries the element type's derivation identity (the resolved type name, anyType for an absent type, nil for an inline anonymous type). Record named simpleType restriction derivations in the derivation table alongside complex ones. The element-vs-element check walks the user restriction chain and then the built-in lattice to decide derivation by name; an ur-type base, a union base (membership is not a restriction chain), and an inline anonymous type are permitted, so no valid restriction is rejected. XSTS: invalid schemas accepted 616 to 594, invalid instances accepted 165 to 160 (more invalid schemas rejected up front), valid schemas rejected held at 72, valid instances rejected held at 214.
90 lines
4.6 KiB
Swift
90 lines
4.6 KiB
Swift
public extension PureXML.Schema {
|
|
/// One named complex type's compile-time derivation facts: the subject of
|
|
/// the schema-consistency rules.
|
|
struct SchemaTypeFact: PureXML.Validation.Validatable {
|
|
public let name: String
|
|
public let derivation: TypeDerivation?
|
|
|
|
public init(name: String, derivation: TypeDerivation?) {
|
|
self.name = name
|
|
self.derivation = derivation
|
|
}
|
|
}
|
|
|
|
/// The compiled context the schema-consistency rules read: the named-type
|
|
/// table and each type's `final` controls.
|
|
struct CompiledSchemaFacts {
|
|
public let types: [String: ElementType]
|
|
public let typeDerivation: [String: TypeDerivation]
|
|
public let typeFinal: [String: Set<DerivationMethod>]
|
|
|
|
public init(types: [String: ElementType], typeDerivation: [String: TypeDerivation], typeFinal: [String: Set<DerivationMethod>]) {
|
|
self.types = types
|
|
self.typeDerivation = typeDerivation
|
|
self.typeFinal = typeFinal
|
|
}
|
|
}
|
|
}
|
|
|
|
public extension PureXML.Validation {
|
|
/// Schema-consistency validation, expressed in the validation framework:
|
|
/// each named type is a ``PureXML/Schema/SchemaTypeFact`` subject checked by
|
|
/// composable rules, so a schema with several problems reports them all at
|
|
/// once instead of failing on the first.
|
|
enum XSDSchema {
|
|
/// A type derives from its base only by methods the base permits.
|
|
static var finalRespected: Validation<PureXML.Schema.SchemaTypeFact, PureXML.Schema.CompiledSchemaFacts> {
|
|
.init(description: "A type derives from its base only by methods the base permits") { context in
|
|
guard let derivation = context.subject.derivation,
|
|
context.document.typeFinal[derivation.base]?.contains(derivation.method) == true
|
|
else { return [] }
|
|
let method = methodName(derivation.method)
|
|
let reason = "type '\(context.subject.name)' derives from '\(derivation.base)' by \(method), which '\(derivation.base)' declares final"
|
|
return [ValidationError(reason: reason, at: context.codingPath)]
|
|
}
|
|
}
|
|
|
|
/// A complex type derived by restriction accepts a subset of its base
|
|
/// ("Particle Valid (Restriction)", XSD 1.0).
|
|
static var restrictionsAreSubsets: Validation<PureXML.Schema.SchemaTypeFact, PureXML.Schema.CompiledSchemaFacts> {
|
|
.init(description: "A restriction's content model accepts a subset of its base's") { context in
|
|
guard let derivation = context.subject.derivation, derivation.method == .restriction,
|
|
case let .complex(restricted)? = context.document.types[context.subject.name],
|
|
case let .complex(base)? = context.document.types[derivation.base],
|
|
let reason = PureXML.Schema.ParticleRestriction.violation(
|
|
restricted: restricted.content,
|
|
base: base.content,
|
|
types: context.document.types,
|
|
derivation: context.document.typeDerivation,
|
|
)
|
|
else { return [] }
|
|
let text = "type '\(context.subject.name)' is not a valid restriction of '\(derivation.base)': \(reason)"
|
|
return [ValidationError(reason: text, at: context.codingPath)]
|
|
}
|
|
}
|
|
|
|
/// Every consistency finding across the schema's named types, each
|
|
/// located at its type, in deterministic order.
|
|
static func consistencyErrors(
|
|
types: [String: PureXML.Schema.ElementType],
|
|
typeDerivation: [String: PureXML.Schema.TypeDerivation],
|
|
typeFinal: [String: Set<PureXML.Schema.DerivationMethod>],
|
|
) -> [ValidationError] {
|
|
let facts = PureXML.Schema.CompiledSchemaFacts(types: types, typeDerivation: typeDerivation, typeFinal: typeFinal)
|
|
let rules = [finalRespected, restrictionsAreSubsets]
|
|
return typeDerivation.keys.sorted().flatMap { name -> [ValidationError] in
|
|
let fact = PureXML.Schema.SchemaTypeFact(name: name, derivation: typeDerivation[name])
|
|
return rules.flatMap { $0.apply(to: fact, at: [.element(name)], in: facts) }
|
|
}
|
|
}
|
|
|
|
private static func methodName(_ method: PureXML.Schema.DerivationMethod) -> String {
|
|
switch method {
|
|
case .extension: "extension"
|
|
case .restriction: "restriction"
|
|
case .substitution: "substitution"
|
|
}
|
|
}
|
|
}
|
|
}
|