Generic templates (Was: RE: [xsl] using call-template to call template from another xsl)

Subject: Generic templates (Was: RE: [xsl] using call-template to call template from another xsl)
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Fri, 4 May 2001 22:14:14 -0700 (PDT)
Michael Kay wrote:

> > i was using call-template as i need to pass a  parameter.How do i use
> > apply-templates  in this situation.Also i need to be able to
> > apply-template/call-template different templates based on the value of
> > $reqtype variable.
> 
> You can pass parameters with xsl:apply-templates just as easily as with
> xsl:call-template.
> 
> With xsl:apply-templates you can also use different modes, which might solve
> the problem of calling different templates based on the value of $reqtype.
> In both cases you'll need an xsl:choose.
> 
> Mike Kay
> Software AG

If a template must be instantiated (based on the value of $someVar) using
xsl:choose, this means that the names/modes of all such templates must be known in
advance.
Every time we add a new template to be called, the calling template's xsl:choose
must be updated to incorporate the new case.

Obviously this static invocation scheme is hardly convenient.

Actually there is a method for building generic templates so that they can cause
different templates to be istantiated dynamically and without using xsl:choose.

It is not necessary to know anything in advance about the templates to be
istantiated, and in fact they may be written long after the generic template.

The idea is to pass to the generic template a separate parameter, which uniquely and
***dynamically*** identifies the template that will be instantiated. This parameter
is of type node-set (single node), that has a unique namespace-uri.

The caller of the generic template may define many different "action templates" that
match only specific unique types of nodes. During the execution of a stylesheet a
generic template may be called many times with different "action template types" to
perform similar processing.

Below is an example of a generic template that finds and returns the "maximum" node
in a node-set. The meaning of "maximum" is determined completely by an external
template, which implements the relation "greater". A "template type" parameter is
passed to the generic template, so that it can dynamically invoke the necessary
"greater"-action template.

In this example there are two "greater"-action templates implemented -- one that
uses the traditional ">" relation for numbers, and another that compares their
reciprocal values.

There could be potentially unlimited number of different "greater"-action templates
-- e.g. comparing distance, space, perimeter,... etc.

They can all be passed to a single generic "maximum" template and it will always
find the correct maximum node.

The same method can be used for a generic sort template and in the implementation of
many other useful generic algorithms. 

This method does't have the problem of "the last overrides override all" (described
a few months ago by Jeni) when there are several templates overriding some templates
of an imported stylesheet.

Genericity is an extremely useful feature -- at present it is missing from both the
EXSLT site and the Standard XSLT Library site. 

In my opinion in order for a standard template library to be really useful, it must
contain ***generic*** templates. Thus it will have an unlimited number of
applicationsas opposed to a fixed, very small number of applications. 

In the near future I will provide some initial set of useful generic templates --
min, max, sort, partial sort, binary search,... etc.


Here's the example:

XML source document:
-------------------
<numbers>
 <num>1</num> 
 <num>3</num> 
 <num>2</num> 
 <num>9</num> 
 <num>4</num> 
 <num>6</num> 
 <num>5</num> 
 <num>7</num> 
 <num>8</num> 
</numbers>

Stylesheet:
----------
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:num="num"
xmlns:num2="num2"
>

  <num:node/>
  <num2:node/>

  <xsl:output omit-xml-declaration="yes" />
  
  <xsl:variable name="gtNum-node" select="document('')//num:*"/>
  <xsl:variable name="gtNum2-node" select="document('')//num2:*"/>

  <xsl:template match="/">
    <xsl:call-template name="get-max">
      <xsl:with-param name="greaterSelector" select="$gtNum-node" />
      <xsl:with-param name="nodes" select="/numbers/num" />
    </xsl:call-template>
    
    <xsl:text>&#xA;</xsl:text>

    <xsl:call-template name="get-max">
      <xsl:with-param name="greaterSelector" select="$gtNum2-node" />
      <xsl:with-param name="nodes" select="/numbers/num" />
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="get-max">
    <xsl:param name="greaterSelector" select="/*"/>
    <xsl:param name="nodes" />
    <xsl:choose>
      <xsl:when test="$nodes">
        <xsl:variable name="max-of-rest">
          <xsl:call-template name="get-max">
            <xsl:with-param name="greaterSelector" select="$greaterSelector" />
            <xsl:with-param name="nodes"
            select="$nodes[position()!=1]" />
          </xsl:call-template>
        </xsl:variable>

        <xsl:variable name="isGreater">
         <xsl:apply-templates select="$greaterSelector" >
           <xsl:with-param name="n1" select="$nodes[1]"/>
           <xsl:with-param name="n2" select="$max-of-rest"/>
         </xsl:apply-templates>
        </xsl:variable>
        
        <xsl:choose>
          <xsl:when test="$isGreater = 'true'">
            <xsl:value-of select="$nodes[1]" />
          </xsl:when>

          <xsl:otherwise>
            <xsl:value-of select="$max-of-rest" />
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>

      <xsl:otherwise>
        <xsl:value-of select="-999999999" />

<!-- minus infinity -->
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="isGreaterNum" match="node()[namespace-uri()='num']">
    <xsl:param name="n1"/>
    <xsl:param name="n2"/>

    <xsl:value-of select="$n1 > $n2"/>
  </xsl:template>

  <xsl:template name="isGreaterNum2" match="node()[namespace-uri()='num2']">
    <xsl:param name="n1"/>
    <xsl:param name="n2"/>

    <xsl:value-of select="1 div $n1 > 1 div $n2"/>
  </xsl:template>

</xsl:stylesheet>



The result produced is the two correct maximums:
-----------------------------------------------
9
1


Cheers,
Dimitre Novatchev.




__________________________________________________
Do You Yahoo!?
Yahoo! Auctions - buy the things you want at great prices
http://auctions.yahoo.com/

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread