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 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>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.67.251