XSLT's Template Dispatch
XSLT's Template Dispatch by Clark C. Evans December 1, 2000 XSLT is a language for transforming XML texts. The language includes familiar constructs such as for-each iteration, conditional statements, and callable functions. XSLT also includes an additional control structure, the template dispatch, which occurs through the interaction of apply-template/select and template/match. This paper presents two templates, one of which uses the template dispatch, and a second, functionally equivalent to the first, which is constructed without the help of this control structure. This paper then concludes with a comparison which may help to elucidate the power and elegance of XSLT's template dispatch. Consider the XML input <bookcase> <book> <title>The C Programming Language</title> <author>Brian W. Kernighan</author> <author>Dennis M. Richie</author> </book> <book> <title>Compilers: Principles, Techniques, and Tools</title> <author>Alfred V. Aho</author> <author>Ravi Sethi</author> <author>Jeffrey D. Ullman</author> </book> </bookcase> processed by the XSLT stylesheet <stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"> <template match="author"> Author: <apply-templates/> </template> <template match="book"> Book: <apply-templates select="title|author" /> </template> </stylesheet> to produce the following output: Book: The C Programming Language Author: Brian W. Kernighan Author: Dennis M. Richie Book: Compilers: Principles, Techniques, and Tools Author: Alfred V. Aho Author: Ravi Sethi Author: Jeffrey D. Ullman Given any input text, the following stylesheet will produce exactly the same output as the stylesheet above, only it will do so without the aid of apply-template/select and apply-template/match. As a consequence, much of its code necessarily emulates functionality required by the XSLT specification and built into a compliant XSLT processor. <stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"> <template match="/"> <call-template name="dispatch"/> </template> <template name="dispatch"> <variable name="id" select="generate-id(.)" /> <choose> <when test="//author[generate-id(.) = $id]"> Author: <call-template name="apply" /> </when> <when test="//book[generate-id(.) = $id]"> Book: <call-template name="apply" > <with-param name="select" select="title|author" /> </call-template> </when> <when test="self::text()"> <value-of select="." /> </when> <otherwise> <call-template name="apply" /> </otherwise> </choose> </template> <template name="apply"> <param name="select" select="node()" /> <for-each select="$select"> <call-template name="dispatch" /> </for-each> </template> </stylesheet> The entry point for a procedural stylesheet is marked by <template match="/">, a special case similar to a "C" style main() function. When this template is executed, the current node for the process is initialized to root node, and then the body, <call-template name="dispatch" />, transfers control to the template named dispatch. The dispatch template begins by creating a variable, $id, which is used to hold an unique string identifier generated by the XSLT processor for the current node. Following is a conditional switch statement having three when clauses and a single otherwise. For each when clause, a path expression is evaluated and converted into a boolean value. If true, then the corresponding body is executed and control resumes immediately after the choose construct ends. If all of the when clauses fail to fire, then the body of the otherwise is executed. The first case, <when test="//author[generate-id(.)=$id]">, has a path expression returning the node-set consisting of any author element having an identifier equal to the current node's identifier. Therefore, if the current node happens to be an author, the node-set returned will be non-empty, which converts to a true boolean value. This body is executed with two operations: the non-empty text node "\n Author: " is printed and then control is transferred to the apply template as specified by <call-template name="apply"/>. The second case, <when test="//book[generate-id(.)=$id]">, is very similar. Only here, the apply template is called with a parameter named select. The select parameter is the node-set containing all element children of the current node with a name of either title or author. The third case, <when test="self::text()">, only fires when the current node is a text node. Here <value-of select="."> instructs the processor to print the text value of the current node. Finally, in default, the apply template is called when none of the previous when clauses have executed. The last template in the procedural stylesheet, apply, has an optional parameter select, which is passed as a node-set. If this parameter is missing, then every child of the current node is selected. The remainder of this function then iterates through the selected node-set, calling the dispatch template. A comparison of the two stylesheets reveals that the first when clause described above corresponds directly to the first template of the original stylesheet. In a similar manner, the second when clause corresponds to the second template of the original stylesheet. The third when and the default otherwise clause are needed to emulate the built-in-rule required by the XSLT specification. A fully compliant emulation would also order the tests according to XSLT's priority rules. As you can see with this emulation, a good amount of code is built into the XSLT processor. Specifically, functionality similar to the apply template, a default entry point, and a mechanism similar to the dispatch template are included. Furthermore, the complexity of this dispatch mechanism increases when the import statement and mode attribute are considered. The administration of these and other details is handled by the XSLT processor, allowing succinct stylesheets like the first one presented. Examination of this procedural stylesheet also clarifies the complementary roles played by select and match expressions. The select expression chooses which nodes to visit, and, for each node, the match expressions designate which template to execute. This allows an ordered set to be selected as a whole, yet each node in the set to be treated individually. This decoupling of roles is elegantly managed by the XSLT processor and invoked by apply-template/select and template/match. The decoupling of select and match also allows a processor to pre-compute the match expressions up-front. For example, given an in-memory node-based implementation, an additional pointer could be added to each node. After the tree is loaded and before processing commences, the processor could visit each node in the tree, filling in a pointer to the template which best matches the node according to the priority rules. Then, while iterating through a selected node-set, the template to dispatch is immediately available without further computation. XSLT's template mechanism may not be the best solution to every transformation requirement; however, when the inputs have varying structure such that relative order among nodes with different matching criteria is important, its template dispatch approach is a clear winner.
Prepared by Robin Cover for The XML Cover Pages archive.