|
|
Table of Contents |
|
Recipe 3.1. Formatting NumbersProblemYou need to display numbers in various formats. SolutionThis problem has two general solutions. Use xsl:decimal-format in conjunction with format-number( )The top-level element xsl:decimal-format establishes a named formatting rule that can be referenced by the format-number( ) function whenever that format rule is required. xsl:decimal-format has a rich set of attributes that describe the formatting rules. Table 3-1 explains each attribute and shows its default value in parentheses.
The format-number( ) function takes the arguments shown in Table 3-2.
Use xsl:numberThe most common use of xsl:number is to number nodes sequentially. However, it can also format numbers. When used to perform the later, the relevant attributes are shown in Table 3-3.
Table 3-4 shows how formatting tokens are used with a format attribute.
The format string is an alternating sequence of format and punctuation tokens. Using multiple format tokens makes sense only when the value contains a set of numbers. DiscussionGiven the formatting machinery defined earlier, almost any numeric-formatting task can be handled. Formatting numbers into columns using a fixed number of decimal placesHere we can take advantage of leading and trailing zero padding and then map the leading zeros to spaces and use a trailing minus sign. This solution gives a nice columnar, right-justified output when the final display medium uses a fixed-width font. Example 3-1 through Example 3-3 show more conventional ways of padding. The examples illustrate the behavior of the 0 digit when used as a format character. Example 3-1. Input<numbers> <number>10</number> <number>3.5</number> <number>4.44</number> <number>77.7777</number> <number>-8</number> <number>1</number> <number>444</number> <number>1.1234</number> <number>7.77</number> <number>3.1415927</number> <number>10</number> <number>9</number> <number>8</number> <number>7</number> <number>666</number> <number>5555</number> <number>-4444444</number> <number>22.33</number> <number>18</number> <number>36.54</number> <number>43</number> <number>99999</number> <number>999999</number> <number>9999999</number> <number>32</number> <number>64</number> <number>-64.0001</number> </numbers> Example 3-2. format-numbers-into-columns.xslt<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:variable name="numCols" select="3"/>
<xsl:template match="numbers">
<xsl:for-each select="number[position( ) mod $numCols = 1]">
<xsl:apply-templates
select=". | following-sibling::number[position( ) < $numCols]"
mode="format"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="number" name="format" mode="format">
<xsl:param name="number" select="." />
<xsl:call-template name="leading-zero-to-space">
<xsl:with-param name="input"
select="format-number($number,
'0000000.0000 ;0000000.0000- ')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="leading-zero-to-space">
<xsl:param name="input"/>
<xsl:choose>
<xsl:when test="starts-with($input,'0')">
<xsl:value-of select="' '"/>
<xsl:call-template name="leading-zero-to-space">
<xsl:with-param name="input" select="substring-after($input,'0')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$input"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>Example 3-3. Output 10.0000 3.5000 4.4400
77.7777 8.0000- 1.0000
444.0000 1.1234 7.7700
3.1416 10.0000 9.0000
8.0000 7.0000 666.0000
5555.0000 4444444.0000- 22.3300
18.0000 36.5400 43.0000
99999.0000 999999.0000 9999999.0000
32.0000 64.0000 64.0001-Formatting money like U.S. accountantsExample 3-4 gives a variation of the previous format template that will make your accountant happy. Example 3-4. Accountant-friendly format<xsl:template match="number" name="format" mode="format">
<xsl:param name="number" select="." />
<xsl:text> $ </xsl:text>
<xsl:call-template name="leading-zero-to-space">
<xsl:with-param name="input"
select="format-number($number,
' 0000000.00 ;(0000000.00)')"/>
</xsl:call-template>
</xsl:template>
Output:
$ 10.00 $ 3.50 $ 4.44
$ 77.78 $ (8.00) $ 1.00
$ 444.00 $ 1.12 $ 7.77
$ 3.14 $ 10.00 $ 9.00
$ 8.00 $ 7.00 $ 666.00
$ 5555.00 $ (4444444.00) $ 22.33
$ 18.00 $ 36.54 $ 43.00
$ 99999.00 $ 999999.00 $ 9999999.00
$ 32.00 $ 64.00 $ (64.00)Formatting numbers for many European countriesExample 3-5 demonstrates the use of a named format. Example 3-5. European-number format<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
<xsl:output method="text" />
<!-- From Recipe 2.5 ... -->
<xsl:include href="../strings/str.dup.xslt"/>
<xsl:variable name="numCols" select="3"/>
<xsl:decimal-format name="WesternEurope"
decimal-separator="," grouping-separator="."/>
<xsl:template match="numbers">
<xsl:for-each select="number[position( ) mod $numCols = 1]">
<xsl:apply-templates
select=". | following-sibling::number[position( ) < $numCols]"
mode="format"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="number" name="format" mode="format">
<xsl:param name="number" select="." />
<xsl:call-template name="pad">
<xsl:with-param name="string"
select="format-number($number,'#.###,00','WesternEurope')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="pad">
<xsl:param name="string"/>
<xsl:param name="width" select="16"/>
<xsl:call-template name="str:dup">
<xsl:with-param name="input" select="' '"/>
<xsl:with-param name="count" select="$width - string-length($string)"/>
</xsl:call-template>
<xsl:value-of select="$string"/>
</xsl:template>
</xsl:stylesheet>
Output:
10,00 3,50 4,44
77,78 -8,00 1,00
444,00 1,12 7,77
3,14 10,00 9,00
8,00 7,00 666,00
5.555,00 -4.444.444,00 22,33
18,00 36,54 43,00
99.999,00 999.999,00 9.999.999,00
32,00 64,00 -64,00Converting numbers to Roman numeralsExample 3-6 uses xsl:number as a Roman numeral formatter to label columns as rows: Example 3-6. Roman-numeral format<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
<xsl:output method="text" />
<xsl:include href="../strings/str.dup.xslt"/>
<xsl:variable name="numCols" select="3"/>
<xsl:template match="numbers">
<xsl:for-each select="number[position() <= $numCols]">
<xsl:text> </xsl:text>
<xsl:number value="position()" format="I"/><xsl:text> </xsl:text>
</xsl:for-each>
<xsl:text>
 </xsl:text>
<xsl:for-each select="number[position() <= $numCols]">
<xsl:text>---------------- </xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="number[position( ) mod $numCols = 1]">
<xsl:call-template name="pad">
<xsl:with-param name="string">
<xsl:number value="position()" format="i"/>
</xsl:with-param>
<xsl:with-param name="width" select="4"/>
</xsl:call-template>|<xsl:text/> <!-- See recipe 7.1-->
<xsl:apply-templates
select=". | following-sibling::number[position( ) < $numCols]"
mode="format"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="number" name="format" mode="format">
<xsl:param name="number" select="." />
<xsl:call-template name="pad">
<xsl:with-param name="string" select="format-number(.,'#,###.00')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="pad">
<xsl:param name="string"/>
<xsl:param name="width" select="16"/>
<xsl:call-template name="str:dup">
<xsl:with-param name="input" select="' '"/>
<xsl:with-param name="count" select="$width - string-length($string)"/>
</xsl:call-template>
<xsl:value-of select="$string"/>
</xsl:template>
</xsl:stylesheet>
Output:
I II III
---------------- -------------- --------------
i| 10.00 3.50 4.44
ii| 77.78 -8.00 1.00
iii| 444.00 1.12 7.77
iv| 3.14 10.00 9.00
v| 8.00 7.00 666.00
vi| 5,555.00 -4,444,444.00 22.33
vii| 18.00 36.54 43.00
viii| 99,999.00 999,999.00 9,999,999.00
ix| 32.00 64.00 -64.00Creating column numbers like a spreadsheetSpreadsheets number columns in the alpha sequence A, B, C ... ZZ, and we can do the same using xsl:number (see Example 3-7). Example 3-7. Spreadsheet-like column numbers<xsl:template match="numbers">
<xsl:for-each select="number[position( ) <= $numCols]">
<xsl:text> </xsl:text>
<xsl:number value="position()" format="A"/><xsl:text> </xsl:text>
<xsl:text> </xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="number[position( ) <= $numCols]">
<xsl:text> ---------------- </xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="number[position( ) mod $numCols = 1]">
<xsl:value-of select="position()"/><xsl:text>|</xsl:text>
<xsl:apply-templates
select=". | following-sibling::number[position( ) < $numCols]"
mode="format"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="number" name="format" mode="format">
<xsl:param name="number" select="." />
<xsl:call-template name="pad">
<xsl:with-param name="string" select="format-number(.,'#,###.00')"/>
</xsl:call-template>
<xsl:text> </xsl:text>
</xsl:template>
Output:
A B C
------------ ------------ ------------
1| 10.0000 3.5000 4.4400
2| 77.7777 8.0000- 1.0000
3| 444.0000 1.1234 7.7700
4| 3.1416 10.0000 9.0000
5| 8.0000 7.0000 666.0000
6| 5555.0000 4444444.0000- 22.3300
7| 18.0000 36.5400 43.0000
8| 99999.0000 999999.0000 9999999.0000
9| 32.0000 64.0000 64.0001-Formatting numbers using Arabic charactersNumeric characters from other languages can be used by setting the zero digit in xsl:decimal-format to the zero of that language. This example uses the Unicode character 0x660 (Arabic-Indic Digit Zero): <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings"> <xsl:output method="text" encoding="UTF-8"/> <xsl:include href="../strings/str.dup.xslt"/> <!-- This states that zero starts at character 0x660, which implies one is 0x661, etc. --> <xsl:decimal-format name="Arabic" zero-digit="٠"/> <xsl:template match="numbers"> <xsl:for-each select="number"> <xsl:call-template name="pad"> <xsl:with-param name="string" select="format-number(.,'#,###.00')"/> </xsl:call-template> = <xsl:text/> <xsl:value-of select="format-number(.,'#,###.٠٠','Arabic')"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> <xsl:template name="pad"> <xsl:param name="string"/> <xsl:param name="width" select="16"/> <xsl:value-of select="$string"/> <xsl:call-template name="str:dup"> <xsl:with-param name="input" select="' '"/> <xsl:with-param name="count" select="$width - string-length($string)"/> </xsl:call-template> </xsl:template> </xsl:stylesheet> Here is the output of this code:
|
|
|
Table of Contents |
|