In the frame of the preparation of a DITA Open Toolkit course, I have to explain how template priority rules work in XSLT. Before preparing this course, I had a vague idea of how it worked. I knew how import precedence worked, but for templates located in the same file, I didn’t really know which
@match patterns had priority over others. I just fixed the conflicts when they popped up.
I had a look at the dedicated section in the XSLT 2.0 specifications, but it didn’t help me much as I wasn’t able to draw simple rules out of sub-section 2).
The best thing I could do was to experiment and see what happened. And I did it.
In this article I only deal with the precedence of templates within the same XSLT file, with no
@priority specified. Import precedence is clear enough to be left aside.
How I tested
- I downloaded Saxon HE 18.104.22.168J from Sourceforge (the only JAR we need is saxon9he.jar)
- I created a test XML file (it’s actually a DITA XML file)
- I created a test XSLT 2.0 file
- I ran saxon9he.jar many times via command line, always leaving only two templates active (= not commented out). And I took note of the results. The command looks like
java -jar saxon9he.jar -s:test.xml -xsl:test.xslt -expand:on.
The input XML (DITA XML) and the XSLT
A few comments
In the XML, the content of the
@class attribute is normally extracted upon transformation from the schema (thanks to the
-expand:on command line parameter). I pre-filled the
@class values in the XML to make the XSLT easier to understand. The DTD was available locally for better performance and to give a rest to OASIS servers. If you want to reproduce the test and not bother downloading the DTD, you can use this doctype declaration instead:
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "http://docs.oasis-open.org/dita/v1.2/os/dtd1.2/technicalContent/dtd/topic.dtd">.
In the stylesheet, we only have templates whose purpose is to match the
<b> element (
/topic/body/p/b). And they actually all match it, but using different patterns.
I save you the individual results, here is the outcome:
- Templates A, B, C, D, E, F and G have the same priority level (0.5)
- Template H has a lower priority level than the others (0)
- If two or more templates share the highest priority level, the last one is used (here it’s G) and the processor issues a warning:
Recoverable error XTRE0540: Ambiguous rule match for /topic/body/p/b Matches both "b[@outputclass != 'weak']" on line 32 of file:/C:/Users/colin/dita/dita-ot-course/xslt/XSLT/04-template_priority.xsl and "p/b[. = 'all']" on line 29 of file:/C:/Users/colin/dita/dita-ot-course/xslt/XSLT/04-template_priority.xsl Recoverable error XTRE0540: Ambiguous rule match for /topic/body/p/b Matches both "b[@outputclass != 'weak']" on line 32 of file:/C:/Users/colin/dita/dita-ot-course/xslt/XSLT/04-template_priority.xsl and "*[contains(@class, ' topic/ph ')]" on line 17 of file:/C:/Users/colin/dita-ot-course/xslt/XSLT/04-template_priority.xsl
Looking back at the priority rules in the XSLT spec, we see in the table that
element(E), which is equivalent to our template H, has priority 0. Removing template H and setting the priority of template A at
0.51 makes of A the highest priority template, while setting it to
0.49 yields the same result as without the priority attribute: all other templates have the highest priority and template G is used. This means that without priority attribute, templates A to G have a priority of
If you want to rule the priority between templates that have the same priority level:
@priorityto force a lower or higher priority
- OR move the templates that should have a lower priority higher in the import sequence (and vice versa for a higher priority)