<xsl:apply-templates>

We already used xsl:for-each to iterate over a list of element nodes. XSL offers a different possibility for this purpose. The idea is to define the formatting rules at a centralized location. So the solution to The XPath functions position() and last() in an equivalent way:

<xsl:template match="/">
  <xsl:apply-templates select="memo/to" ❶/>
</xsl:template>

<xsl:template match="to" ❷>
  <xsl:value-of select="."/>
  <xsl:if test="position() &lt; last()">
    <xsl:text>,</xsl:text>
  </xsl:if>
</xsl:template>

Definition of the recipient node list. Each element of this list shall be processed further.

This template may be used by a XSL processor to format nodes of type <to>. Since the processor is asked to do exactly this in the current template will really be used in this example.

The procedure outlined above may have the following advantages:

  • Some elements may appear at different places of a given document hierarchy. For example a <title> element is likely to appear as a child of chapters, sections, tables figures and so on. It may be sufficient to define a single template with a match="title" attribute which contains all rules being required.

  • Sometimes the body of a <xsl:for-each> ... </xsl:for-each> spans multiple screens thus limiting code readability. Factoring out the body into a template may avoid this obstacle.

This method is well known from programming languages: If the code inside a loop is needed multiple times or reaches a painful line count good programmers tend to define a separate method. For example:

for (int i = 0; i < 10; i++){
  if (a[i] < b[i]){
    max[i] = b;
  } else {
    max[i] = a;
  }
  ...
}

Inside the loop's body the relative maximum value of two variables gets computed. This may be needed at several locations and thus it is convenient to centralize this code into a method:

// cf. <xsl:template match="...">
static int maximum(int a, int b){
 if (a < b){
   return b;
 } else {
   return a;
  }
}
...
// cf. <xsl:apply-templates select="..."/>
for (int i = 0; i < 10; i++){
  max[i] = maximum(a[i], b[i]);
}

So far calling a static method in Java may be compared to a <xsl:apply-templates>. There is however one big difference. In XSL the method being called may not exist at all. A <xsl:apply-templates> instructs a processor to format a set of nodes. It does not contain information about any rules being defined to do this job:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:output method="text"/>

  <xsl:template match="/memo">
    <xsl:apply-templates select="content"/>
  </xsl:template>

</xsl:stylesheet>

Since no suitable template supplying rules for <content> nodes exists a XSL processor uses a default formatting rule instead:

Thanks for your excellent work.Our firewall is definitely
broken! This bug has been reported by the sender.

We observe that the #PCDATA content strings of the element itself and all (recursive) sub elements get glued together into one string. In most cases this is definitely not intended. Omitting a necessary template is usually a programming error. It is thus good programming practice during style sheet development to define a special template catching forgotten rules:

<xsl:template match="/memo">
  <xsl:apply-templates select="content"/>
</xsl:template>

<xsl:template match="*">
  <xsl:message>
    <xsl:text>Error: No template defined matching element '</xsl:text>
    <xsl:value-of select="name(.)"/>
    <xsl:text>'</xsl:text>
  </xsl:message>
</xsl:template>

The * matches any element if there is no better matching rule defined. Since we did not supply any template for <content> nodes at all this default template will match nodes of type <content>. The function name() is predefined in XSL and returns the element type name of a node. During the formatting process we will now see the following warning message:

Error: No template defined matching element 'content'

We note that for document nodes <xyz>foo</xyz> containing only #PCDATA a simple <xsl:apply-templates select="xyz"/> is sufficient: A XSL processor uses its default rule and copies the node's content foo to its output.

exercise No. 54

Extending the export to a RDBMS Create comment in forum

Q:

We assume that our RDBMS table Customer from Figure 961, “Exporting SQL statements. ” shall be replaced by a table Person. We expect the senders of memo documents to be employees of a given company. Conversely the recipients of memos are expected to be customers. Our Person table shall have a tag like column named type having exactly two allowed values customer or employee being controlled by a CHECK constraint, see Table 3, “The Person table”. Create a style sheet generating the necessary SQL statements from a memo document instance. Hint: Define two different templates for <from> and <to> nodes.

A:

We define two templates differing only in the static string value for a person's type. The relevant XSL portion reads:

<xsl:template match="/memo">
  <xsl:apply-templates select="from|to"/>
</xsl:template>

<xsl:template match="from">
  <xsl:text>INSERT INTO Person (name, type) VALUES('</xsl:text>
  <xsl:value-of select="."/>
  <xsl:text>', 'employee')</xsl:text>
  <xsl:value-of select="$newline"/>
</xsl:template>

  <xsl:template match="to">
  <xsl:text>INSERT INTO Person (name, type) VALUES('</xsl:text>
  <xsl:value-of select="."/>
  <xsl:text>', 'customer')</xsl:text>
  <xsl:value-of select="$newline"/>
</xsl:template>
Table 3. The Person table
name type
Martin Goik employee
Adam Hacker customer
Eve intruder customer