10.1 Template Priority

What happens if you have more than one template rule that matches the exact same pattern, or more than one template rule with different patterns that happen to match the exact same nodes? Which template, if any, gets instantiated? Multiple templates matching the same node present a problem. When this happens, an XSLT processor has the option to stop processing and recover from the error, or recover from the error after issuing a warning. The way a processor recovers from conflicting templates is to instantiate only the last template in a stylesheet that matches the pattern. I'll show you what I mean.

Example 10-1, the document ri.xml in examples/ch10, lists the five counties in the state of Rhode Island in the U.S.

Example 10-1. An XML document listing Rhode Island counties
<?xml version="1.0" encoding="US-ASCII"?>     <state name="Rhode Island">  <county>Bristol</county>  <county>Kent</county>  <county>Newport</county>  <county>Providence</county>  <county>Washington</county> </state>

The stylesheet last.xsl, shown in Example 10-2, has two templates that match the pattern for the state element node, each producing a different result.

Example 10-2. A stylesheet that matches a state twice
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:template match="/">   <county state="{state/@name}">    <xsl:apply-templates select="state"/>   </county>  </xsl:template>      <xsl:template match="state">    <xsl:apply-templates select="county"/>  </xsl:template>      <xsl:template match="state">   <xsl:apply-templates select="county[starts-with(.,'K')]"/>  </xsl:template>  <xsl:template match="county">   <name><xsl:apply-templates/></name>  </xsl:template>     </xsl:stylesheet>

In order of appearance, the first template that matches state elements selects all the county children of state. The second template that matches state selects only the county child whose text content starts with the letter K (using the starts-with( ) function). Both templates invoke the last template, which matches county elements. Process ri.xml with last.xsl like this:

xalan -i 1 ri.xml last.xsl

Because an XSLT processor uses the last template when a match conflict arises, the stylesheet last.xsl produces this output:

<?xml version="1.0" encoding="UTF-8"?> <county state="Rhode Island">  <name>Kent</name> </county>

Only the state node containing Kent is instantiated because that is what the last template matching state instructed the processor to do.

Notice that the encoding for the document is UTF-8 even though the source document has an encoding of US-ASCII. To change the encoding US-ASCII in the output, you'd have to add an encoding attribute with a value of US-ASCII to the output element.


Now in contrast, if you apply last.xsl to ri.xml with Instant Saxon, as follows:

saxon ri.xml last.xsl

you will see the following error report:

Recoverable error Ambiguous rule match for /state[1] Matches both "state" on line 14 of file:/C:/LearningXSLT/examples/ch08/last.xsl and "state" on line 10 of file:/C:/LearningXSLT/examples/ch08/last.xsl <?xml version="1.0" encoding="utf-8"?> <county state="Rhode Island">    <name>Kent</name> </county>

An XSLT processor is not required to report multiple templates matching one template rule, but processors may report such an error and may recover from the error. Either way, the processor must either stop or recover from the error by applying the last template that matches the rule. (See the last paragraph in Section 5.5 of the XSLT specification.)

10.1.1 The priority Attribute

Now compare last.xsl with priority.xsl, which is a very similar stylesheet shown in Example 10-3.

Example 10-3. A stylesheet using priorities
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:template match="/">   <county state="{state/@name}">    <xsl:apply-templates select="state"/>   </county>  </xsl:template>      <xsl:template match="state" priority="2">   <xsl:apply-templates select="county"/>  </xsl:template>      <xsl:template match="state" priority="1">   <xsl:apply-templates select="county[starts-with(.,'K')]"/>  </xsl:template>  <xsl:template match="county">   <name><xsl:apply-templates/></name>  </xsl:template>     </xsl:stylesheet>

The only difference between last.xsl and priority.xsl is that the two templates matching state elements each have priority attributes. The priority attribute can explicitly set which of two or more conflicting templates gets used first. The higher the value of the priority attribute, the higher the priority of the template; in other words, a template with a priority of 2 trumps a template with a priority of 1.

When applied to ri.xml, priority.xsl produces the following output:

<?xml version="1.0" encoding="UTF-8"?> <county state="Rhode Island">  <name>Bristol</name>  <name>Kent</name>  <name>Newport</name>  <name>Providence</name>  <name>Washington</name> </county>

The template with a priority of 2 is invoked (the first template of the two that matches state), but the template with the priority of 1 is not. Trivially, using the priority attribute can allow you to switch templates on and off, which can be useful for testing.

More formally, however, the priority attribute's reason for being is to help distinguish the patterns that match the same node but use different patterns. Example 10-4, same.xsl, shows you an example of this.

Example 10-4. A stylesheet that matches the same node with different patterns
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:template match="/">   <county state="{state/@name}">    <xsl:apply-templates select="state"/>   </county>  </xsl:template>      <xsl:template match="state">   <xsl:apply-templates select="county"/>  </xsl:template>      <xsl:template match="county[starts-with(.,'K')]">   <first-match><xsl:apply-templates/></first-match>  </xsl:template>      <xsl:template match="county[2]">   <last-match><xsl:apply-templates/></last-match>  </xsl:template>      <xsl:template match="county">   <name><xsl:apply-templates/></name>  </xsl:template>     </xsl:stylesheet>

The template rule matching county[starts-with(.,'K')] and the one matching county[2] both match the same node count, but each uses a different pattern (in that each uses a different predicate) to identify the node. This results in a recoverable error.

As with last.xsl, when ri.xml is processed with same.xsl:

xalan -i 1 ri.xml same.xsl

Xalan recovers from the error silently by using the last matching rule:

<?xml version="1.0" encoding="UTF-8"?> <county state="Rhode Island">  <name>Bristol</name>  <last-match>Kent</last-match>  <name>Newport</name>  <name>Providence</name>  <name>Washington</name> </county>

When ri.xml is processed with Instant Saxon, using:

saxon ri.xml same.xsl

it issues a warning about the error before recovering and using the last template rule:

Recoverable error Ambiguous rule match for /state[1]/county[2] Matches both "county[2]" on line 18 of file:/C:/LearningXSLT/examples/ch10/same.xsl and "county[starts-with(.,'K')]" on line 14 of file:/C:/LearningXSLT/examples/ch10/same.xsl <?xml version="1.0" encoding="utf-8"?> <county state="Rhode Island">    <name>Bristol</name>    <last-match>Kent</last-match>    <name>Newport</name>    <name>Providence</name>    <name>Washington</name> </county>

prior.xsl, as shown in Example 10-5, changes the priority of these conflicting rules by using the priority attribute.

Example 10-5. Avoiding a conflict with the priority attribute
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/>      <xsl:template match="/">   <county state="{state/@name}">    <xsl:apply-templates select="state"/>   </county>  </xsl:template>      <xsl:template match="state">   <xsl:apply-templates select="county"/>  </xsl:template>      <xsl:template match="county[starts-with(.,'K')]" priority="2">   <first-match><xsl:apply-templates/></first-match>  </xsl:template>      <xsl:template match="county[2]" priority="1">   <last-match><xsl:apply-templates/></last-match>  </xsl:template>      <xsl:template match="county">   <name><xsl:apply-templates/></name>  </xsl:template>     </xsl:stylesheet>

Process it with:

saxon ri.xml prior.xsl

and the error is avoided:

<?xml version="1.0" encoding="utf-8"?> <county state="Rhode Island">    <name>Bristol</name>    <first-match>Kent</first-match>    <name>Newport</name>    <name>Providence</name>    <name>Washington</name> </county>

The template matching county[starts-with(.,'K')] is instantiated because it has a priority of 2 while the one matching county[2] is not because it has a lower priority value (1).

Another feature that affects template priority is import precedence. Using the top-level XSLT element import, you can import other stylesheets into a given stylesheet. Import precedence is determined by the order in which stylesheets are imported, which has an influence over template priority. This topic is explored in Chapter 13.

The XSLT specification spells out the default priorities what template has priority over another by default with no priority attribute in Section 5.5. In general, the more specific the pattern, the higher its priority. For example, county[1] has a higher default priority than county because it is more specific.




Learning XSLT
Learning XSLT
ISBN: 0596003277
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net