Recipe16.5.Creating Generic Node-Set Generators


Recipe 16.5. Creating Generic Node-Set Generators

Problem

You want to create reusable templates for generating a node set computationally.

Solution

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 &lt;= $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

Discussion

Recipe 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>




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net