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 two routines (xsltUTF8Size and xsltUTF8Charcmp)
52 * will be replaced with calls to the corresponding libxml routines
53 * at a later date (when other inter-library dependencies require it)
58 * @utf: pointer to the UTF8 character
60 * returns the numbers of bytes in the character, -1 on format error
63 xsltUTF8Size(xmlChar
*utf
) {
71 /* check valid UTF8 character */
74 /* determine number of bytes in char */
76 for (mask
=0x20; mask
!= 0; mask
>>=1) {
86 * @utf1: pointer to first UTF8 char
87 * @utf2: pointer to second UTF8 char
89 * returns result of comparing the two UCS4 values
93 xsltUTF8Charcmp(xmlChar
*utf1
, xmlChar
*utf2
) {
100 return xmlStrncmp(utf1
, utf2
, xsltUTF8Size(utf1
));
103 /***** Stop temp insert *****/
104 /************************************************************************
106 * Utility functions *
108 ************************************************************************/
110 #define IS_SPECIAL(self,letter) \
111 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
112 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
113 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
114 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
115 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
117 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
118 #define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
121 xsltIsDigitZero(unsigned int ch
)
124 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
127 case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
128 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
129 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
130 case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
131 case 0x1810: case 0xFF10:
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");
209 xmlBufferCat(buffer
, pointer
);
213 xsltNumberFormatAlpha(xmlBufferPtr buffer
,
217 char temp_string
[sizeof(double) * CHAR_BIT
* sizeof(xmlChar
) + 1];
221 double alpha_size
= (double)(sizeof(alpha_upper_list
) - 1);
223 /* Build buffer from back */
224 pointer
= &temp_string
[sizeof(temp_string
)];
226 alpha_list
= (is_upper
) ? alpha_upper_list
: alpha_lower_list
;
228 for (i
= 1; i
< (int)sizeof(temp_string
); i
++) {
230 *(--pointer
) = alpha_list
[((int)fmod(number
, alpha_size
))];
231 number
/= alpha_size
;
232 if (fabs(number
) < 1.0)
235 xmlBufferCCat(buffer
, pointer
);
239 xsltNumberFormatRoman(xmlBufferPtr buffer
,
244 * Based on an example by Jim Walsh
246 while (number
>= 1000.0) {
247 xmlBufferCCat(buffer
, (is_upper
) ? "M" : "m");
250 if (number
>= 900.0) {
251 xmlBufferCCat(buffer
, (is_upper
) ? "CM" : "cm");
254 while (number
>= 500.0) {
255 xmlBufferCCat(buffer
, (is_upper
) ? "D" : "d");
258 if (number
>= 400.0) {
259 xmlBufferCCat(buffer
, (is_upper
) ? "CD" : "cd");
262 while (number
>= 100.0) {
263 xmlBufferCCat(buffer
, (is_upper
) ? "C" : "c");
266 if (number
>= 90.0) {
267 xmlBufferCCat(buffer
, (is_upper
) ? "XC" : "xc");
270 while (number
>= 50.0) {
271 xmlBufferCCat(buffer
, (is_upper
) ? "L" : "l");
274 if (number
>= 40.0) {
275 xmlBufferCCat(buffer
, (is_upper
) ? "XL" : "xl");
278 while (number
>= 10.0) {
279 xmlBufferCCat(buffer
, (is_upper
) ? "X" : "x");
283 xmlBufferCCat(buffer
, (is_upper
) ? "IX" : "ix");
286 while (number
>= 5.0) {
287 xmlBufferCCat(buffer
, (is_upper
) ? "V" : "v");
291 xmlBufferCCat(buffer
, (is_upper
) ? "IV" : "iv");
294 while (number
>= 1.0) {
295 xmlBufferCCat(buffer
, (is_upper
) ? "I" : "i");
301 xsltNumberFormatTokenize(const xmlChar
*format
,
302 xsltFormatPtr tokens
)
309 default_token
.token
= DEFAULT_TOKEN
;
310 default_token
.width
= 1;
311 default_token
.separator
= BAD_CAST(DEFAULT_SEPARATOR
);
314 tokens
->start
= NULL
;
315 tokens
->tokens
[0].separator
= NULL
;
319 * Insert initial non-alphanumeric token.
320 * There is always such a token in the list, even if NULL
322 while (! (IS_LETTER(val
=xmlStringCurrentChar(NULL
, format
+ix
, &len
)) ||
324 if (format
[ix
] == 0) /* if end of format string */
329 tokens
->start
= xmlStrndup(format
, ix
);
332 for (tokens
->nTokens
= 0; tokens
->nTokens
< MAX_TOKENS
;
338 * separator has already been parsed (except for the first
339 * number) in tokens->end, recover it.
341 if (tokens
->nTokens
> 0) {
342 tokens
->tokens
[tokens
->nTokens
].separator
= tokens
->end
;
346 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
347 if (IS_DIGIT_ONE(val
) ||
348 IS_DIGIT_ZERO(val
)) {
349 tokens
->tokens
[tokens
->nTokens
].width
= 1;
350 while (IS_DIGIT_ZERO(val
)) {
351 tokens
->tokens
[tokens
->nTokens
].width
++;
353 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
355 if (IS_DIGIT_ONE(val
)) {
356 tokens
->tokens
[tokens
->nTokens
].token
= val
- 1;
358 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
360 } else if ( (val
== (xmlChar
)'A') ||
361 (val
== (xmlChar
)'a') ||
362 (val
== (xmlChar
)'I') ||
363 (val
== (xmlChar
)'i') ) {
364 tokens
->tokens
[tokens
->nTokens
].token
= val
;
366 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
369 * "Any other format token indicates a numbering sequence
370 * that starts with that token. If an implementation does
371 * not support a numbering sequence that starts with that
372 * token, it must use a format token of 1."
374 tokens
->tokens
[tokens
->nTokens
].token
= (xmlChar
)'0';
375 tokens
->tokens
[tokens
->nTokens
].width
= 1;
378 * Skip over remaining alphanumeric characters from the Nd
379 * (Number, decimal digit), Nl (Number, letter), No (Number,
380 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
381 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
382 * (Letters, other (uncased)) Unicode categories. This happens
383 * to correspond to the Letter and Digit classes from XML (and
384 * one wonders why XSLT doesn't refer to these instead).
386 while (IS_LETTER(val
) || IS_DIGIT(val
)) {
388 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
392 * Insert temporary non-alphanumeric final tooken.
395 while (! (IS_LETTER(val
) || IS_DIGIT(val
))) {
399 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
402 tokens
->end
= xmlStrndup(&format
[j
], ix
- j
);
407 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data
,
410 xsltFormatPtr tokens
,
415 xsltFormatTokenPtr token
;
418 * Handle initial non-alphanumeric token
420 if (tokens
->start
!= NULL
)
421 xmlBufferCat(buffer
, tokens
->start
);
423 for (i
= 0; i
< numbers_max
; i
++) {
425 number
= numbers
[(numbers_max
- 1) - i
];
426 if (i
< tokens
->nTokens
) {
428 * The "n"th format token will be used to format the "n"th
431 token
= &(tokens
->tokens
[i
]);
432 } else if (tokens
->nTokens
> 0) {
434 * If there are more numbers than format tokens, then the
435 * last format token will be used to format the remaining
438 token
= &(tokens
->tokens
[tokens
->nTokens
- 1]);
441 * If there are no format tokens, then a format token of
442 * 1 is used to format all numbers.
444 token
= &default_token
;
447 /* Print separator, except for the first number */
449 if (token
->separator
!= NULL
)
450 xmlBufferCat(buffer
, token
->separator
);
452 xmlBufferCCat(buffer
, DEFAULT_SEPARATOR
);
455 switch (xmlXPathIsInf(number
)) {
457 xmlBufferCCat(buffer
, "-Infinity");
460 xmlBufferCCat(buffer
, "Infinity");
463 if (xmlXPathIsNaN(number
)) {
464 xmlBufferCCat(buffer
, "NaN");
467 switch (token
->token
) {
469 xsltNumberFormatAlpha(buffer
,
475 xsltNumberFormatAlpha(buffer
,
481 xsltNumberFormatRoman(buffer
,
487 xsltNumberFormatRoman(buffer
,
493 if (IS_DIGIT_ZERO(token
->token
)) {
494 xsltNumberFormatDecimal(buffer
,
498 data
->digitsPerGroup
,
499 data
->groupingCharacter
,
500 data
->groupingCharacterLen
);
510 * Handle final non-alphanumeric token
512 if (tokens
->end
!= NULL
)
513 xmlBufferCat(buffer
, tokens
->end
);
518 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context
,
520 xsltCompMatchPtr countPat
,
521 xsltCompMatchPtr fromPat
,
530 /* select the starting node */
531 switch (node
->type
) {
532 case XML_ELEMENT_NODE
:
535 case XML_ATTRIBUTE_NODE
:
536 cur
= ((xmlAttrPtr
) node
)->parent
;
540 case XML_COMMENT_NODE
:
548 while (cur
!= NULL
) {
549 /* process current node */
550 if (countPat
== NULL
) {
551 if ((node
->type
== cur
->type
) &&
552 /* FIXME: must use expanded-name instead of local name */
553 xmlStrEqual(node
->name
, cur
->name
)) {
554 if ((node
->ns
== cur
->ns
) ||
555 ((node
->ns
!= NULL
) &&
557 (xmlStrEqual(node
->ns
->href
,
562 if (xsltTestCompMatchList(context
, cur
, countPat
))
565 if ((fromPat
!= NULL
) &&
566 xsltTestCompMatchList(context
, cur
, fromPat
)) {
570 /* Skip to next preceding or ancestor */
571 if ((cur
->type
== XML_DOCUMENT_NODE
) ||
572 #ifdef LIBXML_DOCB_ENABLED
573 (cur
->type
== XML_DOCB_DOCUMENT_NODE
) ||
575 (cur
->type
== XML_HTML_DOCUMENT_NODE
))
578 while ((cur
->prev
!= NULL
) && ((cur
->prev
->type
== XML_DTD_NODE
) ||
579 (cur
->prev
->type
== XML_XINCLUDE_START
) ||
580 (cur
->prev
->type
== XML_XINCLUDE_END
)))
582 if (cur
->prev
!= NULL
) {
583 for (cur
= cur
->prev
; cur
->last
!= NULL
; cur
= cur
->last
);
590 array
[amount
++] = (double) cnt
;
596 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context
,
598 xsltCompMatchPtr countPat
,
599 xsltCompMatchPtr fromPat
,
608 xmlNodePtr preceding
;
609 xmlXPathParserContextPtr parser
;
611 context
->xpathCtxt
->node
= node
;
612 parser
= xmlXPathNewParserContext(NULL
, context
->xpathCtxt
);
614 /* ancestor-or-self::*[count] */
615 for (ancestor
= node
;
616 (ancestor
!= NULL
) && (ancestor
->type
!= XML_DOCUMENT_NODE
);
617 ancestor
= xmlXPathNextAncestor(parser
, ancestor
)) {
619 if ((fromPat
!= NULL
) &&
620 xsltTestCompMatchList(context
, ancestor
, fromPat
))
623 if ((countPat
== NULL
&& node
->type
== ancestor
->type
&&
624 xmlStrEqual(node
->name
, ancestor
->name
)) ||
625 xsltTestCompMatchList(context
, ancestor
, countPat
)) {
626 /* count(preceding-sibling::*) */
628 for (preceding
= ancestor
;
631 xmlXPathNextPrecedingSibling(parser
, preceding
)) {
632 if (countPat
== NULL
) {
633 if ((preceding
->type
== ancestor
->type
) &&
634 xmlStrEqual(preceding
->name
, ancestor
->name
)){
635 if ((preceding
->ns
== ancestor
->ns
) ||
636 ((preceding
->ns
!= NULL
) &&
637 (ancestor
->ns
!= NULL
) &&
638 (xmlStrEqual(preceding
->ns
->href
,
639 ancestor
->ns
->href
) )))
643 if (xsltTestCompMatchList(context
, preceding
,
648 array
[amount
++] = (double)cnt
;
653 xmlXPathFreeParserContext(parser
);
659 xsltNumberFormatGetValue(xmlXPathContextPtr context
,
661 const xmlChar
*value
,
665 xmlBufferPtr pattern
;
666 xmlXPathObjectPtr obj
;
668 pattern
= xmlBufferCreate();
669 if (pattern
!= NULL
) {
670 xmlBufferCCat(pattern
, "number(");
671 xmlBufferCat(pattern
, value
);
672 xmlBufferCCat(pattern
, ")");
673 context
->node
= node
;
674 obj
= xmlXPathEvalExpression(xmlBufferContent(pattern
),
677 *number
= obj
->floatval
;
679 xmlXPathFreeObject(obj
);
681 xmlBufferFree(pattern
);
688 * @ctxt: the XSLT transformation context
689 * @data: the formatting informations
690 * @node: the data to format
692 * Convert one number.
695 xsltNumberFormat(xsltTransformContextPtr ctxt
,
696 xsltNumberDataPtr data
,
699 xmlBufferPtr output
= NULL
;
705 if ((data
->format
== NULL
) && (data
->has_format
!= 0)) {
706 data
->format
= xsltEvalAttrValueTemplate(ctxt
, data
->node
,
707 (const xmlChar
*) "format",
711 if (data
->format
== NULL
) {
715 output
= xmlBufferCreate();
717 goto XSLT_NUMBER_FORMAT_END
;
719 xsltNumberFormatTokenize(data
->format
, &tokens
);
722 * Evaluate the XPath expression to find the value(s)
725 amount
= xsltNumberFormatGetValue(ctxt
->xpathCtxt
,
730 xsltNumberFormatInsertNumbers(data
,
737 } else if (data
->level
) {
739 if (xmlStrEqual(data
->level
, (const xmlChar
*) "single")) {
740 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
749 xsltNumberFormatInsertNumbers(data
,
755 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "multiple")) {
756 double numarray
[1024];
757 int max
= sizeof(numarray
)/sizeof(numarray
[0]);
758 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
767 xsltNumberFormatInsertNumbers(data
,
773 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "any")) {
774 amount
= xsltNumberFormatGetAnyLevel(ctxt
,
782 xsltNumberFormatInsertNumbers(data
,
790 /* Insert number as text node */
791 xsltCopyTextString(ctxt
, ctxt
->insert
, xmlBufferContent(output
), 0);
793 if (tokens
.start
!= NULL
)
794 xmlFree(tokens
.start
);
795 if (tokens
.end
!= NULL
)
797 for (i
= 0;i
< tokens
.nTokens
;i
++) {
798 if (tokens
.tokens
[i
].separator
!= NULL
)
799 xmlFree(tokens
.tokens
[i
].separator
);
802 XSLT_NUMBER_FORMAT_END
:
803 if (tempformat
== 1) {
804 /* The format need to be recomputed each time */
808 xmlBufferFree(output
);
812 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self
, xmlChar
**format
, xsltFormatNumberInfoPtr info
)
814 int count
=0; /* will hold total length of prefix/suffix */
819 * prefix / suffix ends at end of string or at
820 * first 'special' character
824 /* if next character 'escaped' just count it */
825 if (**format
== SYMBOL_QUOTE
) {
826 if (*++(*format
) == 0)
829 else if (IS_SPECIAL(self
, *format
))
832 * else treat percent/per-mille as special cases,
833 * depending on whether +ve or -ve
837 * for +ve prefix/suffix, allow only a
838 * single occurence of either
840 if (xsltUTF8Charcmp(*format
, self
->percent
) == 0) {
841 if (info
->is_multiplier_set
)
843 info
->multiplier
= 100;
844 info
->is_multiplier_set
= TRUE
;
845 } else if (xsltUTF8Charcmp(*format
, self
->permille
) == 0) {
846 if (info
->is_multiplier_set
)
848 info
->multiplier
= 1000;
849 info
->is_multiplier_set
= TRUE
;
853 if ((len
=xsltUTF8Size(*format
)) < 1)
861 * xsltFormatNumberConversion:
862 * @self: the decimal format
863 * @format: the format requested
864 * @number: the value to format
865 * @result: the place to ouput the result
867 * format-number() uses the JDK 1.1 DecimalFormat class:
869 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
873 * pattern := subpattern{;subpattern}
874 * subpattern := {prefix}integer{.fraction}{suffix}
875 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
876 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
877 * integer := '#'* '0'* '0'
878 * fraction := '0'* '#'*
881 * X* 0 or more instances of X
882 * (X | Y) either X or Y.
883 * X..Y any character from X up to Y, inclusive.
884 * S - T characters in S, except those in T
886 * Special Characters:
890 * # a digit, zero shows as absent
891 * . placeholder for decimal separator
892 * , placeholder for grouping separator.
893 * ; separates formats.
894 * - default negative prefix.
895 * % multiply by 100 and show as percentage
896 * ? multiply by 1000 and show as per mille
897 * X any other characters can be used in the prefix or suffix
898 * ' used to quote special characters in a prefix or suffix.
900 * Returns a possible XPath error
903 xsltFormatNumberConversion(xsltDecimalFormatPtr self
,
908 xmlXPathError status
= XPATH_EXPRESSION_OK
;
910 xmlChar
*the_format
, *prefix
= NULL
, *suffix
= NULL
;
911 xmlChar
*nprefix
, *nsuffix
= NULL
;
913 int prefix_length
, suffix_length
= 0, nprefix_length
, nsuffix_length
;
916 int self_grouping_len
;
917 xsltFormatNumberInfo format_info
;
919 * delayed_multiplier allows a 'trailing' percent or
920 * permille to be treated as suffix
922 int delayed_multiplier
= 0;
923 /* flag to show no -ve format present for -ve number */
924 char default_sign
= 0;
925 /* flag to show error found, should use default format */
926 char found_error
= 0;
928 if (xmlStrlen(format
) <= 0) {
929 xsltTransformError(NULL
, NULL
, NULL
,
930 "xsltFormatNumberConversion : "
931 "Invalid format (0-length)\n");
934 switch (xmlXPathIsInf(number
)) {
936 if (self
->minusSign
== NULL
)
937 *result
= xmlStrdup(BAD_CAST
"-");
939 *result
= xmlStrdup(self
->minusSign
);
940 /* no-break on purpose */
942 if ((self
== NULL
) || (self
->infinity
== NULL
))
943 *result
= xmlStrcat(*result
, BAD_CAST
"Infinity");
945 *result
= xmlStrcat(*result
, self
->infinity
);
948 if (xmlXPathIsNaN(number
)) {
949 if ((self
== NULL
) || (self
->noNumber
== NULL
))
950 *result
= xmlStrdup(BAD_CAST
"NaN");
952 *result
= xmlStrdup(self
->noNumber
);
957 buffer
= xmlBufferCreate();
958 if (buffer
== NULL
) {
959 return XPATH_MEMORY_ERROR
;
962 format_info
.integer_hash
= 0;
963 format_info
.integer_digits
= 0;
964 format_info
.frac_digits
= 0;
965 format_info
.frac_hash
= 0;
966 format_info
.group
= -1;
967 format_info
.multiplier
= 1;
968 format_info
.add_decimal
= FALSE
;
969 format_info
.is_multiplier_set
= FALSE
;
970 format_info
.is_negative_pattern
= FALSE
;
975 * First we process the +ve pattern to get percent / permille,
976 * as well as main format
979 prefix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
980 if (prefix_length
< 0) {
986 * Here we process the "number" part of the format. It gets
987 * a little messy because of the percent/per-mille - if that
988 * appears at the end, it may be part of the suffix instead
989 * of part of the number, so the variable delayed_multiplier
990 * is used to handle it
992 self_grouping_len
= xmlStrlen(self
->grouping
);
993 while ((*the_format
!= 0) &&
994 (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) != 0) &&
995 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) {
997 if (delayed_multiplier
!= 0) {
998 format_info
.multiplier
= delayed_multiplier
;
999 format_info
.is_multiplier_set
= TRUE
;
1000 delayed_multiplier
= 0;
1002 if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1003 if (format_info
.integer_digits
> 0) {
1007 format_info
.integer_hash
++;
1008 if (format_info
.group
>= 0)
1009 format_info
.group
++;
1010 } else if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1011 format_info
.integer_digits
++;
1012 if (format_info
.group
>= 0)
1013 format_info
.group
++;
1014 } else if ((self_grouping_len
> 0) &&
1015 (!xmlStrncmp(the_format
, self
->grouping
, self_grouping_len
))) {
1016 /* Reset group count */
1017 format_info
.group
= 0;
1018 the_format
+= self_grouping_len
;
1020 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1021 if (format_info
.is_multiplier_set
) {
1025 delayed_multiplier
= 100;
1026 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1027 if (format_info
.is_multiplier_set
) {
1031 delayed_multiplier
= 1000;
1035 if ((len
=xsltUTF8Size(the_format
)) < 1) {
1043 /* We have finished the integer part, now work on fraction */
1044 if (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) == 0) {
1045 format_info
.add_decimal
= TRUE
;
1046 the_format
+= xsltUTF8Size(the_format
); /* Skip over the decimal */
1049 while (*the_format
!= 0) {
1051 if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1052 if (format_info
.frac_hash
!= 0) {
1056 format_info
.frac_digits
++;
1057 } else if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1058 format_info
.frac_hash
++;
1059 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1060 if (format_info
.is_multiplier_set
) {
1064 delayed_multiplier
= 100;
1065 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1070 continue; /* while */
1071 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1072 if (format_info
.is_multiplier_set
) {
1076 delayed_multiplier
= 1000;
1077 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1082 continue; /* while */
1083 } else if (xsltUTF8Charcmp(the_format
, self
->grouping
) != 0) {
1086 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1091 if (delayed_multiplier
!= 0) {
1092 format_info
.multiplier
= delayed_multiplier
;
1093 delayed_multiplier
= 0;
1094 format_info
.is_multiplier_set
= TRUE
;
1099 * If delayed_multiplier is set after processing the
1100 * "number" part, should be in suffix
1102 if (delayed_multiplier
!= 0) {
1104 delayed_multiplier
= 0;
1107 suffix
= the_format
;
1108 suffix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
1109 if ( (suffix_length
< 0) ||
1110 ((*the_format
!= 0) &&
1111 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) ) {
1117 * We have processed the +ve prefix, number part and +ve suffix.
1118 * If the number is -ve, we must substitute the -ve prefix / suffix
1122 * Note that j is the number of UTF8 chars before the separator,
1123 * not the number of bytes! (bug 151975)
1125 j
= xmlUTF8Strloc(format
, self
->patternSeparator
);
1127 /* No -ve pattern present, so use default signing */
1131 /* Skip over pattern separator (accounting for UTF8) */
1132 the_format
= (xmlChar
*)xmlUTF8Strpos(format
, j
+ 1);
1134 * Flag changes interpretation of percent/permille
1137 format_info
.is_negative_pattern
= TRUE
;
1138 format_info
.is_multiplier_set
= FALSE
;
1140 /* First do the -ve prefix */
1141 nprefix
= the_format
;
1142 nprefix_length
= xsltFormatNumberPreSuffix(self
,
1143 &the_format
, &format_info
);
1144 if (nprefix_length
<0) {
1149 while (*the_format
!= 0) {
1150 if ( (xsltUTF8Charcmp(the_format
, (self
)->percent
) == 0) ||
1151 (xsltUTF8Charcmp(the_format
, (self
)->permille
)== 0) ) {
1152 if (format_info
.is_multiplier_set
) {
1156 format_info
.is_multiplier_set
= TRUE
;
1157 delayed_multiplier
= 1;
1159 else if (IS_SPECIAL(self
, the_format
))
1160 delayed_multiplier
= 0;
1163 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1169 if (delayed_multiplier
!= 0) {
1170 format_info
.is_multiplier_set
= FALSE
;
1174 /* Finally do the -ve suffix */
1175 if (*the_format
!= 0) {
1176 nsuffix
= the_format
;
1177 nsuffix_length
= xsltFormatNumberPreSuffix(self
,
1178 &the_format
, &format_info
);
1179 if (nsuffix_length
< 0) {
1186 if (*the_format
!= 0) {
1191 * Here's another Java peculiarity:
1192 * if -ve prefix/suffix == +ve ones, discard & use default
1194 if ((nprefix_length
!= prefix_length
) ||
1195 (nsuffix_length
!= suffix_length
) ||
1196 ((nprefix_length
> 0) &&
1197 (xmlStrncmp(nprefix
, prefix
, prefix_length
) !=0 )) ||
1198 ((nsuffix_length
> 0) &&
1199 (xmlStrncmp(nsuffix
, suffix
, suffix_length
) !=0 ))) {
1201 prefix_length
= nprefix_length
;
1203 suffix_length
= nsuffix_length
;
1212 if (found_error
!= 0) {
1213 xsltTransformError(NULL
, NULL
, NULL
,
1214 "xsltFormatNumberConversion : "
1215 "error in format string '%s', using default\n", format
);
1216 default_sign
= (number
< 0.0) ? 1 : 0;
1217 prefix_length
= suffix_length
= 0;
1218 format_info
.integer_hash
= 0;
1219 format_info
.integer_digits
= 1;
1220 format_info
.frac_digits
= 1;
1221 format_info
.frac_hash
= 4;
1222 format_info
.group
= -1;
1223 format_info
.multiplier
= 1;
1224 format_info
.add_decimal
= TRUE
;
1227 /* Ready to output our number. First see if "default sign" is required */
1228 if (default_sign
!= 0)
1229 xmlBufferAdd(buffer
, self
->minusSign
, xsltUTF8Size(self
->minusSign
));
1231 /* Put the prefix into the buffer */
1232 for (j
= 0; j
< prefix_length
; j
++) {
1233 if ((pchar
= *prefix
++) == SYMBOL_QUOTE
) {
1234 len
= xsltUTF8Size(prefix
);
1235 xmlBufferAdd(buffer
, prefix
, len
);
1237 j
+= len
- 1; /* length of symbol less length of quote */
1239 xmlBufferAdd(buffer
, &pchar
, 1);
1242 /* Next do the integer part of the number */
1243 number
= fabs(number
) * (double)format_info
.multiplier
;
1244 scale
= pow(10.0, (double)(format_info
.frac_digits
+ format_info
.frac_hash
));
1245 number
= floor((scale
* number
+ 0.5)) / scale
;
1246 if ((self
->grouping
!= NULL
) &&
1247 (self
->grouping
[0] != 0)) {
1249 len
= xmlStrlen(self
->grouping
);
1250 pchar
= xsltGetUTF8Char(self
->grouping
, &len
);
1251 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1252 format_info
.integer_digits
,
1256 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1257 format_info
.integer_digits
,
1261 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1262 if ((format_info
.integer_digits
+ format_info
.integer_hash
+
1263 format_info
.frac_digits
== 0) && (format_info
.frac_hash
> 0)) {
1264 ++format_info
.frac_digits
;
1265 --format_info
.frac_hash
;
1268 /* Add leading zero, if required */
1269 if ((floor(number
) == 0) &&
1270 (format_info
.integer_digits
+ format_info
.frac_digits
== 0)) {
1271 xmlBufferAdd(buffer
, self
->zeroDigit
, xsltUTF8Size(self
->zeroDigit
));
1274 /* Next the fractional part, if required */
1275 if (format_info
.frac_digits
+ format_info
.frac_hash
== 0) {
1276 if (format_info
.add_decimal
)
1277 xmlBufferAdd(buffer
, self
->decimalPoint
,
1278 xsltUTF8Size(self
->decimalPoint
));
1281 number
-= floor(number
);
1282 if ((number
!= 0) || (format_info
.frac_digits
!= 0)) {
1283 xmlBufferAdd(buffer
, self
->decimalPoint
,
1284 xsltUTF8Size(self
->decimalPoint
));
1285 number
= floor(scale
* number
+ 0.5);
1286 for (j
= format_info
.frac_hash
; j
> 0; j
--) {
1287 if (fmod(number
, 10.0) >= 1.0)
1291 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1292 format_info
.frac_digits
+ j
,
1296 /* Put the suffix into the buffer */
1297 for (j
= 0; j
< suffix_length
; j
++) {
1298 if ((pchar
= *suffix
++) == SYMBOL_QUOTE
) {
1299 len
= xsltUTF8Size(suffix
);
1300 xmlBufferAdd(buffer
, suffix
, len
);
1302 j
+= len
- 1; /* length of symbol less length of escape */
1304 xmlBufferAdd(buffer
, &pchar
, 1);
1307 *result
= xmlStrdup(xmlBufferContent(buffer
));
1308 xmlBufferFree(buffer
);