Template Priorities and Modes

When several rules match, the highest import precedence (Overriding and Importing) wins, then the highest priority. Defaults: book 0, bn:* -0.25, * -0.5, and patterns with predicates or paths 0.5. xsl:next-match hands the node to the next-best rule, so rules can wrap each other, and a mode is a separate rule set. Run xslt3 -s:booknest-catalog.xml -xsl:modes.xsl:

modes.xsl: a table of contents mode and three competing rulesXML
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    expand-text="yes">
  <xsl:output method="text"/>
  <xsl:template match="/catalog">
    <xsl:apply-templates select="book" mode="toc"/>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="book[position() le 3]"/>
  </xsl:template>
  <xsl:template match="book" mode="toc">{@id} </xsl:template>
  <xsl:template match="book">{title}&#10;</xsl:template>
  <xsl:template match="book[rating > 4.7]">Top rated: <xsl:next-match/></xsl:template>
  <xsl:template match="book[pages > 400]" priority="1">Long: <xsl:next-match/></xsl:template>
</xsl:stylesheet>
Output
b1 b2 b3 b4 b5 b6 
The Quiet Harbor
Long: Patterns of the Deep Web
Top rated: Salt and Saffron

Both predicate rules have priority 0.5. Without priority="1", a book matching both (try rating > 4.2) makes Saxon 726,956 warn XTDE0540 Ambiguous rule match and use the rule declared last. xsl:mode sets what happens to unmatched nodes (on-no-match="shallow-copy", "shallow-skip", "fail").


Multiple Templates

<xsl:apply-templates>

<xsl:apply-templates> is similar to <xsl:value-of> in that it replaces itself with data, but where <xsl:value-of> copies data from an XPath expression evaluated in the current context, <xsl:apply-templates> copies data produced by whichever template rule matches the selected nodes. Its select attribute is optional; when omitted, the children of the context node are processed. The example below applies a shared template to both <Name> and <Age> elements, inserting a comma between sibling values.
ch06-apply-templates.xml, ch06-apply-templates.xslXML
<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Employee>
      <Name>Dash</Name>
      <Age>23</Age>
   </Employee>
   <Employee>
      <Name>Gwen</Name>
      <Age>22</Age>
   </Employee>
</Root>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="/Root">
      <Root>
         <Employee>
            <Name>
                <xsl:apply-templates select="Employee/Name"/>
            </Name>
            <Age>
                <xsl:apply-templates select="Employee/Age"/>
            </Age>
         </Employee>
      </Root>
   </xsl:template>
   <xsl:template match="Name|Age">
      <xsl:value-of select="."/>
      <xsl:if test="position()!=last()">
         <xsl:text>,</xsl:text>
      </xsl:if>
   </xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Employee>
      <Name>Dash,Gwen</Name>
      <Age>23,22</Age>
   </Employee>
</Root>
The same result can be obtained more simply with <xsl:value-of separator="," select="…"/>.

<xsl:apply-templates> can contain <xsl:sort> and <xsl:with-param> as child elements, and can carry a mode (token) attribute. It can also be used to execute a multi-staged pipeline of transformations, as in the example below, which sorts a tree of directory/file nodes and then discards deeper nesting by re-matching inner nodes against a more specific pattern.
ch06-apply-templates-pipeline.xml, ch06-apply-templates-pipeline.xslXML
<?xml version="1.0" encoding="UTF-8"?>
<treeview>
  <treenode>
    <caption>Directory Z</caption>
    <nodes>
      <treenode><caption>File B</caption></treenode>
      <treenode><caption>File Z</caption></treenode>
      <treenode><caption>File A</caption></treenode>
    </nodes>
  </treenode>
  <treenode>
    <caption>Directory G</caption>
    <nodes>
      <treenode><caption>File F</caption></treenode>
      <treenode><caption>File O</caption></treenode>
      <treenode><caption>File B</caption></treenode>
    </nodes>
  </treenode>
</treeview>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes" />
   <xsl:template match="/">
      <treeview>
         <xsl:apply-templates select="treeview/treenode">
            <xsl:sort select="caption" data-type="text"/>
         </xsl:apply-templates>
      </treeview>
   </xsl:template>
   <xsl:template match="treenode">
      <treenode>
         <xsl:copy-of select="caption"/>
         <nodes>
            <xsl:apply-templates select="nodes/treenode">
               <xsl:sort select="caption" data-type="text"/>
            </xsl:apply-templates>
         </nodes>
      </treenode>
   </xsl:template>
   <xsl:template match= "/treeview/treenode/nodes/treenode">
      <xsl:copy-of select="."/>
   </xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<treeview>
   <treenode>
      <caption>Directory G</caption>
      <nodes>
         <treenode>
            <caption>File B</caption>
         </treenode>
         <treenode>
            <caption>File F</caption>
         </treenode>
         <treenode>
            <caption>File O</caption>
         </treenode>
      </nodes>
   </treenode>
   <treenode>
      <caption>Directory Z</caption>
      <nodes>
         <treenode>
            <caption>File A</caption>
         </treenode>
         <treenode>
            <caption>File B</caption>
         </treenode>
         <treenode>
            <caption>File Z</caption>
         </treenode>
      </nodes>
   </treenode>
</treeview>

<xsl:next-match>

<xsl:next-match> is similar to <xsl:apply-templates> in that it invokes another matching template, but unlike <xsl:apply-templates>, which builds on the current context, <xsl:next-match> resets the context and excludes the current template rule from the next match. Note that this makes it possible to build a recursive chain of templates, as shown below where an element with several boolean attributes is progressively wrapped by the templates matching each attribute, in priority order.
ch06-next-match.xml, ch06-next-match.xslXML
<?xml version="1.0" encoding="UTF-8"?>
<element bold="true"
               subscript="false"
               italic="true"
               text="stuff"/>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="element" priority="1">
      <xsl:value-of select="@text" />
   </xsl:template>
   <xsl:template match="element[@italic = 'true']"
                        priority="2">
      <sup><xsl:next-match/></sup>
   </xsl:template>
   <xsl:template match="element[@subscript = 'true']"
                        priority="3">
      <sub><xsl:next-match/></sub>
   </xsl:template>
   <xsl:template match="element[@bold = 'true']"
                        priority="4">
     <strong><xsl:next-match/></strong>
   </xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<strong>
   <sup>stuff</sup>
</strong>

<xsl:call-template>, <xsl:context-item>

<xsl:call-template> invokes a named template directly, independent of the current node's matching rules, passing parameters with <xsl:with-param>.
ch06-call-template.xml, ch06-call-template.xslXML
<?xml version="1.0" encoding="UTF-8"?>
<a>
  <b>1<c/>a</b>
  <b>2<c/>b</b>
  <b>3<c/>c</b>
  <b>4<c/>d</b>
</a>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       xmlns:xs="http://www.w3.org/2001/XMLSchema"
       xmlns:fn="http://www.w3.org/2005/xpath-functions">
   <xsl:template name="T">
     <xsl:context-item as="node()"/>
     <xsl:param name="P"/>
     <xsl:apply-templates select="b[position() lt 3]"/>
     <xsl:value-of select="$P"/>
   </xsl:template>
   <xsl:template match="a">
      <xsl:call-template name="T">
        <xsl:with-param name="P" select="'X'"/>
      </xsl:call-template>
   </xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>1a2bX
<xsl:context-item> declares the required type of the context item when the containing template is called; it can also exist as a child of <xsl:mode> (see Declarations). It can have the following attribute: