XSD 1.0 3.3.2: an element with neither a `type` attribute nor an inline type, but
a `substitutionGroup`, takes the {type definition} of its head. PureXML defaulted
such an element to `anyType`, so an instance of it accepted any content.
`XSDParser.elementType` now resolves the head declaration through
`context.globalElements` (transitively, with a cycle guard) and uses its type.
XSTS invalid-instances-accepted 29 -> 28 (typeDef00204m: <root>Yes</root> is no
longer accepted where root substitutes a boolean head), no other bucket moved
(valid-schemas-rejected and valid-instances-rejected held at 0 across all 14383
groups). 1451 unit tests pass.
74 lines
3.7 KiB
Swift
74 lines
3.7 KiB
Swift
private typealias XSDNode = PureXML.Schema.XSDNode
|
|
private typealias XSDContext = PureXML.Schema.XSDContext
|
|
private typealias Particle = PureXML.Schema.Particle
|
|
private typealias ComplexType = PureXML.Schema.ComplexType
|
|
private typealias ElementType = PureXML.Schema.ElementType
|
|
private typealias XSDSimpleParser = PureXML.Schema.XSDSimpleParser
|
|
private typealias BuiltinType = PureXML.Schema.BuiltinType
|
|
private typealias Wildcard = PureXML.Schema.Wildcard
|
|
private typealias SimpleType = PureXML.Schema.SimpleType
|
|
|
|
extension PureXML.Schema.XSDParser {
|
|
// MARK: Element and type references
|
|
|
|
static func elementType(
|
|
_ node: PureXML.Model.TreeNode,
|
|
_ context: PureXML.Schema.XSDContext,
|
|
resolvingHeads: Set<String> = [],
|
|
) -> PureXML.Schema.ElementType {
|
|
if let typeName = XSDNode.attribute(node, "type") {
|
|
return typeReference(typeName, context, at: node)
|
|
}
|
|
if let inline = XSDNode.firstChild(node, named: "simpleType") {
|
|
return .simple(XSDSimpleParser.simpleType(inline, context))
|
|
}
|
|
if let inline = XSDNode.firstChild(node, named: "complexType") {
|
|
return .complex(complexType(inline, context))
|
|
}
|
|
// XSD 1.0 3.3.2: an element with neither `type` nor an inline type, but a
|
|
// `substitutionGroup`, takes the {type definition} of its head. Resolve the
|
|
// head declaration and use its type, guarding against a substitutionGroup cycle.
|
|
if let headReference = XSDNode.attribute(node, "substitutionGroup") {
|
|
let bindings = PureXML.Schema.XSDParser.namespaceBindingsInScope(of: node, defaultBindings: context.namespaceBindings)
|
|
let headKey = derivationKey(XSDNode.stripPrefix(headReference), in: XSDNode.referenceNamespace(headReference, bindings))
|
|
if !resolvingHeads.contains(headKey), let headNode = context.globalElements[headKey] {
|
|
return elementType(headNode, context, resolvingHeads: resolvingHeads.union([headKey]))
|
|
}
|
|
}
|
|
return .complex(anyType)
|
|
}
|
|
|
|
private static func typeReference(_ typeName: String, _ context: XSDContext, at node: XSDTree? = nil) -> ElementType {
|
|
let bindings = node.map {
|
|
PureXML.Schema.XSDParser.namespaceBindingsInScope(of: $0, defaultBindings: context.namespaceBindings)
|
|
} ?? context.namespaceBindings
|
|
let local = XSDNode.stripPrefix(typeName)
|
|
let uri = XSDNode.schemaComponentNamespace(typeName, bindings, targetNamespace: context.targetNamespace)
|
|
if uri == PureXML.Schema.XSDParser.xsdNamespace {
|
|
if let builtin = BuiltinType(rawValue: local) {
|
|
return .simple(SimpleType(base: builtin))
|
|
}
|
|
if local == "anySimpleType" {
|
|
return .simple(SimpleType(base: .string, isAnySimpleType: true))
|
|
}
|
|
if local == "anyType" {
|
|
return .complex(anyType)
|
|
}
|
|
if let item = XSDSimpleParser.listBuiltinItem(local) {
|
|
return .simple(.list(item: SimpleType(base: item), isBuiltinList: true))
|
|
}
|
|
}
|
|
return .typeReference(typeDeclarationKey(local, namespaceURI: uri))
|
|
}
|
|
|
|
/// The ur-type `xsd:anyType`. Per XSD 1.0 §3.4.7 its element and attribute
|
|
/// wildcards are `lax`, not skip: a child or attribute that has a global
|
|
/// declaration is validated against it, while undeclared content is admitted.
|
|
private static var anyType: ComplexType {
|
|
ComplexType(
|
|
attributeWildcard: Wildcard(processContents: .lax),
|
|
content: .mixed(Particle(minOccurs: 0, maxOccurs: nil, term: .wildcard(Wildcard(processContents: .lax)))),
|
|
)
|
|
}
|
|
}
|