XSLT 1.0 names every QName-keyed declaration by expanded name, so two prefixes
bound to the same namespace name the same thing. mode and variable names were
already resolved this way; named templates, xsl:key, xsl:attribute-set, and
xsl:decimal-format names were still keyed and compared as raw prefix strings.
A declaration name now resolves to {uri}local at parse time, and the references
resolve their prefix against the in-scope namespaces the same way: call-template
and use-attribute-sets at parse time, and the key() name argument and
format-number's format name at evaluation (a shared Library.expandedName helper
covers the two runtime sites). The parse-time expansion reuses a factored
expandedQName helper shared with mode and variable.
Closes Apache Xalan conformance cases namedtemplate16, attribset48, idkey53.
Full suite 1632 tests green in debug and release; the Xalan suite passes.
59 lines
2.3 KiB
Swift
59 lines
2.3 KiB
Swift
import Testing
|
|
@testable import PureXML
|
|
|
|
/// XSLT 1.0 names every QName-keyed declaration by expanded name, so two
|
|
/// prefixes bound to the same namespace name the same thing. Declared with one
|
|
/// prefix and referenced with another (both bound to `urn:q`), each must still
|
|
/// resolve (Apache Xalan namedtemplate16, attribset48, idkey53).
|
|
@Suite("XSLT expanded-name QName declarations")
|
|
struct XSLTExpandedQNameTests {
|
|
private let namespaces = "xmlns:a=\"urn:q\" xmlns:b=\"urn:q\""
|
|
private let xsl = "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\""
|
|
|
|
private func run(_ body: String, _ extra: String = "") throws -> String {
|
|
try PureXML.XSLT.transform(
|
|
stylesheet: """
|
|
<xsl:stylesheet version="1.0" \(xsl) \(namespaces) exclude-result-prefixes="a b">
|
|
<xsl:output omit-xml-declaration="yes"/>
|
|
\(extra)
|
|
<xsl:template match="/"><out>\(body)</out></xsl:template>
|
|
</xsl:stylesheet>
|
|
""",
|
|
source: "<doc><item k=\"K\">V</item></doc>",
|
|
)
|
|
}
|
|
|
|
@Test("call-template matches a template named with a different same-namespace prefix")
|
|
func test_namedTemplate() throws {
|
|
let out = try run("<xsl:call-template name=\"a:t\"/>", "<xsl:template name=\"b:t\">T</xsl:template>")
|
|
#expect(out == "<out>T</out>")
|
|
}
|
|
|
|
@Test("use-attribute-sets matches an attribute-set named with a different prefix")
|
|
func test_attributeSet() throws {
|
|
let out = try run(
|
|
"<e xsl:use-attribute-sets=\"a:s\"/>",
|
|
"<xsl:attribute-set name=\"b:s\"><xsl:attribute name=\"x\">1</xsl:attribute></xsl:attribute-set>",
|
|
)
|
|
#expect(out == "<out><e x=\"1\"/></out>")
|
|
}
|
|
|
|
@Test("key() matches an xsl:key named with a different prefix")
|
|
func test_key() throws {
|
|
let out = try run(
|
|
"<xsl:value-of select=\"key('a:k', 'K')\"/>",
|
|
"<xsl:key name=\"b:k\" match=\"item\" use=\"@k\"/>",
|
|
)
|
|
#expect(out == "<out>V</out>")
|
|
}
|
|
|
|
@Test("format-number matches an xsl:decimal-format named with a different prefix")
|
|
func test_decimalFormat() throws {
|
|
let out = try run(
|
|
"<xsl:value-of select=\"format-number(5, '0!', 'a:f')\"/>",
|
|
"<xsl:decimal-format name=\"b:f\" digit=\"!\"/>",
|
|
)
|
|
#expect(out == "<out>5</out>")
|
|
}
|
|
}
|