|
|
Table of Contents |
|
Recipe 16.5. Creating Generic Node-Set GeneratorsProblemYou want to create reusable templates for generating a node set computationally. SolutionThe 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 100The 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 65536DiscussionRecipe 16.2 and Recipe 16.3 were many-to-one generic transformations, and Recipe 16.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> |
|
|
Table of Contents |
|