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 (xmlChar)'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 two routines (xsltUTF8Size and xsltUTF8Charcmp)
69 * will be replaced with calls to the corresponding libxml routines
70 * at a later date (when other inter-library dependencies require it)
75 * @utf: pointer to the UTF8 character
77 * returns the numbers of bytes in the character, -1 on format error
80 xsltUTF8Size(xmlChar
*utf
) {
88 /* check valid UTF8 character */
91 /* determine number of bytes in char */
93 for (mask
=0x20; mask
!= 0; mask
>>=1) {
103 * @utf1: pointer to first UTF8 char
104 * @utf2: pointer to second UTF8 char
106 * returns result of comparing the two UCS4 values
110 xsltUTF8Charcmp(xmlChar
*utf1
, xmlChar
*utf2
) {
117 return xmlStrncmp(utf1
, utf2
, xsltUTF8Size(utf1
));
120 /***** Stop temp insert *****/
121 /************************************************************************
123 * Utility functions *
125 ************************************************************************/
127 #define IS_SPECIAL(self,letter) \
128 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
129 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
130 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
131 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
132 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
134 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
135 #define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
138 xsltIsDigitZero(unsigned int ch
)
141 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
144 case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
145 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
146 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
147 case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
148 case 0x1810: case 0xFF10:
156 xsltNumberFormatDecimal(xmlBufferPtr buffer
,
161 int groupingCharacter
,
162 int groupingCharacterLen
)
166 * xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4];
167 * which would be length 68 on x86 arch. It was changed to be a longer,
168 * fixed length in order to try to cater for (reasonable) UTF8
169 * separators and numeric characters. The max UTF8 char size will be
170 * 6 or less, so the value used [500] should be *much* larger than needed
172 xmlChar temp_string
[500];
174 xmlChar temp_char
[6];
179 /* Build buffer from back */
180 pointer
= &temp_string
[sizeof(temp_string
)] - 1; /* last char */
183 while (pointer
> temp_string
) {
184 if ((i
>= width
) && (fabs(number
) < 1.0))
186 if ((i
> 0) && (groupingCharacter
!= 0) &&
187 (digitsPerGroup
> 0) &&
188 ((i
% digitsPerGroup
) == 0)) {
189 if (pointer
- groupingCharacterLen
< temp_string
) {
190 i
= -1; /* flag error */
193 pointer
-= groupingCharacterLen
;
194 xmlCopyCharMultiByte(pointer
, groupingCharacter
);
197 val
= digit_zero
+ (int)fmod(number
, 10.0);
198 if (val
< 0x80) { /* shortcut if ASCII */
199 if (pointer
<= temp_string
) { /* Check enough room */
207 * Here we have a multibyte character. It's a little messy,
208 * because until we generate the char we don't know how long
209 * it is. So, we generate it into the buffer temp_char, then
210 * copy from there into temp_string.
212 len
= xmlCopyCharMultiByte(temp_char
, val
);
213 if ( (pointer
- len
) < temp_string
) {
218 memcpy(pointer
, temp_char
, len
);
224 xsltGenericError(xsltGenericErrorContext
,
225 "xsltNumberFormatDecimal: Internal buffer size exceeded");
226 xmlBufferCat(buffer
, pointer
);
230 xsltNumberFormatAlpha(xmlBufferPtr buffer
,
234 char temp_string
[sizeof(double) * CHAR_BIT
* sizeof(xmlChar
) + 1];
238 double alpha_size
= (double)(sizeof(alpha_upper_list
) - 1);
240 /* Build buffer from back */
241 pointer
= &temp_string
[sizeof(temp_string
)];
243 alpha_list
= (is_upper
) ? alpha_upper_list
: alpha_lower_list
;
245 for (i
= 1; i
< (int)sizeof(temp_string
); i
++) {
247 *(--pointer
) = alpha_list
[((int)fmod(number
, alpha_size
))];
248 number
/= alpha_size
;
249 if (fabs(number
) < 1.0)
252 xmlBufferCCat(buffer
, pointer
);
256 xsltNumberFormatRoman(xmlBufferPtr buffer
,
261 * Based on an example by Jim Walsh
263 while (number
>= 1000.0) {
264 xmlBufferCCat(buffer
, (is_upper
) ? "M" : "m");
267 if (number
>= 900.0) {
268 xmlBufferCCat(buffer
, (is_upper
) ? "CM" : "cm");
271 while (number
>= 500.0) {
272 xmlBufferCCat(buffer
, (is_upper
) ? "D" : "d");
275 if (number
>= 400.0) {
276 xmlBufferCCat(buffer
, (is_upper
) ? "CD" : "cd");
279 while (number
>= 100.0) {
280 xmlBufferCCat(buffer
, (is_upper
) ? "C" : "c");
283 if (number
>= 90.0) {
284 xmlBufferCCat(buffer
, (is_upper
) ? "XC" : "xc");
287 while (number
>= 50.0) {
288 xmlBufferCCat(buffer
, (is_upper
) ? "L" : "l");
291 if (number
>= 40.0) {
292 xmlBufferCCat(buffer
, (is_upper
) ? "XL" : "xl");
295 while (number
>= 10.0) {
296 xmlBufferCCat(buffer
, (is_upper
) ? "X" : "x");
300 xmlBufferCCat(buffer
, (is_upper
) ? "IX" : "ix");
303 while (number
>= 5.0) {
304 xmlBufferCCat(buffer
, (is_upper
) ? "V" : "v");
308 xmlBufferCCat(buffer
, (is_upper
) ? "IV" : "iv");
311 while (number
>= 1.0) {
312 xmlBufferCCat(buffer
, (is_upper
) ? "I" : "i");
318 xsltNumberFormatTokenize(const xmlChar
*format
,
319 xsltFormatPtr tokens
)
326 default_token
.token
= DEFAULT_TOKEN
;
327 default_token
.width
= 1;
328 default_token
.separator
= BAD_CAST(DEFAULT_SEPARATOR
);
331 tokens
->start
= NULL
;
332 tokens
->tokens
[0].separator
= NULL
;
336 * Insert initial non-alphanumeric token.
337 * There is always such a token in the list, even if NULL
339 while (! (IS_LETTER(val
=xmlStringCurrentChar(NULL
, format
+ix
, &len
)) ||
341 if (format
[ix
] == 0) /* if end of format string */
346 tokens
->start
= xmlStrndup(format
, ix
);
349 for (tokens
->nTokens
= 0; tokens
->nTokens
< MAX_TOKENS
;
355 * separator has already been parsed (except for the first
356 * number) in tokens->end, recover it.
358 if (tokens
->nTokens
> 0) {
359 tokens
->tokens
[tokens
->nTokens
].separator
= tokens
->end
;
363 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
364 if (IS_DIGIT_ONE(val
) ||
365 IS_DIGIT_ZERO(val
)) {
366 tokens
->tokens
[tokens
->nTokens
].width
= 1;
367 while (IS_DIGIT_ZERO(val
)) {
368 tokens
->tokens
[tokens
->nTokens
].width
++;
370 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
372 if (IS_DIGIT_ONE(val
)) {
373 tokens
->tokens
[tokens
->nTokens
].token
= val
- 1;
375 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
377 } else if ( (val
== (xmlChar
)'A') ||
378 (val
== (xmlChar
)'a') ||
379 (val
== (xmlChar
)'I') ||
380 (val
== (xmlChar
)'i') ) {
381 tokens
->tokens
[tokens
->nTokens
].token
= val
;
383 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
386 * "Any other format token indicates a numbering sequence
387 * that starts with that token. If an implementation does
388 * not support a numbering sequence that starts with that
389 * token, it must use a format token of 1."
391 tokens
->tokens
[tokens
->nTokens
].token
= (xmlChar
)'0';
392 tokens
->tokens
[tokens
->nTokens
].width
= 1;
395 * Skip over remaining alphanumeric characters from the Nd
396 * (Number, decimal digit), Nl (Number, letter), No (Number,
397 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
398 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
399 * (Letters, other (uncased)) Unicode categories. This happens
400 * to correspond to the Letter and Digit classes from XML (and
401 * one wonders why XSLT doesn't refer to these instead).
403 while (IS_LETTER(val
) || IS_DIGIT(val
)) {
405 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
409 * Insert temporary non-alphanumeric final tooken.
412 while (! (IS_LETTER(val
) || IS_DIGIT(val
))) {
416 val
= xmlStringCurrentChar(NULL
, format
+ix
, &len
);
419 tokens
->end
= xmlStrndup(&format
[j
], ix
- j
);
424 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data
,
427 xsltFormatPtr tokens
,
432 xsltFormatTokenPtr token
;
435 * Handle initial non-alphanumeric token
437 if (tokens
->start
!= NULL
)
438 xmlBufferCat(buffer
, tokens
->start
);
440 for (i
= 0; i
< numbers_max
; i
++) {
442 number
= numbers
[(numbers_max
- 1) - i
];
443 if (i
< tokens
->nTokens
) {
445 * The "n"th format token will be used to format the "n"th
448 token
= &(tokens
->tokens
[i
]);
449 } else if (tokens
->nTokens
> 0) {
451 * If there are more numbers than format tokens, then the
452 * last format token will be used to format the remaining
455 token
= &(tokens
->tokens
[tokens
->nTokens
- 1]);
458 * If there are no format tokens, then a format token of
459 * 1 is used to format all numbers.
461 token
= &default_token
;
464 /* Print separator, except for the first number */
466 if (token
->separator
!= NULL
)
467 xmlBufferCat(buffer
, token
->separator
);
469 xmlBufferCCat(buffer
, DEFAULT_SEPARATOR
);
472 switch (xmlXPathIsInf(number
)) {
474 xmlBufferCCat(buffer
, "-Infinity");
477 xmlBufferCCat(buffer
, "Infinity");
480 if (xmlXPathIsNaN(number
)) {
481 xmlBufferCCat(buffer
, "NaN");
484 switch (token
->token
) {
486 xsltNumberFormatAlpha(buffer
,
492 xsltNumberFormatAlpha(buffer
,
498 xsltNumberFormatRoman(buffer
,
504 xsltNumberFormatRoman(buffer
,
510 if (IS_DIGIT_ZERO(token
->token
)) {
511 xsltNumberFormatDecimal(buffer
,
515 data
->digitsPerGroup
,
516 data
->groupingCharacter
,
517 data
->groupingCharacterLen
);
527 * Handle final non-alphanumeric token
529 if (tokens
->end
!= NULL
)
530 xmlBufferCat(buffer
, tokens
->end
);
535 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context
,
537 const xmlChar
*count
,
546 xsltCompMatchPtr countPat
= NULL
;
547 xsltCompMatchPtr fromPat
= NULL
;
550 countPat
= xsltCompilePattern(count
, doc
, elem
, NULL
, context
);
552 fromPat
= xsltCompilePattern(from
, doc
, elem
, NULL
, context
);
554 /* select the starting node */
555 switch (node
->type
) {
556 case XML_ELEMENT_NODE
:
559 case XML_ATTRIBUTE_NODE
:
560 cur
= ((xmlAttrPtr
) node
)->parent
;
564 case XML_COMMENT_NODE
:
572 while (cur
!= NULL
) {
573 /* process current node */
575 if ((node
->type
== cur
->type
) &&
576 /* FIXME: must use expanded-name instead of local name */
577 xmlStrEqual(node
->name
, cur
->name
)) {
578 if ((node
->ns
== cur
->ns
) ||
579 ((node
->ns
!= NULL
) &&
581 (xmlStrEqual(node
->ns
->href
,
586 if (xsltTestCompMatchList(context
, cur
, countPat
))
589 if ((from
!= NULL
) &&
590 xsltTestCompMatchList(context
, cur
, fromPat
)) {
594 /* Skip to next preceding or ancestor */
595 if ((cur
->type
== XML_DOCUMENT_NODE
) ||
596 #ifdef LIBXML_DOCB_ENABLED
597 (cur
->type
== XML_DOCB_DOCUMENT_NODE
) ||
599 (cur
->type
== XML_HTML_DOCUMENT_NODE
))
602 while ((cur
->prev
!= NULL
) && ((cur
->prev
->type
== XML_DTD_NODE
) ||
603 (cur
->prev
->type
== XML_XINCLUDE_START
) ||
604 (cur
->prev
->type
== XML_XINCLUDE_END
)))
606 if (cur
->prev
!= NULL
) {
607 for (cur
= cur
->prev
; cur
->last
!= NULL
; cur
= cur
->last
);
614 array
[amount
++] = (double) cnt
;
616 if (countPat
!= NULL
)
617 xsltFreeCompMatchList(countPat
);
619 xsltFreeCompMatchList(fromPat
);
624 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context
,
626 const xmlChar
*count
,
636 xmlNodePtr preceding
;
637 xmlXPathParserContextPtr parser
;
638 xsltCompMatchPtr countPat
;
639 xsltCompMatchPtr fromPat
;
642 countPat
= xsltCompilePattern(count
, doc
, elem
, NULL
, context
);
646 fromPat
= xsltCompilePattern(from
, doc
, elem
, NULL
, context
);
649 context
->xpathCtxt
->node
= node
;
650 parser
= xmlXPathNewParserContext(NULL
, context
->xpathCtxt
);
652 /* ancestor-or-self::*[count] */
653 for (ancestor
= node
;
654 (ancestor
!= NULL
) && (ancestor
->type
!= XML_DOCUMENT_NODE
);
655 ancestor
= xmlXPathNextAncestor(parser
, ancestor
)) {
657 if ((from
!= NULL
) &&
658 xsltTestCompMatchList(context
, ancestor
, fromPat
))
661 if ((count
== NULL
&& node
->type
== ancestor
->type
&&
662 xmlStrEqual(node
->name
, ancestor
->name
)) ||
663 xsltTestCompMatchList(context
, ancestor
, countPat
)) {
664 /* count(preceding-sibling::*) */
666 for (preceding
= ancestor
;
669 xmlXPathNextPrecedingSibling(parser
, preceding
)) {
671 if ((preceding
->type
== ancestor
->type
) &&
672 xmlStrEqual(preceding
->name
, ancestor
->name
)){
673 if ((preceding
->ns
== ancestor
->ns
) ||
674 ((preceding
->ns
!= NULL
) &&
675 (ancestor
->ns
!= NULL
) &&
676 (xmlStrEqual(preceding
->ns
->href
,
677 ancestor
->ns
->href
) )))
681 if (xsltTestCompMatchList(context
, preceding
,
686 array
[amount
++] = (double)cnt
;
691 xmlXPathFreeParserContext(parser
);
693 xsltFreeCompMatchList(countPat
);
694 xsltFreeCompMatchList(fromPat
);
699 xsltNumberFormatGetValue(xmlXPathContextPtr context
,
701 const xmlChar
*value
,
705 xmlBufferPtr pattern
;
706 xmlXPathObjectPtr obj
;
708 pattern
= xmlBufferCreate();
709 if (pattern
!= NULL
) {
710 xmlBufferCCat(pattern
, "number(");
711 xmlBufferCat(pattern
, value
);
712 xmlBufferCCat(pattern
, ")");
713 context
->node
= node
;
714 obj
= xmlXPathEvalExpression(xmlBufferContent(pattern
),
717 *number
= obj
->floatval
;
719 xmlXPathFreeObject(obj
);
721 xmlBufferFree(pattern
);
728 * @ctxt: the XSLT transformation context
729 * @data: the formatting informations
730 * @node: the data to format
732 * Convert one number.
735 xsltNumberFormat(xsltTransformContextPtr ctxt
,
736 xsltNumberDataPtr data
,
739 xmlBufferPtr output
= NULL
;
745 if ((data
->format
== NULL
) && (data
->has_format
!= 0)) {
746 data
->format
= xsltEvalAttrValueTemplate(ctxt
, data
->node
,
747 (const xmlChar
*) "format",
751 if (data
->format
== NULL
) {
755 output
= xmlBufferCreate();
757 goto XSLT_NUMBER_FORMAT_END
;
759 xsltNumberFormatTokenize(data
->format
, &tokens
);
762 * Evaluate the XPath expression to find the value(s)
765 amount
= xsltNumberFormatGetValue(ctxt
->xpathCtxt
,
770 xsltNumberFormatInsertNumbers(data
,
777 } else if (data
->level
) {
779 if (xmlStrEqual(data
->level
, (const xmlChar
*) "single")) {
780 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
789 xsltNumberFormatInsertNumbers(data
,
795 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "multiple")) {
796 double numarray
[1024];
797 int max
= sizeof(numarray
)/sizeof(numarray
[0]);
798 amount
= xsltNumberFormatGetMultipleLevel(ctxt
,
807 xsltNumberFormatInsertNumbers(data
,
813 } else if (xmlStrEqual(data
->level
, (const xmlChar
*) "any")) {
814 amount
= xsltNumberFormatGetAnyLevel(ctxt
,
822 xsltNumberFormatInsertNumbers(data
,
830 /* Insert number as text node */
831 xsltCopyTextString(ctxt
, ctxt
->insert
, xmlBufferContent(output
), 0);
833 if (tokens
.start
!= NULL
)
834 xmlFree(tokens
.start
);
835 if (tokens
.end
!= NULL
)
837 for (i
= 0;i
< tokens
.nTokens
;i
++) {
838 if (tokens
.tokens
[i
].separator
!= NULL
)
839 xmlFree(tokens
.tokens
[i
].separator
);
842 XSLT_NUMBER_FORMAT_END
:
843 if (tempformat
== 1) {
844 /* The format need to be recomputed each time */
848 xmlBufferFree(output
);
852 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self
, xmlChar
**format
, xsltFormatNumberInfoPtr info
)
854 int count
=0; /* will hold total length of prefix/suffix */
859 * prefix / suffix ends at end of string or at
860 * first 'special' character
864 /* if next character 'escaped' just count it */
865 if (**format
== SYMBOL_QUOTE
) {
866 if (*++(*format
) == 0)
869 else if (IS_SPECIAL(self
, *format
))
872 * else treat percent/per-mille as special cases,
873 * depending on whether +ve or -ve
877 * for +ve prefix/suffix, allow only a
878 * single occurence of either
880 if (xsltUTF8Charcmp(*format
, self
->percent
) == 0) {
881 if (info
->is_multiplier_set
)
883 info
->multiplier
= 100;
884 info
->is_multiplier_set
= TRUE
;
885 } else if (xsltUTF8Charcmp(*format
, self
->permille
) == 0) {
886 if (info
->is_multiplier_set
)
888 info
->multiplier
= 1000;
889 info
->is_multiplier_set
= TRUE
;
893 if ((len
=xsltUTF8Size(*format
)) < 1)
901 * xsltFormatNumberConversion:
902 * @self: the decimal format
903 * @format: the format requested
904 * @number: the value to format
905 * @result: the place to ouput the result
907 * format-number() uses the JDK 1.1 DecimalFormat class:
909 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
913 * pattern := subpattern{;subpattern}
914 * subpattern := {prefix}integer{.fraction}{suffix}
915 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
916 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
917 * integer := '#'* '0'* '0'
918 * fraction := '0'* '#'*
921 * X* 0 or more instances of X
922 * (X | Y) either X or Y.
923 * X..Y any character from X up to Y, inclusive.
924 * S - T characters in S, except those in T
926 * Special Characters:
930 * # a digit, zero shows as absent
931 * . placeholder for decimal separator
932 * , placeholder for grouping separator.
933 * ; separates formats.
934 * - default negative prefix.
935 * % multiply by 100 and show as percentage
936 * ? multiply by 1000 and show as per mille
937 * X any other characters can be used in the prefix or suffix
938 * ' used to quote special characters in a prefix or suffix.
940 * Returns a possible XPath error
943 xsltFormatNumberConversion(xsltDecimalFormatPtr self
,
948 xmlXPathError status
= XPATH_EXPRESSION_OK
;
950 xmlChar
*the_format
, *prefix
= NULL
, *suffix
= NULL
;
951 xmlChar
*nprefix
, *nsuffix
= NULL
;
953 int prefix_length
, suffix_length
= 0, nprefix_length
, nsuffix_length
;
956 int self_grouping_len
;
957 xsltFormatNumberInfo format_info
;
959 * delayed_multiplier allows a 'trailing' percent or
960 * permille to be treated as suffix
962 int delayed_multiplier
= 0;
963 /* flag to show no -ve format present for -ve number */
964 char default_sign
= 0;
965 /* flag to show error found, should use default format */
966 char found_error
= 0;
968 if (xmlStrlen(format
) <= 0) {
969 xsltTransformError(NULL
, NULL
, NULL
,
970 "xsltFormatNumberConversion : "
971 "Invalid format (0-length)\n");
974 switch (xmlXPathIsInf(number
)) {
976 if (self
->minusSign
== NULL
)
977 *result
= xmlStrdup(BAD_CAST
"-");
979 *result
= xmlStrdup(self
->minusSign
);
980 /* no-break on purpose */
982 if ((self
== NULL
) || (self
->infinity
== NULL
))
983 *result
= xmlStrcat(*result
, BAD_CAST
"Infinity");
985 *result
= xmlStrcat(*result
, self
->infinity
);
988 if (xmlXPathIsNaN(number
)) {
989 if ((self
== NULL
) || (self
->noNumber
== NULL
))
990 *result
= xmlStrdup(BAD_CAST
"NaN");
992 *result
= xmlStrdup(self
->noNumber
);
997 buffer
= xmlBufferCreate();
998 if (buffer
== NULL
) {
999 return XPATH_MEMORY_ERROR
;
1002 format_info
.integer_hash
= 0;
1003 format_info
.integer_digits
= 0;
1004 format_info
.frac_digits
= 0;
1005 format_info
.frac_hash
= 0;
1006 format_info
.group
= -1;
1007 format_info
.multiplier
= 1;
1008 format_info
.add_decimal
= FALSE
;
1009 format_info
.is_multiplier_set
= FALSE
;
1010 format_info
.is_negative_pattern
= FALSE
;
1012 the_format
= format
;
1015 * First we process the +ve pattern to get percent / permille,
1016 * as well as main format
1018 prefix
= the_format
;
1019 prefix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
1020 if (prefix_length
< 0) {
1026 * Here we process the "number" part of the format. It gets
1027 * a little messy because of the percent/per-mille - if that
1028 * appears at the end, it may be part of the suffix instead
1029 * of part of the number, so the variable delayed_multiplier
1030 * is used to handle it
1032 self_grouping_len
= xmlStrlen(self
->grouping
);
1033 while ((*the_format
!= 0) &&
1034 (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) != 0) &&
1035 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) {
1037 if (delayed_multiplier
!= 0) {
1038 format_info
.multiplier
= delayed_multiplier
;
1039 format_info
.is_multiplier_set
= TRUE
;
1040 delayed_multiplier
= 0;
1042 if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1043 if (format_info
.integer_digits
> 0) {
1047 format_info
.integer_hash
++;
1048 if (format_info
.group
>= 0)
1049 format_info
.group
++;
1050 } else if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1051 format_info
.integer_digits
++;
1052 if (format_info
.group
>= 0)
1053 format_info
.group
++;
1054 } else if ((self_grouping_len
> 0) &&
1055 (!xmlStrncmp(the_format
, self
->grouping
, self_grouping_len
))) {
1056 /* Reset group count */
1057 format_info
.group
= 0;
1058 the_format
+= self_grouping_len
;
1060 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1061 if (format_info
.is_multiplier_set
) {
1065 delayed_multiplier
= 100;
1066 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1067 if (format_info
.is_multiplier_set
) {
1071 delayed_multiplier
= 1000;
1075 if ((len
=xsltUTF8Size(the_format
)) < 1) {
1083 /* We have finished the integer part, now work on fraction */
1084 if (xsltUTF8Charcmp(the_format
, self
->decimalPoint
) == 0) {
1085 format_info
.add_decimal
= TRUE
;
1086 the_format
+= xsltUTF8Size(the_format
); /* Skip over the decimal */
1089 while (*the_format
!= 0) {
1091 if (xsltUTF8Charcmp(the_format
, self
->zeroDigit
) == 0) {
1092 if (format_info
.frac_hash
!= 0) {
1096 format_info
.frac_digits
++;
1097 } else if (xsltUTF8Charcmp(the_format
, self
->digit
) == 0) {
1098 format_info
.frac_hash
++;
1099 } else if (xsltUTF8Charcmp(the_format
, self
->percent
) == 0) {
1100 if (format_info
.is_multiplier_set
) {
1104 delayed_multiplier
= 100;
1105 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1110 continue; /* while */
1111 } else if (xsltUTF8Charcmp(the_format
, self
->permille
) == 0) {
1112 if (format_info
.is_multiplier_set
) {
1116 delayed_multiplier
= 1000;
1117 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1122 continue; /* while */
1123 } else if (xsltUTF8Charcmp(the_format
, self
->grouping
) != 0) {
1126 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1131 if (delayed_multiplier
!= 0) {
1132 format_info
.multiplier
= delayed_multiplier
;
1133 delayed_multiplier
= 0;
1134 format_info
.is_multiplier_set
= TRUE
;
1139 * If delayed_multiplier is set after processing the
1140 * "number" part, should be in suffix
1142 if (delayed_multiplier
!= 0) {
1144 delayed_multiplier
= 0;
1147 suffix
= the_format
;
1148 suffix_length
= xsltFormatNumberPreSuffix(self
, &the_format
, &format_info
);
1149 if ( (suffix_length
< 0) ||
1150 ((*the_format
!= 0) &&
1151 (xsltUTF8Charcmp(the_format
, self
->patternSeparator
) != 0)) ) {
1157 * We have processed the +ve prefix, number part and +ve suffix.
1158 * If the number is -ve, we must substitute the -ve prefix / suffix
1162 * Note that j is the number of UTF8 chars before the separator,
1163 * not the number of bytes! (bug 151975)
1165 j
= xmlUTF8Strloc(format
, self
->patternSeparator
);
1167 /* No -ve pattern present, so use default signing */
1171 /* Skip over pattern separator (accounting for UTF8) */
1172 the_format
= (xmlChar
*)xmlUTF8Strpos(format
, j
+ 1);
1174 * Flag changes interpretation of percent/permille
1177 format_info
.is_negative_pattern
= TRUE
;
1178 format_info
.is_multiplier_set
= FALSE
;
1180 /* First do the -ve prefix */
1181 nprefix
= the_format
;
1182 nprefix_length
= xsltFormatNumberPreSuffix(self
,
1183 &the_format
, &format_info
);
1184 if (nprefix_length
<0) {
1189 while (*the_format
!= 0) {
1190 if ( (xsltUTF8Charcmp(the_format
, (self
)->percent
) == 0) ||
1191 (xsltUTF8Charcmp(the_format
, (self
)->permille
)== 0) ) {
1192 if (format_info
.is_multiplier_set
) {
1196 format_info
.is_multiplier_set
= TRUE
;
1197 delayed_multiplier
= 1;
1199 else if (IS_SPECIAL(self
, the_format
))
1200 delayed_multiplier
= 0;
1203 if ((len
= xsltUTF8Size(the_format
)) < 1) {
1209 if (delayed_multiplier
!= 0) {
1210 format_info
.is_multiplier_set
= FALSE
;
1214 /* Finally do the -ve suffix */
1215 if (*the_format
!= 0) {
1216 nsuffix
= the_format
;
1217 nsuffix_length
= xsltFormatNumberPreSuffix(self
,
1218 &the_format
, &format_info
);
1219 if (nsuffix_length
< 0) {
1226 if (*the_format
!= 0) {
1231 * Here's another Java peculiarity:
1232 * if -ve prefix/suffix == +ve ones, discard & use default
1234 if ((nprefix_length
!= prefix_length
) ||
1235 (nsuffix_length
!= suffix_length
) ||
1236 ((nprefix_length
> 0) &&
1237 (xmlStrncmp(nprefix
, prefix
, prefix_length
) !=0 )) ||
1238 ((nsuffix_length
> 0) &&
1239 (xmlStrncmp(nsuffix
, suffix
, suffix_length
) !=0 ))) {
1241 prefix_length
= nprefix_length
;
1243 suffix_length
= nsuffix_length
;
1252 if (found_error
!= 0) {
1253 xsltTransformError(NULL
, NULL
, NULL
,
1254 "xsltFormatNumberConversion : "
1255 "error in format string '%s', using default\n", format
);
1256 default_sign
= (number
< 0.0) ? 1 : 0;
1257 prefix_length
= suffix_length
= 0;
1258 format_info
.integer_hash
= 0;
1259 format_info
.integer_digits
= 1;
1260 format_info
.frac_digits
= 1;
1261 format_info
.frac_hash
= 4;
1262 format_info
.group
= -1;
1263 format_info
.multiplier
= 1;
1264 format_info
.add_decimal
= TRUE
;
1267 /* Ready to output our number. First see if "default sign" is required */
1268 if (default_sign
!= 0)
1269 xmlBufferAdd(buffer
, self
->minusSign
, xsltUTF8Size(self
->minusSign
));
1271 /* Put the prefix into the buffer */
1272 for (j
= 0; j
< prefix_length
; j
++) {
1273 if ((pchar
= *prefix
++) == SYMBOL_QUOTE
) {
1274 len
= xsltUTF8Size(prefix
);
1275 xmlBufferAdd(buffer
, prefix
, len
);
1277 j
+= len
- 1; /* length of symbol less length of quote */
1279 xmlBufferAdd(buffer
, &pchar
, 1);
1282 /* Next do the integer part of the number */
1283 number
= fabs(number
) * (double)format_info
.multiplier
;
1284 scale
= pow(10.0, (double)(format_info
.frac_digits
+ format_info
.frac_hash
));
1285 number
= floor((scale
* number
+ 0.5)) / scale
;
1286 if ((self
->grouping
!= NULL
) &&
1287 (self
->grouping
[0] != 0)) {
1289 len
= xmlStrlen(self
->grouping
);
1290 pchar
= xsltGetUTF8Char(self
->grouping
, &len
);
1291 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1292 format_info
.integer_digits
,
1296 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1297 format_info
.integer_digits
,
1301 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1302 if ((format_info
.integer_digits
+ format_info
.integer_hash
+
1303 format_info
.frac_digits
== 0) && (format_info
.frac_hash
> 0)) {
1304 ++format_info
.frac_digits
;
1305 --format_info
.frac_hash
;
1308 /* Add leading zero, if required */
1309 if ((floor(number
) == 0) &&
1310 (format_info
.integer_digits
+ format_info
.frac_digits
== 0)) {
1311 xmlBufferAdd(buffer
, self
->zeroDigit
, xsltUTF8Size(self
->zeroDigit
));
1314 /* Next the fractional part, if required */
1315 if (format_info
.frac_digits
+ format_info
.frac_hash
== 0) {
1316 if (format_info
.add_decimal
)
1317 xmlBufferAdd(buffer
, self
->decimalPoint
,
1318 xsltUTF8Size(self
->decimalPoint
));
1321 number
-= floor(number
);
1322 if ((number
!= 0) || (format_info
.frac_digits
!= 0)) {
1323 xmlBufferAdd(buffer
, self
->decimalPoint
,
1324 xsltUTF8Size(self
->decimalPoint
));
1325 number
= floor(scale
* number
+ 0.5);
1326 for (j
= format_info
.frac_hash
; j
> 0; j
--) {
1327 if (fmod(number
, 10.0) >= 1.0)
1331 xsltNumberFormatDecimal(buffer
, floor(number
), self
->zeroDigit
[0],
1332 format_info
.frac_digits
+ j
,
1336 /* Put the suffix into the buffer */
1337 for (j
= 0; j
< suffix_length
; j
++) {
1338 if ((pchar
= *suffix
++) == SYMBOL_QUOTE
) {
1339 len
= xsltUTF8Size(suffix
);
1340 xmlBufferAdd(buffer
, suffix
, len
);
1342 j
+= len
- 1; /* length of symbol less length of escape */
1344 xmlBufferAdd(buffer
, &pchar
, 1);
1347 *result
= xmlStrdup(xmlBufferContent(buffer
));
1348 xmlBufferFree(buffer
);