[xsl] best practices re xsl:{import,include,next-match,apply-imports}

Subject: [xsl] best practices re xsl:{import,include,next-match,apply-imports}
From: "James A. Robinson" <jim.robinson@xxxxxxxxxxxx>
Date: Wed, 07 May 2008 08:43:38 -0700
Hi folks,

For the first time I've had an application where I think
xsl:next-match or xsl:apply-imports might applicable.

This application needs to be able to transform various elements in various
schema into be a fairly complicated atom:entry format (where atom is
the Atom Syndication Format, http://www.w3.org/2005/Atom) which we have
developed to indicate hyperlinking relationships.  The constructions of
the atom:entry takes 15 variables into account when it constructed.

A specific example is the need to transform an element from the NLM
Journal Publishing DTD, the relateed-article element into this format.
Many of the 15 variables used to create the atom:entry are generic
when dealing with a related-article element.  Two are not.

I thought it'd be nice to keep construction of these entries as simple
as possible for the people who will be maintaining these stylesheets.
Specificaly I want to make it simple to add new templates for transforming
new types of related-article elements into these atom:entry forms.

I thought what I would do is create a generic library stylesheet with
a callable template which takes 15 tunneled parameters and creates the
proper atom:entry.

Then I would create a stylesheet which matches on any "related-article"
element, using a priority of 1, which called the library template,
constructing and passing along those tunneled parameters which are generic
(i.e., all those parameters which are applicable to all related-article

Finally, I wanted to add a last stylesheet with very specific
"related-article" element match, one which matched on specific attributes,
and which had a priority of 2.  This top level stylesheet would use
xsl:next-match to tunnel through the one or two parameters which weren't
handled by the more generic layer.

So I'd have

  xsl:template match="related-article[...predicates...]" priority="2"
    -> xsl:next-match w/ tunneled 2 parameters

       xsl:template match="related-article" priority="1"
         -> xsl:call-template w/ 13 tunneled parameters

           xsl:template name="atom:hyperlink"
             xsl:param ...13 tunneled parameters ...

This is the first time I'd used xsl:next-match, and I found myself being
confounded by how one should use xsl:import vs. xsl:include or should
use xsl:next-match vs. xsl:apply-imports.

My first reaction was that one should use xsl:import and
xsl:next-match to offer the most flexibility re future changes.  E.g.,
introduction of overridable variables or the need to "next-match" at
various priorities within the highest level stylesheet.

What I've realized is that the rules of xsl:import and xsl:next-match
and how XSLT defines the next-match, by lower priority -or- by lower
precedence, means I run into trouble if I introduce a template which
suppresses the default behavior of XSLT to print the string value of
elements as it walks the tree.

To try and boil this down to a simple set of examples, let's say I
have three stylesheets and a source document:

(1) A named template stylesheet, t.xsl.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="2.0"
      <xsl:template name="t">
        <xsl:param name="a" as="xs:string?" tunnel="yes" />
        <xsl:param name="b" as="xs:string?" tunnel="yes" />
        <xsl:message select="concat('t: ', string-join(($a,$b), ', '))"/>

(2) A "generic" stylesheet, g.xsl, which matches on an element 't' and
    calls the named template in (1) with a tunneled parameter.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="2.0">
      <xsl:import href="t.xsl"/>
      <xsl:template mode="t" match="t" priority="1">
        <xsl:call-template name="t">
          <xsl:with-param name="a" select="'g'" tunnel="yes"/>
(3) A "specific" stylesheet, s.xsl, which assumes it can fall-back to
    templates in (2) with a tunneled parameter.

    Because it is processing a source document filled with other
    elements and text nodes, it also contains a generic match to
    navigate the document while surpressing default XSLT behavior.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="2.0">
      <xsl:import href="g.xsl"/>
      <xsl:template mode="t" match="@*|node()">
        <xsl:apply-templates mode="#current"/>
      <xsl:template mode="t" match="t[@specific eq 'yes']" priority="2">
          <xsl:with-param name="b" select="'s'" tunnel="yes"/>
(4) A source document with a lot of text nodes in it, none of which I
    want to have emitted.  I just want a final result of an
    xsl:message ('t: g, s') to be emitted.

    <x>text<y>text<t specific="yes"/>text</y>text</x>

Because (3) has a template match "@*|node()", which is lower
precedence, the xsl:next-match won't ever fall back to (2).  This
means I have to make one of the following changes to (3):

 (a) change the xsl:import to xsl:include.
 (b) change the xsl:next-match to xsl:apply-import.
 (c) not use a generic match="@*|node()", require that callers
     specifically target the elements to be processed

I'm wondering if there are any other options available?  If not, is
there one of those which the developers here would recommend as being
likely to be better for long term maintenance?

I'm disclined to choose (c).  The problems I have with (a) and (b) are
that they seem to imply a limitation in later flexibility of the
stylesheets (e.g., having parameters w/ the same name but different
precedence in (2) and (3), or allowing for xsl:next-match to work on
templates introduced into (3))

My inclination, at this point, is that it will be better to use
xsl:include and require that both generic and specific template
matches have their priorities renumbered as new functionality as

I'd appreciate any thoughts on this from you folks!

Thank you,


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
James A. Robinson                       jim.robinson@xxxxxxxxxxxx
Stanford University HighWire Press      http://highwire.stanford.edu/
+1 650 7237294 (Work)                   +1 650 7259335 (Fax)

Current Thread