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

Subject: RE: [xsl] best practices re xsl:{import,include,next-match,apply-imports}
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Wed, 7 May 2008 23:51:49 +0100
No magic answer to this one. You could use apply-imports instead of
next-match - interesting to see that there is a use case for this. Is there
any reason the generic match="node()|@*" template can't be moved into the
generic stylesheet module?

Changing the import to include seems to me to be storing up other problems
for the future.

Michael Kay
http://www.saxonica.com/ 

> -----Original Message-----
> From: James A. Robinson [mailto:jim.robinson@xxxxxxxxxxxx] 
> Sent: 07 May 2008 16:44
> To: XSL-List
> Subject: [xsl] best practices re 
> xsl:{import,include,next-match,apply-imports}
> 
> 
> 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 elements).
> 
> 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 ...
>              <atom:entry>...</atom:entry>
> 
> 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"
>       xmlns:xs="http://www.w3.org/2001/XMLSchema";>
>       <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), ', '))"/>
>       </xsl:template>
>     </xsl:stylesheet>
> 
> (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"/>
>         </xsl:call-template>
>       </xsl:template>
>     </xsl:stylesheet>
>     
> (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>
>     
>       <xsl:template mode="t" match="t[@specific eq 'yes']" 
> priority="2">
>         <xsl:next-match>
>           <xsl:with-param name="b" select="'s'" tunnel="yes"/>
>         </xsl:next-match>
>       </xsl:template>
>     </xsl:stylesheet>
>     
> (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 added.
> 
> I'd appreciate any thoughts on this from you folks!
> 
> 
> Thank you,
> 
> Jim
> 
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> James A. Robinson                       jim.robinson@xxxxxxxxxxxx
> Stanford University HighWire Press      http://highwire.stanford.edu/
> +1 650 7237294 (Work)                   +1 650 7259335 (Fax)

Current Thread