Recipe7.5.Numbering Textual Output


Recipe 7.5. Numbering Textual Output

Problem

You want to create sequentially numbered output.

Solution

Since output can be numbered in many ways, this recipe presents a series of increasingly complex examples that address the most common (and a few uncommon) numbering needs.

Number siblings sequentially

This category is the simplest form of numbering. For example, you can produce a numbered list of people using the stylesheet in Example 7-37 and Example 7-38. In these examples, I make use of xsl:number and its count attribute. You can omit the count attribute if you like because the default behavior is to count all the nodes in the current context.

Example 7-37. Stylesheet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>       <xsl:template match="person">     <xsl:number count="*" format="1. "/>      <xsl:value-of select="@name"/>   </xsl:template>     </xsl:stylesheet>

Example 7-38. Output
1. Al Zehtooney 2. Brad York 3. Charles Xavier 4. David Williams 5. Edward Ulster 6. Frank Townsend 7. Greg Sutter 8. Harry Rogers 9. John Quincy 10. Kent Peterson ...

You can use the justify template discussed in Recipe 7.3 if you want right-justified numbers.

Start from a number other than one

xsl:number does not provide a standard facility for starting from or incrementing by a number other than one, but you can handle this task with a little math. Example 7-39 and Example 7-40 show how.

Example 7-39. Stylesheet using nonsequential numbering
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="person">     <xsl:variable name="num">       <xsl:number count="*"/>     </xsl:variable>        <xsl:number value="($num - 1) * 5 + 10" format="1. "/>     <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>

Example 7-40. Output
10. Al Zehtooney 15. Brad York 20. Charles Xavier 25. David Williams 30. Edward Ulster 35. Frank Townsend 40. Greg Sutter 45. Harry Rogers 50. John Quincy 55. Kent Peterson ...

This scenario works even if you want the final output to use a non-numerical format. For example, Example 7-41 and Example 7-42 use the same technique to start numbering at L.

Example 7-41. Stylesheet for numbering from L
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="person">     <xsl:variable name="num">       <xsl:number count="*"/>     </xsl:variable>        <xsl:number value="$num + 11" format="A. "/>     <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>

Example 7-42. People numbered successively from letter L
L. Al Zehtooney M. Brad York N. Charles Xavier O. David Williams P. Edward Ulster Q. Frank Townsend R. Greg Sutter S. Harry Rogers T. John Quincy U. Kent Peterson ...

Number elements globally

Sometimes you want to number elements sequentially without regard to their context. The most common example involves a document that contains footnote elements. The footnotes can appear at any level in the document's structure, yet they should be numbered sequentially. However, to continue the theme of your example, here is a document that divides people into various groups and subgroups:

<people>   <group>     <person name="Al Zehtooney" age="33" sex="m" smoker="no"/>     <person name="Brad York" age="38" sex="m" smoker="yes"/>     <person name="Charles Xavier" age="32" sex="m" smoker="no"/>     <person name="David Williams" age="33" sex="m" smoker="no"/>     <person name="Edward Ulster" age="33" sex="m" smoker="yes"/>     <person name="Frank Townsend" age="35" sex="m" smoker="no"/>   </group>   <group>     <person name="Greg Sutter" age="40" sex="m" smoker="no"/>     <person name="Harry Rogers" age="37" sex="m" smoker="no"/>     <group>       <person name="John Quincy" age="43" sex="m" smoker="yes"/>       <person name="Kent Peterson" age="31" sex="m" smoker="no"/>       <person name="Larry Newell" age="23" sex="m" smoker="no"/>       <group>         <person name="Max Milton" age="22" sex="m" smoker="no"/>         <person name="Norman Lamagna" age="30" sex="m" smoker="no"/>         <person name="Ollie Kensinton" age="44" sex="m" smoker="no"/>       </group>       <person name="John Frank" age="24" sex="m" smoker="no"/>     </group>     <group>       <person name="Mary Williams" age="33" sex="f" smoker="no"/>       <person name="Jane Frank" age="38" sex="f" smoker="yes"/>       <person name="Jo Peterson" age="32" sex="f" smoker="no"/>       <person name="Angie Frost" age="33" sex="f" smoker="no"/>       <person name="Betty Bates" age="33" sex="f" smoker="no"/>       <person name="Connie Date" age="35" sex="f" smoker="no"/>       <person name="Donna Finster" age="20" sex="f" smoker="no"/>     </group>     <group>       <person name="Esther Gates" age="37" sex="f" smoker="no"/>       <person name="Fanny Hill" age="33" sex="f" smoker="yes"/>       <person name="Geta Iota" age="27" sex="f" smoker="no"/>       <person name="Hillary Johnson" age="22" sex="f" smoker="no"/>       <person name="Ingrid Kent" age="21" sex="f" smoker="no"/>       <person name="Jill Larson" age="20" sex="f" smoker="no"/>       <person name="Kim Mulrooney" age="41" sex="f" smoker="no"/>       <person name="Lisa Nevins" age="21" sex="f" smoker="no"/>     </group>   </group> </people>

The only necessary change is to use the xsl:number attribute level="any". This attribute instructs the XSLT processor to consider all preceding occurrences of the person element when determining numbering. See Example 7-43 and Example 7-44.

Example 7-43. Stylesheet for level="any"
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="person">     <xsl:number count="person" level="any" format="1. "/>      <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>

Example 7-44. Output with level="any"
1. Al Zehtooney 2. Brad York 3. Charles Xavier 4. David Williams 5. Edward Ulster 6. Frank Townsend 7. Greg Sutter 8. Harry Rogers 9. John Quincy 10. Kent Peterson 11. Larry Newell 12. Max Milton 13. Norman Lamagna 14. Ollie Kensinton 15. John Frank 16. Mary Williams 17. Jane Frank 18. Jo Peterson 19. Angie Frost 20. Betty Bates 21. Connie Date 22. Donna Finster 23. Esther Gates 24. Fanny Hill 25. Geta Iota 26. Hillary Johnson 27. Ingrid Kent 28. Jill Larson 29. Kim Mulrooney 30. Lisa Nevins

Number elements globally within a subcontext

Sometimes you want to restrict global numbering to a specific context. For example, suppose you want to number people within their top-level group and ignore subgroups:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="people/group">     <xsl:text>Group </xsl:text>     <xsl:number count="group"/>     <xsl:text>&#xa;</xsl:text>     <xsl:apply-templates/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>       <xsl:template match="person">     <xsl:number count="person" level="any" from="people/group" format="1. "/>      <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>     Group 1 1. Al Zehtooney 2. Brad York 3. Charles Xavier 4. David Williams 5. Edward Ulster 6. Frank Townsend     Group 2 1. Greg Sutter 2. Harry Rogers 3. John Quincy 4. Kent Peterson 5. Larry Newell 6. Max Milton 7. Norman Lamagna 8. Ollie Kensinton 9. John Frank 10. Mary Williams 11. Jane Frank 12. Jo Peterson 13. Angie Frost 14. Betty Bates 15. Connie Date 16. Donna Finster 17. Esther Gates 18. Fanny Hill 19. Geta Iota 20. Hillary Johnson 21. Ingrid Kent 22. Jill Larson 23. Kim Mulrooney 24. Lisa Nevins

Number hierarchically

In formal and legal documents, items are often numbered based on both their sequence and level within a hierarchy. As shown in Example 7-45, xsl:number supports this via attribute level="multiple".

Example 7-45. Hierarchical numbering based on group and person
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="people/group">     <xsl:text>Group </xsl:text>     <xsl:number count="group"/>     <xsl:text>&#xa;</xsl:text>     <xsl:apply-templates/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>       <xsl:template match="person">     <xsl:number count="group | person" level="multiple" format="1.1.1 "/>      <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>

The numbering achieved by the stylesheet in Example 7-45 is somewhat odd, but it effectively illustrates the effect of attribute count when it is used with level = "multiple". The count attribute is simply a specification for determining what ancestor elements should be included when composing a hierarchical number. The stylesheet assigned numbers to people based on group or person elements. Bard York is assigned 1.2 because he is in Group 1 and is the second person in the group. Likewise, Max Milton is assigned 2.3.4.1 because he is in Group 2 when considering only top-level groups; he is in Group 3 when considering both top- and second-level groups; he is in Group 4 when considering all top-, second-, and third-level groups; and he is the first person within his own group:

Group 1 1.1 Al Zehtooney 1.2 Brad York 1.3 Charles Xavier 1.4 David Williams 1.5 Edward Ulster 1.6 Frank Townsend     Group 2 2.1 Greg Sutter 2.2 Harry Rogers 2.3.1 John Quincy 2.3.2 Kent Peterson 2.3.3 Larry Newell 2.3.4.1 Max Milton 2.3.4.2 Norman Lamagna 2.3.4.3 Ollie Kensinton 2.3.5 John Frank 2.4.1 Mary Williams 2.4.2 Jane Frank 2.4.3 Jo Peterson 2.4.4 Angie Frost 2.4.5 Betty Bates 2.4.6 Connie Date 2.4.7 Donna Finster 2.5.1 Esther Gates 2.5.2 Fanny Hill 2.5.3 Geta Iota 2.5.4 Hillary Johnson 2.5.5 Ingrid Kent 2.5.6 Jill Larson 2.5.7 Kim Mulrooney 2.5.8 Lisa Nevins

In typical applications, you expect a numbering scheme in which the number at any level is relative to the number at the next higher level. You can achieve this relationship by using multiple and adjacent xsl:number elements, as shown in Example 7-46 and Example 7-47.

Example 7-46. Stylesheet for creating muliple ordered levels
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="group">     <xsl:text>Group </xsl:text>     <xsl:number count="group" level="multiple"/>     <xsl:text>&#xa;</xsl:text>     <xsl:apply-templates/>   </xsl:template>       <xsl:template match="person">     <xsl:number count="group" level="multiple" format="1.1.1."/>     <xsl:number count="person" level="single" format="1 "/>      <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>

Example 7-47. Output
Group 1 1.1 Al Zehtooney 1.2 Brad York 1.3 Charles Xavier 1.4 David Williams 1.5 Edward Ulster 1.6 Frank Townsend Group 2 2.1 Greg Sutter 2.2 Harry Rogers Group 2.1 2.1.1 John Quincy 2.1.2 Kent Peterson 2.1.3 Larry Newell Group 2.1.1 2.1.1.1 Max Milton 2.1.1.2 Norman Lamagna 2.1.1.3 Ollie Kensinton 2.1.4 John Frank Group 2.2 2.2.1 Mary Williams 2.2.2 Jane Frank 2.2.3 Jo Peterson 2.2.4 Angie Frost 2.2.5 Betty Bates 2.2.6 Connie Date 2.2.7 Donna Finster Group 2.3 2.3.1 Esther Gates 2.3.2 Fanny Hill 2.3.3 Geta Iota 2.3.4 Hillary Johnson 2.3.5 Ingrid Kent 2.3.6 Jill Larson 2.3.7 Kim Mulrooney 2.3.8 Lisa Nevins

Discussion

Almost any numbering scheme is realizable by using one or more xsl:number elements with the appropriate attribute settings. However, extensive use of xsl:number (especially with level="multiple") can slow down your stylesheets. With very deeply nested hierarchical numbers, you can achieve a performance boost by passing the parent-level numbering down to the children via a parameter. Notice how you can achieve a hierarchal numbering in this fashion without using xsl:number at all:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <xsl:template match="group">     <xsl:param name="parent-level" select=" '' "/>          <xsl:variable name="number" select="concat($parent-level,position( ))"/>          <xsl:text>Group </xsl:text>     <xsl:value-of select="$number"/>     <xsl:text>&#xa;</xsl:text>         <xsl:apply-templates>       <xsl:with-param name="parent-level" select="concat($number,'.')"/>     </xsl:apply-templates>        </xsl:template>       <xsl:template match="person">     <xsl:param name="parent-level" select=" '' "/>         <xsl:variable name="number">       <xsl:value-of select="concat($parent-level,position( ),' ')"/>     </xsl:variable>           <xsl:value-of select="$number"/>     <xsl:value-of select="@name"/>     <xsl:text>&#xa;</xsl:text>   </xsl:template>     </xsl:stylesheet>

This use of position is less convenient when the numbering scheme requires letters for roman numerals.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

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