2 * numbers.c: Implementation of the XSLT number functions
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
10 * Bjorn Reese <breese@users.sourceforge.net>
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parserInternals.h>
23 #include <libxml/xpath.h>
24 #include <libxml/xpathInternals.h>
25 #include <libxml/encoding.h>
26 #include "xsltutils.h"
28 #include "templates.h"
29 #include "transform.h"
30 #include "numbersInternals.h"
33 # define FALSE (0 == 1)
34 # define TRUE (1 == 1)
37 #define SYMBOL_QUOTE ((xmlChar)'\'')
39 #define DEFAULT_TOKEN '0'
40 #define DEFAULT_SEPARATOR "."
42 #define MAX_TOKENS 1024
44 typedef struct _xsltFormatToken xsltFormatToken
;
45 typedef xsltFormatToken
*xsltFormatTokenPtr
;
46 struct _xsltFormatToken
{
52 typedef struct _xsltFormat xsltFormat
;
53 typedef xsltFormat
*xsltFormatPtr
;
56 xsltFormatToken tokens
[MAX_TOKENS
];
61 static char alpha_upper_list
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
62 static char alpha_lower_list
[] = "abcdefghijklmnopqrstuvwxyz";
63 static xsltFormatToken default_token
;
66 * **** Start temp insert ****
68 * The following routine xsltUTF8Charcmp will be replaced with calls to
69 * the corresponding libxml routine at a later date (when other
70 * inter-library dependencies require it).
75 * @utf1: pointer to first UTF8 char
76 * @utf2: pointer to second UTF8 char
78 * returns result of comparing the two UCS4 values
82 xsltUTF8Charcmp(xmlChar
*utf1
, xmlChar
*utf2
) {
83 int len
= xmlUTF8Strsize(utf1
, 1);
92 return xmlStrncmp(utf1
, utf2
, len
);
96 xsltIsLetterDigit(int val
) {
97 return xmlIsBaseCharQ(val
) || xmlIsIdeographicQ(val
) ||
101 /***** Stop temp insert *****/
102 /************************************************************************
104 * Utility functions *
106 ************************************************************************/
108 #define IS_SPECIAL(self,letter) \
109 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
110 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
111 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
112 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
113 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
115 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
116 #define IS_DIGIT_ONE(x) xsltIsDigitZero((x)-1)
119 xsltIsDigitZero(int ch
)
122 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
124 * There a many more digit ranges in newer Unicode versions. These
125 * are only the zeros that match Digit in XML 1.0 (IS_DIGIT macro).
128 case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
129 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
130 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
131 case 0x0ED0: case 0x0F20:
139 xsltNumberFormatDecimal(xmlBufferPtr buffer
,
144 int groupingCharacter
,
145 int groupingCharacterLen
)
149 * xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4];
150 * which would be length 68 on x86 arch. It was changed to be a longer,
151 * fixed length in order to try to cater for (reasonable) UTF8
152 * separators and numeric characters. The max UTF8 char size will be
153 * 6 or less, so the value used [500] should be *much* larger than needed
155 xmlChar temp_string
[500];
157 xmlChar temp_char
[6];
162 /* Build buffer from back */
163 pointer
= &temp_string
[sizeof(temp_string
)] - 1; /* last char */
166 while (pointer
> temp_string
) {
167 if ((i
>= width
) && (fabs(number
) < 1.0))
169 if ((i
> 0) && (groupingCharacter
!= 0) &&
170 (digitsPerGroup
> 0) &&
171 ((i
% digitsPerGroup
) == 0)) {
172 if (pointer
- groupingCharacterLen
< temp_string
) {
173 i
= -1; /* flag error */
176 pointer
-= groupingCharacterLen
;
177 xmlCopyCharMultiByte(pointer
, groupingCharacter
);
180 val
= digit_zero
+ (int)fmod(number
, 10.0);
181 if (val
< 0x80) { /* shortcut if ASCII */
182 if (pointer
<= temp_string
) { /* Check enough room */
190 * Here we have a multibyte character. It's a little messy,
191 * because until we generate the char we don't know how long
192 * it is. So, we generate it into the buffer temp_char, then
193 * copy from there into temp_string.
195 len
= xmlCopyCharMultiByte(temp_char
, val
);
196 if ( (pointer
- len
) < temp_string
) {
201 memcpy(pointer
, temp_char
, len
);
207 xsltGenericError(xsltGenericErrorContext
,
208 "xsltNumberFormatDecimal: Internal buffer size exceeded\n");
209 xmlBufferCat(buffer
, pointer
);
213 xsltNumberFormatAlpha(xsltNumberDataPtr data
,
218 char temp_string
[sizeof(double) * CHAR_BIT
* sizeof(xmlChar
) + 1];
222 double alpha_size
= (double)(sizeof(alpha_upper_list
) - 1);
225 * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says:
227 * For all format tokens other than the first kind above (one that
228 * consists of decimal digits), there may be implementation-defined
229 * lower and upper bounds on the range of numbers that can be
230 * formatted using this format token; indeed, for some numbering
231 * sequences there may be intrinsic limits. [...] Numbers that fall
232 * outside this range must be formatted using the format token 1.
234 * The "a" token has an intrinsic lower limit of 1.
237 xsltNumberFormatDecimal(buffer
, number
, '0', 1,
238 data
->digitsPerGroup
,
239 data
->groupingCharacter
,
240 data
->groupingCharacterLen
);
244 /* Build buffer from back */
245 pointer
= &temp_string
[sizeof(temp_string
)];
247 alpha_list
= (is_upper
) ? alpha_upper_list
: alpha_lower_list
;
249 for (i
= 1; i
< (int)sizeof(temp_string
); i
++) {
251 *(--pointer
) = alpha_list
[((int)fmod(number
, alpha_size
))];
252 number
/= alpha_size
;
256 xmlBufferCCat(buffer
, pointer
);
260 xsltNumberFormatRoman(xsltNumberDataPtr data
,
266 * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper
267 * bound to avoid denial of service.
269 if (number
< 1.0 || number
> 5000.0) {
270 xsltNumberFormatDecimal(buffer
, number
, '0', 1,
271 data
->digitsPerGroup
,
272 data
->groupingCharacter
,
273 data
->groupingCharacterLen
);
278 * Based on an example by Jim Walsh
280 while (number
>= 1000.0) {
281 xmlBufferCCat(buffer
, (is_upper
) ? "M" : "m");
284 if (number
>= 900.0) {
285 xmlBufferCCat(buffer
, (is_upper
) ? "CM" : "cm");
288 while (number
>= 500.0) {
289 xmlBufferCCat(buffer
, (is_upper
) ? "D" : "d");
292 if (number
>= 400.0) {
293 xmlBufferCCat(buffer
, (is_upper
) ? "CD" : "cd");
296 while (number
>= 100.0) {
297 xmlBufferCCat(buffer
, (is_upper
) ? "C" : "c");
300 if (number
>= 90.0) {
301 xmlBufferCCat(buffer
, (is_upper
) ? "XC" : "xc");
304 while (number
>= 50.0) {
305 xmlBufferCCat(buffer
, (is_upper
) ? "L" : "l");
308 if (number
>= 40.0) {
309 xmlBufferCCat(buffer
, (is_upper
) ? "XL" : "xl");
312 while (number
>= 10.0) {
313 xmlBufferCCat(buffer
, (is_upper
) ? "X" : "x");
317 xmlBufferCCat(buffer
, (is_upper
) ? "IX" : "ix");
320 while (number
>= 5.0) {
321 xmlBufferCCat(buffer
, (is_upper
) ? "V" : "v");
325 xmlBufferCCat(buffer
, (is_upper
) ? "IV" : "iv");
328 while (number
>= 1.0) {
329 xmlBufferCCat(buffer
, (is_upper
) ? "I" : "i");
335 xsltNumberFormatTokenize(const xmlChar
*format
,
336 xsltFormatPtr tokens
)
343 default_token
.token
= DEFAULT_TOKEN
;
344 default_token
.width
= 1;
345 default_token
.separator
= BAD_CAST(DEFAULT_SEPARATOR
);
348 tokens
->start
= NULL
;
349 tokens
->tokens
[0].separator
= NULL
;
353 * Insert initial non-alphanumeric token.
354 * There is always such a token in the list, even if NULL
356 while (!xsltIsLetterDigit(val
= xsltGetUTF8CharZ(format
+ix
, &len
))) {
357 if (format
[ix
] == 0) /* if end of format string */
362 tokens
->start
= xmlStrndup(format
, ix
);
365 for (tokens
->nTokens
= 0; tokens
->nTokens
< MAX_TOKENS
;
371 * separator has already been parsed (except for the first
372 * number) in tokens->end, recover it.
374 if (tokens
->nTokens
> 0) {
375 tokens
->tokens
[tokens
->nTokens
].separator
= tokens
->end
;
379 val
= xsltGetUTF8CharZ(format
+ix
, &len
);
380 if (IS_DIGIT_ONE(val
) ||
381 IS_DIGIT_ZERO(val
)) {
382 tokens
->tokens
[tokens
->nTokens
].width
= 1;
383 while (IS_DIGIT_ZERO(val
)) {
384 tokens
->tokens
[tokens
->nTokens
].width
++;
386 val
= xsltGetUTF8CharZ(format
+ix
, &len
);
388 if (IS_DIGIT_ONE(val
)) {
389 tokens
->tokens
[tokens
->nTokens
].token
= val
- 1;
391 val
= xsltGetUTF8CharZ(format
+ix
, &len
);
393 tokens
->tokens
[tokens
->nTokens
].token
= '0';
394 tokens
->tokens
[tokens
->nTokens
].width
= 1;
396 } else if ( (val
== 'A') ||
400 tokens
->tokens
[tokens
->nTokens
].token
= val
;
402 val
= xsltGetUTF8CharZ(format
+ix
, &len
);
405 * "Any other format token indicates a numbering sequence
406 * that starts with that token. If an implementation does
407 * not support a numbering sequence that starts with that
408 * token, it must use a format token of 1."
410 tokens
->tokens
[tokens
->nTokens
].token
= '0';
411 tokens
->tokens
[tokens
->nTokens
].width
= 1;
414 * Skip over remaining alphanumeric characters from the Nd
415 * (Number, decimal digit), Nl (Number, letter), No (Number,
416 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
417 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
418 * (Letters, other (uncased)) Unicode categories. This happens
419 * to correspond to the Letter and Digit classes from XML (and
420 * one wonders why XSLT doesn't refer to these instead).
422 while (xsltIsLetterDigit(val
)) {
424 val
= xsltGetUTF8CharZ(format
+ix
, &len
);
428 * Insert temporary non-alphanumeric final tooken.
431 while (!xsltIsLetterDigit(val
)) {
435 val
= xsltGetUTF8CharZ(format
+ix
, &len
);
438 tokens
->end
= xmlStrndup(&format
[j
], ix
- j
);
443 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data
,
446 xsltFormatPtr tokens
,
451 xsltFormatTokenPtr token
;
454 * Handle initial non-alphanumeric token
456 if (tokens
->start
!= NULL
)
457 xmlBufferCat(buffer
, tokens
->start
);
459 for (i
= 0; i
< numbers_max
; i
++) {
461 number
= numbers
[(numbers_max
- 1) - i
];
462 /* Round to nearest like XSLT 2.0 */
463 number
= floor(number
+ 0.5);
465 * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT
468 * It is a non-recoverable dynamic error if any undiscarded item
469 * in the atomized sequence supplied as the value of the value
470 * attribute of xsl:number cannot be converted to an integer, or
471 * if the resulting integer is less than 0 (zero).
474 xsltTransformError(NULL
, NULL
, NULL
,
475 "xsl-number : negative value\n");
476 /* Recover by treating negative values as zero. */
479 if (i
< tokens
->nTokens
) {
481 * The "n"th format token will be used to format the "n"th
484 token
= &(tokens
->tokens
[i
]);
485 } else if (tokens
->nTokens
> 0) {
487 * If there are more numbers than format tokens, then the
488 * last format token will be used to format the remaining
491 token
= &(tokens
->tokens
[tokens
->nTokens
- 1]);
494 * If there are no format tokens, then a format token of
495 * 1 is used to format all numbers.
497 token
= &default_token
;
500 /* Print separator, except for the first number */
502 if (token
->separator
!= NULL
)
503 xmlBufferCat(buffer
, token
->separator
);
505 xmlBufferCCat(buffer
, DEFAULT_SEPARATOR
);
508 switch (xmlXPathIsInf(number
)) {
510 xmlBufferCCat(buffer
, "-Infinity");
513 xmlBufferCCat(buffer
, "Infinity");
516 if (xmlXPathIsNaN(number
)) {
517 xmlBufferCCat(buffer
, "NaN");
520 switch (token
->token
) {
522 xsltNumberFormatAlpha(data
, buffer
, number
, TRUE
);
525 xsltNumberFormatAlpha(data
, buffer
, number
, FALSE
);
528 xsltNumberFormatRoman(data
, buffer
, number
, TRUE
);
531 xsltNumberFormatRoman(data
, buffer
, number
, FALSE
);
534 if (IS_DIGIT_ZERO(token
->token
)) {
535 xsltNumberFormatDecimal(buffer
,
539 data
->digitsPerGroup
,
540 data
->groupingCharacter
,
541 data
->groupingCharacterLen
);
551 * Handle final non-alphanumeric token
553 if (tokens
->end
!= NULL
)
554 xmlBufferCat(buffer
, tokens
->end
);
559 xsltTestCompMatchCount(xsltTransformContextPtr context
,
561 xsltCompMatchPtr countPat
,
564 if (countPat
!= NULL
) {
565 return xsltTestCompMatchList(context
, node
, countPat
);
571 * If count attribute is not specified, then it defaults to the
572 * pattern that matches any node with the same node type as the
573 * current node and, if the current node has an expanded-name, with
574 * the same expanded-name as the current node.
576 if (node
->type
!= cur
->type
)
578 if (node
->type
== XML_NAMESPACE_DECL
)
580 * Namespace nodes have no preceding siblings and no parents
581 * that are namespace nodes. This means that node == cur.
584 /* TODO: Skip node types without expanded names like text nodes. */
585 if (!xmlStrEqual(node
->name
, cur
->name
))
587 if (node
->ns
== cur
->ns
)
589 if ((node
->ns
== NULL
) || (cur
->ns
== NULL
))
591 return (xmlStrEqual(node
->ns
->href
, cur
->ns
->href
));
596 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context
,
598 xsltCompMatchPtr countPat
,
599 xsltCompMatchPtr fromPat
,
604 xmlNodePtr cur
= node
;
606 while (cur
!= NULL
) {
607 /* process current node */
608 if (xsltTestCompMatchCount(context
, cur
, countPat
, node
))
610 if ((fromPat
!= NULL
) &&
611 xsltTestCompMatchList(context
, cur
, fromPat
)) {
615 /* Skip to next preceding or ancestor */
616 if ((cur
->type
== XML_DOCUMENT_NODE
) ||
617 #ifdef LIBXML_DOCB_ENABLED
618 (cur
->type
== XML_DOCB_DOCUMENT_NODE
) ||
620 (cur
->type
== XML_HTML_DOCUMENT_NODE
))
623 if (cur
->type
== XML_NAMESPACE_DECL
) {
625 * The XPath module stores the parent of a namespace node in
626 * the ns->next field.
628 cur
= (xmlNodePtr
) ((xmlNsPtr
) cur
)->next
;
629 } else if (cur
->type
== XML_ATTRIBUTE_NODE
) {
632 while ((cur
->prev
!= NULL
) && ((cur
->prev
->type
== XML_DTD_NODE
) ||
633 (cur
->prev
->type
== XML_XINCLUDE_START
) ||
634 (cur
->prev
->type
== XML_XINCLUDE_END
)))
636 if (cur
->prev
!= NULL
) {
637 for (cur
= cur
->prev
; cur
->last
!= NULL
; cur
= cur
->last
);
644 array
[amount
++] = (double) cnt
;
650 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context
,
652 xsltCompMatchPtr countPat
,
653 xsltCompMatchPtr fromPat
,
659 xmlNodePtr oldCtxtNode
;
661 xmlNodePtr preceding
;
662 xmlXPathParserContextPtr parser
;
664 oldCtxtNode
= context
->xpathCtxt
->node
;
665 parser
= xmlXPathNewParserContext(NULL
, context
->xpathCtxt
);
667 /* ancestor-or-self::*[count] */
669 while ((ancestor
!= NULL
) && (ancestor
->type
!= XML_DOCUMENT_NODE
)) {
670 if ((fromPat
!= NULL
) &&
671 xsltTestCompMatchList(context
, ancestor
, fromPat
))
675 * The xmlXPathNext* iterators require that the context node is
676 * set to the start node. Calls to xsltTestCompMatch* may also
677 * leave the context node in an undefined state, so make sure
678 * that the context node is reset before each iterator invocation.
681 if (xsltTestCompMatchCount(context
, ancestor
, countPat
, node
)) {
682 /* count(preceding-sibling::*) */
684 context
->xpathCtxt
->node
= ancestor
;
685 preceding
= xmlXPathNextPrecedingSibling(parser
, ancestor
);
686 while (preceding
!= NULL
) {
687 if (xsltTestCompMatchCount(context
, preceding
, countPat
,
690 context
->xpathCtxt
->node
= ancestor
;
692 xmlXPathNextPrecedingSibling(parser
, preceding
);
694 array
[amount
++] = (double)cnt
;
698 context
->xpathCtxt
->node
= node
;
699 ancestor
= xmlXPathNextAncestor(parser
, ancestor
);
701 xmlXPathFreeParserContext(parser
);
703 context
->xpathCtxt
->node
= oldCtxtNode
;
708 xsltNumberFormatGetValue(xmlXPathContextPtr context
,
710 const xmlChar
*value
,
714 xmlBufferPtr pattern
;
715 xmlXPathObjectPtr obj
;
717 pattern
= xmlBufferCreate();
718 if (pattern
!= NULL
) {
719 xmlBufferCCat(pattern
, "number(");
720 xmlBufferCat(pattern
, value
);
721 xmlBufferCCat(pattern
, ")");
722 context
->node
= node
;
723 obj
= xmlXPathEvalExpression(xmlBufferContent(pattern
),
726 *number
= obj
->floatval
;
728 xmlXPathFreeObject(obj
);
730 xmlBufferFree(pattern
);
737 * @ctxt: the XSLT transformation context
738 * @data: the formatting information
739 * @node: the data to format
741 * Convert one number.
744 xsltNumberFormat(xsltTransformContextPtr ctxt
,
745 xsltNumberDataPtr data
,
748 xmlBufferPtr output
= NULL
;
753 if (data
->format
!= NULL
) {
754 xsltNumberFormatTokenize(data
->format
, &tokens
);
759 /* The format needs to be recomputed each time */
760 if (data
->has_format
== 0)
762 format
= xsltEvalAttrValueTemplate(ctxt
, data
->node
,
763 (const xmlChar
*) "format",
767 xsltNumberFormatTokenize(format
, &tokens
);
771 output
= xmlBufferCreate();
773 goto XSLT_NUMBER_FORMAT_END
;
776 * Evaluate the XPath expression to find the value(s)
779 amount
= xsltNumberFormatGetValue(ctxt
->xpathCtxt
,
784 xsltNumberFormatInsertNumbers(data
,
791 } else if (data
->level
) {
793 if (xmlStrEqual(data
->level
, (const xmlChar
*) "single")) {
794 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
801 xsltNumberFormatInsertNumbers(data
,
807 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "multiple")) {
808 double numarray
[1024];
809 int max
= sizeof(numarray
)/sizeof(numarray
[0]);
810 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
817 xsltNumberFormatInsertNumbers(data
,
823 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "any")) {
824 amount
= xsltNumberFormatGetAnyLevel(ctxt
,
830 xsltNumberFormatInsertNumbers(data
,
839 * Unlike `match` patterns, `count` and `from` patterns can contain
840 * variable references, so we have to clear the pattern match
841 * cache if the "direct" matching algorithm was used.
843 if (data
->countPat
!= NULL
)
844 xsltCompMatchClearCache(ctxt
, data
->countPat
);
845 if (data
->fromPat
!= NULL
)
846 xsltCompMatchClearCache(ctxt
, data
->fromPat
);
848 /* Insert number as text node */
849 xsltCopyTextString(ctxt
, ctxt
->insert
, xmlBufferContent(output
), 0);
851 xmlBufferFree(output
);
853 XSLT_NUMBER_FORMAT_END
:
854 if (tokens
.start
!= NULL
)
855 xmlFree(tokens
.start
);
856 if (tokens
.end
!= NULL
)
858 for (i
= 0;i
< tokens
.nTokens
;i
++) {
859 if (tokens
.tokens
[i
].separator
!= NULL
)
860 xmlFree(tokens
.tokens
[i
].separator
);
865 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self
, xmlChar
**format
, xsltFormatNumberInfoPtr info
)
867 /* will hold total length of prefix/suffix without quote characters */
873 * prefix / suffix ends at end of string or at
874 * first 'special' character
878 /* if next character 'escaped' just count it */
879 if (**format
== SYMBOL_QUOTE
) {
880 if (*++(*format
) == 0)
883 else if (IS_SPECIAL(self
, *format
))
886 * else treat percent/per-mille as special cases,
887 * depending on whether +ve or -ve
891 * for +ve prefix/suffix, allow only a
892 * single occurence of either
894 if (xsltUTF8Charcmp(*format
, self
->percent
) == 0) {
895 if (info
->is_multiplier_set
)
897 info
->multiplier
= 100;
898 info
->is_multiplier_set
= TRUE
;
899 } else if (xsltUTF8Charcmp(*format
, self
->permille
) == 0) {
900 if (info
->is_multiplier_set
)
902 info
->multiplier
= 1000;
903 info
->is_multiplier_set
= TRUE
;
907 if ((len
=xmlUTF8Strsize(*format
, 1)) < 1)
915 * xsltFormatNumberConversion:
916 * @self: the decimal format
917 * @format: the format requested
918 * @number: the value to format
919 * @result: the place to output the result
921 * format-number() uses the JDK 1.1 DecimalFormat class:
923 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
927 * pattern := subpattern{;subpattern}
928 * subpattern := {prefix}integer{.fraction}{suffix}
929 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
930 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
931 * integer := '#'* '0'* '0'
932 * fraction := '0'* '#'*
935 * X* 0 or more instances of X
936 * (X | Y) either X or Y.
937 * X..Y any character from X up to Y, inclusive.
938 * S - T characters in S, except those in T
940 * Special Characters:
944 * # a digit, zero shows as absent
945 * . placeholder for decimal separator
946 * , placeholder for grouping separator.
947 * ; separates formats.
948 * - default negative prefix.
949 * % multiply by 100 and show as percentage
950 * ? multiply by 1000 and show as per mille
951 * X any other characters can be used in the prefix or suffix
952 * ' used to quote special characters in a prefix or suffix.
954 * Returns a possible XPath error
957 xsltFormatNumberConversion(xsltDecimalFormatPtr self
,
962 xmlXPathError status
= XPATH_EXPRESSION_OK
;
964 xmlChar
*the_format
, *prefix
= NULL
, *suffix
= NULL
;
965 xmlChar
*nprefix
, *nsuffix
= NULL
;
966 int prefix_length
, suffix_length
= 0, nprefix_length
, nsuffix_length
;
970 int self_grouping_len
;
971 xsltFormatNumberInfo format_info
;
973 * delayed_multiplier allows a 'trailing' percent or
974 * permille to be treated as suffix
976 int delayed_multiplier
= 0;
977 /* flag to show no -ve format present for -ve number */
978 char default_sign
= 0;
979 /* flag to show error found, should use default format */
980 char found_error
= 0;
982 if (xmlStrlen(format
) <= 0) {
983 xsltTransformError(NULL
, NULL
, NULL
,
984 "xsltFormatNumberConversion : "
985 "Invalid format (0-length)\n");
988 if (xmlXPathIsNaN(number
)) {
989 if ((self
== NULL
) || (self
->noNumber
== NULL
))
990 *result
= xmlStrdup(BAD_CAST
"NaN");
992 *result
= xmlStrdup(self
->noNumber
);
996 format_info
.integer_hash
= 0;
997 format_info
.integer_digits
= 0;
998 format_info
.frac_digits
= 0;
999 format_info
.frac_hash
= 0;
1000 format_info
.group
= -1;
1001 format_info
.multiplier
= 1;
1002 format_info
.add_decimal
= FALSE
;
1003 format_info
.is_multiplier_set
= FALSE
;
1004 format_info
.is_negative_pattern
= FALSE
;
1006 the_format
= format
;
1009 * First we process the +ve pattern to get percent / permille,
1010 * as well as main format
1012 prefix
= the_format
;
1013 prefix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
1014 if (prefix_length
< 0) {
1020 * Here we process the "number" part of the format. It gets
1021 * a little messy because of the percent/per-mille - if that
1022 * appears at the end, it may be part of the suffix instead
1023 * of part of the number, so the variable delayed_multiplier
1024 * is used to handle it
1026 self_grouping_len
= xmlStrlen(self
->grouping
);
1027 while ((*the_format
!= 0) &&
1028 (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) != 0) &&
1029 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) {
1031 if (delayed_multiplier
!= 0) {
1032 format_info
.multiplier
= delayed_multiplier
;
1033 format_info
.is_multiplier_set
= TRUE
;
1034 delayed_multiplier
= 0;
1036 if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1037 if (format_info
.integer_digits
> 0) {
1041 format_info
.integer_hash
++;
1042 if (format_info
.group
>= 0)
1043 format_info
.group
++;
1044 } else if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1045 format_info
.integer_digits
++;
1046 if (format_info
.group
>= 0)
1047 format_info
.group
++;
1048 } else if ((self_grouping_len
> 0) &&
1049 (!xmlStrncmp(the_format
, self
->grouping
, self_grouping_len
))) {
1050 /* Reset group count */
1051 format_info
.group
= 0;
1052 the_format
+= self_grouping_len
;
1054 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1055 if (format_info
.is_multiplier_set
) {
1059 delayed_multiplier
= 100;
1060 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1061 if (format_info
.is_multiplier_set
) {
1065 delayed_multiplier
= 1000;
1069 if ((len
=xmlUTF8Strsize(the_format
, 1)) < 1) {
1077 /* We have finished the integer part, now work on fraction */
1078 if ( (*the_format
!= 0) &&
1079 (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) == 0) ) {
1080 format_info
.add_decimal
= TRUE
;
1081 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1085 the_format
+= len
; /* Skip over the decimal */
1088 while (*the_format
!= 0) {
1090 if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1091 if (format_info
.frac_hash
!= 0) {
1095 format_info
.frac_digits
++;
1096 } else if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1097 format_info
.frac_hash
++;
1098 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1099 if (format_info
.is_multiplier_set
) {
1103 delayed_multiplier
= 100;
1104 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1109 continue; /* while */
1110 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1111 if (format_info
.is_multiplier_set
) {
1115 delayed_multiplier
= 1000;
1116 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1121 continue; /* while */
1122 } else if (xsltUTF8Charcmp(the_format
, self
->grouping
) != 0) {
1125 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1130 if (delayed_multiplier
!= 0) {
1131 format_info
.multiplier
= delayed_multiplier
;
1132 delayed_multiplier
= 0;
1133 format_info
.is_multiplier_set
= TRUE
;
1138 * If delayed_multiplier is set after processing the
1139 * "number" part, should be in suffix
1141 if (delayed_multiplier
!= 0) {
1143 delayed_multiplier
= 0;
1146 suffix
= the_format
;
1147 suffix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
1148 if ( (suffix_length
< 0) ||
1149 ((*the_format
!= 0) &&
1150 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) ) {
1156 * We have processed the +ve prefix, number part and +ve suffix.
1157 * If the number is -ve, we must substitute the -ve prefix / suffix
1161 * Note that j is the number of UTF8 chars before the separator,
1162 * not the number of bytes! (bug 151975)
1164 j
= xmlUTF8Strloc(format
, self
->patternSeparator
);
1166 /* No -ve pattern present, so use default signing */
1170 /* Skip over pattern separator (accounting for UTF8) */
1171 the_format
= (xmlChar
*)xmlUTF8Strpos(format
, j
+ 1);
1173 * Flag changes interpretation of percent/permille
1176 format_info
.is_negative_pattern
= TRUE
;
1177 format_info
.is_multiplier_set
= FALSE
;
1179 /* First do the -ve prefix */
1180 nprefix
= the_format
;
1181 nprefix_length
= xsltFormatNumberPreSuffix(self
,
1182 &the_format
, &format_info
);
1183 if (nprefix_length
<0) {
1188 while (*the_format
!= 0) {
1189 if ( (xsltUTF8Charcmp(the_format
, (self
)->percent
) == 0) ||
1190 (xsltUTF8Charcmp(the_format
, (self
)->permille
)== 0) ) {
1191 if (format_info
.is_multiplier_set
) {
1195 format_info
.is_multiplier_set
= TRUE
;
1196 delayed_multiplier
= 1;
1198 else if (IS_SPECIAL(self
, the_format
))
1199 delayed_multiplier
= 0;
1202 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1208 if (delayed_multiplier
!= 0) {
1209 format_info
.is_multiplier_set
= FALSE
;
1213 /* Finally do the -ve suffix */
1214 if (*the_format
!= 0) {
1215 nsuffix
= the_format
;
1216 nsuffix_length
= xsltFormatNumberPreSuffix(self
,
1217 &the_format
, &format_info
);
1218 if (nsuffix_length
< 0) {
1225 if (*the_format
!= 0) {
1230 * Here's another Java peculiarity:
1231 * if -ve prefix/suffix == +ve ones, discard & use default
1233 if ((nprefix_length
!= prefix_length
) ||
1234 (nsuffix_length
!= suffix_length
) ||
1235 ((nprefix_length
> 0) &&
1236 (xmlStrncmp(nprefix
, prefix
, prefix_length
) !=0 )) ||
1237 ((nsuffix_length
> 0) &&
1238 (xmlStrncmp(nsuffix
, suffix
, suffix_length
) !=0 ))) {
1240 prefix_length
= nprefix_length
;
1242 suffix_length
= nsuffix_length
;
1251 if (found_error
!= 0) {
1252 xsltTransformError(NULL
, NULL
, NULL
,
1253 "xsltFormatNumberConversion : "
1254 "error in format string '%s', using default\n", format
);
1255 default_sign
= (number
< 0.0) ? 1 : 0;
1256 prefix_length
= suffix_length
= 0;
1257 format_info
.integer_hash
= 0;
1258 format_info
.integer_digits
= 1;
1259 format_info
.frac_digits
= 1;
1260 format_info
.frac_hash
= 4;
1261 format_info
.group
= -1;
1262 format_info
.multiplier
= 1;
1263 format_info
.add_decimal
= TRUE
;
1266 /* Apply multiplier */
1267 number
*= (double)format_info
.multiplier
;
1268 switch (xmlXPathIsInf(number
)) {
1270 if (self
->minusSign
== NULL
)
1271 *result
= xmlStrdup(BAD_CAST
"-");
1273 *result
= xmlStrdup(self
->minusSign
);
1274 /* Intentional fall-through */
1276 if ((self
== NULL
) || (self
->infinity
== NULL
))
1277 *result
= xmlStrcat(*result
, BAD_CAST
"Infinity");
1279 *result
= xmlStrcat(*result
, self
->infinity
);
1285 buffer
= xmlBufferCreate();
1286 if (buffer
== NULL
) {
1287 return XPATH_MEMORY_ERROR
;
1290 /* Ready to output our number. First see if "default sign" is required */
1291 if (default_sign
!= 0)
1292 xmlBufferAdd(buffer
, self
->minusSign
, xmlUTF8Strsize(self
->minusSign
, 1));
1294 /* Put the prefix into the buffer */
1295 for (j
= 0; j
< prefix_length
; ) {
1296 if (*prefix
== SYMBOL_QUOTE
)
1298 len
= xmlUTF8Strsize(prefix
, 1);
1299 xmlBufferAdd(buffer
, prefix
, len
);
1304 /* Round to n digits */
1305 number
= fabs(number
);
1306 exp10
= format_info
.frac_digits
+ format_info
.frac_hash
;
1307 /* DBL_MAX_10_EXP should be 308 on IEEE platforms. */
1308 if (exp10
> DBL_MAX_10_EXP
) {
1309 if (format_info
.frac_digits
> DBL_MAX_10_EXP
) {
1310 format_info
.frac_digits
= DBL_MAX_10_EXP
;
1311 format_info
.frac_hash
= 0;
1313 format_info
.frac_hash
= DBL_MAX_10_EXP
- format_info
.frac_digits
;
1315 exp10
= DBL_MAX_10_EXP
;
1317 scale
= pow(10.0, (double) exp10
);
1318 number
+= .5 / scale
;
1319 number
-= fmod(number
, 1 / scale
);
1321 /* Next do the integer part of the number */
1322 if ((self
->grouping
!= NULL
) &&
1323 (self
->grouping
[0] != 0)) {
1326 len
= xmlStrlen(self
->grouping
);
1327 gchar
= xsltGetUTF8Char(self
->grouping
, &len
);
1328 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1329 format_info
.integer_digits
,
1333 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1334 format_info
.integer_digits
,
1338 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1339 if ((format_info
.integer_digits
+ format_info
.integer_hash
+
1340 format_info
.frac_digits
== 0) && (format_info
.frac_hash
> 0)) {
1341 ++format_info
.frac_digits
;
1342 --format_info
.frac_hash
;
1345 /* Add leading zero, if required */
1346 if ((floor(number
) == 0) &&
1347 (format_info
.integer_digits
+ format_info
.frac_digits
== 0)) {
1348 xmlBufferAdd(buffer
, self
->zeroDigit
, xmlUTF8Strsize(self
->zeroDigit
, 1));
1351 /* Next the fractional part, if required */
1352 if (format_info
.frac_digits
+ format_info
.frac_hash
== 0) {
1353 if (format_info
.add_decimal
)
1354 xmlBufferAdd(buffer
, self
->decimalPoint
,
1355 xmlUTF8Strsize(self
->decimalPoint
, 1));
1358 number
-= floor(number
);
1359 if ((number
!= 0) || (format_info
.frac_digits
!= 0)) {
1360 xmlBufferAdd(buffer
, self
->decimalPoint
,
1361 xmlUTF8Strsize(self
->decimalPoint
, 1));
1362 number
= floor(scale
* number
+ 0.5);
1363 for (j
= format_info
.frac_hash
; j
> 0; j
--) {
1364 if (fmod(number
, 10.0) >= 1.0)
1368 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1369 format_info
.frac_digits
+ j
,
1373 /* Put the suffix into the buffer */
1374 for (j
= 0; j
< suffix_length
; ) {
1375 if (*suffix
== SYMBOL_QUOTE
)
1377 len
= xmlUTF8Strsize(suffix
, 1);
1378 xmlBufferAdd(buffer
, suffix
, len
);
1383 *result
= xmlStrdup(xmlBufferContent(buffer
));
1384 xmlBufferFree(buffer
);