PureXML/Sources/XSLT/XSLTElements.swift
Mihaela Mihaljevic e30672d72b feat(xslt): implement xsl:sort case-order
case-order=upper-first/lower-first makes the string comparison
case-insensitive, with case used only to break ties among otherwise-equal
strings; without it the default codepoint order is unchanged. Part of #82.
2026-06-09 02:37:43 +02:00

38 lines
1.9 KiB
Swift

extension PureXML.XSLT.Transformer {
/// Compares strings case-insensitively, with `caseOrder` breaking ties among
/// strings that differ only in case (the XSLT `case-order` semantics).
static func caseInsensitiveCompare(_ left: String, _ right: String, _ caseOrder: PureXML.XSLT.CaseOrder) -> Int {
let leftLower = left.lowercased()
let rightLower = right.lowercased()
if leftLower != rightLower { return leftLower < rightLower ? -1 : 1 }
if left == right { return 0 }
// Equal apart from case: codepoint order puts uppercase first.
let upperFirst = left < right
let leftFirst = caseOrder == .upperFirst ? upperFirst : !upperFirst
return leftFirst ? -1 : 1
}
/// Rewrites a literal name in an `xsl:namespace-alias`ed stylesheet namespace
/// to its result namespace and prefix; other names pass through unchanged.
func aliased(_ name: PureXML.Model.QualifiedName) -> PureXML.Model.QualifiedName {
guard let alias = stylesheet.namespaceAliases[name.namespaceURI ?? ""] else { return name }
return PureXML.Model.QualifiedName(prefix: alias.prefix, localName: name.localName, namespaceURI: alias.uri)
}
/// The attributes with duplicates by name removed, keeping the last (so a
/// later attribute overrides one from an attribute set or an earlier source).
static func deduplicated(_ attributes: [PureXML.Model.Attribute]) -> [PureXML.Model.Attribute] {
var indexByName: [String: Int] = [:]
var result: [PureXML.Model.Attribute] = []
for attribute in attributes {
if let index = indexByName[attribute.name.description] {
result[index] = attribute
} else {
indexByName[attribute.name.description] = result.count
result.append(attribute)
}
}
return result
}
}