PureXML/Sources/Schema/SchemaDocumentHints.swift
Mihaela Mihaljevic ca56caf11c fix(schema): default/fixed value for identity-constraint fields; invalid-instances-accepted 50 to 45 (#147)
An identity-constraint (unique/key/keyref) field that selects an absent attribute
or an empty element whose declaration carries a default or fixed value now takes
that value as its identity-tuple component, so two members can collide on the
defaulted value (idG011/idG012 default, idF016/idF017 fixed) where the duplicate
was previously missed.

The default/fixed value constraints on identity-field targets are gathered at
compile time (identityFieldConstraints, mirroring identityFieldTypes) and threaded
through to the identity validator. The substitution is exact about absent versus
present-empty: an @attr field is defaulted only when the attribute is ABSENT (an
empty node-set), never when present with value ""; a "." element field only when
the selected element has no character content. Applying a default only adds an
identity value where the schema declares one, so it can create a genuine
duplicate-key detection but never reject a valid instance.

To keep within the file/type-size and one-type-per-file limits, FieldValue,
XPathQueryCache, and the schema-location hints were extracted to their own files
(behavior-preserving moves).

invalid-instances-accepted 50 to 45, no false positive (valid-instances-rejected
and valid-schemas-rejected held at 0), no other bucket moved. An adversarial
review probed present-empty attributes, element content winning over a default,
fixed values, keyref matching, multi-field keys, and merged-schema defaults, and
found no over-rejection.
2026-06-18 14:47:47 +02:00

31 lines
1.3 KiB
Swift

extension PureXML.Schema.Document {
/// The schema-document locations an instance points at through
/// `xsi:schemaLocation` (the second token of each namespace/location pair)
/// and `xsi:noNamespaceSchemaLocation` (a single location).
static func hintedSchemaLocations(in node: PureXML.Model.Node) -> [String] {
guard case let .document(children) = node, let root = children.compactMap(\.element).first else {
return []
}
var locations: [String] = []
for attribute in root.attributes {
let isInstance = attribute.name.namespaceURI == "http://www.w3.org/2001/XMLSchema-instance"
|| attribute.name.prefix == "xsi"
guard isInstance else { continue }
switch attribute.name.localName {
case "schemaLocation":
let tokens = attribute.value.split(whereSeparator: \.isWhitespace).map(String.init)
var index = 1
while index < tokens.count {
locations.append(tokens[index])
index += 2
}
case "noNamespaceSchemaLocation":
locations.append(attribute.value)
default:
break
}
}
return locations
}
}