Re: [xsl] modular xslt design question

Subject: Re: [xsl] modular xslt design question
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Thu, 1 Mar 2001 10:33:13 +0000
Hi Dave,

> Now, I have some items that should be handled in the same way,
> regardless of the mode.
>
> <xsl:template match="D" mode="foo bar wow">...</xsl:template>
>
> Of course, the above isn't legal, because mode only holds a QName,
> not a list of them. I would like to avoid cut-and-pasting the common
> template n times (where n is the number of styles I have -- it will
> be more than three!). I also don't see a way to apply-templates
> (back in A, B, and C) in document order such that the child elements
> can be matching in different modes.

You could have another template (perhaps another mode, 'common', or
non-moded) that contains the logic for each, and then apply templates
to the current node in that mode within each of the templates, i.e.:

<xsl:template match="D" mode="foo">
   <xsl:apply-templates select="." mode="common" />
</xsl:template>
<xsl:template match="D" mode="bar">
   <xsl:apply-templates select="." mode="common" />
</xsl:template>
<xsl:template match="D" mode="wow">
   <xsl:apply-templates select="." mode="common" />
</xsl:template>

<xsl:template match="D" mode="common">
   <!-- do your stuff -->
</xsl:template>

> My intended workaround was to use templates without modes to manage
> the general flow, with a parameter being passed through all(!) of
> them to indicate the intended mode/style. Templates with behaviour
> common to all modes can be implemented directly; others can be
> implemented by dispatching to mode-specific named templates, e.g.

The only way to do this kind of dispatching is unfortunately:

<xsl:template match="A">
   <xsl:param name="mode" />
   <xsl:choose>
      <xsl:when test="$mode = 'foo'">
         <xsl:apply-templates select="." mode="foo" />
      </xsl:when>
      <xsl:when test="$mode = 'bar'">
         <xsl:apply-templates select="." mode="bar" />
      </xsl:when>
      <xsl:when test="$mode = 'wow'">
         <xsl:apply-templates select="." mode="wow" />
      </xsl:when>
   </xsl:choose>
</xsl:template>

Of course you don't have to do this for every single element, you
could make it match on any number of elements, or all of them.

In fact, you could make a reasonable general dispatching template
with:

<xsl:template match="*">
   <xsl:param name="mode" />
   <xsl:variable name="result">
      <xsl:choose>
         <xsl:when test="$mode = 'foo'">
            <xsl:apply-templates select="." mode="foo" />
         </xsl:when>
         <xsl:when test="$mode = 'bar'">
            <xsl:apply-templates select="." mode="bar" />
         </xsl:when>
         <xsl:when test="$mode = 'wow'">
            <xsl:apply-templates select="." mode="wow" />
         </xsl:when>
      </xsl:choose>
   </xsl:variable>
   <xsl:choose>
      <xsl:when test="string($result)">
         <xsl:copy-of select="$result" />
      </xsl:when>
      <xsl:otherwise>
         <xsl:apply-templates select="." mode="general" />
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

So if applying templates in the specific mode doesn't work (you don't
get any result from it) then you apply templates in 'general' mode.
It means that you can create a template in 'general' mode for modes
that are very similar for particular elements.

You have to watch the test, though. string($result) will only be
true() if there is some textual content in the result, which there
might not be.  It'd be better if you could tested with:

  saxon:node-set($result)/node()

or the equivalent in your processor.

The other danger is if some of the templates in foo, bar or wow mode
purposefully produce nothing.  Then you need a general mode template
that also produces nothing.  You could pass the proper $mode into the
general-mode template as a parameter to get around that.

I hope that gives you some ideas,

Jeni

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



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


Current Thread