Chapter 9. Duplication, Iteration, and Conditional XSLT Elements

CONTENTS

Chapter 9. Duplication, Iteration, and Conditional XSLT Elements

  •  9.1 The <xsl:copy-of> Instruction Element
  •  9.2 The <xsl:copy> Instruction Element
  •  9.3 The <xsl:for-each> Instruction Element
  •  9.4 The <xsl:sort> Element
  •  9.5 The <xsl:if> Instruction Element
  •  9.6 The <xsl:choose> Instruction Element
  •  9.7 The <xsl:number> Instruction Element
  • <xsl:copy-of>

  • <xsl:copy>

  • <xsl:if>

  • <xsl:for-each>

  • <xsl:choose>

  • <xsl:when>

  • <xsl:otherwise>

  • <xsl:sort>

  • <xsl:number>

  • <xsl:fallback>

There are three very important concepts in XSLT, as in any programming language, that encompass the ability to duplicate objects, conditionally process objects, and iterate through a list of objects sequentially. This chapter will discuss these three concepts and their applicable XSLT elements.

The duplication elements in XSLT that provide the ability to copy an entire XML structure, or just a tag name, are <xsl:copy-of> and <xsl:copy>. Conditional elements, those that involve a condition or test to select objects, are <xsl:if>, <xsl:choose>, and <xsl:fallback>. The elements that are in some way iterative are <xsl:for-each>, <xsl:sort>, and <xsl:number>. The <xsl:sort> element, while not necessarily iterative, works on node-sets to reorder them prior to an iteration. The two children of <xsl:choose>, <xsl:when> and <xsl:otherwise>, are also covered in this chapter.

The <xsl:fallback> instruction element, while not technically a conditional element, is used to provide an alternative process when an XSLT instruction fails. It is covered in the last section of this chapter.

9.1 The <xsl:copy-of> Instruction Element

The <xsl:copy-of> instruction element is used to create a copy of a node branch, or subtree, from the input tree. It is an empty element that has one required attribute, select, which contains an expression, as shown in the following element model definition:

<!-- Category: instruction --> <xsl:copy-of   select = expression /> 

When the expression in the select attribute is evaluated, the resulting object is copied to the output result tree. However, what is copied depends on the type of object produced by the expression.

If the expression produces either a node-set or an RTF, <xsl:copy-of> will copy each node in the node-set or RTF, including markup, children elements, and descendents of the node, and place the results in the output result tree. Attributes, namespaces, text, PIs, and comments in the node-set are also copied. The <xsl:copy-of> instruction element effectively "transplants" a node branch to the output result tree. If the select attribute matches a root, or document root node (/), then that root and its children and descendants are copied to the output result tree.

If the expression in the select attribute returns any object other than a node-set or RTF, then that object is converted to a string and copied to the output.

It is important to know that <xsl:copy-of> does not allow you to perform operations on the object returned by the expression before sending it to the output. It is, effectively, a "cut-and-paste" of the matched node. For example, it is not possible to selectively exclude some of the nodes, or to include new elements with LREs. For this reason the <xsl:copy-of> element is empty.

The stylesheet shown in Example 9-1 shows the use of <xsl:copy-of> with a pattern to select the <thoroughfare> elements and copy them into a new element-type name. Because the pattern returns a node-set, the entire <thoroughfare> node is copied for each match.

Example 9-1 Using <xsl:copy-of> to copy a node branch.
INPUT: <?xml version="1.0"?> <main>       <parkway>             <thoroughfare>Governor Drive</thoroughfare>             <thoroughfare name="Whitesburg Drive">                   <sidestreet>Bob Wallace Avenue</sidestreet>                   <block>1st Street</block>                   <block>2nd Street</block>                   <block>3rd Street</block>                   <sidestreet>Woodridge Street</sidestreet>             </thoroughfare>             <thoroughfare name="Bankhead">                   <sidestreet>Tollgate Road</sidestreet>                   <block>First Street</block>                   <block>Second Street</block>                   <block>Third Street</block>                   <sidestreet>Oak Drive</sidestreet>             </thoroughfare>       </parkway>       <boulevard>             <block>Panorama Street</block>             <block>Highland Plaza</block>             <block>Hutchens Avenue</block>             <block>Wildwood Drive</block>             <block>Old Chimney Road</block>             <block>Carrol Circle</block>       </boulevard> </main> STYLESHEET: <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                 version="1.0"> <xsl:template match="boulevard"/> <xsl:template match="parkway">       <new-parkway> <xsl:copy-of select="thoroughfare"/>       </new-parkway> </xsl:template> </xsl:stylesheet> RESULT: <?xml version="1.0" encoding="utf-8"?> <new-parkway> <thoroughfare>Governor Drive</thoroughfare> <thoroughfare name="Whitesburg Drive"> <sidestreet>Bob Wallace Avenue</sidestreet> <block>1st Street</block> <block>2nd Street</block> <block>3rd Street</block> <sidestreet>Woodridge Street</sidestreet> </thoroughfare> <thoroughfare name="Bankhead"> <sidestreet>Tollgate Road</sidestreet> <block>First Street</block> <block>Second Street</block> <block>Third Street</block> <sidestreet>Oak Drive</sidestreet> </thoroughfare> </new-parkway> 

This example shows two template rules: <xsl:template match="boulevard"/> to match the <boulevard> element and suppress its contents, and <xsl:template match="parkway"> to match the <parkway> element and replace it with the <new-parkway> element. The template rule that matches on <parkway> contains an LRE for the <new-parkway> and uses the <xsl:copy-of select="thoroughfare"/> to send each <thoroughfare> element to the output result tree.

9.2 The <xsl:copy> Instruction Element

The <xsl:copy> element creates a copy of the current node's markup. If the current node is an element, the element's tags, including the element-type name and its associated namespace, are copied. However, <xsl:copy> does not copy the attribute or children nodes of the element. The optional attribute, use-attribute-sets, can be used to include a set of attributes that was defined using the <xsl:attribute-set> element (see Section 6.6.1 for more information on the <xsl:attribute-set> element).The <xsl:copy> element is an instruction element that contains a template, as shown in the following element model definition:

<!-- Category: instruction --> <xsl:copy   use-attribute-sets = qnames>   <!-- Content: template --> </xsl:copy> 

Working with <xsl:copy> is distinctively different from working with <xsl:copy-of> in two primary ways. On the one hand, the <xsl:copy> instruction element can contain children instruction elements, where the <xsl:copy-of> element cannot. Far more significantly, <xsl:copy-of> copies an entire node branch or RTF, where <xsl:copy> only copies the current node's markup. The children of the matched node are ignored, as are any attribute nodes. The <xsl:copy> effectively erases all the matched element's attributes in the output.

Using <xsl:copy> without any content in the template simply copies the element's tags from the input to the output. Because the <xsl:copy> element ignores children of the current node, this output will be an empty element, as shown in Example 9-2.

Example 9-2 Simple demonstration of <xsl:copy>.
<xsl:template match="block">       <xsl:copy></xsl:copy> </xsl:template> 

This produces <block/>, an empty block element, for each block matched in the input. However, because <xsl:copy> contains a template, it is possible to use additional instruction elements and LREs to process and/or modify the content of the current node when it is copied. As shown in Example 9-3, adding content to the template causes the instantiation of the template to be applied to the current node within the context of the copied tag.

Example 9-3 Using <xsl:copy> to add content to the current node.
<xsl:template match="block">       <xsl:copy>             <name>             <xsl:apply-templates/>             </name>       </xsl:copy> </xsl:template> 

In this example, the <xsl:copy> element sends the <block> and </block> tags to the output result tree for each <block> element. The <name> LRE appears in the output exactly as-is, and the children of the original input <block> element are sent through using the <xsl:apply-templates> element. The resulting XML structure, using the first <block>, is as follows:

<block><name>1st Street</name></block> 

When <xsl:copy> is used to copy a namespaced node, the local name of the element is copied to the output as well as the prefix for that namespace.

If the element that was used to declare the namespace is not copied to the output with a template rule, the first element that uses the prefix for the namespace will be used to declare the namespace in the output result tree. The processor automatically handles the namespace declaration if it is not copied with a template rule.

Using <xsl:copy> and <xsl:copy-of> together, it is possible to create combinations of otherwise fairly unaltered node-sets from the XML data instance. As Example 9-4 shows, our obsessive city planners in the municipal division of Markup City are rearranging the streets, this time to have all <block>s in just one area, that of the <boulevard>.

Example 9-4 Using <xsl:copy> together with <xsl:copy-of>.
STYLESHEET: <?xml version="1.0"> <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="boulevard">       <xsl:copy>             <xsl:copy-of select="//block" />       </xsl:copy> </xsl:template> <xsl:template match="parkway"/> </xsl:stylesheet> RESULT: <boulevard> <block>1st Street</block> <block>2nd Street</block> <block>3rd Street</block> <block>First Street</block> <block>Second Street</block> <block>Third Street</block> <block>Panorama Street</block> <block>Highland Plaza</block> <block>Hutchens Avenue</block> <block>Wildwood Drive</block> <block>Old Chimney Road</block> <block>Carrol Circle</block> </boulevard> 

The template rule matching on the <boulevard> element contains an <xsl:copy> element used to copy the <boulevard> tags into the output result tree. We then take each <block> element from the input source document, regardless of its original location, and copy it with the <xsl:copy-of select="//block"/> instruction element. Finally, to remove the contents of the old <parkway>, we add an empty template rule, <xsl:template match="parkway"/>.

While it may not seem very useful to copy just the element's tags to the output, <xsl:copy> becomes very useful when the name of the current node is not known. As shown in Example 9-5, using <xsl:copy> inside a template that employs the wildcard * to match all the elements, each element's tags, will be copied into the output.

Example 9-5 Using <xsl:copy> to copy all elements.
<xsl:template match="*">       <xsl:copy>             <xsl:apply-templates />       </xsl:copy> </xsl:template> 

This template will effectively copy the structure of the document, removing all the attributes. New attributes can then be added using <xsl:attribute> or the use-attribute-sets attribute. You can also copy the original attributes from an element using a combination of <xsl:for-each> (discussed in Section 9.3) and <xsl:copy>, as shown in Example 9-6.

Example 9-6 Using <xsl:copy> and <xsl:for-each> to copy attributes.
<xsl:template match="*">       <xsl:copy>             <xsl:for-each select="@*">                   <xsl:copy/>             </xsl:for-each>             <xsl:apply-templates />       </xsl:copy> </xsl:template> 

The <xsl:for-each> element selects all attributes, using the "@*" abbreviation, and the <xsl:copy> element inside the <xsl:for-each> copies the attribute name and its original value into the output result tree. The content of each element is processed with <xsl:apply-templates>. Note that the text nodes are still sent to the output because of built-in template rules, discussed in Chapter 3. To suppress text output, it would be necessary to add an empty template rule, <xsl:template match="text()"/>.

9.3 The <xsl:for-each> Instruction Element

The <xsl:for-each> element provides the ability to recursively process a list of nodes. It is an instruction element that can contain zero or more <xsl:sort> elements (see Section 9.4), followed by a template, as shown in the following element model definition.

<!-- Category: instruction --> <xsl:for-each   select = node-set-expression>   <!-- Content: (xsl:sort*, template) --> </xsl:for-each> 

The value for the required select attribute is a special kind of expression, a node-set-expression, which must always result in a node-set. It is an error to use an expression that returns any other kind of object. The instructions contained in the template will be applied to each node matched by the select attribute. The matched nodes are processed in document order, unless a revised order has been selected using <xsl:sort>.

Using Markup City, the stylesheet shown in Example 9-7 allows us to use <xsl:for-each> to process each <block> and use the name of its parent as an attribute named branch on each new <block> element in the output result tree.

Notice that the instruction elements in <xsl:for-each> apply to the current node as selected by the <xsl:for-each> element's select attribute, not the current node matched by the <xsl:template> element. The <xsl:for-each> element is one of the few instruction elements that can change the context of the current node list. It effectively suspends the processing of the node-set matched with <xsl:template>'s match attribute while it processes the new node-set.

If any <xsl:sort> elements are used in an <xsl:for-each>, they must come before any other elements or LREs in the template. The <xsl:sort> element sorts the nodes matched by the select attribute on <xsl:for-each> prior to instantiating the template for each node. This means that the nodes in the node-set are processed in the new sorted order, which may affect the output.

Example 9-7 Using <xsl:for-each> to add attributes.
STYLESHEET: <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="text()"/> <xsl:output indent="yes"/> <xsl:template match="boulevard"> <xsl:copy>       <xsl:for-each select="//block">             <xsl:copy>             <xsl:attribute name="branch">                   <xsl:value-of select="name(..)" />             </xsl:attribute>             <xsl:value-of select="text()"/>             </xsl:copy>       </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet> RESULT: <boulevard> <block branch="thoroughfare">1st Street</block> <block branch="thoroughfare">2nd Street</block> <block branch="thoroughfare">3rd Street</block> <block branch="thoroughfare">First Street</block> <block branch="thoroughfare">Second Street</block> <block branch="thoroughfare">Third Street</block> <block branch="boulevard">Panorama Street</block> <block branch="boulevard">Highland Plaza</block> <block branch="boulevard">Hutchens Avenue</block> <block branch="boulevard">Wildwood Drive</block> <block branch="boulevard">Old Chimney Road</block> <block branch="boulevard">Carrol Circle</block> </boulevard> 

9.4 The <xsl:sort> Element

The <xsl:sort> element is used to reorder each node in the current node-set prior to processing the node list further with instruction elements. The current node-set for <xsl:sort> is the list of nodes returned from the select attribute of either <xsl:apply-templates> or <xsl:for-each>. Technically, <xsl:sort> is not classified as an instruction element because it is only allowed within two specific elements: <xsl:apply-templates> and <xsl:for-each>. The <xsl:sort> element is empty and has five optional attributes, as shown in the following element model definition. The main attribute is select, which contains an expression used as the key for sorting the node list.

<xsl:sort   select = string-expression   lang = { nmtoken }   data-type = { "text" | "number" | qname-but-not- ncname }   order = { "ascending" | "descending" }   case-order = { "upper-first" | "lower-first" } /> 

If there is a value for the select attribute, each node in the current node-set is evaluated based on the expression in the select attribute and the result is converted to a string. This string is then used as the sort key. A sort key is the value used by the processor to perform the sort. The default value for the select attribute is ., which means that when the select attribute is not used, the sorting key is the content of the current node.

Example 9-8 Using <xsl:sort> to reorder elements.
STYLESHEET: <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"             version="1.0"> <xsl:template match="text()" /> <xsl:template match="boulevard"> <xsl:copy>       <xsl:for-each select="//block">       <xsl:sort/>             <xsl:copy>             <xsl:attribute name="branch">                   <xsl:value-of select="name(..)" />             </xsl:attribute>                   <xsl:value-of select="text()"/>             </xsl:copy>       </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet> RESULT: <?xml version="1.0" encoding="utf-8"?> <boulevard> <block branch="thoroughfare">1st Street</block> <block branch="thoroughfare">2nd Street</block> <block branch="thoroughfare">3rd Street</block> <block branch="boulevard">Carrol Circle</block> <block branch="thoroughfare">First Street</block> <block branch="boulevard">Highland Plaza</block> <block branch="boulevard">Hutchens Avenue</block> <block branch="boulevard">Old Chimney Road</block> <block branch="boulevard">Panorama Street</block> <block branch="thoroughfare">Second Street</block> <block branch="thoroughfare">Third Street</block> <block branch="boulevard">Wildwood Drive</block> </boulevard> 

In Example 9-8, we use our previous stylesheet for creating a new boulevard to reorder all the <block>s in Markup City, using the default values of the five <xsl:sort> attributes.

The sort works on the nodes that are selected by the <xsl:for-each> element, without changing the result of the instantiation of the rest of the instructions that follow it. The result is basically the same as in Example 9-7, except that now the <block>s are in alphabetical order based on the text they contain. Numbered streets come first, in ascending order, followed by the alphabetized streets.

When used within the <xsl:for-each> element, <xsl:sort> must come before any other instruction element, and the instructions are then applied to the nodes in sorted order. When used within <xsl:apply-templates>, <xsl:sort> can come before or after any <xsl:with-param> elements, which are the only other valid children of <xsl:apply-templates>.

Multiple <xsl:sort> elements are allowed in both <xsl:for-each> and <xsl:apply-templates>, and each <xsl:sort> element is used as an additional sort key; the first <xsl:sort> is used to specify the primary sort key, the second <xsl:sort> is used to specify the secondary sort key, and so on.

The four other attributes on <xsl:sort> are: lang, data-type, order, and case-order. Each of the attributes for <xsl:sort>, including select, are discussed in the following sections.

9.4.1 The select Attribute of <xsl:sort>

The select attribute contains an expression that returns a string. Each node in the current node-set is evaluated based on the expression in the select attribute, and the value is converted into a string, which is then used as the sort key for the sort.

Sort keys are often used to decide which child of the current node to sort on. If the node-set selected by the <xsl:apply-templates> or <xsl:for-each> elements returns a set of nodes that contains children, those children or descendants can be specified as sort keys. If there is no select attribute specified, the text of the current node is used as the default sort key. Note that using a select attribute to select children on a node-set that contains no children nodes may cause unpredictable results, if any. The select attribute can also be used to identify other objects to be used as a sort key, such as a parent or ancestor node, or an attribute value from the input element.

Using the same example from the previous section, we could modify our <xsl:sort> element to add a select attribute as follows:

<xsl:sort select="name(..)"/> 

Using the name(..) value for the select attribute sorts the <block> elements in order based on the parent of the <block>, as shown in Example 9-9.

Example 9-9 Result of using the <xsl:sort> select attribute.
<?xml version="1.0" encoding="utf-8"?> <boulevard> <block branch="boulevard">Panorama Street</block> <block branch="boulevard">Highland Plaza</block> <block branch="boulevard">Hutchens Avenue</block> <block branch="boulevard">Wildwood Drive</block> <block branch="boulevard">Old Chimney Road</block> <block branch="boulevard">Carrol Circle</block> <block branch="thoroughfare">1st Street</block> <block branch="thoroughfare">2nd Street</block> <block branch="thoroughfare">3rd Street</block> <block branch="thoroughfare">First Street</block> <block branch="thoroughfare">Second Street</block> <block branch="thoroughfare">Third Street</block> </boulevard> 

Notice in this example that although the <block>s are now sorted according to either boulevard or thoroughfare, they are still in document order within that sorting. In other words, each <block> is in the original order it was in from the input document, within the context of its parent element. We can do an additional sort using another <xsl:sort> element to specify the secondary sort key for the text of the <block>s, as shown in Example 9-10.

The additional <xsl:sort> in this example is using the default values to select the content of each <block> as the secondary sort key. The result is to sort the <block> elements by their text node within the original sort based on the parent of each <block>. In this case, the same effect can be accomplished using <xsl:sort/>, <xsl:sort select="."/>, or <xsl:sort select="text()"/>. The first two values of select apply to the content of the currently selected element, and in our example, each selected element is a <block> that only contains text, allowing the third value of select to also apply.

Example 9-10 Using multiple <xsl:sort> elements.
MODIFIED TEMPLATE RULE: <xsl:template match="boulevard"> <xsl:copy>       <xsl:for-each select="//block">       <xsl:sort select="name(..)"/>       <xsl:sort/>             <xsl:copy>             <xsl:attribute name="branch">                   <xsl:value-of select="name(..)" />             </xsl:attribute>                   <xsl:value-of select="text()"/>             </xsl:copy>       </xsl:for-each> </xsl:copy> </xsl:template> RESULT: <?xml version="1.0" encoding="utf-8"?> <boulevard> <block branch="boulevard">Carrol Circle</block> <block branch="boulevard">Highland Plaza</block> <block branch="boulevard">Hutchens Avenue</block> <block branch="boulevard">Old Chimney Road</block> <block branch="boulevard">Panorama Street</block> <block branch="boulevard">Wildwood Drive</block> <block branch="thoroughfare">1st Street</block> <block branch="thoroughfare">2nd Street</block> <block branch="thoroughfare">3rd Street</block> <block branch="thoroughfare">First Street</block> <block branch="thoroughfare">Second Street</block> <block branch="thoroughfare">Third Street</block> </boulevard> 

9.4.2 The data-type Attribute of <xsl:sort>

The data-type attribute determines what kind of data the sort process uses: text, numeric, or some other unspecified type. The options for the value of data-type are: text, number, or a namespace-prefixed QName. A text value is the default that is assumed if no data-type is explicitly declared.

Text sorting converts the value of the node being sorted to a string prior to sorting, and uses sorting algorithms to sort lexicographically according to the language being used, defined in the lang attribute (see Section 9.4.5).

Numerical sorting converts the value of the node being sorted to a number prior to sorting, and then sorts the nodes numerically according to the value returned by that conversion. The lang attribute is ignored when the data-type is number.

A QName as the value of the data-type attribute may be used by some extension functions to define some other type of sorting criteria. In the case of a prefixed QName, the W3C specification for XSLT, section 10, states, " the behavior in this case is not specified by this document." The namespace for this value will determine the functionality of the sorting.

Example 9-11 shows the different results of sorting the numbers 1, 01, 001, 2, 02, and 002 using either text or number as the value of the data-type attribute.

Notice that the sorting order using number recognizes each value of 1 as a 1, ignoring the preceding zeros. As far as the processor is concerned, the values, 1, 01, and 001 all have the same value, so it does not sort them further than placing them together. Their original sequence in the input is maintained. The sorting order using text, however, recognizes the leading zeros as part of the string and sorts them accordingly. In text sorting, leading zeros are sorted before other numbers, so 002 comes before 01.

The values of text or number for the most part behave as you would expect. However, there is an element of "conversion" that occurs that can at first be surprising when there is mixed content of numbers and text in the sort key.

9.4.2.1 Sorting Numbers as Text

Using <xsl:sort> to sort numbers with a data-type of text converts the value of the number being sorted to a string prior to sorting. For example, sorting numbers using the text data-type, the number 13 will come before the number 6. This is because, as a text string, the value 1 as the first digit of 13 comes before 6. By contrast, if the same pair of values was sorted with a specified data-type of number, the 6 would precede the 13, as you would normally expect with numerical sequences.

Example 9-11 Results of sorting using number vs. using text.
INPUT: <?xml version="1.0"?> <list>       <num>002</num>       <num>01</num>       <num>1</num>       <num>2</num>       <num>001</num>       <num>02</num> </list> STYLESHEET: <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="/"> <xsl:for-each select="//num">       <xsl:sort data-type="number"/>       <xsl:value-of select="."/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:template> </xsl:stylesheet> TEXT SORT RESULT:       001       002       01       02       1       2 NUMBER SORT RESULT:      01      1      001      002      2      02 
9.4.2.2 Sorting Text as Numbers

When number is set for the data-type and the nodes being sorted contain a mixture of text and numbers, or just text, sorting is essentially ignored. This is because the use of number as the value for the data-type attribute causes the processor to attempt to convert the data to a number. If the data contains text, the result of the conversion will be the value "NaN" and the data will be sorted accordingly. This means that, if we tried to sort our <block> elements using number as the value of data-type, the result would be the same order as the inputs, because in all cases, the resulting value of the sort key for each <block> would be NaN.

9.4.3 The order Attribute of <xsl:sort>

The order attribute is used to specify whether the sort (alphabetic or numerical) is ascending or descending. These are the only two values allowed for the order attribute. The default order is ascending, so the order attribute only needs to be specified if descending order is needed. This would then reverse the order of the results. For example, using Markup City as input, the <block>s in our new <boulevard> would end up in reverse order, as shown in Example 9-12.

9.4.4 The case-order Attribute of <xsl:sort>

The case-order attribute provides the means to specify a case-specific sort, sorting either uppercase at the top, using the upper-first value, or lowercase at the top, using the lower-first value.

In Example 9-13, we have created duplicate <block>s, except for changing the case of every duplicated block name (panorama and Panorama, highland and Highland, etc.).

The default value of case-order is language-dependent, so that for English, for example, it would be upper-first. Language is specified using the lang attribute, discussed in the next section.

Example 9-12 Using the order attribute with the descending value.
STYLESHEET: <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"             version="1.0"> <xsl:output indent="yes"/> <xsl:template match="text()" /> <xsl:template match="boulevard">       <xsl:copy>             <xsl:for-each select="//block">                   <xsl:sort order="descending"/>                   <xsl:copy>                         <xsl:value-of select="text()"/>                   </xsl:copy>             </xsl:for-each>       </xsl:copy> </xsl:template> </xsl:stylesheet> RESULT: <?xml version="1.0" encoding="utf-8"?> <boulevard> <block>Wildwood Drive</block> <block>Third Street</block> <block>Second Street</block> <block>Panorama Street</block> <block>Old Chimney Road</block> <block>Hutchens Avenue</block> <block>Highland Plaza</block> <block>First Street</block> <block>Carrol Circle</block> <block>3rd Street</block> <block>2nd Street</block> <block>1st Street</block> </boulevard> 
Example 9-13 Using <xsl:sort> with the case-order attribute.
INPUT: <boulevard>       <block>panorama Street</block>       <block>Panorama Street</block>       <block>highland Plaza</block>       <block>Highland Plaza</block>       <block>hutchens Avenue</block>       <block>Hutchens Avenue</block>       <block>wildwood Drive</block>       <block>Wildwood Drive</block>       <block>old Chimney Road</block>       <block>Old Chimney Road</block>       <block>carrol Circle</block>       <block>Carrol Circle</block> </boulevard> STYLESHEET: <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"             version="1.0"> <xsl:output indent="yes"/> <xsl:template match="text()" /> <xsl:template match="boulevard">       <xsl:copy>             <xsl:for-each select="//block">                   <xsl:sort case-order="upper-first"/>                   <xsl:copy>                         <xsl:value-of select="text()"/>                   </xsl:copy>             </xsl:for-each>       </xsl:copy> </xsl:template> </xsl:stylesheet> RESULT: <?xml version="1.0" encoding="utf-8"?> <boulevard> <block>Carrol Circle</block> <block>carrol Circle</block> <block>Highland Plaza</block> <block>highland Plaza</block> <block>Hutchens Avenue</block> <block>hutchens Avenue</block> <block>Old Chimney Road</block> <block>old Chimney Road</block> <block>Panorama Street</block> <block>panorama Street</block> <block>Wildwood Drive</block> <block>wildwood Drive</block> </boulevard> 

When case-order is declared as lower-first, the reverse would apply, as follows:

<?xml version="1.0" encoding="utf-8"?> <boulevard> <block>carrol Circle</block> <block>Carrol Circle</block> <block>highland Plaza</block> <block>Highland Plaza</block> <block>hutchens Avenue</block> <block>Hutchens Avenue</block> <block>old Chimney Road</block> <block>Old Chimney Road</block> <block>panorama Street</block> <block>Panorama Street</block> <block>wildwood Drive</block> <block>Wildwood Drive</block> </boulevard> 

9.4.5 The lang Attribute of <xsl:sort>

The lang attribute is used to specify the language that is being used for sorting. It accepts a two-character language value, such as "en" for English, from the ISO 639 specification, or a subcode, such as "en-US," for specific countries defined by the ISO 3166 specification. The XML recommendation[1] suggests using lowercase prefixes, followed by uppercase suffixes, even though the value of the lang attribute is not case-sensitive.

Using the lang attribute enables specific nuances of alphabetic order with languages other than English to be explicitly accommodated in <xsl:sort>. If there is no language specified, the default value for the lang attribute is based on the language that is specified by the system environment or regional settings.

9.5 The <xsl:if> Instruction Element

The <xsl:if> element performs a simple Boolean test, evaluating an expression in the required test attribute and returning either true or false. The content of <xsl:if>, which is a template, is instantiated if the result of evaluating the expression returns true; otherwise, it is ignored. The special kind of expression (boolean-expression) designated as the value of the test attribute can only produce a Boolean result. In other words, the expression will be evaluated and converted to a Boolean by the XSLT processor. The <xsl:if> element is an instruction element, as shown in the following element model definition:

<!-- Category: instruction --> <xsl:if   test = boolean-expression>   <!-- Content: template --> </xsl:if> 

The most simple use of <xsl:if> is a test for existence. Using a pattern in the expression will test to see if the element returned by the pattern actually exists, and return true if it does. The content of the template in <xsl:if> will then be instantiated. In Example 9-14, the <xsl:if> element's test attribute is checking to see if the <boulevard> exists somewhere in the document.

Since the <boulevard> does exist, the result of the test is true and the template is instantiated. Additionally, pattern expressions can be used to check for the existence of children elements, text, or attribute nodes.

<xsl:if test="//boulevard/block"> 
Example 9-14 Simple test for existence with <xsl:if>.
<xsl:template match="/">       <xsl:if test="//boulevard">             <xsl:text>Found a Boulevard!</xsl:text>       </xsl:if> </xsl:template> 

In this example, the <xsl:if> element is checking to see if there are any <block> elements in the <boulevard>. The result of this expression using Markup City is true.

<xsl:if test="//boulevard/text()"> 

In this example, the <xsl:if> element's test attribute is checking to see if the <boulevard> contains text() elements. Note that this may be true or false depending on if the <boulevard> contains whitespace. If it contains any whitespace that is not stripped, the result will be true, because whitespace is considered text.

<xsl:if test="//boulevard/@*"> 

In this example, the <xsl:if> element's test attribute is checking to see if the <boulevard> contains any attribute nodes. The result of this expression using Markup City is false because <boulevard> does not have any attributes.

For programmers who use if constructs in other programming languages, it may be helpful to know that there are no elements (or instructions) in XSLT corresponding to then, else, or else-if statements in conjunction with <xsl:if>. The <xsl:choose> element (see Section 9.6) provides functionality similar to an if-then-else programming construct.

However, the <xsl:if> element can contain other <xsl:if> elements to provide the necessary functionality for then, else, or else-if structures. The power of nesting elements in XML can be leveraged in this case to use many layers of <xsl:if> elements. For example, if we wanted to change the tags of a <thoroughfare> in our full Markup City based on whether the element has an attribute, the value of the attribute, and the content of the element, we could use nested <xsl:if> elements, as shown in Example 9-15.

Walking through the process, the first <thoroughfare> does not have an attribute called name, so the <xsl:if> template is skipped for that element and no content is sent to the output.

Example 9-15 Using nested <xsl:if> elements.
STYLESHEET: <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"             version="1.0"> <xsl:template match="text()"/> <xsl:template match="thoroughfare">       <!-- check to see if there's a name attribute -->       <xsl:if test="@name">             <xsl:copy>             <name>             <xsl:value-of select="@name"/>             </name>             <!-- check to see if the attribute has a specific value -->             <xsl:if test="@name[contains(., 'Drive')]">                   <drive>             <!-- check to see if it has a block child -->             <xsl:if test="block">                   <xsl:copy-of select="block"/>             </xsl:if>             </drive>          </xsl:if>          </xsl:copy>       </xsl:if> </xsl:template> </xsl:stylesheet> RESULT: <?xml version="1.0" encoding="utf-8"?> <thoroughfare> <name>Whitesburg Drive</name> <drive> <block>1st Street</block> <block>2nd Street</block> <block>3rd Street</block> </drive> </thoroughfare> <thoroughfare> <name>Bankhead</name> </thoroughfare> 

The second <thoroughfare> does have a name attribute, so the processor instantiates the template. It uses the second <xsl:if> rule in the template to check the content of the name attribute for the text Drive. Since the value of the name attribute contains Drive, a new tag called <drive> is created. The third <xsl:if> element checks to see if one of the children of the <thoroughfare> is a <block>, and if so, all the <block> elements are copied to the output using the <xsl:copy> element.

The third <thoroughfare> has a name attribute, so it is copied to the output with the new <name> element. Since the name attribute does not have the text Drive in it, the rest of the instructions are skipped.

9.6 The <xsl:choose> Instruction Element

The <xsl:choose> instruction element allows several conditions to be tested for the selective processing of nodes. Using two child elements, <xsl:when> and <xsl:otherwise>, it provides alternatives to be used for testing and one default if no condition is met. Each test provides a new template for instantiation when the condition for that test is met. The <xsl:choose> element must contain one or more <xsl:when> elements, followed by one optional <xsl:otherwise> element. The <xsl:choose> element does not have any attributes, as shown in the following element model definition:

<!-- Category: instruction --> <xsl:choose>   <!-- Content: (xsl:when+, xsl:otherwise?) --> </xsl:choose> 

The <xsl:choose> instruction element offers a significant advantage over <xsl:if>, because <xsl:if> only provides a singular instance of a test resulting in either true or false. On the other hand, <xsl:choose> allows for multiple mutually exclusive conditions, using <xsl:when>, plus a default template, using <xsl:otherwise>, to be instantiated when none of the <xsl:when> elements are chosen. If no <xsl:otherwise> is supplied and no <xsl:when> elements return a value of true, then the template rule is not instantiated.

Note

As a simple reference to programming language structures, <xsl:choose> can be compared with a "case" statement, which provides a test, several possible results, and a default process.

Each <xsl:when> in an <xsl:choose> element is checked until a match is found, or until the default <xsl:otherwise> is reached. Once an <xsl:when> element's test returns true, then no further <xsl:when> elements will be evaluated.

9.6.1 The <xsl:when> Conditional Element

The <xsl:when> element is used within an <xsl:choose> element to stipulate a condition that, when evaluated to a value of true, instantiates the instructions contained in the template. The <xsl:when> element has one required attribute, which is a test attribute, as shown in the following element model definition:

<xsl:when   test = boolean-expression>   <!-- Content: template --> </xsl:when> 

The test attribute returns a Boolean true or false, which then determines if the template contained within <xsl:when> will be instantiated. This element must come as the first child to <xsl:choose> (it is only allowed as a child of <xsl:choose>), and there must be at least one <xsl:when> in an <xsl:choose> instruction element or else the stylesheet will be invalid.

9.6.2 The <xsl:otherwise> Contingency Condition

The <xsl:otherwise> provides a default, or contingency, process in the case that any <xsl:when> elements within an <xsl:choose> element fail to return a value of true. The <xsl:otherwise> element accepts no attributes, and its content is a template, as shown in the following element model definition:

<xsl:otherwise>   <!-- Content: template --> </xsl:otherwise> 

The <xsl:otherwise> element can only appear as a child of <xsl:choose>, and it must be the last child in the <xsl:choose>, preceded by at least one <xsl:when> element. The <xsl:otherwise> provides as its name indicates a set of instructions for what to do "otherwise," when all the <xsl:when> elements have been tested and a true value is not found. It contains a set of instructions that are instantiated if all the <xsl:when> elements preceding it in the <xsl:choose> return Boolean false.

9.6.3 Using <xsl:when> and <xsl:otherwise> with <xsl:choose>

The strength of <xsl:choose> is the ability to add as many <xsl:when> elements as are required to do a proper test. Where <xsl:if> only evaluates one condition to return true or false, <xsl:choose> gives the process additional tests if the first one doesn't match, recursively through the rest of the possible matches. If the same functionality was to be accomplished with <xsl:if>, it would require multiple nested <xsl:if>s for each choice, and additional nested <xsl:if>s with the opposite test to process the options when the <xsl:if>s didn't match. For example, in our first use of <xsl:if>, we had the following rule:

<xsl:if test="block"> 

To catch any elements that did not match that test, we would need to add another opposite <xsl:if> element, using the not() function as follows:

<xsl:if test="not(block)"> 

The same functionality in these two instructions can be accomplished with one <xsl:choose>, as shown in Example 9-16.

Example 9-16 Demonstration of the efficiency of <xsl:choose>.
<xsl:choose>       <xsl:when test="block">             <xsl:text>Found a block.</xsl:text>       </xsl:when>       <xsl:otherwise>             <xsl:text>Something other than a block was found.             </xsl:text>       </xsl:otherwise> </xsl:choose> 

The best part is that you can keep adding tests that are not necessarily opposites, as in Example 9-17.

In this example we've added <xsl:when> elements to "catch" occurrences of sidestreet and boulevard elements, and our <xsl:otherwise> lets us know when we've found something else, this time using the name() function to pull the value of the name of the element. Now we can add instruction elements or LREs as required to create meaningful output for each choice.

Example 9-17 Adding additional <xsl:when> tests to <xsl:choose>.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"             version="1.0"> <xsl:template match="text()"/> <xsl:output indent="yes"/> <xsl:template match="*"> <xsl:choose>       <xsl:when test="name() = 'block'">             <xsl:text>Found a block.             </xsl:text>       </xsl:when>       <xsl:when test="name() = 'sidestreet'">             <xsl:text>Found a sidestreet.             </xsl:text>       </xsl:when>       <xsl:when test="name() = 'boulevard'">             <xsl:text>Found a boulevard.             </xsl:text>       </xsl:when>       <xsl:otherwise>             <xsl:text>Found a </xsl:text>             <xsl:value-of select="name()"/>             <xsl:text>.             </xsl:text>       </xsl:otherwise> </xsl:choose> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet> 

9.7 The <xsl:number> Instruction Element

The <xsl:number> element performs the traditional numbering role, which you might be accustomed to in the HTML ordered list (<ol>) or in word processor Bullets and Numbering tools. Its nine attributes are all optional, as shown in the following element model definition, and serve to nuance the format, starting number, and so on for the numbering:

<!-- Category: instruction --> <xsl:number level = "single" | "multiple" | "any" count = pattern from = pattern value = number-expression format = { string } lang = { nmtoken } letter-value = { "alphabetic" | "traditional" } grouping-separator = { char } grouping-size = { number } /> 

The <xsl:number> element can be used anywhere in a template and is always an empty element. Its output is a text node, which represents the appropriate level and respective number for the given context of the current node being numbered.

The simplest use of <xsl:number> is without attributes, using the default values which return the current node number, equal to the position of the node in its current context. Example 9-18 shows the use of an empty <xsl:number> to generate numbers for each <block> element.

Example 9-18 Using an empty <xsl:number> to generate basic numbers.
TEMPLATE: <xsl:template match="block"> <xsl:number/> <xsl:text> - </xsl:text> <xsl:value-of select="."/> <xsl:text> </xsl:text> </xsl:template> RESULT: 1 - 1st Street 2 - 2nd Street 3 - 3rd Street 1 - First Street 2 - Second Street 3 - Third Street 1 - Panorama Street 2 - Highland Plaza 3 - Hutchens Avenue 4 - Wildwood Drive 5 - Old Chimney Road 6 - Carrol Circle 

The result of this template rule lists each <block> element preceded by the number of the node, generated with <xsl:number>. The numbering is essentially reset at the parent element of <block>, regardless of the type of parent. The additional formatting using <xsl:text> adds a dash and the break between lines.

More complex combinations of numbering can be accomplished using the attributes specified for <xsl:number>. Most of the attributes are directly affected by the use of the count attribute, which is used to select the nodes to be counted. Without the count attribute, the default behavior is to number only the elements with the same name as the currently selected node, matched from the template rule containing the <xsl:number> element.

9.7.1 The count Attribute of <xsl:number>

The count attribute determines which nodes will be counted in the numbering. Its value is a pattern expression, which can reference any node in the input document. This includes the root node, element nodes, text nodes, attribute nodes (although numbering is not valid for attribute nodes because they do not have a specified order in a node list), etc. The count attribute can be used to select one or more element-type names, or to select a specific subset of nodes to number, using predicates. Nodes identified in the count attribute can also be ancestors or descendants of the current node. For example:

<xsl:number count="block|thoroughfare"/> <xsl:number count="thoroughfare[@*]"/> <xsl:number count="block|thoroughfare[@*]"/> 

These elements show different combinations of the count attribute, the first counting both <block> and <thoroughfare> elements, the second counting only <thoroughfare> elements that have an attribute, and the third a combination of the first two.

Note that although a node can be identified in the count attribute, it is not necessarily sent to the output result tree, but it is counted in the numbering. Any elements that are sent to the output result tree will reflect the numbering, including the elements not shown. In Example 9-19, we are changing our stylesheet to count both <block> and <sidestreet> elements. This will increment the numbering by including the <sidestreet> elements in the count. However, if we don't actually use the <sidestreet> in our output, the numbering will appear to skip wherever the <sidestreet> originally appeared.

Example 9-19 Output counting two element types when one is not displayed.
TEMPLATE: <xsl:template match="block"> <xsl:number count="block|sidestreet"/> <xsl:text> - </xsl:text> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:apply-templates/> </xsl:template> RESULT: 2 - 1st Street 3 - 2nd Street 4 - 3rd Street 2 - First Street 3 - Second Street 4 - Third Street 1 - Panorama Street 2 - Highland Plaza 3 - Hutchens Avenue 4 - Wildwood Drive 5 - Old Chimney Road 6 - Carrol Circle 

The three <sidestreet> elements from the input are counted as 1 and 5 in the first <thoroughfare>, and 1 in the second. However, since the <sidestreet>s are not being sent to the output, these numbers do not show up, but are still used in the overall count.

9.7.2 The level Attribute of <xsl:number>

The level attribute of <xsl:number> is used to stipulate which levels of numbering will be output for each object. It has three predefined values single, multiple, and any. A value of single only outputs the number of the count for that object; for example, 1, 2, and 3. A value of multiple outputs all the numbers according to which level the object is at in the hierarchy; for example, 1, 2.1, and 2.1.1. Numbering is still reset at the parent of the current node for each level. The any value basically ignores hierarchical levels to generate a single sequence of numbers that do not reset.

9.7.2.1 The single Value for the level Attribute

The single value for the level attribute numbers each node in the matched node-set sequentially within the context of each parent node. The XSLT processor will reset the count to begin numbering with a value of 1 for each level in the hierarchy. The single value is the default setting for <xsl:number>'s level attribute, so it need not be declared. Simply invoking <xsl:number> without attributes will generate the single-level count of the current node type, as shown in Example 9-20.

9.7.2.2 The multiple Value for the level Attribute

Using level with a value of multiple numbers each node in the node-set according to the hierarchy, including the number of the parent and ancestor elements. The parent or ancestor elements to be used must be included in the count attribute for their value to be included. Nested elements of the same element-type name will also be treated as multiple values for numbering. Using the same input from Example 9-20, <xsl:number level="multiple"/> would result in a hierarchical count of <block>s, with the numbers of each level displayed, as shown in Example 9-21.

Example 9-20 Using an <xsl:number> with single-level numbering.
INPUT: <?xml version="1.0"?> <block>Block 1       <block>Child 1 of Block 1             <block>Child 1 of Child 1 of Block 1</block>             <block>Child 2 of Child 1 of Block 1</block>             <block>Child 3 of Child 1 of Block 1</block>       </block>       <block>Child 2 of Block 1</block>       <block>Child 3 of Block 1             <block>Child 1 of Child 3 of Block 1</block>             <block>Child 2 of Child 3 of Block 1</block>             <block>Child 3 of Child 3 of Block 1</block>        </block> </block> TEMPLATE: <xsl:template match="block"> <xsl:number level="single"/> <xsl:text> - </xsl:text> <xsl:apply-templates/> </xsl:template> RESULT: <?xml version="1.0" encoding="utf-8"?> 1 - Block 1       1 - Child 1 of Block 1             1 - Child 1 of Child 1 of Block 1             2 - Child 2 of Child 1 of Block 1             3 - Child 3 of Child 1 of Block 1       2 - Child 2 of Block 1       3 - Child 3 of Block 1             1 - Child 1 of Child 3 of Block 1             2 - Child 2 of Child 3 of Block 1             3 - Child 3 of Child 3 of Block 1 
Example 9-21 Using an <xsl:number> with multiple-level <block>s.
TEMPLATE: <xsl:template match="block"> <xsl:number level="multiple"/> <xsl:text> - </xsl:text> <xsl:apply-templates/> </xsl:template> RESULT: <?xml version="1.0" encoding="utf-8"?> 1 - Block 1       1.1 - Child 1 of Block 1              1.1.1 - Child 1 of Child 1 of Block 1              1.1.2 - Child 2 of Child 1 of Block 1              1.1.3 - Child 3 of Child 1 of Block 1       1.2 - Child 2 of Block 1       1.3 - Child 3 of Block 1              1.3.1 - Child 1 of Child 3 of Block 1              1.3.2 - Child 2 of Child 3 of Block 1              1.3.3 - Child 3 of Child 3 of Block 1 

The more common use of the level attribute is when elements of different element-type names are used to number hierarchically. Example 9-22 shows multiple elements numbered using the count and level attributes.

We are using <block>s and <thoroughfare>s that contain <block>s as levels of counting. We don't want to count <thoroughfare> elements that don't have <block>s, so we use the predicate [block] on thoroughfare. Notice the use of <xsl:template match="text()"/> to suppress the output of unwanted text nodes.

9.7.2.3 The any Value for the level Attribute

Using level with a value of any numbers the nodes globally, without considering the context of the current node. With this value, it is possible to sequentially number nodes that occur at any level of the document's hierarchy. For example, using <xsl:number level="any"/> would result in a sequential count of <block>s, without resetting the number at the parent of <block>, as shown in Example 9-23.

Example 9-22 Using multiple level elements with count.
INPUT: <?xml version="1.0"?> <main>       <parkway>             <thoroughfare>Governor Drive</thoroughfare>             <thoroughfare name="Whitesburg Drive">                   <sidestreet>Bob Wallace Avenue</sidestreet>                   <block>1st Street</block>                   <block>2nd Street</block>                   <block>3rd Street</block>                   <sidestreet>Woodridge Street</sidestreet>             </thoroughfare>             <thoroughfare name="Bankhead">                   <sidestreet>Tollgate Road</sidestreet>                   <block>First Street</block>                   <block>Second Street</block>                   <block>Third Street</block>                   <sidestreet>Oak Drive</sidestreet>             </thoroughfare>       </parkway>       <boulevard>             <block>Panorama Street</block>             <block>Highland Plaza</block>             <block>Hutchens Avenue</block>             <block>Wildwood Drive</block>             <block>Old Chimney Road</block>             <block>Carrol Circle</block>       </boulevard> </main> STYLESHEET: <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="text()"/> <xsl:template match="thoroughfare/block"> <xsl:number level="multiple"count="block|thoroughfare[block]"/> <xsl:text> - </xsl:text> <xsl:value-of select="text()"/> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> RESULT: 1.1 - 1st Street 1.2 - 2nd Street 1.3 - 3rd Street 2.1 - First Street 2.2 - Second Street 2.3 - Third Street <xsl:text> - </xsl:text> 

A typical use for the any value of the level attribute is to number global elements, such as tables or figures that need to be numbered independently of the relative hierarchical position each occupies in the input XML document instance.

9.7.3 The from Attribute of <xsl:number>

The from attribute of <xsl:number> is used to identify an element (or elements) from the input document to use as the starting point for the numbering. Its value is a pattern expression that identifies specific nodes, essentially restarting the numbering at that point, instead of using the normal contextual resetting. This allows the stylesheet to reset the numbering using a different ancestor than the immediate parent. For example, the numbering for <block> resets at the parent element, which is either <thoroughfare> or <boulevard>. Using the from attribute, in combination with the level attribute set to any, we can change the starting point of the numbering to <parkway> and <boulevard>, as shown in Example 9-24.

Example 9-23 Result of using the level attribute with a value of any.
TEMPLATE: <xsl:template match="block"> <xsl:number level="any"/> <xsl:text> - </xsl:text> <xsl:value-of select="text()"/> <xsl:text> </xsl:text> </xsl:template> RESULT: 1 - 1st Street 2 - 2nd Street 3 - 3rd Street 4 - First Street 5 - Second Street 6 - Third Street 7 - Panorama Street 8 - Highland Plaza 9 - Hutchens Avenue 10 - Wildwood Drive 11 - Old Chimney Road 12 - Carrol Circle 

The numbering for all the <block>s inside <thoroughfare> elements are now numbering sequentially, starting at 1, without regard to which <thoroughfare> they're in, while the <block>s inside the <boulevard> are also numbered sequentially starting at 1.

9.7.4 The value Attribute

The value attribute allows the use of a specified number, generated by an expression, for the numbering sequence. The expression contained in the value attribute is evaluated and converted to a number, rounded, and then converted to a string prior to being used. The default, if there is no value specified, is to number each node according to its position in context.For example, using <xsl:number value="position()"/> performs the same function as an empty <xsl:number> element, assuming no whitespace nodes between elements in the input document structure.

Example 9-24 Using the from attribute to reset numbering.
TEMPLATE: <xsl:template match="block"> <xsl:number level="any" from="parkway|boulevard"/> <xsl:text> - </xsl:text> <xsl:value-of select="text()"/> <xsl:text> </xsl:text> </xsl:template> RESULT: 1 - 1st Street 2 - 2nd Street 3 - 3rd Street 4 - First Street 5 - Second Street 6 - Third Street 1 - Panorama Street 2 - Highland Plaza 3 - Hutchens Avenue 4 - Wildwood Drive 5 - Old Chimney Road 6 - Carrol Circle 

Using expressions in the value attribute, it is possible to extract a different value for the numbering, for example, if your numbering scheme starts with 0 instead of the default starting value of 1. Example 9-25 shows the renumbering of the <block>s in the <boulevard> to start from 0.

The numbering is now effectively changed to start at 0 instead of 1. Notice that we are using the <xsl:strip-space> element, discussed in Chapter 10, to remove any extra whitespace from our input so that the empty nodes are not counted.

Example 9-25 Using the value attribute to change the starting value of the numbering.
STYLESHEET: <?xml version="1.0"?> <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="text()"/> <xsl:output omit-xml-declaration="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="boulevard/block"> <xsl:number value="position() - 1"/> <xsl:text> - </xsl:text> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet> RESULT: 0 - Panorama Street 1 - Highland Plaza 2 - Hutchens Avenue 3 - Wildwood Drive 4 - Old Chimney Road 5 - Carrol Circle 

9.7.5 The format Attribute

The format attribute is used to specify the model that will be used to represent numbering sequences in the output. The value of the format attribute is parsed and separated according to two classes of characters, either alphanumeric or non-alphanumeric. Alphanumeric characters are used as character models for the sequencing, and non-alphanumeric characters are used as punctuation or separators. Punctuation and separator characters are added to each sequence in the output in the same position they occupy in the format attribute, using the same character specified.

The format attribute can model five types of sequences: numeric, represented by 1 or 0; alphabetic lowercase, represented by a; alphabetic uppercase, represented by A; Roman numeral lowercase, represented by i; and Roman numeral uppercase, represented by I.

When using a numeric format, any combination of concurrent numbers is interpreted to be a complete number for the start of the sequence, such as 0001, which will increment as 0002, 0003, etc. Note that other digits besides 0 and 1 are allowed, but do not change the numbering sequence. So, using 9999 or 1111 will default the numbering to either 1 or 0001, depending on the processor used. The functionality of zero-padding, or zero-fill, for the format attribute is discussed in Section 9.7.5.2.

Alphabetic sequencing does not allow multiple combinations of characters, and any extra characters are ignored by the processor. Depending on the processor used, the combination "aaa" will be interpreted as the single character a, or default to 1. Any alphabetic character other than a or A may be interpreted by the processor as a separator, or default to 1, depending on the processor used.

Other sequences may be defined and supported based on processor-specific implementations, possibly to support language-specific character sequences.

9.7.5.1 Attribute Value Templates in the format Attribute

The value of the format attribute can be interpreted as an AVT (discussed in Section 6.6.1) when used with {}, allowing the sequencing format to be derived from an expression. The expression is processed based on the context of the current node and the result is converted to a string to determine the model for the sequencing. For example, the value for the format could be stored in a variable and retrieved with a variable reference, as shown in Example 9-26.

9.7.5.2 Zero-padding with the format Attribute

Especially handy is the option to pad a number with right-justified zeros, sometimes known as RJZF, or right-justified, zero-fill.[2] This enables a user to set a numerical set of spaces, which will always be filled with 0 regardless of the relative size of the actual sequential count at any given node. For instance, the format model 001 will number sequentially as 001, 002, 003, ... 010, 011, 012, etc., until 999 is reached.

Example 9-26 Using AVTs to retrieve a variable for format.
STYLESHEET: <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"             version="1.0"> <xsl:output indent="yes"/> <xsl:output omit-xml-declaration="yes"/> <xsl:template match="text()"/> <xsl:variable name="list-form" select="'1.a - '"/> <xsl:template match="thoroughfare/block">       <xsl:number count="block|thoroughfare[block]" level="multiple" format="{$list-form}"/>       <xsl:value-of select="."/>       <xsl:text>       </xsl:text> </xsl:template> </xsl:stylesheet> RESULT: 1.a - 1st Street 1.b - 2nd Street 1.c - 3rd Street 2.a - First Street 2.b - Second Street 2.c - Third Street 

9.7.6 The lang Attribute of <xsl:number>

The lang attribute is used to specify the language that is being used for numbering when the numbering is an alphabetic sequence. It accepts a two-character language value, such as "en" for English, from the ISO 639 specification, or a subcode, such as "en-US," for specific countries defined by the ISO 3166 specification. The XML recommendation[3] suggests using lowercase prefixes, followed by uppercase suffixes, even though the value of the lang attribute is not case-sensitive.

Using this attribute enables specific nuances of alphabetic order with languages other than English to be explicitly accommodated in <xsl:number>. If there is no language specified, the default value for the lang attribute is based on the language that is specified by the system environment or regional settings.

The value of the lang attribute can be interpreted as an attribute value template, or AVT (discussed in Section 6.6.1) when used with {}, allowing lang to be derived from an expression.

9.7.7 The letter-value Attribute

The letter-value attribute makes it possible to specify alphabetical representations of numbers. For instance, in English, Roman numerals are actually letters that are used as a sequence for numbering. When the language specified is English, however, Roman sequencing is handled with the format attribute.

If the language specified is something other than English, the traditional value for the letter-value attribute can be used to specify a letter sequencing other than alphabetic. When normal alphabetic sequencing is required for the language specified, the letter-value attribute should be specified as alphabetic. Note that this feature is implementation- and language-dependent.

The value of the letter-value attribute can also be interpreted as an AVT (discussed in Section 6.6.1) when used with {}, allowing letter-value to be derived from an expression.

9.7.8 The grouping-separator Attribute

The grouping-separator attribute makes it possible to specify the punctuation between groups of numbers. For instance, 1000 could be formatted as 1,000 with a comma, and it could also be formatted with a period as 1.000, using grouping-separator="." and grouping-size="3".

This attribute works closely with the grouping-size attribute, which serves to identify how many digits or characters a group is broken into and, accordingly, where the separating punctuation mark is to fall. The two are effectively interdependent, and if only one of either the grouping-separator or grouping-size attribute is used, then it is ignored.

The value of the grouping-separator attribute can also be interpreted as an AVT (discussed in Section 6.6.1) when used with {}, allowing grouping-separator to be derived from an expression.

9.7.9 The grouping-size Attribute

The grouping-size attribute identifies how groups of numbers are to be separated when they are displayed, using the value in the grouping-separator attribute as the separating character . For example, using a value of 3 for grouping-size results in a separation every third digit, starting from the right and working towards the left, as in the grouping in the number 1,000,000.

Using <xsl:number grouping-separator="." grouping-size="2">, we can change this number display to 1.00.00.00. Note, that when used in this context, the "." in the XPath connotation of "current node" is obviated.

The value of the grouping-size attribute can be interpreted as an AVT (discussed in Section 6.6.1) when used with {}, allowing grouping-size to be derived from an expression.

9.7.10 The <xsl:fallback> Instruction Element

The <xsl:fallback> instruction element is used to provide an alternative process when an XSLT instruction fails. The content for the <xsl:fallback> instruction is a template, and it has no attributes, as shown in the following element model definition:

<!-- Category: instruction --> <xsl:fallback>   <!-- Content: template --> </xsl:fallback> 

Normally, the processor signals an error if an instruction element that is not supported is used. The <xsl:fallback> instruction element catches the error and uses the instructions in the template provided instead of failing the process. If the <xsl:fallback> element is empty, the result is to output nothing. Note that this element is similar to a "catch" in some programming languages, which catches the error before it is sent through the process and redirects it to some other process. Example 9-27 demonstrates how we might use the <xsl:fallback> element with a hypothetical namespace with a prefix of go.

Example 9-27 Using the xsl:fallback element.
<go:jump>       <!-- some instruction elements here --> <xsl:fallback>       <!-- some alternative instruction elements here --> </xsl:fallback> </go:jump> 

In this example, the contents of the <xsl:fallback> will be instantiated if the <go:jump> extension element is not supported by the processor. Of course, the namespace for the extension element must be declared in the stylesheet for the processor to work. It is an error to use a prefixed extension function without declaring the namespace for that prefix, and <xsl:fallback> is not intended to catch legitimate errors.

It should also be noted that <xsl:fallback> is an instruction element and only works at the instruction level that is, it is only effective as a part of a template rule or as a child of <xsl:template>. This means that any extension elements defined by some other namespace as top-level elements cannot be "caught" using the <xsl:fallback> element. Unsupported top-level elements are simply ignored by the processor.

[1] The XML recommendation can be found at http://www.w3.org/TR/REC-xml.

[2] RJZF is used in specifications such as the Machine Readable Record format, or MARC record digital cataloguing system, used by most online library catalogs such as the Library of Congress.

[3] The XML recommendation can be found at http://www.w3.org/TR/REC-xml.

 

CONTENTS


XSLT and XPATH(c) A Guide to XML Transformations
XSLT and XPATH: A Guide to XML Transformations
ISBN: 0130404462
EAN: 2147483647
Year: 2005
Pages: 18

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