The first generic function in this category generates a node set by executing a function over successive values, as defined by an incrementing function, until an upper bound is reached:
<xsl:template name="generic:gen-set"> <xsl:param name="x" select="1"/> <xsl:param name="func" select=" 'identity' "/> <xsl:param name="func-param1" select="$generic:generics[self::generic:func and @name = $func]/@param1"/> <xsl:param name="test-func" select=" 'less-than' "/> <xsl:param name="test-param1" select="$x + 1"/> <xsl:param name="incr-func" select=" 'incr' "/> <xsl:param name="incr-param1" select="1"/> <xsl:param name="i" select="1"/> <xsl:param name="result" select="/.."/> <!-- Check if aggregation should continue --> <xsl:variable name="continue"> <xsl:apply-templates select="$generic:generics[self::generic:func and @name = $test-func]"> <xsl:with-param name="x" select="$x"/> <xsl:with-param name="param1" select="$test-param1"/> </xsl:apply-templates> </xsl:variable> <xsl:choose> <xsl:when test="string($continue)"> <!--Compute func($x) --> <xsl:variable name="f-of-x"> <xsl:apply-templates select="$generic:generics[self::generic:func and @name = $func]"> <xsl:with-param name="x" select="$x"/> <xsl:with-param name="i" select="$i"/> <xsl:with-param name="param1" select="$func-param1"/> </xsl:apply-templates> </xsl:variable> <!-- Compute the next value of $x--> <xsl:variable name="next-x"> <xsl:apply-templates select="$generic:generics[self::generic:func and @name = $incr-func]"> <xsl:with-param name="x" select="$x"/> <xsl:with-param name="param1" select="$incr-param1"/> </xsl:apply-templates> </xsl:variable> <xsl:call-template name="generic:gen-set"> <xsl:with-param name="x" select="$next-x"/> <xsl:with-param name="func" select="$func"/> <xsl:with-param name="func-param1" select="$func-param1"/> <xsl:with-param name="test-func" select="$test-func"/> <xsl:with-param name="test-param1" select="$test-param1"/> <xsl:with-param name="incr-func" select="$incr-func"/> <xsl:with-param name="incr-param1" select="$incr-param1"/> <xsl:with-param name="i" select="$i + 1"/> <xsl:with-param name="result" select="$result | exslt:node-set($f-of-x)"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="$result" mode="generic:gen-set"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="node( )" mode="generic:gen-set"> <gen-set> <xsl:copy-of select="."/> </gen-set> </xsl:template>
Here you use this template to generate a list of squares of the first ten integers:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:generic="http://www.ora.com/XSLTCookbook/namespaces/generic"> <xsl:import href="aggregation.xslt"/> <xsl:output method="text" /> <xsl:template match="/"> <xsl:call-template name="generic:gen-set"> <xsl:with-param name="x" select="1"/> <xsl:with-param name="func" select=" 'square' "/> <xsl:with-param name="incr-param1" select="1"/> <xsl:with-param name="test-func" select=" 'less-than-eq' "/> <xsl:with-param name="test-param1" select="10"/> </xsl:call-template> </xsl:template> <xsl:template match="node( )" mode="generic:gen-set"> <xsl:value-of select="."/> <xsl:text> </xsl:text> </xsl:template> 1 4 9 16 25 36 49 64 81 100
The second generic function in this category generates a node set by
n
successive applications of a function starting
with an initial seed value:
<xsl:template name="generic:gen-nested"> <xsl:param name="x" select="1"/> <xsl:param name="func" select=" 'identity' "/> <xsl:param name="func-param1" select="$generic:generics[self::generic:func and @name = $func]/@param1"/> <xsl:param name="i" select="1"/> <xsl:param name="n" select="2"/> <xsl:param name="result"> <xsl:value-of select="$x"/> </xsl:param> <xsl:choose> <xsl:when test="$i <= $n"> <!--Compute func($x) --> <xsl:variable name="f-of-x"> <xsl:apply-templates select="$generic:generics[self::generic:func and @name = $func]"> <xsl:with-param name="x" select="$x"/> <xsl:with-param name="i" select="$i"/> <xsl:with-param name="param1" select="$func-param1"/> </xsl:apply-templates> </xsl:variable> <xsl:call-template name="generic:gen-nested"> <xsl:with-param name="x" select="$f-of-x"/> <xsl:with-param name="func" select="$func"/> <xsl:with-param name="func-param1" select="$func-param1"/> <xsl:with-param name="i" select="$i + 1"/> <xsl:with-param name="n" select="$n"/> <xsl:with-param name="result" select="exslt:node-set($result) | exslt:node-set($f-of-x)"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="$result" mode="generic:gen-nested"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="node( )" mode="generic:gen-nested"> <gen-nested> <xsl:copy-of select="."/> </gen-nested> </xsl:template>
Here you use this template to build the series 2, 2 ** 2, (2 ** 2) **
2, ((2 ** 2) ** 2) ** 2, (((2 ** 2) ** 2) ** 2) ** 2, where
**
means to the power of
:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:generic="http://www.ora.com/XSLTCookbook/namespaces/generic"> <xsl:import href="aggregation.xslt"/> <xsl:output method="text" /> <xsl:template match="/"> <xsl:call-template name="generic:gen-nested"> <xsl:with-param name="x" select="2"/> <xsl:with-param name="func" select=" 'square' "/> <xsl:with-param name="n" select="4"/> </xsl:call-template> </xsl:template> <xsl:template match="node( )" mode="generic:gen-nested"> <xsl:value-of select="."/> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> 2 4 16 256 65536
Recipe 14.2 and Recipe 14.3 were many-to-one generic transformations, and Recipe 14.5 explained many-to-many transformations. Naturally, this chapter would not be complete without a one-to-many generic transform.
With a generator, you can create random numbers that can select
random nodes from an XML document. This chapter uses a simple
linear congruential
generator (see, for example,
http://www.taygeta.com/rwalks/node1.html). Here is a stylesheet that displays a random
selection of names from an input document:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:generic="http://www.ora.com/XSLTCookbook/namespaces/generic"> <xsl:import href="aggregation.xslt"/> <xsl:output method="xml" indent="yes"/> <!-- Extend the available generic functions --> <xsl:variable name="generic:generics" select="$generic:public-generics | document('')/*/generic:*"/> <!-- These values give good random results but you can tweak --> <xsl:variable name="a" select="16807"/> <xsl:variable name="c" select="0"/> <xsl:variable name="m" select="2147483647"/> <!-- Store the root for later use --> <xsl:variable name="doc" select="/"/> <!-- The random generator --> <generic:func name="linear-congruential"/> <xsl:template match="generic:func[@name='linear-congruential']"> <xsl:param name="x"/> <xsl:value-of select="($a * $x + $c) mod $m"/> </xsl:template> <xsl:template match="/"> <names> <xsl:call-template name="generic:gen-nested"> <xsl:with-param name="x" select="1"/> <xsl:with-param name="func" select=" 'linear-congruential' "/> <xsl:with-param name="n" select="100"/> <!-- Don't include initial seed --> <xsl:with-param name="result" select="/.."/> </xsl:call-template> </names> </xsl:template> <xsl:template match="node( )" mode="generic:gen-nested"> <!-- Restrict the range to 1 through 100 --> <xsl:variable name="random" select=". mod 99 + 1"/> <name> <xsl:value-of select="$doc/names/name[$random]"/> </name> </xsl:template> </xsl:stylesheet>