Re: [xsl] XSL nodes evaluation on the fly

Subject: Re: [xsl] XSL nodes evaluation on the fly
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Mon, 25 Feb 2002 16:35:47 +0000
Hi Maros,

> Is XSL nodes evaluation on the fly possible? I would like to create
> set of "standard" templates which have only different body
> (something like FUNCALL in LISP or function template in C++).

You can't do it in the way that you've tried, but you can do it using
Dimitre Novatchev's 'generic templates' technique. You can find out
more about the technique in general in Dimitre's article on functional
programming in XSLT at:

  http://www.topxml.com/xsl/articles/fp

Dimitre has implemented a large number of generic templates using this
technique, so you'd be well advised to check those out before you
start creating your own:

  http://www.topxml.com/downloads/default.asp?id=v20021156554
  
To illustrate the approach with your example, you could create a set
of templates that contain the XSLT code that you want to act as the
functions. For example:

<xsl:template name="createRow">
  <xsl:param name="i" />
  <TR>
    <TD height="$i"/>
  </TR>
</xsl:template>

As well as naming these templates, you should make these templates
match themselves, as follows:

<xsl:template name="createRow"
              match="xsl:template[@name = 'createRow']">
  <xsl:param name="i" />
  <TR>
    <TD height="{$i}"/>
  </TR>
</xsl:template>

Then, in your generic for.loop template, rather than the $body
parameter holding the code itself, you can make it hold a reference
node that identifies the template that you want to use (in this case,
the xsl:template element for the relevant template). The code for the
for.loop template should apply templates to this reference node,
passing any required parameters using xsl:with-param, as follows:

<xsl:template name="for.loop">
  <xsl:param name="i"/>
  <xsl:param name="count"/>
  <xsl:param name="body"/>
  <xsl:if test="$i &lt;= $count">
    <!-- apply templates to the $body parameter, passing the $i
         parameter -->
    <xsl:apply-templates select="$body">
      <xsl:with-param name="i" select="$i" />
    </xsl:apply-templates>
    <xsl:if test="$i &lt; $count">
      <xsl:call-template name="for.loop">
        <xsl:with-param name="i" select="$i + 1"/>
        <xsl:with-param name="count" select="$count"/>
        <xsl:with-param name="body" select="$body" />
      </xsl:call-template>
    </xsl:if>
  </xsl:if>
</xsl:template>

When you call the for.loop template, the $body parameter is set to the
reference node. In this case, it's the xsl:template element in the
current stylesheet whose name attribute is equal to 'createRow'. The
call is therefore as follows:

  <xsl:call-template name="for.loop">
    <xsl:with-param name="i" select="1"/>
    <xsl:with-param name="count" select="count(BODY/ROW)"/>
    <xsl:with-param name="body"
      select="document('')/*/xsl:template[@name = 'createRow']" />
  </xsl:call-template>

This example uses a slightly different technique from Dimitre's
standard method for identifying the template that you want to use.
Dimitre generally adds a separate data element at the top level of the
stylesheet, which is matched by the relevant template and acts as the
reference node, whereas in the above I've used the xsl:template
element itself as the reference node. But the fundamentals are the
same; the only reason I've done this is because it keeps the
explanation shorter - using a node other than the xsl:template element
is more flexible in general.

By the way, you used xsl:eval in your example, which makes me think
that you might be using WD-xsl rather than XSLT. Check out the MSXML
FAQ at http://www.netcrucible.com/ if you don't know the difference.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


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


Current Thread