[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Re: A Generic template for multi-pass processing (

Subject: Re: Re: A Generic template for multi-pass processing (Was: Re: Applying two transformations consecutively)
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Sat, 7 Jul 2001 12:59:53 -0700 (PDT)
xslt multipass
Chris Nolte wrote:



> Dimitre,
> 
> I'd like to be able to understand how your generic template works so I can
> adapt it.  I was able to get it to work on your example, but I'm confused by
> it.
> 
[snip -- the first group of questions were already answered by David Carlisle: thank
you David] 

> Finally, can you show me how to modify your template so that, say, in pass1
> I call template1 (or apply-templates mode='one') and in pass2 I call
> template2 (or apply-templates mode='two')?
> 
> How would you set it up to use "any number of not known in advance"
> templates?

Hi Chris,

The main mechanism, which I use to implement generic templates is the "template
reference". This is a node of a unique type (having a unique namespace-uri).

In case we also provide a unique (one and only one) template that matches exactly
this type of node, then the node can be used as a template reference.

When such a unique node is specified as the value of the "select" attribute of
xsl:apply-templates, we know that exactly ("our") the template matching it will be
instantiated by the XSLT processor.

This is very similar to using xsl:call-template, with one of the most important
differences between the two being that you can pass a node (the template reference)
as a parameter to another template, but QNames (the values that have to be specified
for the "name" attribute of xsl:call-template) must always be specified explicitly
and a xsl:parameter or xsl:variable reference is not allowed.

Because of this, it is not possible to call a template by name dynamically (without
knowing the ***literal*** name of the template in advance), as you can read in the
xslt-faq (can't give an URL, as stable URLs are not supported by this FAQ site).

However, it is perfectly possible to instantiate a template dynamically, without
knowing anything about it in advance. This has been implemented using the notion of
a template reference.

To return to your question, here's the generic template:

    <xsl:template name="multiPass">
      <xsl:param name="inputPipe" select="/.."/>
      <xsl:param name="passes" select="/.."/>

      <xsl:choose>
        <xsl:when test="not($passes)">
          <xsl:copy-of select="$inputPipe"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="thisPass" select="$passes[1]"/>
          <xsl:variable name="nextPasses" select="$passes[position() > 1]"/>

          <xsl:variable name="vOutput">
            <xsl:apply-templates select="$thisPass/templateRef/*">
              <xsl:with-param name="input" select="$inputPipe"/>
              <xsl:with-param name="params" select="$thisPass/params"/>
            </xsl:apply-templates>
          </xsl:variable>

          <xsl:call-template name="multiPass">
            <xsl:with-param name="inputPipe"
                            select="msxsl:node-set($vOutput)/node()"/>
            <xsl:with-param name="passes" select="$nextPasses"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

Focus your attention on the following snippet:

          <xsl:variable name="vOutput">
            <xsl:apply-templates select="$thisPass/templateRef/*">
              <xsl:with-param name="input" select="$inputPipe"/>
              <xsl:with-param name="params" select="$thisPass/params"/>
            </xsl:apply-templates>
          </xsl:variable>

As you can see, the xsl:apply-templates will instantiate a (unknown) template, based
on a template reference, passed as part of one of the parameters to this template.

The caller of the template decides what template reference(s) to specify as
parameters, and what templates to have that match this template reference.

This allows the author of the generic template (me in this case) to be a different
person from the caller of the template (e.g. you) and not to know anything about any
of the callers of his generic template, and what template references they're going
to pass as parameters, and what actual templates they'd need to instantiate.

So, if you want to instantiate (not to call) template1 in pass1 and template2 in
pass2, you'll do it like this:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:saxon="http://icl.com/saxon"
xmlns:pass1="yourPass1"
xmlns:pass2="yourPass2"
exclude-result-prefixes="xsl msxsl saxon pass1 pass2"
>
    <xsl:output  encoding="US-ASCII" omit-xml-declaration="yes"/>

      <xsl:variable name="pipeParam">
        <pass1>
          <templateRef><pass1:node /></templateRef>
          <params>
<!-- Put here whatever parameters template1 needs, 
     replace the next two lines with your <param> elements -->
             <param>a</param>
             <param>b</param>
          </params>
        </pass1>
        <pass2>
          <templateRef><pass2:node /></templateRef>
          <params>
<!-- Put here whatever parameters template2 needs, 
     replace the next two lines with your <param> elements -->
             <param>b</param>
             <param>c</param>
          </params>
        </pass2>
<!-- You can specify as many passes to be performed with whatever templates
     and whatever parameters you need like in the commented third pass below --> 
<!--
       <pass3>
          <templateRef><pass3:node /></templateRef>
          <params>
             <param>c</param>
             <param>d</param>
          </params>
        </pass3>
-->
      </xsl:variable>


    <xsl:template match="/">

      <xsl:call-template name="multiPass">
        <!-- Specify whatever necessary for initial input to template1 as below -->
        <xsl:with-param name="inputPipe" select="/*"/>
        <xsl:with-param name="passes" select="msxsl:node-set($pipeParam)/*"/>
      </xsl:call-template>
    </xsl:template>

<!-- Your template1 -->
    <xsl:template match="*[namespace-uri() = 'myPass1']">
         <xsl:param name="input" select="/.."/>
         <xsl:param name="params" select="/.."/>

<!-- Perform any necessary transformation here -->
<!-- $params/param contains all "param" elements specified 
     for this instantiation -->

    </xsl:template>

<!-- Your template2 -->
    <xsl:template match="*[namespace-uri() = 'myPass2']">
         <xsl:param name="input" select="/.."/>
         <xsl:param name="params" select="/.."/>

<!-- Perform any necessary transformation here -->
<!-- $params/param contains all "param" elements specified 
     for this instantiation -->

    </xsl:template>

I think I answered your question completely and in detail.

You can also read my explanation of generic templates in:

http://lists.fourthought.com/pipermail/exslt/2001-May/000169.html

Hope this helped.

Cheers,
Dimitre Novatchev.


__________________________________________________
Do You Yahoo!?
Get personalized email addresses from Yahoo! Mail
http://personal.mail.yahoo.com/

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


Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.