extension PureXML.Schema.ComplexValidator { /// The errors from an `xsi:nil="true"` element: rejecting it when the /// element is not nillable, or when it carries content. Returns nil when /// the element is not nilled. func nilErrors(_ element: PureXML.Model.Element, fixedValue: String? = nil, at path: XSDPath) -> [XSDFinding]? { let name = element.name.localName // cvc-elt.3.1: a non-nillable element must carry no `xsi:nil` attribute at // all, whatever its value (so `xsi:nil="false"` is equally forbidden, not // only `xsi:nil="true"`). if Self.nilAttributeValue(element) != nil, !nillableElements.contains(name) { return [XSDFinding(.nilAttributeRequiresNillable, reason: "element '\(name)' is not nillable but has xsi:nil", at: path)] } guard Self.isNil(element) else { return nil } if Self.hasContent(element) { return [XSDFinding(.nilElementMustBeEmpty, reason: "nil element '\(name)' must be empty", at: path)] } // cvc-elt.3.2.2: a nilled element may not have a fixed {value constraint}. // The matched particle's constraint is authoritative; the by-local-name map // is a fallback for paths without a particle (e.g. the resolved-type entry). if (fixedValue ?? elementConstraints[name]?.fixedValue) != nil { return [XSDFinding(.nilElementNoFixedConstraint, reason: "nil element '\(name)' must not have a fixed value constraint", at: path)] } return [] } /// The text an element's simple content is validated against: its character /// content, or, when the element is empty, its `default`/`fixed` value (an /// empty element takes that value, and the value, not the empty string, must /// satisfy the type). The matched particle's constraint takes precedence over /// the by-local-name `elementConstraints` map, exactly as the tree path's /// `validateChild` resolves it. func effectiveSimpleText(for element: PureXML.Model.Element, particleConstraint: PureXML.Schema.ValueConstraint? = nil) -> String { let text = Self.rawTextContent(element) let constraint = particleConstraint ?? elementConstraints[element.name.localName] return text.isEmpty ? (constraint?.value ?? text) : text } /// The error from a `fixed` element value constraint: the element's text /// must equal the fixed value in the element type's value space when known. func elementFixedErrors( _ element: PureXML.Model.Element, valueType: PureXML.Schema.SimpleType? = nil, particleFixed: String? = nil, at path: XSDPath, ) -> [XSDFinding] { guard let fixed = particleFixed ?? elementConstraints[element.name.localName]?.fixedValue else { return [] } // A fixed element's content is its fixed character value: it may not carry // element children, even under a mixed content type (cvc-elt.5.2.2.2.1). if !element.children.compactMap(\.element).isEmpty { return [XSDFinding(.fixedElementNoChildren, reason: "fixed element '\(element.name.localName)' must not have element children", at: path)] } let text = Self.rawTextContent(element) // An empty element takes the fixed value; only present content must equal it. if text.isEmpty { return [] } if let valueType, valueType.valueMatches(text, literal: fixed) { return [] } return text == fixed ? [] : [XSDFinding(.fixedElementValueMatches, reason: "element '\(element.name.localName)' is fixed and must be '\(fixed)'", at: path)] } }