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(xsltNumberDataPtr data,
235 char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
239 double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
242 * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says:
244 * For all format tokens other than the first kind above (one that
245 * consists of decimal digits), there may be implementation-defined
246 * lower and upper bounds on the range of numbers that can be
247 * formatted using this format token; indeed, for some numbering
248 * sequences there may be intrinsic limits. [...] Numbers that fall
249 * outside this range must be formatted using the format token 1.
251 * The "a" token has an intrinsic lower limit of 1.
254 xsltNumberFormatDecimal(buffer, number, '0', 1,
255 data->digitsPerGroup,
256 data->groupingCharacter,
257 data->groupingCharacterLen);
261 /* Build buffer from back */
262 pointer = &temp_string[sizeof(temp_string)];
264 alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
266 for (i = 1; i < (int)sizeof(temp_string); i++) {
268 *(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
269 number /= alpha_size;
273 xmlBufferCCat(buffer, pointer);
277 xsltNumberFormatRoman(xsltNumberDataPtr data,
283 * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper
284 * bound to avoid denial of service.
286 if (number < 1.0 || number > 5000.0) {
287 xsltNumberFormatDecimal(buffer, number, '0', 1,
288 data->digitsPerGroup,
289 data->groupingCharacter,
290 data->groupingCharacterLen);
295 * Based on an example by Jim Walsh
297 while (number >= 1000.0) {
298 xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
301 if (number >= 900.0) {
302 xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
305 while (number >= 500.0) {
306 xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
309 if (number >= 400.0) {
310 xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
313 while (number >= 100.0) {
314 xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
317 if (number >= 90.0) {
318 xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
321 while (number >= 50.0) {
322 xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
325 if (number >= 40.0) {
326 xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
329 while (number >= 10.0) {
330 xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
334 xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
337 while (number >= 5.0) {
338 xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
342 xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
345 while (number >= 1.0) {
346 xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
352 xsltNumberFormatTokenize(const xmlChar *format,
353 xsltFormatPtr tokens)
360 default_token.token = DEFAULT_TOKEN;
361 default_token.width = 1;
362 default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);
365 tokens->start = NULL;
366 tokens->tokens[0].separator = NULL;
370 * Insert initial non-alphanumeric token.
371 * There is always such a token in the list, even if NULL
373 while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) ||
375 if (format[ix] == 0) /* if end of format string */
380 tokens->start = xmlStrndup(format, ix);
383 for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
389 * separator has already been parsed (except for the first
390 * number) in tokens->end, recover it.
392 if (tokens->nTokens > 0) {
393 tokens->tokens[tokens->nTokens].separator = tokens->end;
397 val = xmlStringCurrentChar(NULL, format+ix, &len);
398 if (IS_DIGIT_ONE(val) ||
399 IS_DIGIT_ZERO(val)) {
400 tokens->tokens[tokens->nTokens].width = 1;
401 while (IS_DIGIT_ZERO(val)) {
402 tokens->tokens[tokens->nTokens].width++;
404 val = xmlStringCurrentChar(NULL, format+ix, &len);
406 if (IS_DIGIT_ONE(val)) {
407 tokens->tokens[tokens->nTokens].token = val - 1;
409 val = xmlStringCurrentChar(NULL, format+ix, &len);
411 } else if ( (val == (xmlChar)'A') ||
412 (val == (xmlChar)'a') ||
413 (val == (xmlChar)'I') ||
414 (val == (xmlChar)'i') ) {
415 tokens->tokens[tokens->nTokens].token = val;
417 val = xmlStringCurrentChar(NULL, format+ix, &len);
420 * "Any other format token indicates a numbering sequence
421 * that starts with that token. If an implementation does
422 * not support a numbering sequence that starts with that
423 * token, it must use a format token of 1."
425 tokens->tokens[tokens->nTokens].token = (xmlChar)'0';
426 tokens->tokens[tokens->nTokens].width = 1;
429 * Skip over remaining alphanumeric characters from the Nd
430 * (Number, decimal digit), Nl (Number, letter), No (Number,
431 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
432 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
433 * (Letters, other (uncased)) Unicode categories. This happens
434 * to correspond to the Letter and Digit classes from XML (and
435 * one wonders why XSLT doesn't refer to these instead).
437 while (IS_LETTER(val) || IS_DIGIT(val)) {
439 val = xmlStringCurrentChar(NULL, format+ix, &len);
443 * Insert temporary non-alphanumeric final tooken.
446 while (! (IS_LETTER(val) || IS_DIGIT(val))) {
450 val = xmlStringCurrentChar(NULL, format+ix, &len);
453 tokens->end = xmlStrndup(&format[j], ix - j);
458 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
461 xsltFormatPtr tokens,
466 xsltFormatTokenPtr token;
469 * Handle initial non-alphanumeric token
471 if (tokens->start != NULL)
472 xmlBufferCat(buffer, tokens->start);
474 for (i = 0; i < numbers_max; i++) {
476 number = numbers[(numbers_max - 1) - i];
477 /* Round to nearest like XSLT 2.0 */
478 number = floor(number + 0.5);
480 * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT
483 * It is a non-recoverable dynamic error if any undiscarded item
484 * in the atomized sequence supplied as the value of the value
485 * attribute of xsl:number cannot be converted to an integer, or
486 * if the resulting integer is less than 0 (zero).
489 xsltTransformError(NULL, NULL, NULL,
490 "xsl-number : negative value\n");
491 /* Recover by treating negative values as zero. */
494 if (i < tokens->nTokens) {
496 * The "n"th format token will be used to format the "n"th
499 token = &(tokens->tokens[i]);
500 } else if (tokens->nTokens > 0) {
502 * If there are more numbers than format tokens, then the
503 * last format token will be used to format the remaining
506 token = &(tokens->tokens[tokens->nTokens - 1]);
509 * If there are no format tokens, then a format token of
510 * 1 is used to format all numbers.
512 token = &default_token;
515 /* Print separator, except for the first number */
517 if (token->separator != NULL)
518 xmlBufferCat(buffer, token->separator);
520 xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
523 switch (xmlXPathIsInf(number)) {
525 xmlBufferCCat(buffer, "-Infinity");
528 xmlBufferCCat(buffer, "Infinity");
531 if (xmlXPathIsNaN(number)) {
532 xmlBufferCCat(buffer, "NaN");
535 switch (token->token) {
537 xsltNumberFormatAlpha(data, buffer, number, TRUE);
540 xsltNumberFormatAlpha(data, buffer, number, FALSE);
543 xsltNumberFormatRoman(data, buffer, number, TRUE);
546 xsltNumberFormatRoman(data, buffer, number, FALSE);
549 if (IS_DIGIT_ZERO(token->token)) {
550 xsltNumberFormatDecimal(buffer,
554 data->digitsPerGroup,
555 data->groupingCharacter,
556 data->groupingCharacterLen);
566 * Handle final non-alphanumeric token
568 if (tokens->end != NULL)
569 xmlBufferCat(buffer, tokens->end);
574 xsltTestCompMatchCount(xsltTransformContextPtr context,
576 xsltCompMatchPtr countPat,
579 if (countPat != NULL) {
580 return xsltTestCompMatchList(context, node, countPat);
586 * If count attribute is not specified, then it defaults to the
587 * pattern that matches any node with the same node type as the
588 * current node and, if the current node has an expanded-name, with
589 * the same expanded-name as the current node.
591 if (node->type != cur->type)
593 if (node->type == XML_NAMESPACE_DECL)
595 * Namespace nodes have no preceding siblings and no parents
596 * that are namespace nodes. This means that node == cur.
599 /* TODO: Skip node types without expanded names like text nodes. */
600 if (!xmlStrEqual(node->name, cur->name))
602 if (node->ns == cur->ns)
604 if ((node->ns == NULL) || (cur->ns == NULL))
606 return (xmlStrEqual(node->ns->href, cur->ns->href));
611 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
613 xsltCompMatchPtr countPat,
614 xsltCompMatchPtr fromPat,
621 /* select the starting node */
622 switch (node->type) {
623 case XML_ELEMENT_NODE:
626 case XML_ATTRIBUTE_NODE:
627 cur = ((xmlAttrPtr) node)->parent;
631 case XML_COMMENT_NODE:
639 while (cur != NULL) {
640 /* process current node */
641 if (xsltTestCompMatchCount(context, cur, countPat, node))
643 if ((fromPat != NULL) &&
644 xsltTestCompMatchList(context, cur, fromPat)) {
648 /* Skip to next preceding or ancestor */
649 if ((cur->type == XML_DOCUMENT_NODE) ||
650 #ifdef LIBXML_DOCB_ENABLED
651 (cur->type == XML_DOCB_DOCUMENT_NODE) ||
653 (cur->type == XML_HTML_DOCUMENT_NODE))
656 while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
657 (cur->prev->type == XML_XINCLUDE_START) ||
658 (cur->prev->type == XML_XINCLUDE_END)))
660 if (cur->prev != NULL) {
661 for (cur = cur->prev; cur->last != NULL; cur = cur->last);
668 array[amount++] = (double) cnt;
674 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
676 xsltCompMatchPtr countPat,
677 xsltCompMatchPtr fromPat,
684 xmlNodePtr preceding;
685 xmlXPathParserContextPtr parser;
687 context->xpathCtxt->node = node;
688 parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
690 /* ancestor-or-self::*[count] */
691 for (ancestor = node;
692 (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE);
693 ancestor = xmlXPathNextAncestor(parser, ancestor)) {
695 if ((fromPat != NULL) &&
696 xsltTestCompMatchList(context, ancestor, fromPat))
699 if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
700 /* count(preceding-sibling::*) */
703 xmlXPathNextPrecedingSibling(parser, ancestor);
706 xmlXPathNextPrecedingSibling(parser, preceding)) {
708 if (xsltTestCompMatchCount(context, preceding, countPat,
712 array[amount++] = (double)cnt;
717 xmlXPathFreeParserContext(parser);
723 xsltNumberFormatGetValue(xmlXPathContextPtr context,
725 const xmlChar *value,
729 xmlBufferPtr pattern;
730 xmlXPathObjectPtr obj;
732 pattern = xmlBufferCreate();
733 if (pattern != NULL) {
734 xmlBufferCCat(pattern, "number(");
735 xmlBufferCat(pattern, value);
736 xmlBufferCCat(pattern, ")");
737 context->node = node;
738 obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
741 *number = obj->floatval;
743 xmlXPathFreeObject(obj);
745 xmlBufferFree(pattern);
752 * @ctxt: the XSLT transformation context
753 * @data: the formatting informations
754 * @node: the data to format
756 * Convert one number.
759 xsltNumberFormat(xsltTransformContextPtr ctxt,
760 xsltNumberDataPtr data,
763 xmlBufferPtr output = NULL;
768 if (data->format != NULL) {
769 xsltNumberFormatTokenize(data->format, &tokens);
774 /* The format needs to be recomputed each time */
775 if (data->has_format == 0)
777 format = xsltEvalAttrValueTemplate(ctxt, data->node,
778 (const xmlChar *) "format",
782 xsltNumberFormatTokenize(format, &tokens);
786 output = xmlBufferCreate();
788 goto XSLT_NUMBER_FORMAT_END;
791 * Evaluate the XPath expression to find the value(s)
794 amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
799 xsltNumberFormatInsertNumbers(data,
806 } else if (data->level) {
808 if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
809 amount = xsltNumberFormatGetMultipleLevel(ctxt,
816 xsltNumberFormatInsertNumbers(data,
822 } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
823 double numarray[1024];
824 int max = sizeof(numarray)/sizeof(numarray[0]);
825 amount = xsltNumberFormatGetMultipleLevel(ctxt,
832 xsltNumberFormatInsertNumbers(data,
838 } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
839 amount = xsltNumberFormatGetAnyLevel(ctxt,
845 xsltNumberFormatInsertNumbers(data,
853 /* Insert number as text node */
854 xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
856 xmlBufferFree(output);
858 XSLT_NUMBER_FORMAT_END:
859 if (tokens.start != NULL)
860 xmlFree(tokens.start);
861 if (tokens.end != NULL)
863 for (i = 0;i < tokens.nTokens;i++) {
864 if (tokens.tokens[i].separator != NULL)
865 xmlFree(tokens.tokens[i].separator);
870 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
872 int count=0; /* will hold total length of prefix/suffix */
877 * prefix / suffix ends at end of string or at
878 * first 'special' character
882 /* if next character 'escaped' just count it */
883 if (**format == SYMBOL_QUOTE) {
884 if (*++(*format) == 0)
887 else if (IS_SPECIAL(self, *format))
890 * else treat percent/per-mille as special cases,
891 * depending on whether +ve or -ve
895 * for +ve prefix/suffix, allow only a
896 * single occurence of either
898 if (xsltUTF8Charcmp(*format, self->percent) == 0) {
899 if (info->is_multiplier_set)
901 info->multiplier = 100;
902 info->is_multiplier_set = TRUE;
903 } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
904 if (info->is_multiplier_set)
906 info->multiplier = 1000;
907 info->is_multiplier_set = TRUE;
911 if ((len=xsltUTF8Size(*format)) < 1)
919 * xsltFormatNumberConversion:
920 * @self: the decimal format
921 * @format: the format requested
922 * @number: the value to format
923 * @result: the place to ouput the result
925 * format-number() uses the JDK 1.1 DecimalFormat class:
927 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
931 * pattern := subpattern{;subpattern}
932 * subpattern := {prefix}integer{.fraction}{suffix}
933 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
934 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
935 * integer := '#'* '0'* '0'
936 * fraction := '0'* '#'*
939 * X* 0 or more instances of X
940 * (X | Y) either X or Y.
941 * X..Y any character from X up to Y, inclusive.
942 * S - T characters in S, except those in T
944 * Special Characters:
948 * # a digit, zero shows as absent
949 * . placeholder for decimal separator
950 * , placeholder for grouping separator.
951 * ; separates formats.
952 * - default negative prefix.
953 * % multiply by 100 and show as percentage
954 * ? multiply by 1000 and show as per mille
955 * X any other characters can be used in the prefix or suffix
956 * ' used to quote special characters in a prefix or suffix.
958 * Returns a possible XPath error
961 xsltFormatNumberConversion(xsltDecimalFormatPtr self,
966 xmlXPathError status = XPATH_EXPRESSION_OK;
968 xmlChar *the_format, *prefix = NULL, *suffix = NULL;
969 xmlChar *nprefix, *nsuffix = NULL;
971 int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
974 int self_grouping_len;
975 xsltFormatNumberInfo format_info;
977 * delayed_multiplier allows a 'trailing' percent or
978 * permille to be treated as suffix
980 int delayed_multiplier = 0;
981 /* flag to show no -ve format present for -ve number */
982 char default_sign = 0;
983 /* flag to show error found, should use default format */
984 char found_error = 0;
986 if (xmlStrlen(format) <= 0) {
987 xsltTransformError(NULL, NULL, NULL,
988 "xsltFormatNumberConversion : "
989 "Invalid format (0-length)\n");
992 switch (xmlXPathIsInf(number)) {
994 if (self->minusSign == NULL)
995 *result = xmlStrdup(BAD_CAST "-");
997 *result = xmlStrdup(self->minusSign);
998 /* no-break on purpose */
1000 if ((self == NULL) || (self->infinity == NULL))
1001 *result = xmlStrcat(*result, BAD_CAST "Infinity");
1003 *result = xmlStrcat(*result, self->infinity);
1006 if (xmlXPathIsNaN(number)) {
1007 if ((self == NULL) || (self->noNumber == NULL))
1008 *result = xmlStrdup(BAD_CAST "NaN");
1010 *result = xmlStrdup(self->noNumber);
1015 buffer = xmlBufferCreate();
1016 if (buffer == NULL) {
1017 return XPATH_MEMORY_ERROR;
1020 format_info.integer_hash = 0;
1021 format_info.integer_digits = 0;
1022 format_info.frac_digits = 0;
1023 format_info.frac_hash = 0;
1024 format_info.group = -1;
1025 format_info.multiplier = 1;
1026 format_info.add_decimal = FALSE;
1027 format_info.is_multiplier_set = FALSE;
1028 format_info.is_negative_pattern = FALSE;
1030 the_format = format;
1033 * First we process the +ve pattern to get percent / permille,
1034 * as well as main format
1036 prefix = the_format;
1037 prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1038 if (prefix_length < 0) {
1044 * Here we process the "number" part of the format. It gets
1045 * a little messy because of the percent/per-mille - if that
1046 * appears at the end, it may be part of the suffix instead
1047 * of part of the number, so the variable delayed_multiplier
1048 * is used to handle it
1050 self_grouping_len = xmlStrlen(self->grouping);
1051 while ((*the_format != 0) &&
1052 (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
1053 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
1055 if (delayed_multiplier != 0) {
1056 format_info.multiplier = delayed_multiplier;
1057 format_info.is_multiplier_set = TRUE;
1058 delayed_multiplier = 0;
1060 if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1061 if (format_info.integer_digits > 0) {
1065 format_info.integer_hash++;
1066 if (format_info.group >= 0)
1067 format_info.group++;
1068 } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1069 format_info.integer_digits++;
1070 if (format_info.group >= 0)
1071 format_info.group++;
1072 } else if ((self_grouping_len > 0) &&
1073 (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
1074 /* Reset group count */
1075 format_info.group = 0;
1076 the_format += self_grouping_len;
1078 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1079 if (format_info.is_multiplier_set) {
1083 delayed_multiplier = 100;
1084 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1085 if (format_info.is_multiplier_set) {
1089 delayed_multiplier = 1000;
1093 if ((len=xsltUTF8Size(the_format)) < 1) {
1101 /* We have finished the integer part, now work on fraction */
1102 if (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) {
1103 format_info.add_decimal = TRUE;
1104 the_format += xsltUTF8Size(the_format); /* Skip over the decimal */
1107 while (*the_format != 0) {
1109 if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1110 if (format_info.frac_hash != 0) {
1114 format_info.frac_digits++;
1115 } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1116 format_info.frac_hash++;
1117 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1118 if (format_info.is_multiplier_set) {
1122 delayed_multiplier = 100;
1123 if ((len = xsltUTF8Size(the_format)) < 1) {
1128 continue; /* while */
1129 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1130 if (format_info.is_multiplier_set) {
1134 delayed_multiplier = 1000;
1135 if ((len = xsltUTF8Size(the_format)) < 1) {
1140 continue; /* while */
1141 } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
1144 if ((len = xsltUTF8Size(the_format)) < 1) {
1149 if (delayed_multiplier != 0) {
1150 format_info.multiplier = delayed_multiplier;
1151 delayed_multiplier = 0;
1152 format_info.is_multiplier_set = TRUE;
1157 * If delayed_multiplier is set after processing the
1158 * "number" part, should be in suffix
1160 if (delayed_multiplier != 0) {
1162 delayed_multiplier = 0;
1165 suffix = the_format;
1166 suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1167 if ( (suffix_length < 0) ||
1168 ((*the_format != 0) &&
1169 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
1175 * We have processed the +ve prefix, number part and +ve suffix.
1176 * If the number is -ve, we must substitute the -ve prefix / suffix
1180 * Note that j is the number of UTF8 chars before the separator,
1181 * not the number of bytes! (bug 151975)
1183 j = xmlUTF8Strloc(format, self->patternSeparator);
1185 /* No -ve pattern present, so use default signing */
1189 /* Skip over pattern separator (accounting for UTF8) */
1190 the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
1192 * Flag changes interpretation of percent/permille
1195 format_info.is_negative_pattern = TRUE;
1196 format_info.is_multiplier_set = FALSE;
1198 /* First do the -ve prefix */
1199 nprefix = the_format;
1200 nprefix_length = xsltFormatNumberPreSuffix(self,
1201 &the_format, &format_info);
1202 if (nprefix_length<0) {
1207 while (*the_format != 0) {
1208 if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
1209 (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
1210 if (format_info.is_multiplier_set) {
1214 format_info.is_multiplier_set = TRUE;
1215 delayed_multiplier = 1;
1217 else if (IS_SPECIAL(self, the_format))
1218 delayed_multiplier = 0;
1221 if ((len = xsltUTF8Size(the_format)) < 1) {
1227 if (delayed_multiplier != 0) {
1228 format_info.is_multiplier_set = FALSE;
1232 /* Finally do the -ve suffix */
1233 if (*the_format != 0) {
1234 nsuffix = the_format;
1235 nsuffix_length = xsltFormatNumberPreSuffix(self,
1236 &the_format, &format_info);
1237 if (nsuffix_length < 0) {
1244 if (*the_format != 0) {
1249 * Here's another Java peculiarity:
1250 * if -ve prefix/suffix == +ve ones, discard & use default
1252 if ((nprefix_length != prefix_length) ||
1253 (nsuffix_length != suffix_length) ||
1254 ((nprefix_length > 0) &&
1255 (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
1256 ((nsuffix_length > 0) &&
1257 (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
1259 prefix_length = nprefix_length;
1261 suffix_length = nsuffix_length;
1270 if (found_error != 0) {
1271 xsltTransformError(NULL, NULL, NULL,
1272 "xsltFormatNumberConversion : "
1273 "error in format string '%s', using default\n", format);
1274 default_sign = (number < 0.0) ? 1 : 0;
1275 prefix_length = suffix_length = 0;
1276 format_info.integer_hash = 0;
1277 format_info.integer_digits = 1;
1278 format_info.frac_digits = 1;
1279 format_info.frac_hash = 4;
1280 format_info.group = -1;
1281 format_info.multiplier = 1;
1282 format_info.add_decimal = TRUE;
1285 /* Ready to output our number. First see if "default sign" is required */
1286 if (default_sign != 0)
1287 xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign));
1289 /* Put the prefix into the buffer */
1290 for (j = 0; j < prefix_length; j++) {
1291 if ((pchar = *prefix++) == SYMBOL_QUOTE) {
1292 len = xsltUTF8Size(prefix);
1293 xmlBufferAdd(buffer, prefix, len);
1295 j += len - 1; /* length of symbol less length of quote */
1297 xmlBufferAdd(buffer, &pchar, 1);
1300 /* Next do the integer part of the number */
1301 number = fabs(number) * (double)format_info.multiplier;
1302 scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
1303 number = floor((scale * number + 0.5)) / scale;
1304 if ((self->grouping != NULL) &&
1305 (self->grouping[0] != 0)) {
1307 len = xmlStrlen(self->grouping);
1308 pchar = xsltGetUTF8Char(self->grouping, &len);
1309 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1310 format_info.integer_digits,
1314 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1315 format_info.integer_digits,
1319 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1320 if ((format_info.integer_digits + format_info.integer_hash +
1321 format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
1322 ++format_info.frac_digits;
1323 --format_info.frac_hash;
1326 /* Add leading zero, if required */
1327 if ((floor(number) == 0) &&
1328 (format_info.integer_digits + format_info.frac_digits == 0)) {
1329 xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit));
1332 /* Next the fractional part, if required */
1333 if (format_info.frac_digits + format_info.frac_hash == 0) {
1334 if (format_info.add_decimal)
1335 xmlBufferAdd(buffer, self->decimalPoint,
1336 xsltUTF8Size(self->decimalPoint));
1339 number -= floor(number);
1340 if ((number != 0) || (format_info.frac_digits != 0)) {
1341 xmlBufferAdd(buffer, self->decimalPoint,
1342 xsltUTF8Size(self->decimalPoint));
1343 number = floor(scale * number + 0.5);
1344 for (j = format_info.frac_hash; j > 0; j--) {
1345 if (fmod(number, 10.0) >= 1.0)
1349 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1350 format_info.frac_digits + j,
1354 /* Put the suffix into the buffer */
1355 for (j = 0; j < suffix_length; j++) {
1356 if ((pchar = *suffix++) == SYMBOL_QUOTE) {
1357 len = xsltUTF8Size(suffix);
1358 xmlBufferAdd(buffer, suffix, len);
1360 j += len - 1; /* length of symbol less length of escape */
1362 xmlBufferAdd(buffer, &pchar, 1);
1365 *result = xmlStrdup(xmlBufferContent(buffer));
1366 xmlBufferFree(buffer);