Numbering Textual Output

Problem

You want to create sequentially numbered output.

Solution

Since output can be numbered in many ways, this example 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 5-35 and Example 5-36.

Example 5-35. 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 5-36. Output

1. Al Zehtooney
2. Brad York
3. Charles Xavier
4. David Willimas
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 5.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 5-37 and Example 5-38 start from ten and increment by five, just to be different.

Example 5-37. 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 5-38. Output

10. Al Zehtooney
15. Brad York
20. Charles Xavier
25. David Willimas
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 5-39 and Example 5-40 use the same technique to start numbering at L.

Example 5-39. 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 5-40. People numbered successively from letter L

L. Al Zehtooney
M. Brad York
N. Charles Xavier
O. David Willimas
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 Willimas" 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 5-41 and Example 5-42.

Example 5-41. 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 5-42. Output with level="any”

1. Al Zehtooney
2. Brad York
3. Charles Xavier
4. David Willimas
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 Willimas
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 5-43 to Example 5-45, xsl:number supports this via attribute level="multiple".

Example 5-43. 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 5-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 Willimas
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 5-44 and 5-45.

Example 5-44. 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 5-45. Output

Group 1
1.1 Al Zehtooney
1.2 Brad York
1.3 Charles Xavier
1.4 David Willimas
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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.248.162