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 9.6.0.5J 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.
The results
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[1]/p[1]/b[1] 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[1]/p[1]/b[1] 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 0.5
.
Conclusion
If you want to rule the priority between templates that have the same priority level:
- assign
@priority
to 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)