Reputation: 412
I would like to create an array of variables with dynamic names and mode names. Also, these variables are a chain which processes the document-node() stepwise. So instead of writing like this:
<xsl:variable name="A">
<xsl:apply-templates mode="A" select="."/>
</xsl:variable>
<xsl:variable name="B">
<xsl:apply-templates mode="B" select="$A"/>
</xsl:variable>
<xsl:variable name="C">
<xsl:apply-templates mode="C" select="$B"/>
</xsl:variable>
I would like to do something like this:
<xsl:variable name="mode-names" select="'A', 'B', 'C'"/>
<xsl:variable name="vars">
<xsl:for-each select="$mode-names">
<xsl:variable name="pos" select="position()" />
<xsl:variable name="{.}">
<xsl:apply-templates mode="{.}" select="if ($pos=1) then . else $modi[$pos -1]"/>
</xsl:variable>
</xsl:for-each>
</xsl:variable>
Error message: Invalid variable name: Invalid QName {{.}}
xslt 3.0
Upvotes: 0
Views: 331
Reputation: 167716
It is not clear how you would want to use the xsl:variable name="{.}"
later anyway, if you want to store more than one item in a variable you can of course use a sequence of items, so for your case of creating various document nodes you could use a variable of type document-node()*
which denotes a sequence of documents/document nodes.
So an example using three (static) modes to be applied and have each result stored as an item in that variable would be
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output indent="yes"/>
<xsl:mode name="A" on-no-match="shallow-copy"/>
<xsl:template match="foo" mode="A">
<bar>
<xsl:apply-templates mode="#current"/>
</bar>
</xsl:template>
<xsl:mode name="B" on-no-match="shallow-copy"/>
<xsl:template match="bar" mode="B">
<whatever>
<xsl:apply-templates mode="#current"/>
</whatever>
</xsl:template>
<xsl:mode name="C" on-no-match="shallow-copy"/>
<xsl:template match="text()" mode="C">
<xsl:value-of select="upper-case(.)"/>
</xsl:template>
<xsl:variable name="results" as="document-node()*">
<xsl:variable name="r1">
<xsl:apply-templates mode="A"/>
</xsl:variable>
<xsl:sequence select="$r1"/>
<xsl:variable name="r2">
<xsl:apply-templates select="$r1" mode="B"/>
</xsl:variable>
<xsl:sequence select="$r2"/>
<xsl:apply-templates select="$r2" mode="C"/>
</xsl:variable>
<xsl:template match="/">
<results>
<xsl:for-each select="$results">
<result step="{position()}">
<xsl:copy-of select="."/>
</result>
</xsl:for-each>
</results>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/3NJ38YX/1
As I said already in a comment, there is no way to construct a mode name at run-time, unless you construct the whole stylesheet on the fly and run it then with the transform
function:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:axsl="http://www.w3.org/1999/XSL/Transform-alias"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="mode-names" as="xs:string*" select="'A', 'B', 'C'"/>
<xsl:param name="stylesheet-template-string" as="xs:string"><![CDATA[
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0" exclude-result-prefixes="#all">
<xsl:mode name="A" on-no-match="shallow-copy"/>
<xsl:template match="foo" mode="A">
<bar>
<xsl:apply-templates mode="#current"/>
</bar>
</xsl:template>
<xsl:mode name="B" on-no-match="shallow-copy"/>
<xsl:template match="bar" mode="B">
<whatever>
<xsl:apply-templates mode="#current"/>
</whatever>
</xsl:template>
<xsl:mode name="C" on-no-match="shallow-copy"/>
<xsl:template match="text()" mode="C">
<xsl:value-of select="upper-case(.)"/>
</xsl:template>
</xsl:stylesheet>
]]></xsl:param>
<xsl:param name="stylesheet-template" select="parse-xml($stylesheet-template-string)"/>
<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
<xsl:mode name="construct-stylesheet" on-no-match="shallow-copy"/>
<xsl:variable name="stylesheet">
<xsl:apply-templates select="$stylesheet-template" mode="construct-stylesheet"/>
</xsl:variable>
<xsl:template match="xsl:stylesheet | xsl:transform" mode="construct-stylesheet">
<xsl:copy>
<xsl:apply-templates select="@* , node()" mode="#current"/>
<axsl:variable name="results" as="document-node()*">
<xsl:iterate select="$mode-names">
<axsl:variable name="result-{.}">
<axsl:apply-templates select="{if (position() eq 1) then '.' else '$result-' || subsequence($mode-names, position() - 1, 1)}" mode="{.}"/>
</axsl:variable>
<axsl:sequence select="$result-{.}"/>
</xsl:iterate>
</axsl:variable>
<axsl:template match="/">
<results>
<axsl:for-each select="$results">
<result step="{{position()}}">
<axsl:copy-of select="."/>
</result>
</axsl:for-each>
</results>
</axsl:template>
</xsl:copy>
</xsl:template>
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:sequence
select="transform(map {
'source-node' : .,
'stylesheet-node' : $stylesheet
})?output"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/3NJ38YX/2 for one order of modes and https://xsltfiddle.liberty-development.net/3NJ38YX/3 for a different order.
The example has one indirection to be self-contained, the stylesheet to be used is passed in as a string parameter but would or could of course be passed in as a document node or parsed from a file with the doc
function in the same way.
Upvotes: 1