extension PureXML.Schema.XSDParser { /// Keys of identity-constraint fields whose target element is, by declaration, /// not of simple type: the ur-type `anyType` from an absent type, or an inline /// complex type without simple content. XSD 1.0 ยง3.11.4 (cvc-identity-constraint.3) /// requires a field to identify a node of simple type, so such a field makes the /// instance invalid (XSTS idZ010). Resolved conservatively: a key is recorded only /// when the selector and field paths resolve unambiguously, by element-name steps, /// to a declared element that is provably non-simple. Any wildcard, descendant /// axis, predicate, named (unresolved) type, or `ref` leaves the key out, so a /// valid field, which always names a simple-typed element or attribute, is never /// flagged. static func identityNonSimpleFieldKeys(_ containers: [XSDTree], _ context: PureXML.Schema.XSDContext) -> Set { var keys: Set = [] for container in containers { for element in descendants(container, named: "element") { let constraints = PureXML.Schema.XSDNode.elementChildren(element).compactMap(constraint) for constraint in constraints { guard let selectorTarget = pathTargetElement(from: element, path: constraint.selector) else { continue } for field in constraint.fields where !field.hasPrefix("@") { guard let fieldTarget = pathTargetElement(from: selectorTarget, path: field), elementIsDeclaredNonSimple(fieldTarget, context) else { continue } keys.insert(identityFieldKey(constraint: constraint, field: field)) } } } } return keys } /// The declared element node a selector or field XPath reaches from `start`, /// following only the restricted-XPath child-name steps (a leading or interior /// `.` is self). Returns nil for any step that is not a plain name test (a /// wildcard `*`, a `//` descendant step, an axis `::`, a predicate `[`, an /// attribute `@`), or that names no direct child element, so the caller flags /// only paths it has resolved with certainty. private static func pathTargetElement(from start: XSDTree, path: String) -> XSDTree? { var current: XSDTree? = start for rawStep in path.split(separator: "/", omittingEmptySubsequences: false) { let step = String(rawStep).trimmingXMLWhitespace() if step == "." { continue } guard isSimpleNameTest(step), let node = current, let child = directChildElement(named: PureXML.Schema.XSDNode.stripPrefix(step), of: node) else { return nil } current = child } return current } /// Whether `step` is a plain element name test (`NCName` or `prefix:NCName`), /// not a wildcard, axis, predicate, or `//` (an empty step from a split). private static func isSimpleNameTest(_ step: String) -> Bool { guard !step.isEmpty, !step.contains(where: { "*[]()|@ \t".contains($0) }) else { return false } return step.count(where: { $0 == ":" }) <= 1 } /// The direct child element declaration of `element`'s `complexType` model with /// the given local name, descending through model groups but not into another /// element's own type, and never matching a `ref` (which has no `name`). private static func directChildElement(named local: String, of element: XSDTree) -> XSDTree? { guard let complexType = PureXML.Schema.XSDNode.firstChild(element, named: "complexType") else { return nil } return modelChildElements(of: complexType).first { PureXML.Schema.XSDNode.attribute($0, "name") == local } } /// Every direct element declaration in a complex type's content model, reached /// through the model-group and derivation wrappers but stopping at each /// `element` (so a nested element's own children are not collected). private static func modelChildElements(of node: XSDTree) -> [XSDTree] { var result: [XSDTree] = [] for child in PureXML.Schema.XSDNode.elementChildren(node) { switch PureXML.Schema.XSDNode.localName(child) { case "element": result.append(child) case "sequence", "choice", "all", "group", "complexContent", "restriction", "extension": result += modelChildElements(of: child) default: break } } return result } /// Whether `element`'s declared type is provably not a simple type: an explicit /// `xsd:anyType`, an inline `complexType` without `simpleContent`, or no type at /// all (the ur-type `anyType`). A builtin, `anySimpleType`, an inline /// `simpleType`, `simpleContent`, or any named non-builtin type is treated as /// possibly-simple and not flagged, so the check never rejects a valid field. private static func elementIsDeclaredNonSimple(_ element: XSDTree, _ context: PureXML.Schema.XSDContext) -> Bool { if let typeName = PureXML.Schema.XSDNode.attribute(element, "type") { let bindings = PureXML.Schema.XSDParser.namespaceBindingsInScope(of: element, defaultBindings: context.namespaceBindings) let local = PureXML.Schema.XSDNode.stripPrefix(typeName) guard PureXML.Schema.XSDNode.referenceNamespace(typeName, bindings) == PureXML.Schema.XSDParser.xsdNamespace else { return false } return local == "anyType" } if PureXML.Schema.XSDNode.firstChild(element, named: "simpleType") != nil { return false } if let complex = PureXML.Schema.XSDNode.firstChild(element, named: "complexType") { return PureXML.Schema.XSDNode.firstChild(complex, named: "simpleContent") == nil } return true } }