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>
16 # define FALSE (0 == 1)
17 # define TRUE (1 == 1)
20 #define SYMBOL_QUOTE ((xmlChar)'\'')
22 #define DEFAULT_TOKEN (xmlChar)'0'
23 #define DEFAULT_SEPARATOR "."
25 #define MAX_TOKENS 1024
27 typedef struct _xsltFormatToken xsltFormatToken
;
28 typedef xsltFormatToken
*xsltFormatTokenPtr
;
29 struct _xsltFormatToken
{
35 typedef struct _xsltFormat xsltFormat
;
36 typedef xsltFormat
*xsltFormatPtr
;
39 xsltFormatToken tokens
[MAX_TOKENS
];
44 static char alpha_upper_list
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
45 static char alpha_lower_list
[] = "abcdefghijklmnopqrstuvwxyz";
46 static xsltFormatToken default_token
;
49 * **** Start temp insert ****
51 * The following routine xsltUTF8Charcmp will be replaced with calls to
52 * the corresponding libxml routine at a later date (when other
53 * inter-library dependencies require it).
58 * @utf1: pointer to first UTF8 char
59 * @utf2: pointer to second UTF8 char
61 * returns result of comparing the two UCS4 values
65 xsltUTF8Charcmp(xmlChar
*utf1
, xmlChar
*utf2
) {
66 int len
= xmlUTF8Strsize(utf1
, 1);
75 return xmlStrncmp(utf1
, utf2
, len
);
78 /***** Stop temp insert *****/
79 /************************************************************************
83 ************************************************************************/
85 #define IS_SPECIAL(self,letter) \
86 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
87 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
88 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
89 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
90 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
92 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
93 #define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
96 xsltIsDigitZero(unsigned int ch
)
99 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
102 case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
103 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
104 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
105 case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
106 case 0x1810: case 0xFF10:
114 xsltNumberFormatDecimal(xmlBufferPtr buffer
,
119 int groupingCharacter
,
120 int groupingCharacterLen
)
124 * xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4];
125 * which would be length 68 on x86 arch. It was changed to be a longer,
126 * fixed length in order to try to cater for (reasonable) UTF8
127 * separators and numeric characters. The max UTF8 char size will be
128 * 6 or less, so the value used [500] should be *much* larger than needed
130 xmlChar temp_string
[500];
132 xmlChar temp_char
[6];
137 /* Build buffer from back */
138 pointer
= &temp_string
[sizeof(temp_string
)] - 1; /* last char */
141 while (pointer
> temp_string
) {
142 if ((i
>= width
) && (fabs(number
) < 1.0))
144 if ((i
> 0) && (groupingCharacter
!= 0) &&
145 (digitsPerGroup
> 0) &&
146 ((i
% digitsPerGroup
) == 0)) {
147 if (pointer
- groupingCharacterLen
< temp_string
) {
148 i
= -1; /* flag error */
151 pointer
-= groupingCharacterLen
;
152 xmlCopyCharMultiByte(pointer
, groupingCharacter
);
155 val
= digit_zero
+ (int)fmod(number
, 10.0);
156 if (val
< 0x80) { /* shortcut if ASCII */
157 if (pointer
<= temp_string
) { /* Check enough room */
165 * Here we have a multibyte character. It's a little messy,
166 * because until we generate the char we don't know how long
167 * it is. So, we generate it into the buffer temp_char, then
168 * copy from there into temp_string.
170 len
= xmlCopyCharMultiByte(temp_char
, val
);
171 if ( (pointer
- len
) < temp_string
) {
176 memcpy(pointer
, temp_char
, len
);
182 xsltGenericError(xsltGenericErrorContext
,
183 "xsltNumberFormatDecimal: Internal buffer size exceeded\n");
184 xmlBufferCat(buffer
, pointer
);
188 xsltNumberFormatAlpha(xsltNumberDataPtr data
,
193 char temp_string
[sizeof(double) * CHAR_BIT
* sizeof(xmlChar
) + 1];
197 double alpha_size
= (double)(sizeof(alpha_upper_list
) - 1);
200 * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says:
202 * For all format tokens other than the first kind above (one that
203 * consists of decimal digits), there may be implementation-defined
204 * lower and upper bounds on the range of numbers that can be
205 * formatted using this format token; indeed, for some numbering
206 * sequences there may be intrinsic limits. [...] Numbers that fall
207 * outside this range must be formatted using the format token 1.
209 * The "a" token has an intrinsic lower limit of 1.
212 xsltNumberFormatDecimal(buffer
, number
, '0', 1,
213 data
->digitsPerGroup
,
214 data
->groupingCharacter
,
215 data
->groupingCharacterLen
);
219 /* Build buffer from back */
220 pointer
= &temp_string
[sizeof(temp_string
)];
222 alpha_list
= (is_upper
) ? alpha_upper_list
: alpha_lower_list
;
224 for (i
= 1; i
< (int)sizeof(temp_string
); i
++) {
226 *(--pointer
) = alpha_list
[((int)fmod(number
, alpha_size
))];
227 number
/= alpha_size
;
231 xmlBufferCCat(buffer
, pointer
);
235 xsltNumberFormatRoman(xsltNumberDataPtr data
,
241 * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper
242 * bound to avoid denial of service.
244 if (number
< 1.0 || number
> 5000.0) {
245 xsltNumberFormatDecimal(buffer
, number
, '0', 1,
246 data
->digitsPerGroup
,
247 data
->groupingCharacter
,
248 data
->groupingCharacterLen
);
253 * Based on an example by Jim Walsh
255 while (number
>= 1000.0) {
256 xmlBufferCCat(buffer
, (is_upper
) ? "M" : "m");
259 if (number
>= 900.0) {
260 xmlBufferCCat(buffer
, (is_upper
) ? "CM" : "cm");
263 while (number
>= 500.0) {
264 xmlBufferCCat(buffer
, (is_upper
) ? "D" : "d");
267 if (number
>= 400.0) {
268 xmlBufferCCat(buffer
, (is_upper
) ? "CD" : "cd");
271 while (number
>= 100.0) {
272 xmlBufferCCat(buffer
, (is_upper
) ? "C" : "c");
275 if (number
>= 90.0) {
276 xmlBufferCCat(buffer
, (is_upper
) ? "XC" : "xc");
279 while (number
>= 50.0) {
280 xmlBufferCCat(buffer
, (is_upper
) ? "L" : "l");
283 if (number
>= 40.0) {
284 xmlBufferCCat(buffer
, (is_upper
) ? "XL" : "xl");
287 while (number
>= 10.0) {
288 xmlBufferCCat(buffer
, (is_upper
) ? "X" : "x");
292 xmlBufferCCat(buffer
, (is_upper
) ? "IX" : "ix");
295 while (number
>= 5.0) {
296 xmlBufferCCat(buffer
, (is_upper
) ? "V" : "v");
300 xmlBufferCCat(buffer
, (is_upper
) ? "IV" : "iv");
303 while (number
>= 1.0) {
304 xmlBufferCCat(buffer
, (is_upper
) ? "I" : "i");
310 xsltNumberFormatTokenize(const xmlChar
*format
,
311 xsltFormatPtr tokens
)
318 default_token
.token
= DEFAULT_TOKEN
;
319 default_token
.width
= 1;
320 default_token
.separator
= BAD_CAST(DEFAULT_SEPARATOR
);
323 tokens
->start
= NULL
;
324 tokens
->tokens
[0].separator
= NULL
;
328 * Insert initial non-alphanumeric token.
329 * There is always such a token in the list, even if NULL
331 while (! (IS_LETTER(val
=xmlStringCurrentChar(NULL
, format
+ix
, &len
)) ||
333 if (format
[ix
] == 0) /* if end of format string */
338 tokens
->start
= xmlStrndup(format
, ix
);
341 for (tokens
->nTokens
= 0; tokens
->nTokens
< MAX_TOKENS
;
347 * separator has already been parsed (except for the first
348 * number) in tokens->end, recover it.
350 if (tokens
->nTokens
> 0) {
351 tokens
->tokens
[tokens
->nTokens
].separator
= tokens
->end
;
355 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
356 if (IS_DIGIT_ONE(val
) ||
357 IS_DIGIT_ZERO(val
)) {
358 tokens
->tokens
[tokens
->nTokens
].width
= 1;
359 while (IS_DIGIT_ZERO(val
)) {
360 tokens
->tokens
[tokens
->nTokens
].width
++;
362 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
364 if (IS_DIGIT_ONE(val
)) {
365 tokens
->tokens
[tokens
->nTokens
].token
= val
- 1;
367 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
369 } else if ( (val
== (xmlChar
)'A') ||
370 (val
== (xmlChar
)'a') ||
371 (val
== (xmlChar
)'I') ||
372 (val
== (xmlChar
)'i') ) {
373 tokens
->tokens
[tokens
->nTokens
].token
= val
;
375 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
378 * "Any other format token indicates a numbering sequence
379 * that starts with that token. If an implementation does
380 * not support a numbering sequence that starts with that
381 * token, it must use a format token of 1."
383 tokens
->tokens
[tokens
->nTokens
].token
= (xmlChar
)'0';
384 tokens
->tokens
[tokens
->nTokens
].width
= 1;
387 * Skip over remaining alphanumeric characters from the Nd
388 * (Number, decimal digit), Nl (Number, letter), No (Number,
389 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
390 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
391 * (Letters, other (uncased)) Unicode categories. This happens
392 * to correspond to the Letter and Digit classes from XML (and
393 * one wonders why XSLT doesn't refer to these instead).
395 while (IS_LETTER(val
) || IS_DIGIT(val
)) {
397 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
401 * Insert temporary non-alphanumeric final tooken.
404 while (! (IS_LETTER(val
) || IS_DIGIT(val
))) {
408 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
411 tokens
->end
= xmlStrndup(&format
[j
], ix
- j
);
416 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data
,
419 xsltFormatPtr tokens
,
424 xsltFormatTokenPtr token
;
427 * Handle initial non-alphanumeric token
429 if (tokens
->start
!= NULL
)
430 xmlBufferCat(buffer
, tokens
->start
);
432 for (i
= 0; i
< numbers_max
; i
++) {
434 number
= numbers
[(numbers_max
- 1) - i
];
435 /* Round to nearest like XSLT 2.0 */
436 number
= floor(number
+ 0.5);
438 * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT
441 * It is a non-recoverable dynamic error if any undiscarded item
442 * in the atomized sequence supplied as the value of the value
443 * attribute of xsl:number cannot be converted to an integer, or
444 * if the resulting integer is less than 0 (zero).
447 xsltTransformError(NULL
, NULL
, NULL
,
448 "xsl-number : negative value\n");
449 /* Recover by treating negative values as zero. */
452 if (i
< tokens
->nTokens
) {
454 * The "n"th format token will be used to format the "n"th
457 token
= &(tokens
->tokens
[i
]);
458 } else if (tokens
->nTokens
> 0) {
460 * If there are more numbers than format tokens, then the
461 * last format token will be used to format the remaining
464 token
= &(tokens
->tokens
[tokens
->nTokens
- 1]);
467 * If there are no format tokens, then a format token of
468 * 1 is used to format all numbers.
470 token
= &default_token
;
473 /* Print separator, except for the first number */
475 if (token
->separator
!= NULL
)
476 xmlBufferCat(buffer
, token
->separator
);
478 xmlBufferCCat(buffer
, DEFAULT_SEPARATOR
);
481 switch (xmlXPathIsInf(number
)) {
483 xmlBufferCCat(buffer
, "-Infinity");
486 xmlBufferCCat(buffer
, "Infinity");
489 if (xmlXPathIsNaN(number
)) {
490 xmlBufferCCat(buffer
, "NaN");
493 switch (token
->token
) {
495 xsltNumberFormatAlpha(data
, buffer
, number
, TRUE
);
498 xsltNumberFormatAlpha(data
, buffer
, number
, FALSE
);
501 xsltNumberFormatRoman(data
, buffer
, number
, TRUE
);
504 xsltNumberFormatRoman(data
, buffer
, number
, FALSE
);
507 if (IS_DIGIT_ZERO(token
->token
)) {
508 xsltNumberFormatDecimal(buffer
,
512 data
->digitsPerGroup
,
513 data
->groupingCharacter
,
514 data
->groupingCharacterLen
);
524 * Handle final non-alphanumeric token
526 if (tokens
->end
!= NULL
)
527 xmlBufferCat(buffer
, tokens
->end
);
532 xsltTestCompMatchCount(xsltTransformContextPtr context
,
534 xsltCompMatchPtr countPat
,
537 if (countPat
!= NULL
) {
538 return xsltTestCompMatchList(context
, node
, countPat
);
544 * If count attribute is not specified, then it defaults to the
545 * pattern that matches any node with the same node type as the
546 * current node and, if the current node has an expanded-name, with
547 * the same expanded-name as the current node.
549 if (node
->type
!= cur
->type
)
551 if (node
->type
== XML_NAMESPACE_DECL
)
553 * Namespace nodes have no preceding siblings and no parents
554 * that are namespace nodes. This means that node == cur.
557 /* TODO: Skip node types without expanded names like text nodes. */
558 if (!xmlStrEqual(node
->name
, cur
->name
))
560 if (node
->ns
== cur
->ns
)
562 if ((node
->ns
== NULL
) || (cur
->ns
== NULL
))
564 return (xmlStrEqual(node
->ns
->href
, cur
->ns
->href
));
569 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context
,
571 xsltCompMatchPtr countPat
,
572 xsltCompMatchPtr fromPat
,
577 xmlNodePtr cur
= node
;
579 while (cur
!= NULL
) {
580 /* process current node */
581 if (xsltTestCompMatchCount(context
, cur
, countPat
, node
))
583 if ((fromPat
!= NULL
) &&
584 xsltTestCompMatchList(context
, cur
, fromPat
)) {
588 /* Skip to next preceding or ancestor */
589 if ((cur
->type
== XML_DOCUMENT_NODE
) ||
590 #ifdef LIBXML_DOCB_ENABLED
591 (cur
->type
== XML_DOCB_DOCUMENT_NODE
) ||
593 (cur
->type
== XML_HTML_DOCUMENT_NODE
))
596 if (cur
->type
== XML_NAMESPACE_DECL
) {
598 * The XPath module stores the parent of a namespace node in
599 * the ns->next field.
601 cur
= (xmlNodePtr
) ((xmlNsPtr
) cur
)->next
;
602 } else if (cur
->type
== XML_ATTRIBUTE_NODE
) {
605 while ((cur
->prev
!= NULL
) && ((cur
->prev
->type
== XML_DTD_NODE
) ||
606 (cur
->prev
->type
== XML_XINCLUDE_START
) ||
607 (cur
->prev
->type
== XML_XINCLUDE_END
)))
609 if (cur
->prev
!= NULL
) {
610 for (cur
= cur
->prev
; cur
->last
!= NULL
; cur
= cur
->last
);
617 array
[amount
++] = (double) cnt
;
623 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context
,
625 xsltCompMatchPtr countPat
,
626 xsltCompMatchPtr fromPat
,
633 xmlNodePtr preceding
;
634 xmlXPathParserContextPtr parser
;
636 context
->xpathCtxt
->node
= node
;
637 parser
= xmlXPathNewParserContext(NULL
, context
->xpathCtxt
);
639 /* ancestor-or-self::*[count] */
640 for (ancestor
= node
;
641 (ancestor
!= NULL
) && (ancestor
->type
!= XML_DOCUMENT_NODE
);
642 ancestor
= xmlXPathNextAncestor(parser
, ancestor
)) {
644 if ((fromPat
!= NULL
) &&
645 xsltTestCompMatchList(context
, ancestor
, fromPat
))
648 if (xsltTestCompMatchCount(context
, ancestor
, countPat
, node
)) {
649 /* count(preceding-sibling::*) */
652 xmlXPathNextPrecedingSibling(parser
, ancestor
);
655 xmlXPathNextPrecedingSibling(parser
, preceding
)) {
657 if (xsltTestCompMatchCount(context
, preceding
, countPat
,
661 array
[amount
++] = (double)cnt
;
666 xmlXPathFreeParserContext(parser
);
672 xsltNumberFormatGetValue(xmlXPathContextPtr context
,
674 const xmlChar
*value
,
678 xmlBufferPtr pattern
;
679 xmlXPathObjectPtr obj
;
681 pattern
= xmlBufferCreate();
682 if (pattern
!= NULL
) {
683 xmlBufferCCat(pattern
, "number(");
684 xmlBufferCat(pattern
, value
);
685 xmlBufferCCat(pattern
, ")");
686 context
->node
= node
;
687 obj
= xmlXPathEvalExpression(xmlBufferContent(pattern
),
690 *number
= obj
->floatval
;
692 xmlXPathFreeObject(obj
);
694 xmlBufferFree(pattern
);
701 * @ctxt: the XSLT transformation context
702 * @data: the formatting information
703 * @node: the data to format
705 * Convert one number.
708 xsltNumberFormat(xsltTransformContextPtr ctxt
,
709 xsltNumberDataPtr data
,
712 xmlBufferPtr output
= NULL
;
717 if (data
->format
!= NULL
) {
718 xsltNumberFormatTokenize(data
->format
, &tokens
);
723 /* The format needs to be recomputed each time */
724 if (data
->has_format
== 0)
726 format
= xsltEvalAttrValueTemplate(ctxt
, data
->node
,
727 (const xmlChar
*) "format",
731 xsltNumberFormatTokenize(format
, &tokens
);
735 output
= xmlBufferCreate();
737 goto XSLT_NUMBER_FORMAT_END
;
740 * Evaluate the XPath expression to find the value(s)
743 amount
= xsltNumberFormatGetValue(ctxt
->xpathCtxt
,
748 xsltNumberFormatInsertNumbers(data
,
755 } else if (data
->level
) {
757 if (xmlStrEqual(data
->level
, (const xmlChar
*) "single")) {
758 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
765 xsltNumberFormatInsertNumbers(data
,
771 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "multiple")) {
772 double numarray
[1024];
773 int max
= sizeof(numarray
)/sizeof(numarray
[0]);
774 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
781 xsltNumberFormatInsertNumbers(data
,
787 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "any")) {
788 amount
= xsltNumberFormatGetAnyLevel(ctxt
,
794 xsltNumberFormatInsertNumbers(data
,
802 /* Insert number as text node */
803 xsltCopyTextString(ctxt
, ctxt
->insert
, xmlBufferContent(output
), 0);
805 xmlBufferFree(output
);
807 XSLT_NUMBER_FORMAT_END
:
808 if (tokens
.start
!= NULL
)
809 xmlFree(tokens
.start
);
810 if (tokens
.end
!= NULL
)
812 for (i
= 0;i
< tokens
.nTokens
;i
++) {
813 if (tokens
.tokens
[i
].separator
!= NULL
)
814 xmlFree(tokens
.tokens
[i
].separator
);
819 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self
, xmlChar
**format
, xsltFormatNumberInfoPtr info
)
821 int count
=0; /* will hold total length of prefix/suffix */
826 * prefix / suffix ends at end of string or at
827 * first 'special' character
831 /* if next character 'escaped' just count it */
832 if (**format
== SYMBOL_QUOTE
) {
833 if (*++(*format
) == 0)
836 else if (IS_SPECIAL(self
, *format
))
839 * else treat percent/per-mille as special cases,
840 * depending on whether +ve or -ve
844 * for +ve prefix/suffix, allow only a
845 * single occurence of either
847 if (xsltUTF8Charcmp(*format
, self
->percent
) == 0) {
848 if (info
->is_multiplier_set
)
850 info
->multiplier
= 100;
851 info
->is_multiplier_set
= TRUE
;
852 } else if (xsltUTF8Charcmp(*format
, self
->permille
) == 0) {
853 if (info
->is_multiplier_set
)
855 info
->multiplier
= 1000;
856 info
->is_multiplier_set
= TRUE
;
860 if ((len
=xmlUTF8Strsize(*format
, 1)) < 1)
868 * xsltFormatNumberConversion:
869 * @self: the decimal format
870 * @format: the format requested
871 * @number: the value to format
872 * @result: the place to output the result
874 * format-number() uses the JDK 1.1 DecimalFormat class:
876 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
880 * pattern := subpattern{;subpattern}
881 * subpattern := {prefix}integer{.fraction}{suffix}
882 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
883 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
884 * integer := '#'* '0'* '0'
885 * fraction := '0'* '#'*
888 * X* 0 or more instances of X
889 * (X | Y) either X or Y.
890 * X..Y any character from X up to Y, inclusive.
891 * S - T characters in S, except those in T
893 * Special Characters:
897 * # a digit, zero shows as absent
898 * . placeholder for decimal separator
899 * , placeholder for grouping separator.
900 * ; separates formats.
901 * - default negative prefix.
902 * % multiply by 100 and show as percentage
903 * ? multiply by 1000 and show as per mille
904 * X any other characters can be used in the prefix or suffix
905 * ' used to quote special characters in a prefix or suffix.
907 * Returns a possible XPath error
910 xsltFormatNumberConversion(xsltDecimalFormatPtr self
,
915 xmlXPathError status
= XPATH_EXPRESSION_OK
;
917 xmlChar
*the_format
, *prefix
= NULL
, *suffix
= NULL
;
918 xmlChar
*nprefix
, *nsuffix
= NULL
;
920 int prefix_length
, suffix_length
= 0, nprefix_length
, nsuffix_length
;
923 int self_grouping_len
;
924 xsltFormatNumberInfo format_info
;
926 * delayed_multiplier allows a 'trailing' percent or
927 * permille to be treated as suffix
929 int delayed_multiplier
= 0;
930 /* flag to show no -ve format present for -ve number */
931 char default_sign
= 0;
932 /* flag to show error found, should use default format */
933 char found_error
= 0;
935 if (xmlStrlen(format
) <= 0) {
936 xsltTransformError(NULL
, NULL
, NULL
,
937 "xsltFormatNumberConversion : "
938 "Invalid format (0-length)\n");
941 switch (xmlXPathIsInf(number
)) {
943 if (self
->minusSign
== NULL
)
944 *result
= xmlStrdup(BAD_CAST
"-");
946 *result
= xmlStrdup(self
->minusSign
);
947 /* no-break on purpose */
949 if ((self
== NULL
) || (self
->infinity
== NULL
))
950 *result
= xmlStrcat(*result
, BAD_CAST
"Infinity");
952 *result
= xmlStrcat(*result
, self
->infinity
);
955 if (xmlXPathIsNaN(number
)) {
956 if ((self
== NULL
) || (self
->noNumber
== NULL
))
957 *result
= xmlStrdup(BAD_CAST
"NaN");
959 *result
= xmlStrdup(self
->noNumber
);
964 buffer
= xmlBufferCreate();
965 if (buffer
== NULL
) {
966 return XPATH_MEMORY_ERROR
;
969 format_info
.integer_hash
= 0;
970 format_info
.integer_digits
= 0;
971 format_info
.frac_digits
= 0;
972 format_info
.frac_hash
= 0;
973 format_info
.group
= -1;
974 format_info
.multiplier
= 1;
975 format_info
.add_decimal
= FALSE
;
976 format_info
.is_multiplier_set
= FALSE
;
977 format_info
.is_negative_pattern
= FALSE
;
982 * First we process the +ve pattern to get percent / permille,
983 * as well as main format
986 prefix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
987 if (prefix_length
< 0) {
993 * Here we process the "number" part of the format. It gets
994 * a little messy because of the percent/per-mille - if that
995 * appears at the end, it may be part of the suffix instead
996 * of part of the number, so the variable delayed_multiplier
997 * is used to handle it
999 self_grouping_len
= xmlStrlen(self
->grouping
);
1000 while ((*the_format
!= 0) &&
1001 (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) != 0) &&
1002 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) {
1004 if (delayed_multiplier
!= 0) {
1005 format_info
.multiplier
= delayed_multiplier
;
1006 format_info
.is_multiplier_set
= TRUE
;
1007 delayed_multiplier
= 0;
1009 if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1010 if (format_info
.integer_digits
> 0) {
1014 format_info
.integer_hash
++;
1015 if (format_info
.group
>= 0)
1016 format_info
.group
++;
1017 } else if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1018 format_info
.integer_digits
++;
1019 if (format_info
.group
>= 0)
1020 format_info
.group
++;
1021 } else if ((self_grouping_len
> 0) &&
1022 (!xmlStrncmp(the_format
, self
->grouping
, self_grouping_len
))) {
1023 /* Reset group count */
1024 format_info
.group
= 0;
1025 the_format
+= self_grouping_len
;
1027 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1028 if (format_info
.is_multiplier_set
) {
1032 delayed_multiplier
= 100;
1033 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1034 if (format_info
.is_multiplier_set
) {
1038 delayed_multiplier
= 1000;
1042 if ((len
=xmlUTF8Strsize(the_format
, 1)) < 1) {
1050 /* We have finished the integer part, now work on fraction */
1051 if ( (*the_format
!= 0) &&
1052 (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) == 0) ) {
1053 format_info
.add_decimal
= TRUE
;
1054 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1058 the_format
+= len
; /* Skip over the decimal */
1061 while (*the_format
!= 0) {
1063 if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1064 if (format_info
.frac_hash
!= 0) {
1068 format_info
.frac_digits
++;
1069 } else if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1070 format_info
.frac_hash
++;
1071 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1072 if (format_info
.is_multiplier_set
) {
1076 delayed_multiplier
= 100;
1077 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1082 continue; /* while */
1083 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1084 if (format_info
.is_multiplier_set
) {
1088 delayed_multiplier
= 1000;
1089 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1094 continue; /* while */
1095 } else if (xsltUTF8Charcmp(the_format
, self
->grouping
) != 0) {
1098 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1103 if (delayed_multiplier
!= 0) {
1104 format_info
.multiplier
= delayed_multiplier
;
1105 delayed_multiplier
= 0;
1106 format_info
.is_multiplier_set
= TRUE
;
1111 * If delayed_multiplier is set after processing the
1112 * "number" part, should be in suffix
1114 if (delayed_multiplier
!= 0) {
1116 delayed_multiplier
= 0;
1119 suffix
= the_format
;
1120 suffix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
1121 if ( (suffix_length
< 0) ||
1122 ((*the_format
!= 0) &&
1123 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) ) {
1129 * We have processed the +ve prefix, number part and +ve suffix.
1130 * If the number is -ve, we must substitute the -ve prefix / suffix
1134 * Note that j is the number of UTF8 chars before the separator,
1135 * not the number of bytes! (bug 151975)
1137 j
= xmlUTF8Strloc(format
, self
->patternSeparator
);
1139 /* No -ve pattern present, so use default signing */
1143 /* Skip over pattern separator (accounting for UTF8) */
1144 the_format
= (xmlChar
*)xmlUTF8Strpos(format
, j
+ 1);
1146 * Flag changes interpretation of percent/permille
1149 format_info
.is_negative_pattern
= TRUE
;
1150 format_info
.is_multiplier_set
= FALSE
;
1152 /* First do the -ve prefix */
1153 nprefix
= the_format
;
1154 nprefix_length
= xsltFormatNumberPreSuffix(self
,
1155 &the_format
, &format_info
);
1156 if (nprefix_length
<0) {
1161 while (*the_format
!= 0) {
1162 if ( (xsltUTF8Charcmp(the_format
, (self
)->percent
) == 0) ||
1163 (xsltUTF8Charcmp(the_format
, (self
)->permille
)== 0) ) {
1164 if (format_info
.is_multiplier_set
) {
1168 format_info
.is_multiplier_set
= TRUE
;
1169 delayed_multiplier
= 1;
1171 else if (IS_SPECIAL(self
, the_format
))
1172 delayed_multiplier
= 0;
1175 if ((len
= xmlUTF8Strsize(the_format
, 1)) < 1) {
1181 if (delayed_multiplier
!= 0) {
1182 format_info
.is_multiplier_set
= FALSE
;
1186 /* Finally do the -ve suffix */
1187 if (*the_format
!= 0) {
1188 nsuffix
= the_format
;
1189 nsuffix_length
= xsltFormatNumberPreSuffix(self
,
1190 &the_format
, &format_info
);
1191 if (nsuffix_length
< 0) {
1198 if (*the_format
!= 0) {
1203 * Here's another Java peculiarity:
1204 * if -ve prefix/suffix == +ve ones, discard & use default
1206 if ((nprefix_length
!= prefix_length
) ||
1207 (nsuffix_length
!= suffix_length
) ||
1208 ((nprefix_length
> 0) &&
1209 (xmlStrncmp(nprefix
, prefix
, prefix_length
) !=0 )) ||
1210 ((nsuffix_length
> 0) &&
1211 (xmlStrncmp(nsuffix
, suffix
, suffix_length
) !=0 ))) {
1213 prefix_length
= nprefix_length
;
1215 suffix_length
= nsuffix_length
;
1224 if (found_error
!= 0) {
1225 xsltTransformError(NULL
, NULL
, NULL
,
1226 "xsltFormatNumberConversion : "
1227 "error in format string '%s', using default\n", format
);
1228 default_sign
= (number
< 0.0) ? 1 : 0;
1229 prefix_length
= suffix_length
= 0;
1230 format_info
.integer_hash
= 0;
1231 format_info
.integer_digits
= 1;
1232 format_info
.frac_digits
= 1;
1233 format_info
.frac_hash
= 4;
1234 format_info
.group
= -1;
1235 format_info
.multiplier
= 1;
1236 format_info
.add_decimal
= TRUE
;
1239 /* Ready to output our number. First see if "default sign" is required */
1240 if (default_sign
!= 0)
1241 xmlBufferAdd(buffer
, self
->minusSign
, xmlUTF8Strsize(self
->minusSign
, 1));
1243 /* Put the prefix into the buffer */
1244 for (j
= 0; j
< prefix_length
; j
++) {
1245 if ((pchar
= *prefix
++) == SYMBOL_QUOTE
) {
1246 len
= xmlUTF8Strsize(prefix
, 1);
1247 xmlBufferAdd(buffer
, prefix
, len
);
1249 j
+= len
- 1; /* length of symbol less length of quote */
1251 xmlBufferAdd(buffer
, &pchar
, 1);
1254 /* Next do the integer part of the number */
1255 number
= fabs(number
) * (double)format_info
.multiplier
;
1256 scale
= pow(10.0, (double)(format_info
.frac_digits
+ format_info
.frac_hash
));
1257 number
= floor((scale
* number
+ 0.5)) / scale
;
1258 if ((self
->grouping
!= NULL
) &&
1259 (self
->grouping
[0] != 0)) {
1261 len
= xmlStrlen(self
->grouping
);
1262 pchar
= xsltGetUTF8Char(self
->grouping
, &len
);
1263 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1264 format_info
.integer_digits
,
1268 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1269 format_info
.integer_digits
,
1273 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1274 if ((format_info
.integer_digits
+ format_info
.integer_hash
+
1275 format_info
.frac_digits
== 0) && (format_info
.frac_hash
> 0)) {
1276 ++format_info
.frac_digits
;
1277 --format_info
.frac_hash
;
1280 /* Add leading zero, if required */
1281 if ((floor(number
) == 0) &&
1282 (format_info
.integer_digits
+ format_info
.frac_digits
== 0)) {
1283 xmlBufferAdd(buffer
, self
->zeroDigit
, xmlUTF8Strsize(self
->zeroDigit
, 1));
1286 /* Next the fractional part, if required */
1287 if (format_info
.frac_digits
+ format_info
.frac_hash
== 0) {
1288 if (format_info
.add_decimal
)
1289 xmlBufferAdd(buffer
, self
->decimalPoint
,
1290 xmlUTF8Strsize(self
->decimalPoint
, 1));
1293 number
-= floor(number
);
1294 if ((number
!= 0) || (format_info
.frac_digits
!= 0)) {
1295 xmlBufferAdd(buffer
, self
->decimalPoint
,
1296 xmlUTF8Strsize(self
->decimalPoint
, 1));
1297 number
= floor(scale
* number
+ 0.5);
1298 for (j
= format_info
.frac_hash
; j
> 0; j
--) {
1299 if (fmod(number
, 10.0) >= 1.0)
1303 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1304 format_info
.frac_digits
+ j
,
1308 /* Put the suffix into the buffer */
1309 for (j
= 0; j
< suffix_length
; j
++) {
1310 if ((pchar
= *suffix
++) == SYMBOL_QUOTE
) {
1311 len
= xmlUTF8Strsize(suffix
, 1);
1312 xmlBufferAdd(buffer
, suffix
, len
);
1314 j
+= len
- 1; /* length of symbol less length of escape */
1316 xmlBufferAdd(buffer
, &pchar
, 1);
1319 *result
= xmlStrdup(xmlBufferContent(buffer
));
1320 xmlBufferFree(buffer
);