Match Patterns (Locating Elements)

Match Patterns (Locating Elements)
One critical capability of a stylesheet language is to locate source elements to be styled. CSS, for example, does this with “selectors.” FOSIs do it with “e-i-c’s”, elements in context. XSLT does it with “match patterns” defined by the XML Path Language (XPath).
XPath has an extensible string-based syntax. It describes “location paths” between parts of a document or documents One inspiration for XPath was the common “path/file” file system syntax
Two things to remember about XPath expressions:
Pattern matching occurs in a context; XPath expressions and XSLT elements can change the current context and consequently the nodes which match XPath is inclusive or greedy, it addresses all matching elements. You must use predicates to refine the set of nodes selected.
Everything you’ve read so far has been fairly straightforward, except for one thing: match patterns, which have been a little mysterious. We’ve used various match patterns, such as “/PLANETS” in <xsl:template> elements, without offering a lot of systematic explanation on how these patterns really work, as in this case:
<xsl:template match=”/PLANETS”>
<HTML>
<HEAD>
<TITLE> The Planets Table </TITLE>
</HEAD>
<BODY>
.
.
.
</BODY>
</HTML>
</xsl:template>
You can also use match patterns in the select attribute of elements such as <xsl:apply-templates>, <xsl:value-of>, <xsl:for-each>, and <xsl:copy-of>. Here’s one important thing you need to know, however:
The select attribute of these elements is more powerful than the match, count, and from attributes of <xsl:template>, <xsl:key>, and <xsl:number>, because you can use full XPath expressions, not just match patterns in select.
The xsl:template element is used to define a template that can be applied to a node to produce a desired output display.
There must be either a match or name attribute, or both, and this determines how the template rule can be invoked. If there is only a match attribute, then you can use the xsl:apply-template element to invoke the template rule defined by the xsl:template element.
If there is only a name attribute, then you can use the xsl:call-template element to invoke the named template defined by the xsl:template element. If both attributes are present, then you may invoke the template by either procedure.
It is quite possible that more than one template can be applied to a node. The highest priority value template is always chosen. If more than one suitable template has the same highest priority value, then the XSLT processor usually chooses the one that appears last. Different templates can not have both the same name and priority values. This is an error.
The xsl:template element is always a child of either the xsl:stylesheet or xsl:transform elements.
This is not a self-closing element. The separate closing element is mandatory.
match=”pattern”
The match attribute is mandatory unless the element has a name attribute, then this attribute is optional. In other words, there must be either a match or name attribute, or both. This attribute is a pattern that is used to define which nodes will have which template rules applied to them.
mode=”qname”
The optional mode attribute allows the same nodes to be processed more than once. Each time the nodes are processed, they can be displayed in a different manner. A qname is a qualified name that is composed of an optional namespace prefix, a colon, which is only present if there is a prefix, and a mandatory XML name (for example, xsl:zipcode or zipcode).
If an xsl:template element has a mode attribute, then it must also have a match attribute. If the xsl:apply-templates element has a mode attribute, then it can only apply templates from xsl:templates elements that also have a mode attribute. Likewise, if the xsl:apply-templates element does not have a mode attribute, then it can only apply templates from xsl:templates elements that also do not have a mode attribute.
name=”qname”
The name attribute is mandatory unless the element has a match attribute, then this attribute is optional. In other words, there must be either a match or name attribute, or both. A qname is a qualified name that is composed of an optional namespace prefix, a colon which is only present if there is a prefix, and a mandatory XML name (for example, xsl:zipcode or zipcode).
If the name attribute is present, you can use the xsl:call-template element to invoke the template. To do this, the name attribute must be the same for both the xsl:template and the xsl:call-template elements.
Match patterns are a subset of XPath expressions that is, all match patterns are valid XPathexpressions, but not all XPath expressions are match patterns. The only XPath expressions that can be patterns are those that return a node set (even a node set with one node) and that use paths that specify only child or attribute nodes.
Match patterns are defined in the XSLT recommendation itself, whereas XPath expressions are defined in the XPath recommendation (www.w3.org/TR/xpath); however, the two are compatible because all match patterns are also XPath expressions.
Creating Full XPath Expressions
You can use full XPath expressions in XSLT in the following places: in the select attribute of the <xsl:apply-templates>, <xsl:value-of>, <xsl:for-each>, <xsl:param>, <xsl:variable>, <xsl:wit-param>, <xsl:copy-of>, and <xsl:sort> elements; in attribute value templates; in the test attribute of <xsl:if> and <xsl:when> elements; in the value attribute of <xsl:number>; and in the predicates of match patterns.
To make things just a little more confusing, however, it turns out that you actually can use XPathexpressions in a special, optional part (and only this part) of match patterns: the predicate. As you’re going to see here, predicates are XPath expressions that evaluate to either true/false values or numbers, which you enclose in brackets, [ and ].
Microsoft and Nonstandard Match Patterns
Microsoft supports match patterns with its MSXML3® XML processor, but there’s one thing you should know: Microsoft also supports a great deal of non-standard, non-W3C syntax in its match patterns. I’m going to stick to the official, W3C, version in this chapter, and if you happen to read Microsoft documentation on match patterns, keep in mind that much of what you read is Microsoft-only.
Matching the Root Node
As you’ve already seen, you can match the root node with the match pattern “/” like this:
<xsl:template match=”/”>
<HTML>
<xsl:apply-templates/>
</HTML>
</xsl:template>
Matching Elements
You can match elements simply by giving their names, as you’ve also seen. The following template matches <PLANETS> elements:
<xsl:template match=”PLANETS”>
<HTML>
<xsl:apply-templates/>
</HTML>
</xsl:template>
Matching Children
You can use the / step operator to separate element names when you want to refer to a child of a particular node. For example, say that you want to create a rule that applies to only those <NAME> elements that are children of <PLANET> elements. In that case, you can match the expression “PLANET/NAME“. Here’s a rule surrounds the text of such elements in a <H3>
HTML element:
<xsl:template match=”PLANET/NAME”>
<H3>
<xsl:value-of select=”.”/>
</H3>
</xsl:template>
You can also use the * character as a wildcard, standing for any element. (* can match only elements, although note that the pattern @* can match any attribute.) For example, the following rule applies to all <NAME> elements that are grandchildren of <PLANET> elements:
<xsl:template match=”PLANET/*/NAME”>
<H3>
<xsl:value-of select=”.”/>
</H3>
</xsl:template>
Matching Element Descendants
In the preceding section, I used the expression “PLANET/NAME” to match all <NAME> elements that are direct children of <PLANET> elements, and the expression “PLANET/*/NAME” to match all <NAME> elements that are grandchildren of <PLANET> elements.
However, there’s an easier way to perform both matches: just use the expression “PLANET//NAME”,which matches all <NAME> elements that are inside <PLANET> elements, no matter how many levels deep (the matched elements are called descendants of the <PLANET> element). In other words, “PLANET//NAME” matches “PLANET/NAME“, “PLANET/*/NAME”, “PLANET/*/*/NAME“, and so on:
<xsl:template match=”PLANETS//NAME”>
<H3>
<xsl:value-of select=”.”/>
</H3>
</xsl:template>
Matching Attributes
You can match attributes if you preface their names with an @. You’ve already worked with the UNITSattribute that most of the children of <PLANET> elements support:
<PLANET>
<NAME>Earth</NAME>
<MASS UNITS=”(Earth = 1)”>
1
</MASS>
<DAY UNITS=”days”>
1
</DAY>
<RADIUS UNITS=”miles”>
2107
</RADIUS>
<DENSITY UNITS=”(Earth = 1)”>
1
</DENSITY>
<DISTANCE UNITS=”million miles”>
128.4
</DISTANCE>
<!–At perihelion–>
</PLANET>
To recover the units and display them as well as the values for the mass and so on, you can match the UNITS attribute with @UNITS, as follows:
<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/PLANETS">

        <HTML>

            <HEAD>

            .

            .

            .

            </HEAD>

            <BODY>

            .

            .

            .

            </BODY>

        </HTML>

    </xsl:template>

       <xsl:template match="PLANET">

       <TR>

          <TD><xsl:value-of select="NAME"/></TD>

          <TD><xsl:apply-templates select="MASS"/></TD>

          <TD><xsl:apply-templates select="RADIUS"/></TD>

       </TR>

   </xsl:template>

       <xsl:template match="MASS">

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

        <xsl:text> </xsl:text>

        <xsl:value-of select="@UNITS"/>

    </xsl:template>

    <xsl:template match="RADIUS">

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

        <xsl:text> </xsl:text>

        <xsl:value-of select="@UNITS"/>

    </xsl:template>

       <xsl:template match="DAY">

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

        <xsl:text> </xsl:text>

        <xsl:value-of select="@UNITS"/>

    </xsl:template>

   </xsl:stylesheet>

Now the resulting HTML table includes not only values, but also their units of measurement:
<HTML>

    <HEAD>

        <TITLE>

            The Planets Table

        </TITLE>

    </HEAD>

    <BODY>

        <H1>

            The Planets Table

        </H1>

        <TABLE>

            <TR>

             .

             .

             .

            <TR>

                <TD>Mercury</TD>

                <TD>.0553 (Earth = 1)</TD>

                <TD>1516 miles</TD>

            </TR>

               <TR>

                <TD>Venus</TD>

                <TD>.815 (Earth = 1)</TD>

                <TD>3716 miles</TD>

            </TR>

            .

            .

            .

        </TABLE>

    </BODY>

</HTML>

You can also use the @* wildcard to select all attributes of an element. For example, “PLANET/@*”selects all attributes of <PLANET> elements.