Deane
Deane

Reputation: 8757

Why does processing a parent element's template prevent the processing of child element templates?

Consider this XML:

<person>
  <name>
    <firstName>James</firstName>
    <lastName>Bond</lastName>
  </name>
</person>

Then consider this XSL:

<xsl:template match="//name">
   [do stuff]
</xsl:template>

<xsl:template match="//firstName">
   [do stuff]
</xsl:template>

Am I correct in saying that existence of the first template ("//name") "hides" the second template ("//firstName"). I know this is true (I tested this before posting), but I'm trying to derive and understand the general rule.

Since...

  1. It ("//name") is less specific
  2. It will be matched first
  3. It does not call "apply-templates"

...the second template will not be processed.

What are the mechanics of this logic, and how can it be predicted? When the "//name" template is processed, does the processor somehow logically "check off" all the child elements as not needing to be processed? Are they considered "done" because their parent was processed?

If I want to ensure the second template is run, is there any other way other than to make sure "apply-templates" is called in the first template?

Again, I know that there are ways around this, I'm just trying to understand the theory/justification behind it.

Upvotes: 1

Views: 342

Answers (3)

C. M. Sperberg-McQueen
C. M. Sperberg-McQueen

Reputation: 25034

The answers offered by michael.hor257k and Ian Roberts are correct and complete, but since you appear still to have questions, perhaps a different way of explaining it may be helpful.

First, note that the // in your patterns has essentially no effect; your templates would have the same meaning if the match patterns were written "name" and "firstName". If in the back of your mind you have been thinking that the // signals any information that could affect the flow of control, it would be good to dispel that thought.

Second, note that in any input/processing/output language for trees or documents, the language designers can either assume a kind of 'push' processing (the processor reads through the input and handles each item in the input, producing output when and as appropriate; when the input is done, the process is completed), or a kind of 'pull' processing (the processor generates the required output item by item, consulting the input as needed; when the output is complete, the process is complete).

In languages which do not allow reordering of data, the two patterns coincide: the input is handled in linear order from start to finish, and the output is produced in linear order from start to finish.

In languages like XSLT, or its predecessor DSSSL, the difference between the two is visible if you consider the sequence in which items in the input are handled and the sequence in which items in the output are produced. In a straightforward (or: naive) implementation of a push language (like DSSSL's tree-to-tree transformation process), the input items are handled in order, and the output is computed out of order, and buffered until complete. In a pull language (like XSLT), the output is computed in order, and the input is buffered so that it can be visited in arbitrary order.

Note that the use of 'push' and 'pull' here is not quite the same as the sense in which many XSLT programmers apply those terms to XSLT stylesheets -- at the language level, as I have just described these terms, XSLT is a pull language. Naive implementations buffer all the input. There is a certain irony here, given that within XSLT, 'push' stylesheets are generally more idiomatic than 'pull' stylesheets.

Note also that I'm talking about naive implementations: XSLT's spec is purely declarative, and processors can do things in any order they like. But the simplest and easiest implementation strategy is to evaluate the expression describing the output of the stylesheet, in order. By default, the output is described in full by the template body for the template matching the document node.


For the XML you show, the flow of control in your stylesheet (assuming you have just the two templates) would be something like this, in a straightforward, naive implementation of XSLT:

  1. If you have invoked it normally (i.e. without any options to override the default behavior), the processor looks for a template to match the document node. Since no template in the stylesheet matches /, the default rule applies, which consists of

    <xsl:apply-templates/>
    

    or, supplying the default value explicitly:

    <xsl:apply-templates select="child::node()"/>
    

    The processor pushes this template on the stack and begins to execute it. The only thing in the template is the call to apply-templates, so that's what the processor does.

  2. The processor now pushes all the children of / on the stack (in reverse document order); let's assume for concreteness that these children are (1) the 'person' element, and (2) a text node consisting of a newline, which follows the 'person' element. For each of them in turn, the processor seeks a matching template. At this point, the 'person' element is at the top of the stack, so the processor looks for a matching template.

  3. The processor finds no template in the stylesheet matching the 'person' element, so it uses the default template already described by Ian Roberts, which does nothing but call

    <xsl:apply-templates select="child::node()"/>
    

    The processor pushes the (default) template for the 'person' element onto the stack and begins to execute it. This template does nothing but call apply-templates on the children.

  4. The processor now pushes all the children of the 'person' element on the stack. The stack now contains:

    • a text node containing zero or more blanks or tabs, a newline, and two blanks (at the start of the 'person' element)
    • a 'name' element
    • a text node containing zero or more blanks or tabs and a newline (between the end-tags of 'name' and 'person')
    • the template for the 'person' element, with an instruction pointer indicating that execution should resume following the 'apply-templates' instruction.
    • the text node containing the newline which follows the end-tag of 'person' in the input (which was placed on the stack in step 2)
    • the template for the document root node, with an instruction pointer indicating that execution should resume after the call to 'apply-templates'.
  5. Finding no template matching the text node on top of the stack, the processor follows the default rule, which essentially calls

    <xsl:value-of select="."/>
    

    So the processor writes out the string value of the text node and pops the stack.

  6. The 'name' element is now at the top of the stack; the processor now looks for a template matching it. It finds the first template in the stylesheet and pushes the template on the stack.

  7. The processor evaluates the body of the matching template -- in the version of your stylesheet shown, this consists of a text node containing some whitespace, the text "[do stuff]", and some white space. It writes the string value of this text node to the output, and (having completed its evaluation of the template) pops the stack.

  8. The next node on the stack is the whitespace node following the 'name' element. The stylesheet has no template to match it, so the processor uses the default text-node template and copies the string value to the output. The processor is now done with the text node which is the last child of the 'person' element, so it pops the stack.

  9. The top of the stack is now occupied by the (default) template which matched the 'person' element. The processor continues at the point indicated by the instruction pointer, which is after the call to 'apply-templates'. There is nothing else in the template body, so the template body has now been evaluated in full. The processor is done with the template, and pops it off the stack.

  10. The stack now presents the whitespace node following the 'person' element. The stylesheet has no template to match it, so the processor uses the default text-node template, copies the string value to the output, and pops the stack.

  11. The only thing on the stack now is the default template which matched the root node of the document. We are positioned after the 'apply-templates' instruction, so there is nothing else to do. The processor pops the stack.

  12. The stack is now empty, and the evaluation of the stylesheet is now complete.

The description just given should be taken, of course, with a grain of salt: it describes the logical flow of control, and not necessarily the sequence of events in a given implementation. For example, in practice processors may conserve stack space by using a form of tail-call optimization to avoid leaving templates on the stack if the instruction pointer has reached the end of the template. And more generally, XSLT has a purely declarative semantics, so ANY discussion of stylesheet evaluation that says "this happens, then that happens" is short-hand for "one way to do it would be for this to happen, then that" -- the spec does not constrain the order in which things happen in time, only the structure of the output.


Simplified as it is, this model of evaluation should help you understand why your second template is never called. The reasons you suggest are a mixed bag:

It has nothing to do with the relative specificity of the match patterns "//name" and "//firstName" -- since they can never match the same node, the relative specificity of these two patterns can never become an issue. (And in any case the two match patterns have the same degree of specificity and the two templates have the same default priority.)

It has something to do with the 'name' element being matched first only for a very special (and not very precise) notion of what "being matched first" might mean. Temporal relations are not specified by the XSLT spec, so 'first' is not really a defined or definable concept. It's true that in any evaluation of the stylesheet, the template for the 'name' element can be said to be be matched before the template for the 'firstName' element, but only in the sense that the one template is matched, and the other is never matched (we are still waiting for it to match, and we will wait forever). The template for 'name' fires because there comes a point where a template being executed contains an 'apply-templates' instruction with a 'select' attribute whose value includes the node for the 'name' element. The template for 'firstName' never fires because that never happens for the 'firstName' element: it never appears in the value of any 'select' attribute on any 'apply-templates' being evaluated.

As Ian Roberts has pointed out, if you want both templates to fire, in a stylesheet that is as similar to the current one as possible, the solution is to add <xsl:apply-templates/> to the template for the 'name' element.

[Addendum] Readers struggling to get their heads around flow of control may find Evan Lenz's answer to a related question helpful.

Upvotes: 4

michael.hor257k
michael.hor257k

Reputation: 117018

Since...

  1. It ("//name") is less specific
  2. It will be matched first
  3. It does not call "apply-templates"

...the second template will not be processed.

The correct answer is #3.

To understand this, you must understand that the default behavior of the processor (i.e. the built-in template rules) is to traverse the entire input tree and visit every node.

What the processor does when it gets to a node depends on the template matching that node. If the template does not tell the processor to continue the traversal, then the rest of that branch is abandoned. (Note that the default template does apply templates to the current node's children nodes).

If I want to ensure the second template is run, is there any other way other than to make sure "apply-templates" is called in the first template?

Yes. You could apply the second template from a third template. You are not limited to applying templates to just the current node's children. You can apply templates to the current node's siblings, ancestors, descendants ... or even nodes in an entirely different branch of the input tree.

Upvotes: 1

Ian Roberts
Ian Roberts

Reputation: 122374

The only thing that causes templates to fire is a call to xsl:apply-templates. The reason it looks like this is automatic in many cases is that XSLT defines default template rules (XSLT 2.0, XSLT 1.0) that fire when there is no explicit template that matches a particular node. The default rule for element nodes is effectively

<xsl:template match="*">
  <xsl:apply-templates/>
</xsl:template>

If you define an explicit template for a particular node it's up to you to decide when (or indeed whether) to apply-templates to its children. For some kinds of transformations you might want to put the result of processing children inside a particular element, for other scenarios you might need to put it before or after. XSLT won't try and second-guess this.

In your example, you don't have an explicit template for person so the default rule applies, which applies templates to the three child nodes of the person element (the whitespace text node before the name element, the name itself, and the whitespace text node after the name). For name there is an explicit template, so that fires, and since it doesn't apply-templates to anything else that's as far as the recursion goes.

Upvotes: 2

Related Questions