xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / numbers.c
blob3cd881e3c8d5f8f41a50412d68a6e4f5bb39b91f
1 /*
2 * numbers.c: Implementation of the XSLT number functions
4 * Reference:
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * daniel@veillard.com
10 * Bjorn Reese <breese@users.sourceforge.net>
13 #define IN_LIBXSLT
14 #include "libxslt.h"
16 #include <math.h>
17 #include <limits.h>
18 #include <float.h>
19 #include <string.h>
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"
27 #include "pattern.h"
28 #include "templates.h"
29 #include "transform.h"
30 #include "numbersInternals.h"
32 #ifndef FALSE
33 # define FALSE (0 == 1)
34 # define TRUE (1 == 1)
35 #endif
37 #define SYMBOL_QUOTE ((xmlChar)'\'')
39 #define DEFAULT_TOKEN '0'
40 #define DEFAULT_SEPARATOR "."
42 #define MAX_TOKENS 1024
44 typedef struct _xsltFormatToken xsltFormatToken;
45 typedef xsltFormatToken *xsltFormatTokenPtr;
46 struct _xsltFormatToken {
47 xmlChar *separator;
48 int token;
49 int width;
52 typedef struct _xsltFormat xsltFormat;
53 typedef xsltFormat *xsltFormatPtr;
54 struct _xsltFormat {
55 xmlChar *start;
56 xsltFormatToken tokens[MAX_TOKENS];
57 int nTokens;
58 xmlChar *end;
61 static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
62 static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
63 static xsltFormatToken default_token;
66 * **** Start temp insert ****
68 * The following routine xsltUTF8Charcmp will be replaced with calls to
69 * the corresponding libxml routine at a later date (when other
70 * inter-library dependencies require it).
73 /**
74 * xsltUTF8Charcmp
75 * @utf1: pointer to first UTF8 char
76 * @utf2: pointer to second UTF8 char
78 * returns result of comparing the two UCS4 values
79 * as with xmlStrncmp
81 static int
82 xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) {
83 int len = xmlUTF8Strsize(utf1, 1);
85 if (len < 1)
86 return -1;
87 if (utf1 == NULL ) {
88 if (utf2 == NULL)
89 return 0;
90 return -1;
92 return xmlStrncmp(utf1, utf2, len);
95 static int
96 xsltIsLetterDigit(int val) {
97 return xmlIsBaseCharQ(val) || xmlIsIdeographicQ(val) ||
98 xmlIsDigitQ(val);
101 /***** Stop temp insert *****/
102 /************************************************************************
104 * Utility functions *
106 ************************************************************************/
108 #define IS_SPECIAL(self,letter) \
109 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
110 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
111 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
112 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
113 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
115 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
116 #define IS_DIGIT_ONE(x) xsltIsDigitZero((x)-1)
118 static int
119 xsltIsDigitZero(int ch)
122 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
124 * There a many more digit ranges in newer Unicode versions. These
125 * are only the zeros that match Digit in XML 1.0 (IS_DIGIT macro).
127 switch (ch) {
128 case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
129 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
130 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
131 case 0x0ED0: case 0x0F20:
132 return TRUE;
133 default:
134 return FALSE;
138 static void
139 xsltNumberFormatDecimal(xmlBufferPtr buffer,
140 double number,
141 int digit_zero,
142 int width,
143 int digitsPerGroup,
144 int groupingCharacter,
145 int groupingCharacterLen)
148 * This used to be
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];
156 xmlChar *pointer;
157 xmlChar temp_char[6];
158 int i;
159 int val;
160 int len;
162 /* Build buffer from back */
163 pointer = &temp_string[sizeof(temp_string)] - 1; /* last char */
164 *pointer = 0;
165 i = 0;
166 while (pointer > temp_string) {
167 if ((i >= width) && (fabs(number) < 1.0))
168 break; /* for */
169 if ((i > 0) && (groupingCharacter != 0) &&
170 (digitsPerGroup > 0) &&
171 ((i % digitsPerGroup) == 0)) {
172 if (pointer - groupingCharacterLen < temp_string) {
173 i = -1; /* flag error */
174 break;
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 */
183 i = -1;
184 break;
186 *(--pointer) = val;
188 else {
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 ) {
197 i = -1;
198 break;
200 pointer -= len;
201 memcpy(pointer, temp_char, len);
203 number /= 10.0;
204 ++i;
206 if (i < 0)
207 xsltGenericError(xsltGenericErrorContext,
208 "xsltNumberFormatDecimal: Internal buffer size exceeded\n");
209 xmlBufferCat(buffer, pointer);
212 static void
213 xsltNumberFormatAlpha(xsltNumberDataPtr data,
214 xmlBufferPtr buffer,
215 double number,
216 int is_upper)
218 char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
219 char *pointer;
220 int i;
221 char *alpha_list;
222 double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
225 * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says:
227 * For all format tokens other than the first kind above (one that
228 * consists of decimal digits), there may be implementation-defined
229 * lower and upper bounds on the range of numbers that can be
230 * formatted using this format token; indeed, for some numbering
231 * sequences there may be intrinsic limits. [...] Numbers that fall
232 * outside this range must be formatted using the format token 1.
234 * The "a" token has an intrinsic lower limit of 1.
236 if (number < 1.0) {
237 xsltNumberFormatDecimal(buffer, number, '0', 1,
238 data->digitsPerGroup,
239 data->groupingCharacter,
240 data->groupingCharacterLen);
241 return;
244 /* Build buffer from back */
245 pointer = &temp_string[sizeof(temp_string)];
246 *(--pointer) = 0;
247 alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
249 for (i = 1; i < (int)sizeof(temp_string); i++) {
250 number--;
251 *(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
252 number /= alpha_size;
253 if (number < 1.0)
254 break; /* for */
256 xmlBufferCCat(buffer, pointer);
259 static void
260 xsltNumberFormatRoman(xsltNumberDataPtr data,
261 xmlBufferPtr buffer,
262 double number,
263 int is_upper)
266 * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper
267 * bound to avoid denial of service.
269 if (number < 1.0 || number > 5000.0) {
270 xsltNumberFormatDecimal(buffer, number, '0', 1,
271 data->digitsPerGroup,
272 data->groupingCharacter,
273 data->groupingCharacterLen);
274 return;
278 * Based on an example by Jim Walsh
280 while (number >= 1000.0) {
281 xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
282 number -= 1000.0;
284 if (number >= 900.0) {
285 xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
286 number -= 900.0;
288 while (number >= 500.0) {
289 xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
290 number -= 500.0;
292 if (number >= 400.0) {
293 xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
294 number -= 400.0;
296 while (number >= 100.0) {
297 xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
298 number -= 100.0;
300 if (number >= 90.0) {
301 xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
302 number -= 90.0;
304 while (number >= 50.0) {
305 xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
306 number -= 50.0;
308 if (number >= 40.0) {
309 xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
310 number -= 40.0;
312 while (number >= 10.0) {
313 xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
314 number -= 10.0;
316 if (number >= 9.0) {
317 xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
318 number -= 9.0;
320 while (number >= 5.0) {
321 xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
322 number -= 5.0;
324 if (number >= 4.0) {
325 xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
326 number -= 4.0;
328 while (number >= 1.0) {
329 xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
330 number--;
334 static void
335 xsltNumberFormatTokenize(const xmlChar *format,
336 xsltFormatPtr tokens)
338 int ix = 0;
339 int j;
340 int val;
341 int len;
343 default_token.token = DEFAULT_TOKEN;
344 default_token.width = 1;
345 default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);
348 tokens->start = NULL;
349 tokens->tokens[0].separator = NULL;
350 tokens->end = NULL;
353 * Insert initial non-alphanumeric token.
354 * There is always such a token in the list, even if NULL
356 while (!xsltIsLetterDigit(val = xsltGetUTF8CharZ(format+ix, &len))) {
357 if (format[ix] == 0) /* if end of format string */
358 break; /* while */
359 ix += len;
361 if (ix > 0)
362 tokens->start = xmlStrndup(format, ix);
365 for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
366 tokens->nTokens++) {
367 if (format[ix] == 0)
368 break; /* for */
371 * separator has already been parsed (except for the first
372 * number) in tokens->end, recover it.
374 if (tokens->nTokens > 0) {
375 tokens->tokens[tokens->nTokens].separator = tokens->end;
376 tokens->end = NULL;
379 val = xsltGetUTF8CharZ(format+ix, &len);
380 if (IS_DIGIT_ONE(val) ||
381 IS_DIGIT_ZERO(val)) {
382 tokens->tokens[tokens->nTokens].width = 1;
383 while (IS_DIGIT_ZERO(val)) {
384 tokens->tokens[tokens->nTokens].width++;
385 ix += len;
386 val = xsltGetUTF8CharZ(format+ix, &len);
388 if (IS_DIGIT_ONE(val)) {
389 tokens->tokens[tokens->nTokens].token = val - 1;
390 ix += len;
391 val = xsltGetUTF8CharZ(format+ix, &len);
392 } else {
393 tokens->tokens[tokens->nTokens].token = '0';
394 tokens->tokens[tokens->nTokens].width = 1;
396 } else if ( (val == 'A') ||
397 (val == 'a') ||
398 (val == 'I') ||
399 (val == 'i') ) {
400 tokens->tokens[tokens->nTokens].token = val;
401 ix += len;
402 val = xsltGetUTF8CharZ(format+ix, &len);
403 } else {
404 /* XSLT section 7.7
405 * "Any other format token indicates a numbering sequence
406 * that starts with that token. If an implementation does
407 * not support a numbering sequence that starts with that
408 * token, it must use a format token of 1."
410 tokens->tokens[tokens->nTokens].token = '0';
411 tokens->tokens[tokens->nTokens].width = 1;
414 * Skip over remaining alphanumeric characters from the Nd
415 * (Number, decimal digit), Nl (Number, letter), No (Number,
416 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
417 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
418 * (Letters, other (uncased)) Unicode categories. This happens
419 * to correspond to the Letter and Digit classes from XML (and
420 * one wonders why XSLT doesn't refer to these instead).
422 while (xsltIsLetterDigit(val)) {
423 ix += len;
424 val = xsltGetUTF8CharZ(format+ix, &len);
428 * Insert temporary non-alphanumeric final tooken.
430 j = ix;
431 while (!xsltIsLetterDigit(val)) {
432 if (val == 0)
433 break; /* while */
434 ix += len;
435 val = xsltGetUTF8CharZ(format+ix, &len);
437 if (ix > j)
438 tokens->end = xmlStrndup(&format[j], ix - j);
442 static void
443 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
444 double *numbers,
445 int numbers_max,
446 xsltFormatPtr tokens,
447 xmlBufferPtr buffer)
449 int i = 0;
450 double number;
451 xsltFormatTokenPtr token;
454 * Handle initial non-alphanumeric token
456 if (tokens->start != NULL)
457 xmlBufferCat(buffer, tokens->start);
459 for (i = 0; i < numbers_max; i++) {
460 /* Insert number */
461 number = numbers[(numbers_max - 1) - i];
462 /* Round to nearest like XSLT 2.0 */
463 number = floor(number + 0.5);
465 * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT
466 * 2.0 says:
468 * It is a non-recoverable dynamic error if any undiscarded item
469 * in the atomized sequence supplied as the value of the value
470 * attribute of xsl:number cannot be converted to an integer, or
471 * if the resulting integer is less than 0 (zero).
473 if (number < 0.0) {
474 xsltTransformError(NULL, NULL, NULL,
475 "xsl-number : negative value\n");
476 /* Recover by treating negative values as zero. */
477 number = 0.0;
479 if (i < tokens->nTokens) {
481 * The "n"th format token will be used to format the "n"th
482 * number in the list
484 token = &(tokens->tokens[i]);
485 } else if (tokens->nTokens > 0) {
487 * If there are more numbers than format tokens, then the
488 * last format token will be used to format the remaining
489 * numbers.
491 token = &(tokens->tokens[tokens->nTokens - 1]);
492 } else {
494 * If there are no format tokens, then a format token of
495 * 1 is used to format all numbers.
497 token = &default_token;
500 /* Print separator, except for the first number */
501 if (i > 0) {
502 if (token->separator != NULL)
503 xmlBufferCat(buffer, token->separator);
504 else
505 xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
508 switch (xmlXPathIsInf(number)) {
509 case -1:
510 xmlBufferCCat(buffer, "-Infinity");
511 break;
512 case 1:
513 xmlBufferCCat(buffer, "Infinity");
514 break;
515 default:
516 if (xmlXPathIsNaN(number)) {
517 xmlBufferCCat(buffer, "NaN");
518 } else {
520 switch (token->token) {
521 case 'A':
522 xsltNumberFormatAlpha(data, buffer, number, TRUE);
523 break;
524 case 'a':
525 xsltNumberFormatAlpha(data, buffer, number, FALSE);
526 break;
527 case 'I':
528 xsltNumberFormatRoman(data, buffer, number, TRUE);
529 break;
530 case 'i':
531 xsltNumberFormatRoman(data, buffer, number, FALSE);
532 break;
533 default:
534 if (IS_DIGIT_ZERO(token->token)) {
535 xsltNumberFormatDecimal(buffer,
536 number,
537 token->token,
538 token->width,
539 data->digitsPerGroup,
540 data->groupingCharacter,
541 data->groupingCharacterLen);
543 break;
551 * Handle final non-alphanumeric token
553 if (tokens->end != NULL)
554 xmlBufferCat(buffer, tokens->end);
558 static int
559 xsltTestCompMatchCount(xsltTransformContextPtr context,
560 xmlNodePtr node,
561 xsltCompMatchPtr countPat,
562 xmlNodePtr cur)
564 if (countPat != NULL) {
565 return xsltTestCompMatchList(context, node, countPat);
567 else {
569 * 7.7 Numbering
571 * If count attribute is not specified, then it defaults to the
572 * pattern that matches any node with the same node type as the
573 * current node and, if the current node has an expanded-name, with
574 * the same expanded-name as the current node.
576 if (node->type != cur->type)
577 return 0;
578 if (node->type == XML_NAMESPACE_DECL)
580 * Namespace nodes have no preceding siblings and no parents
581 * that are namespace nodes. This means that node == cur.
583 return 1;
584 /* TODO: Skip node types without expanded names like text nodes. */
585 if (!xmlStrEqual(node->name, cur->name))
586 return 0;
587 if (node->ns == cur->ns)
588 return 1;
589 if ((node->ns == NULL) || (cur->ns == NULL))
590 return 0;
591 return (xmlStrEqual(node->ns->href, cur->ns->href));
595 static int
596 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
597 xmlNodePtr node,
598 xsltCompMatchPtr countPat,
599 xsltCompMatchPtr fromPat,
600 double *array)
602 int amount = 0;
603 int cnt = 0;
604 xmlNodePtr cur = node;
606 while (cur != NULL) {
607 /* process current node */
608 if (xsltTestCompMatchCount(context, cur, countPat, node))
609 cnt++;
610 if ((fromPat != NULL) &&
611 xsltTestCompMatchList(context, cur, fromPat)) {
612 break; /* while */
615 /* Skip to next preceding or ancestor */
616 if ((cur->type == XML_DOCUMENT_NODE) ||
617 #ifdef LIBXML_DOCB_ENABLED
618 (cur->type == XML_DOCB_DOCUMENT_NODE) ||
619 #endif
620 (cur->type == XML_HTML_DOCUMENT_NODE))
621 break; /* while */
623 if (cur->type == XML_NAMESPACE_DECL) {
625 * The XPath module stores the parent of a namespace node in
626 * the ns->next field.
628 cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
629 } else if (cur->type == XML_ATTRIBUTE_NODE) {
630 cur = cur->parent;
631 } else {
632 while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
633 (cur->prev->type == XML_XINCLUDE_START) ||
634 (cur->prev->type == XML_XINCLUDE_END)))
635 cur = cur->prev;
636 if (cur->prev != NULL) {
637 for (cur = cur->prev; cur->last != NULL; cur = cur->last);
638 } else {
639 cur = cur->parent;
644 array[amount++] = (double) cnt;
646 return(amount);
649 static int
650 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
651 xmlNodePtr node,
652 xsltCompMatchPtr countPat,
653 xsltCompMatchPtr fromPat,
654 double *array,
655 int max)
657 int amount = 0;
658 int cnt;
659 xmlNodePtr oldCtxtNode;
660 xmlNodePtr ancestor;
661 xmlNodePtr preceding;
662 xmlXPathParserContextPtr parser;
664 oldCtxtNode = context->xpathCtxt->node;
665 parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
666 if (parser) {
667 /* ancestor-or-self::*[count] */
668 ancestor = node;
669 while ((ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE)) {
670 if ((fromPat != NULL) &&
671 xsltTestCompMatchList(context, ancestor, fromPat))
672 break; /* for */
675 * The xmlXPathNext* iterators require that the context node is
676 * set to the start node. Calls to xsltTestCompMatch* may also
677 * leave the context node in an undefined state, so make sure
678 * that the context node is reset before each iterator invocation.
681 if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
682 /* count(preceding-sibling::*) */
683 cnt = 1;
684 context->xpathCtxt->node = ancestor;
685 preceding = xmlXPathNextPrecedingSibling(parser, ancestor);
686 while (preceding != NULL) {
687 if (xsltTestCompMatchCount(context, preceding, countPat,
688 node))
689 cnt++;
690 context->xpathCtxt->node = ancestor;
691 preceding =
692 xmlXPathNextPrecedingSibling(parser, preceding);
694 array[amount++] = (double)cnt;
695 if (amount >= max)
696 break; /* for */
698 context->xpathCtxt->node = node;
699 ancestor = xmlXPathNextAncestor(parser, ancestor);
701 xmlXPathFreeParserContext(parser);
703 context->xpathCtxt->node = oldCtxtNode;
704 return amount;
707 static int
708 xsltNumberFormatGetValue(xmlXPathContextPtr context,
709 xmlNodePtr node,
710 const xmlChar *value,
711 double *number)
713 int amount = 0;
714 xmlBufferPtr pattern;
715 xmlXPathObjectPtr obj;
717 pattern = xmlBufferCreate();
718 if (pattern != NULL) {
719 xmlBufferCCat(pattern, "number(");
720 xmlBufferCat(pattern, value);
721 xmlBufferCCat(pattern, ")");
722 context->node = node;
723 obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
724 context);
725 if (obj != NULL) {
726 *number = obj->floatval;
727 amount++;
728 xmlXPathFreeObject(obj);
730 xmlBufferFree(pattern);
732 return amount;
736 * xsltNumberFormat:
737 * @ctxt: the XSLT transformation context
738 * @data: the formatting information
739 * @node: the data to format
741 * Convert one number.
743 void
744 xsltNumberFormat(xsltTransformContextPtr ctxt,
745 xsltNumberDataPtr data,
746 xmlNodePtr node)
748 xmlBufferPtr output = NULL;
749 int amount, i;
750 double number;
751 xsltFormat tokens;
753 if (data->format != NULL) {
754 xsltNumberFormatTokenize(data->format, &tokens);
756 else {
757 xmlChar *format;
759 /* The format needs to be recomputed each time */
760 if (data->has_format == 0)
761 return;
762 format = xsltEvalAttrValueTemplate(ctxt, data->node,
763 (const xmlChar *) "format",
764 XSLT_NAMESPACE);
765 if (format == NULL)
766 return;
767 xsltNumberFormatTokenize(format, &tokens);
768 xmlFree(format);
771 output = xmlBufferCreate();
772 if (output == NULL)
773 goto XSLT_NUMBER_FORMAT_END;
776 * Evaluate the XPath expression to find the value(s)
778 if (data->value) {
779 amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
780 node,
781 data->value,
782 &number);
783 if (amount == 1) {
784 xsltNumberFormatInsertNumbers(data,
785 &number,
787 &tokens,
788 output);
791 } else if (data->level) {
793 if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
794 amount = xsltNumberFormatGetMultipleLevel(ctxt,
795 node,
796 data->countPat,
797 data->fromPat,
798 &number,
800 if (amount == 1) {
801 xsltNumberFormatInsertNumbers(data,
802 &number,
804 &tokens,
805 output);
807 } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
808 double numarray[1024];
809 int max = sizeof(numarray)/sizeof(numarray[0]);
810 amount = xsltNumberFormatGetMultipleLevel(ctxt,
811 node,
812 data->countPat,
813 data->fromPat,
814 numarray,
815 max);
816 if (amount > 0) {
817 xsltNumberFormatInsertNumbers(data,
818 numarray,
819 amount,
820 &tokens,
821 output);
823 } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
824 amount = xsltNumberFormatGetAnyLevel(ctxt,
825 node,
826 data->countPat,
827 data->fromPat,
828 &number);
829 if (amount > 0) {
830 xsltNumberFormatInsertNumbers(data,
831 &number,
833 &tokens,
834 output);
839 * Unlike `match` patterns, `count` and `from` patterns can contain
840 * variable references, so we have to clear the pattern match
841 * cache if the "direct" matching algorithm was used.
843 if (data->countPat != NULL)
844 xsltCompMatchClearCache(ctxt, data->countPat);
845 if (data->fromPat != NULL)
846 xsltCompMatchClearCache(ctxt, data->fromPat);
848 /* Insert number as text node */
849 xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
851 xmlBufferFree(output);
853 XSLT_NUMBER_FORMAT_END:
854 if (tokens.start != NULL)
855 xmlFree(tokens.start);
856 if (tokens.end != NULL)
857 xmlFree(tokens.end);
858 for (i = 0;i < tokens.nTokens;i++) {
859 if (tokens.tokens[i].separator != NULL)
860 xmlFree(tokens.tokens[i].separator);
864 static int
865 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
867 /* will hold total length of prefix/suffix without quote characters */
868 int count=0;
869 int len;
871 while (1) {
873 * prefix / suffix ends at end of string or at
874 * first 'special' character
876 if (**format == 0)
877 return count;
878 /* if next character 'escaped' just count it */
879 if (**format == SYMBOL_QUOTE) {
880 if (*++(*format) == 0)
881 return -1;
883 else if (IS_SPECIAL(self, *format))
884 return count;
886 * else treat percent/per-mille as special cases,
887 * depending on whether +ve or -ve
889 else {
891 * for +ve prefix/suffix, allow only a
892 * single occurence of either
894 if (xsltUTF8Charcmp(*format, self->percent) == 0) {
895 if (info->is_multiplier_set)
896 return -1;
897 info->multiplier = 100;
898 info->is_multiplier_set = TRUE;
899 } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
900 if (info->is_multiplier_set)
901 return -1;
902 info->multiplier = 1000;
903 info->is_multiplier_set = TRUE;
907 if ((len=xmlUTF8Strsize(*format, 1)) < 1)
908 return -1;
909 count += len;
910 *format += len;
915 * xsltFormatNumberConversion:
916 * @self: the decimal format
917 * @format: the format requested
918 * @number: the value to format
919 * @result: the place to output the result
921 * format-number() uses the JDK 1.1 DecimalFormat class:
923 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
925 * Structure:
927 * pattern := subpattern{;subpattern}
928 * subpattern := {prefix}integer{.fraction}{suffix}
929 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
930 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
931 * integer := '#'* '0'* '0'
932 * fraction := '0'* '#'*
934 * Notation:
935 * X* 0 or more instances of X
936 * (X | Y) either X or Y.
937 * X..Y any character from X up to Y, inclusive.
938 * S - T characters in S, except those in T
940 * Special Characters:
942 * Symbol Meaning
943 * 0 a digit
944 * # a digit, zero shows as absent
945 * . placeholder for decimal separator
946 * , placeholder for grouping separator.
947 * ; separates formats.
948 * - default negative prefix.
949 * % multiply by 100 and show as percentage
950 * ? multiply by 1000 and show as per mille
951 * X any other characters can be used in the prefix or suffix
952 * ' used to quote special characters in a prefix or suffix.
954 * Returns a possible XPath error
956 xmlXPathError
957 xsltFormatNumberConversion(xsltDecimalFormatPtr self,
958 xmlChar *format,
959 double number,
960 xmlChar **result)
962 xmlXPathError status = XPATH_EXPRESSION_OK;
963 xmlBufferPtr buffer;
964 xmlChar *the_format, *prefix = NULL, *suffix = NULL;
965 xmlChar *nprefix, *nsuffix = NULL;
966 int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
967 int exp10;
968 double scale;
969 int j, len = 0;
970 int self_grouping_len;
971 xsltFormatNumberInfo format_info;
973 * delayed_multiplier allows a 'trailing' percent or
974 * permille to be treated as suffix
976 int delayed_multiplier = 0;
977 /* flag to show no -ve format present for -ve number */
978 char default_sign = 0;
979 /* flag to show error found, should use default format */
980 char found_error = 0;
982 if (xmlStrlen(format) <= 0) {
983 xsltTransformError(NULL, NULL, NULL,
984 "xsltFormatNumberConversion : "
985 "Invalid format (0-length)\n");
987 *result = NULL;
988 if (xmlXPathIsNaN(number)) {
989 if ((self == NULL) || (self->noNumber == NULL))
990 *result = xmlStrdup(BAD_CAST "NaN");
991 else
992 *result = xmlStrdup(self->noNumber);
993 return(status);
996 format_info.integer_hash = 0;
997 format_info.integer_digits = 0;
998 format_info.frac_digits = 0;
999 format_info.frac_hash = 0;
1000 format_info.group = -1;
1001 format_info.multiplier = 1;
1002 format_info.add_decimal = FALSE;
1003 format_info.is_multiplier_set = FALSE;
1004 format_info.is_negative_pattern = FALSE;
1006 the_format = format;
1009 * First we process the +ve pattern to get percent / permille,
1010 * as well as main format
1012 prefix = the_format;
1013 prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1014 if (prefix_length < 0) {
1015 found_error = 1;
1016 goto OUTPUT_NUMBER;
1020 * Here we process the "number" part of the format. It gets
1021 * a little messy because of the percent/per-mille - if that
1022 * appears at the end, it may be part of the suffix instead
1023 * of part of the number, so the variable delayed_multiplier
1024 * is used to handle it
1026 self_grouping_len = xmlStrlen(self->grouping);
1027 while ((*the_format != 0) &&
1028 (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
1029 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
1031 if (delayed_multiplier != 0) {
1032 format_info.multiplier = delayed_multiplier;
1033 format_info.is_multiplier_set = TRUE;
1034 delayed_multiplier = 0;
1036 if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1037 if (format_info.integer_digits > 0) {
1038 found_error = 1;
1039 goto OUTPUT_NUMBER;
1041 format_info.integer_hash++;
1042 if (format_info.group >= 0)
1043 format_info.group++;
1044 } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1045 format_info.integer_digits++;
1046 if (format_info.group >= 0)
1047 format_info.group++;
1048 } else if ((self_grouping_len > 0) &&
1049 (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
1050 /* Reset group count */
1051 format_info.group = 0;
1052 the_format += self_grouping_len;
1053 continue;
1054 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1055 if (format_info.is_multiplier_set) {
1056 found_error = 1;
1057 goto OUTPUT_NUMBER;
1059 delayed_multiplier = 100;
1060 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1061 if (format_info.is_multiplier_set) {
1062 found_error = 1;
1063 goto OUTPUT_NUMBER;
1065 delayed_multiplier = 1000;
1066 } else
1067 break; /* while */
1069 if ((len=xmlUTF8Strsize(the_format, 1)) < 1) {
1070 found_error = 1;
1071 goto OUTPUT_NUMBER;
1073 the_format += len;
1077 /* We have finished the integer part, now work on fraction */
1078 if ( (*the_format != 0) &&
1079 (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) {
1080 format_info.add_decimal = TRUE;
1081 if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
1082 found_error = 1;
1083 goto OUTPUT_NUMBER;
1085 the_format += len; /* Skip over the decimal */
1088 while (*the_format != 0) {
1090 if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1091 if (format_info.frac_hash != 0) {
1092 found_error = 1;
1093 goto OUTPUT_NUMBER;
1095 format_info.frac_digits++;
1096 } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1097 format_info.frac_hash++;
1098 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1099 if (format_info.is_multiplier_set) {
1100 found_error = 1;
1101 goto OUTPUT_NUMBER;
1103 delayed_multiplier = 100;
1104 if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
1105 found_error = 1;
1106 goto OUTPUT_NUMBER;
1108 the_format += len;
1109 continue; /* while */
1110 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1111 if (format_info.is_multiplier_set) {
1112 found_error = 1;
1113 goto OUTPUT_NUMBER;
1115 delayed_multiplier = 1000;
1116 if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
1117 found_error = 1;
1118 goto OUTPUT_NUMBER;
1120 the_format += len;
1121 continue; /* while */
1122 } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
1123 break; /* while */
1125 if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
1126 found_error = 1;
1127 goto OUTPUT_NUMBER;
1129 the_format += len;
1130 if (delayed_multiplier != 0) {
1131 format_info.multiplier = delayed_multiplier;
1132 delayed_multiplier = 0;
1133 format_info.is_multiplier_set = TRUE;
1138 * If delayed_multiplier is set after processing the
1139 * "number" part, should be in suffix
1141 if (delayed_multiplier != 0) {
1142 the_format -= len;
1143 delayed_multiplier = 0;
1146 suffix = the_format;
1147 suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1148 if ( (suffix_length < 0) ||
1149 ((*the_format != 0) &&
1150 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
1151 found_error = 1;
1152 goto OUTPUT_NUMBER;
1156 * We have processed the +ve prefix, number part and +ve suffix.
1157 * If the number is -ve, we must substitute the -ve prefix / suffix
1159 if (number < 0) {
1161 * Note that j is the number of UTF8 chars before the separator,
1162 * not the number of bytes! (bug 151975)
1164 j = xmlUTF8Strloc(format, self->patternSeparator);
1165 if (j < 0) {
1166 /* No -ve pattern present, so use default signing */
1167 default_sign = 1;
1169 else {
1170 /* Skip over pattern separator (accounting for UTF8) */
1171 the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
1173 * Flag changes interpretation of percent/permille
1174 * in -ve pattern
1176 format_info.is_negative_pattern = TRUE;
1177 format_info.is_multiplier_set = FALSE;
1179 /* First do the -ve prefix */
1180 nprefix = the_format;
1181 nprefix_length = xsltFormatNumberPreSuffix(self,
1182 &the_format, &format_info);
1183 if (nprefix_length<0) {
1184 found_error = 1;
1185 goto OUTPUT_NUMBER;
1188 while (*the_format != 0) {
1189 if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
1190 (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
1191 if (format_info.is_multiplier_set) {
1192 found_error = 1;
1193 goto OUTPUT_NUMBER;
1195 format_info.is_multiplier_set = TRUE;
1196 delayed_multiplier = 1;
1198 else if (IS_SPECIAL(self, the_format))
1199 delayed_multiplier = 0;
1200 else
1201 break; /* while */
1202 if ((len = xmlUTF8Strsize(the_format, 1)) < 1) {
1203 found_error = 1;
1204 goto OUTPUT_NUMBER;
1206 the_format += len;
1208 if (delayed_multiplier != 0) {
1209 format_info.is_multiplier_set = FALSE;
1210 the_format -= len;
1213 /* Finally do the -ve suffix */
1214 if (*the_format != 0) {
1215 nsuffix = the_format;
1216 nsuffix_length = xsltFormatNumberPreSuffix(self,
1217 &the_format, &format_info);
1218 if (nsuffix_length < 0) {
1219 found_error = 1;
1220 goto OUTPUT_NUMBER;
1223 else
1224 nsuffix_length = 0;
1225 if (*the_format != 0) {
1226 found_error = 1;
1227 goto OUTPUT_NUMBER;
1230 * Here's another Java peculiarity:
1231 * if -ve prefix/suffix == +ve ones, discard & use default
1233 if ((nprefix_length != prefix_length) ||
1234 (nsuffix_length != suffix_length) ||
1235 ((nprefix_length > 0) &&
1236 (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
1237 ((nsuffix_length > 0) &&
1238 (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
1239 prefix = nprefix;
1240 prefix_length = nprefix_length;
1241 suffix = nsuffix;
1242 suffix_length = nsuffix_length;
1243 } /* else {
1244 default_sign = 1;
1250 OUTPUT_NUMBER:
1251 if (found_error != 0) {
1252 xsltTransformError(NULL, NULL, NULL,
1253 "xsltFormatNumberConversion : "
1254 "error in format string '%s', using default\n", format);
1255 default_sign = (number < 0.0) ? 1 : 0;
1256 prefix_length = suffix_length = 0;
1257 format_info.integer_hash = 0;
1258 format_info.integer_digits = 1;
1259 format_info.frac_digits = 1;
1260 format_info.frac_hash = 4;
1261 format_info.group = -1;
1262 format_info.multiplier = 1;
1263 format_info.add_decimal = TRUE;
1266 /* Apply multiplier */
1267 number *= (double)format_info.multiplier;
1268 switch (xmlXPathIsInf(number)) {
1269 case -1:
1270 if (self->minusSign == NULL)
1271 *result = xmlStrdup(BAD_CAST "-");
1272 else
1273 *result = xmlStrdup(self->minusSign);
1274 /* Intentional fall-through */
1275 case 1:
1276 if ((self == NULL) || (self->infinity == NULL))
1277 *result = xmlStrcat(*result, BAD_CAST "Infinity");
1278 else
1279 *result = xmlStrcat(*result, self->infinity);
1280 return(status);
1281 default:
1282 break;
1285 buffer = xmlBufferCreate();
1286 if (buffer == NULL) {
1287 return XPATH_MEMORY_ERROR;
1290 /* Ready to output our number. First see if "default sign" is required */
1291 if (default_sign != 0)
1292 xmlBufferAdd(buffer, self->minusSign, xmlUTF8Strsize(self->minusSign, 1));
1294 /* Put the prefix into the buffer */
1295 for (j = 0; j < prefix_length; ) {
1296 if (*prefix == SYMBOL_QUOTE)
1297 prefix++;
1298 len = xmlUTF8Strsize(prefix, 1);
1299 xmlBufferAdd(buffer, prefix, len);
1300 prefix += len;
1301 j += len;
1304 /* Round to n digits */
1305 number = fabs(number);
1306 exp10 = format_info.frac_digits + format_info.frac_hash;
1307 /* DBL_MAX_10_EXP should be 308 on IEEE platforms. */
1308 if (exp10 > DBL_MAX_10_EXP) {
1309 if (format_info.frac_digits > DBL_MAX_10_EXP) {
1310 format_info.frac_digits = DBL_MAX_10_EXP;
1311 format_info.frac_hash = 0;
1312 } else {
1313 format_info.frac_hash = DBL_MAX_10_EXP - format_info.frac_digits;
1315 exp10 = DBL_MAX_10_EXP;
1317 scale = pow(10.0, (double) exp10);
1318 number += .5 / scale;
1319 number -= fmod(number, 1 / scale);
1321 /* Next do the integer part of the number */
1322 if ((self->grouping != NULL) &&
1323 (self->grouping[0] != 0)) {
1324 int gchar;
1326 len = xmlStrlen(self->grouping);
1327 gchar = xsltGetUTF8Char(self->grouping, &len);
1328 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1329 format_info.integer_digits,
1330 format_info.group,
1331 gchar, len);
1332 } else
1333 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1334 format_info.integer_digits,
1335 format_info.group,
1336 ',', 1);
1338 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1339 if ((format_info.integer_digits + format_info.integer_hash +
1340 format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
1341 ++format_info.frac_digits;
1342 --format_info.frac_hash;
1345 /* Add leading zero, if required */
1346 if ((floor(number) == 0) &&
1347 (format_info.integer_digits + format_info.frac_digits == 0)) {
1348 xmlBufferAdd(buffer, self->zeroDigit, xmlUTF8Strsize(self->zeroDigit, 1));
1351 /* Next the fractional part, if required */
1352 if (format_info.frac_digits + format_info.frac_hash == 0) {
1353 if (format_info.add_decimal)
1354 xmlBufferAdd(buffer, self->decimalPoint,
1355 xmlUTF8Strsize(self->decimalPoint, 1));
1357 else {
1358 number -= floor(number);
1359 if ((number != 0) || (format_info.frac_digits != 0)) {
1360 xmlBufferAdd(buffer, self->decimalPoint,
1361 xmlUTF8Strsize(self->decimalPoint, 1));
1362 number = floor(scale * number + 0.5);
1363 for (j = format_info.frac_hash; j > 0; j--) {
1364 if (fmod(number, 10.0) >= 1.0)
1365 break; /* for */
1366 number /= 10.0;
1368 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1369 format_info.frac_digits + j,
1370 0, 0, 0);
1373 /* Put the suffix into the buffer */
1374 for (j = 0; j < suffix_length; ) {
1375 if (*suffix == SYMBOL_QUOTE)
1376 suffix++;
1377 len = xmlUTF8Strsize(suffix, 1);
1378 xmlBufferAdd(buffer, suffix, len);
1379 suffix += len;
1380 j += len;
1383 *result = xmlStrdup(xmlBufferContent(buffer));
1384 xmlBufferFree(buffer);
1385 return status;