Sync with trunk r63502.
[reactos.git] / dll / 3rdparty / libxslt / numbers.c
1 /*
2 * numbers.c: Implementation of the XSLT number functions
3 *
4 * Reference:
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
6 *
7 * See Copyright for the status of this software.
8 *
9 * daniel@veillard.com
10 * Bjorn Reese <breese@users.sourceforge.net>
11 */
12
13 #include "precomp.h"
14
15 #ifndef FALSE
16 # define FALSE (0 == 1)
17 # define TRUE (1 == 1)
18 #endif
19
20 #define SYMBOL_QUOTE ((xmlChar)'\'')
21
22 #define DEFAULT_TOKEN (xmlChar)'0'
23 #define DEFAULT_SEPARATOR "."
24
25 #define MAX_TOKENS 1024
26
27 typedef struct _xsltFormatToken xsltFormatToken;
28 typedef xsltFormatToken *xsltFormatTokenPtr;
29 struct _xsltFormatToken {
30 xmlChar *separator;
31 xmlChar token;
32 int width;
33 };
34
35 typedef struct _xsltFormat xsltFormat;
36 typedef xsltFormat *xsltFormatPtr;
37 struct _xsltFormat {
38 xmlChar *start;
39 xsltFormatToken tokens[MAX_TOKENS];
40 int nTokens;
41 xmlChar *end;
42 };
43
44 static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
45 static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
46 static xsltFormatToken default_token;
47
48 /*
49 * **** Start temp insert ****
50 *
51 * The following two routines (xsltUTF8Size and xsltUTF8Charcmp)
52 * will be replaced with calls to the corresponding libxml routines
53 * at a later date (when other inter-library dependencies require it)
54 */
55
56 /**
57 * xsltUTF8Size:
58 * @utf: pointer to the UTF8 character
59 *
60 * returns the numbers of bytes in the character, -1 on format error
61 */
62 static int
63 xsltUTF8Size(xmlChar *utf) {
64 xmlChar mask;
65 int len;
66
67 if (utf == NULL)
68 return -1;
69 if (*utf < 0x80)
70 return 1;
71 /* check valid UTF8 character */
72 if (!(*utf & 0x40))
73 return -1;
74 /* determine number of bytes in char */
75 len = 2;
76 for (mask=0x20; mask != 0; mask>>=1) {
77 if (!(*utf & mask))
78 return len;
79 len++;
80 }
81 return -1;
82 }
83
84 /**
85 * xsltUTF8Charcmp
86 * @utf1: pointer to first UTF8 char
87 * @utf2: pointer to second UTF8 char
88 *
89 * returns result of comparing the two UCS4 values
90 * as with xmlStrncmp
91 */
92 static int
93 xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) {
94
95 if (utf1 == NULL ) {
96 if (utf2 == NULL)
97 return 0;
98 return -1;
99 }
100 return xmlStrncmp(utf1, utf2, xsltUTF8Size(utf1));
101 }
102
103 /***** Stop temp insert *****/
104 /************************************************************************
105 * *
106 * Utility functions *
107 * *
108 ************************************************************************/
109
110 #define IS_SPECIAL(self,letter) \
111 ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
112 (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
113 (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
114 (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
115 (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
116
117 #define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
118 #define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
119
120 static int
121 xsltIsDigitZero(unsigned int ch)
122 {
123 /*
124 * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
125 */
126 switch (ch) {
127 case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
128 case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
129 case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
130 case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
131 case 0x1810: case 0xFF10:
132 return TRUE;
133 default:
134 return FALSE;
135 }
136 }
137
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)
146 {
147 /*
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
154 */
155 xmlChar temp_string[500];
156 xmlChar *pointer;
157 xmlChar temp_char[6];
158 int i;
159 int val;
160 int len;
161
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;
175 }
176 pointer -= groupingCharacterLen;
177 xmlCopyCharMultiByte(pointer, groupingCharacter);
178 }
179
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;
185 }
186 *(--pointer) = val;
187 }
188 else {
189 /*
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.
194 */
195 len = xmlCopyCharMultiByte(temp_char, val);
196 if ( (pointer - len) < temp_string ) {
197 i = -1;
198 break;
199 }
200 pointer -= len;
201 memcpy(pointer, temp_char, len);
202 }
203 number /= 10.0;
204 ++i;
205 }
206 if (i < 0)
207 xsltGenericError(xsltGenericErrorContext,
208 "xsltNumberFormatDecimal: Internal buffer size exceeded");
209 xmlBufferCat(buffer, pointer);
210 }
211
212 static void
213 xsltNumberFormatAlpha(xmlBufferPtr buffer,
214 double number,
215 int is_upper)
216 {
217 char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
218 char *pointer;
219 int i;
220 char *alpha_list;
221 double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
222
223 /* Build buffer from back */
224 pointer = &temp_string[sizeof(temp_string)];
225 *(--pointer) = 0;
226 alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
227
228 for (i = 1; i < (int)sizeof(temp_string); i++) {
229 number--;
230 *(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
231 number /= alpha_size;
232 if (fabs(number) < 1.0)
233 break; /* for */
234 }
235 xmlBufferCCat(buffer, pointer);
236 }
237
238 static void
239 xsltNumberFormatRoman(xmlBufferPtr buffer,
240 double number,
241 int is_upper)
242 {
243 /*
244 * Based on an example by Jim Walsh
245 */
246 while (number >= 1000.0) {
247 xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
248 number -= 1000.0;
249 }
250 if (number >= 900.0) {
251 xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
252 number -= 900.0;
253 }
254 while (number >= 500.0) {
255 xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
256 number -= 500.0;
257 }
258 if (number >= 400.0) {
259 xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
260 number -= 400.0;
261 }
262 while (number >= 100.0) {
263 xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
264 number -= 100.0;
265 }
266 if (number >= 90.0) {
267 xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
268 number -= 90.0;
269 }
270 while (number >= 50.0) {
271 xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
272 number -= 50.0;
273 }
274 if (number >= 40.0) {
275 xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
276 number -= 40.0;
277 }
278 while (number >= 10.0) {
279 xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
280 number -= 10.0;
281 }
282 if (number >= 9.0) {
283 xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
284 number -= 9.0;
285 }
286 while (number >= 5.0) {
287 xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
288 number -= 5.0;
289 }
290 if (number >= 4.0) {
291 xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
292 number -= 4.0;
293 }
294 while (number >= 1.0) {
295 xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
296 number--;
297 }
298 }
299
300 static void
301 xsltNumberFormatTokenize(const xmlChar *format,
302 xsltFormatPtr tokens)
303 {
304 int ix = 0;
305 int j;
306 int val;
307 int len;
308
309 default_token.token = DEFAULT_TOKEN;
310 default_token.width = 1;
311 default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);
312
313
314 tokens->start = NULL;
315 tokens->tokens[0].separator = NULL;
316 tokens->end = NULL;
317
318 /*
319 * Insert initial non-alphanumeric token.
320 * There is always such a token in the list, even if NULL
321 */
322 while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) ||
323 IS_DIGIT(val)) ) {
324 if (format[ix] == 0) /* if end of format string */
325 break; /* while */
326 ix += len;
327 }
328 if (ix > 0)
329 tokens->start = xmlStrndup(format, ix);
330
331
332 for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
333 tokens->nTokens++) {
334 if (format[ix] == 0)
335 break; /* for */
336
337 /*
338 * separator has already been parsed (except for the first
339 * number) in tokens->end, recover it.
340 */
341 if (tokens->nTokens > 0) {
342 tokens->tokens[tokens->nTokens].separator = tokens->end;
343 tokens->end = NULL;
344 }
345
346 val = xmlStringCurrentChar(NULL, format+ix, &len);
347 if (IS_DIGIT_ONE(val) ||
348 IS_DIGIT_ZERO(val)) {
349 tokens->tokens[tokens->nTokens].width = 1;
350 while (IS_DIGIT_ZERO(val)) {
351 tokens->tokens[tokens->nTokens].width++;
352 ix += len;
353 val = xmlStringCurrentChar(NULL, format+ix, &len);
354 }
355 if (IS_DIGIT_ONE(val)) {
356 tokens->tokens[tokens->nTokens].token = val - 1;
357 ix += len;
358 val = xmlStringCurrentChar(NULL, format+ix, &len);
359 }
360 } else if ( (val == (xmlChar)'A') ||
361 (val == (xmlChar)'a') ||
362 (val == (xmlChar)'I') ||
363 (val == (xmlChar)'i') ) {
364 tokens->tokens[tokens->nTokens].token = val;
365 ix += len;
366 val = xmlStringCurrentChar(NULL, format+ix, &len);
367 } else {
368 /* XSLT section 7.7
369 * "Any other format token indicates a numbering sequence
370 * that starts with that token. If an implementation does
371 * not support a numbering sequence that starts with that
372 * token, it must use a format token of 1."
373 */
374 tokens->tokens[tokens->nTokens].token = (xmlChar)'0';
375 tokens->tokens[tokens->nTokens].width = 1;
376 }
377 /*
378 * Skip over remaining alphanumeric characters from the Nd
379 * (Number, decimal digit), Nl (Number, letter), No (Number,
380 * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
381 * (Letters, titlecase), Lm (Letters, modifiers), and Lo
382 * (Letters, other (uncased)) Unicode categories. This happens
383 * to correspond to the Letter and Digit classes from XML (and
384 * one wonders why XSLT doesn't refer to these instead).
385 */
386 while (IS_LETTER(val) || IS_DIGIT(val)) {
387 ix += len;
388 val = xmlStringCurrentChar(NULL, format+ix, &len);
389 }
390
391 /*
392 * Insert temporary non-alphanumeric final tooken.
393 */
394 j = ix;
395 while (! (IS_LETTER(val) || IS_DIGIT(val))) {
396 if (val == 0)
397 break; /* while */
398 ix += len;
399 val = xmlStringCurrentChar(NULL, format+ix, &len);
400 }
401 if (ix > j)
402 tokens->end = xmlStrndup(&format[j], ix - j);
403 }
404 }
405
406 static void
407 xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
408 double *numbers,
409 int numbers_max,
410 xsltFormatPtr tokens,
411 xmlBufferPtr buffer)
412 {
413 int i = 0;
414 double number;
415 xsltFormatTokenPtr token;
416
417 /*
418 * Handle initial non-alphanumeric token
419 */
420 if (tokens->start != NULL)
421 xmlBufferCat(buffer, tokens->start);
422
423 for (i = 0; i < numbers_max; i++) {
424 /* Insert number */
425 number = numbers[(numbers_max - 1) - i];
426 if (i < tokens->nTokens) {
427 /*
428 * The "n"th format token will be used to format the "n"th
429 * number in the list
430 */
431 token = &(tokens->tokens[i]);
432 } else if (tokens->nTokens > 0) {
433 /*
434 * If there are more numbers than format tokens, then the
435 * last format token will be used to format the remaining
436 * numbers.
437 */
438 token = &(tokens->tokens[tokens->nTokens - 1]);
439 } else {
440 /*
441 * If there are no format tokens, then a format token of
442 * 1 is used to format all numbers.
443 */
444 token = &default_token;
445 }
446
447 /* Print separator, except for the first number */
448 if (i > 0) {
449 if (token->separator != NULL)
450 xmlBufferCat(buffer, token->separator);
451 else
452 xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
453 }
454
455 switch (xmlXPathIsInf(number)) {
456 case -1:
457 xmlBufferCCat(buffer, "-Infinity");
458 break;
459 case 1:
460 xmlBufferCCat(buffer, "Infinity");
461 break;
462 default:
463 if (xmlXPathIsNaN(number)) {
464 xmlBufferCCat(buffer, "NaN");
465 } else {
466
467 switch (token->token) {
468 case 'A':
469 xsltNumberFormatAlpha(buffer,
470 number,
471 TRUE);
472
473 break;
474 case 'a':
475 xsltNumberFormatAlpha(buffer,
476 number,
477 FALSE);
478
479 break;
480 case 'I':
481 xsltNumberFormatRoman(buffer,
482 number,
483 TRUE);
484
485 break;
486 case 'i':
487 xsltNumberFormatRoman(buffer,
488 number,
489 FALSE);
490
491 break;
492 default:
493 if (IS_DIGIT_ZERO(token->token)) {
494 xsltNumberFormatDecimal(buffer,
495 number,
496 token->token,
497 token->width,
498 data->digitsPerGroup,
499 data->groupingCharacter,
500 data->groupingCharacterLen);
501 }
502 break;
503 }
504 }
505
506 }
507 }
508
509 /*
510 * Handle final non-alphanumeric token
511 */
512 if (tokens->end != NULL)
513 xmlBufferCat(buffer, tokens->end);
514
515 }
516
517 static int
518 xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
519 xmlNodePtr node,
520 xsltCompMatchPtr countPat,
521 xsltCompMatchPtr fromPat,
522 double *array,
523 xmlDocPtr doc,
524 xmlNodePtr elem)
525 {
526 int amount = 0;
527 int cnt = 0;
528 xmlNodePtr cur;
529
530 /* select the starting node */
531 switch (node->type) {
532 case XML_ELEMENT_NODE:
533 cur = node;
534 break;
535 case XML_ATTRIBUTE_NODE:
536 cur = ((xmlAttrPtr) node)->parent;
537 break;
538 case XML_TEXT_NODE:
539 case XML_PI_NODE:
540 case XML_COMMENT_NODE:
541 cur = node->parent;
542 break;
543 default:
544 cur = NULL;
545 break;
546 }
547
548 while (cur != NULL) {
549 /* process current node */
550 if (countPat == NULL) {
551 if ((node->type == cur->type) &&
552 /* FIXME: must use expanded-name instead of local name */
553 xmlStrEqual(node->name, cur->name)) {
554 if ((node->ns == cur->ns) ||
555 ((node->ns != NULL) &&
556 (cur->ns != NULL) &&
557 (xmlStrEqual(node->ns->href,
558 cur->ns->href) )))
559 cnt++;
560 }
561 } else {
562 if (xsltTestCompMatchList(context, cur, countPat))
563 cnt++;
564 }
565 if ((fromPat != NULL) &&
566 xsltTestCompMatchList(context, cur, fromPat)) {
567 break; /* while */
568 }
569
570 /* Skip to next preceding or ancestor */
571 if ((cur->type == XML_DOCUMENT_NODE) ||
572 #ifdef LIBXML_DOCB_ENABLED
573 (cur->type == XML_DOCB_DOCUMENT_NODE) ||
574 #endif
575 (cur->type == XML_HTML_DOCUMENT_NODE))
576 break; /* while */
577
578 while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
579 (cur->prev->type == XML_XINCLUDE_START) ||
580 (cur->prev->type == XML_XINCLUDE_END)))
581 cur = cur->prev;
582 if (cur->prev != NULL) {
583 for (cur = cur->prev; cur->last != NULL; cur = cur->last);
584 } else {
585 cur = cur->parent;
586 }
587
588 }
589
590 array[amount++] = (double) cnt;
591
592 return(amount);
593 }
594
595 static int
596 xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
597 xmlNodePtr node,
598 xsltCompMatchPtr countPat,
599 xsltCompMatchPtr fromPat,
600 double *array,
601 int max,
602 xmlDocPtr doc,
603 xmlNodePtr elem)
604 {
605 int amount = 0;
606 int cnt;
607 xmlNodePtr ancestor;
608 xmlNodePtr preceding;
609 xmlXPathParserContextPtr parser;
610
611 context->xpathCtxt->node = node;
612 parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
613 if (parser) {
614 /* ancestor-or-self::*[count] */
615 for (ancestor = node;
616 (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE);
617 ancestor = xmlXPathNextAncestor(parser, ancestor)) {
618
619 if ((fromPat != NULL) &&
620 xsltTestCompMatchList(context, ancestor, fromPat))
621 break; /* for */
622
623 if ((countPat == NULL && node->type == ancestor->type &&
624 xmlStrEqual(node->name, ancestor->name)) ||
625 xsltTestCompMatchList(context, ancestor, countPat)) {
626 /* count(preceding-sibling::*) */
627 cnt = 0;
628 for (preceding = ancestor;
629 preceding != NULL;
630 preceding =
631 xmlXPathNextPrecedingSibling(parser, preceding)) {
632 if (countPat == NULL) {
633 if ((preceding->type == ancestor->type) &&
634 xmlStrEqual(preceding->name, ancestor->name)){
635 if ((preceding->ns == ancestor->ns) ||
636 ((preceding->ns != NULL) &&
637 (ancestor->ns != NULL) &&
638 (xmlStrEqual(preceding->ns->href,
639 ancestor->ns->href) )))
640 cnt++;
641 }
642 } else {
643 if (xsltTestCompMatchList(context, preceding,
644 countPat))
645 cnt++;
646 }
647 }
648 array[amount++] = (double)cnt;
649 if (amount >= max)
650 break; /* for */
651 }
652 }
653 xmlXPathFreeParserContext(parser);
654 }
655 return amount;
656 }
657
658 static int
659 xsltNumberFormatGetValue(xmlXPathContextPtr context,
660 xmlNodePtr node,
661 const xmlChar *value,
662 double *number)
663 {
664 int amount = 0;
665 xmlBufferPtr pattern;
666 xmlXPathObjectPtr obj;
667
668 pattern = xmlBufferCreate();
669 if (pattern != NULL) {
670 xmlBufferCCat(pattern, "number(");
671 xmlBufferCat(pattern, value);
672 xmlBufferCCat(pattern, ")");
673 context->node = node;
674 obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
675 context);
676 if (obj != NULL) {
677 *number = obj->floatval;
678 amount++;
679 xmlXPathFreeObject(obj);
680 }
681 xmlBufferFree(pattern);
682 }
683 return amount;
684 }
685
686 /**
687 * xsltNumberFormat:
688 * @ctxt: the XSLT transformation context
689 * @data: the formatting informations
690 * @node: the data to format
691 *
692 * Convert one number.
693 */
694 void
695 xsltNumberFormat(xsltTransformContextPtr ctxt,
696 xsltNumberDataPtr data,
697 xmlNodePtr node)
698 {
699 xmlBufferPtr output = NULL;
700 int amount, i;
701 double number;
702 xsltFormat tokens;
703 int tempformat = 0;
704
705 if ((data->format == NULL) && (data->has_format != 0)) {
706 data->format = xsltEvalAttrValueTemplate(ctxt, data->node,
707 (const xmlChar *) "format",
708 XSLT_NAMESPACE);
709 tempformat = 1;
710 }
711 if (data->format == NULL) {
712 return;
713 }
714
715 output = xmlBufferCreate();
716 if (output == NULL)
717 goto XSLT_NUMBER_FORMAT_END;
718
719 xsltNumberFormatTokenize(data->format, &tokens);
720
721 /*
722 * Evaluate the XPath expression to find the value(s)
723 */
724 if (data->value) {
725 amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
726 node,
727 data->value,
728 &number);
729 if (amount == 1) {
730 xsltNumberFormatInsertNumbers(data,
731 &number,
732 1,
733 &tokens,
734 output);
735 }
736
737 } else if (data->level) {
738
739 if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
740 amount = xsltNumberFormatGetMultipleLevel(ctxt,
741 node,
742 data->countPat,
743 data->fromPat,
744 &number,
745 1,
746 data->doc,
747 data->node);
748 if (amount == 1) {
749 xsltNumberFormatInsertNumbers(data,
750 &number,
751 1,
752 &tokens,
753 output);
754 }
755 } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
756 double numarray[1024];
757 int max = sizeof(numarray)/sizeof(numarray[0]);
758 amount = xsltNumberFormatGetMultipleLevel(ctxt,
759 node,
760 data->countPat,
761 data->fromPat,
762 numarray,
763 max,
764 data->doc,
765 data->node);
766 if (amount > 0) {
767 xsltNumberFormatInsertNumbers(data,
768 numarray,
769 amount,
770 &tokens,
771 output);
772 }
773 } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
774 amount = xsltNumberFormatGetAnyLevel(ctxt,
775 node,
776 data->countPat,
777 data->fromPat,
778 &number,
779 data->doc,
780 data->node);
781 if (amount > 0) {
782 xsltNumberFormatInsertNumbers(data,
783 &number,
784 1,
785 &tokens,
786 output);
787 }
788 }
789 }
790 /* Insert number as text node */
791 xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
792
793 if (tokens.start != NULL)
794 xmlFree(tokens.start);
795 if (tokens.end != NULL)
796 xmlFree(tokens.end);
797 for (i = 0;i < tokens.nTokens;i++) {
798 if (tokens.tokens[i].separator != NULL)
799 xmlFree(tokens.tokens[i].separator);
800 }
801
802 XSLT_NUMBER_FORMAT_END:
803 if (tempformat == 1) {
804 /* The format need to be recomputed each time */
805 data->format = NULL;
806 }
807 if (output != NULL)
808 xmlBufferFree(output);
809 }
810
811 static int
812 xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
813 {
814 int count=0; /* will hold total length of prefix/suffix */
815 int len;
816
817 while (1) {
818 /*
819 * prefix / suffix ends at end of string or at
820 * first 'special' character
821 */
822 if (**format == 0)
823 return count;
824 /* if next character 'escaped' just count it */
825 if (**format == SYMBOL_QUOTE) {
826 if (*++(*format) == 0)
827 return -1;
828 }
829 else if (IS_SPECIAL(self, *format))
830 return count;
831 /*
832 * else treat percent/per-mille as special cases,
833 * depending on whether +ve or -ve
834 */
835 else {
836 /*
837 * for +ve prefix/suffix, allow only a
838 * single occurence of either
839 */
840 if (xsltUTF8Charcmp(*format, self->percent) == 0) {
841 if (info->is_multiplier_set)
842 return -1;
843 info->multiplier = 100;
844 info->is_multiplier_set = TRUE;
845 } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
846 if (info->is_multiplier_set)
847 return -1;
848 info->multiplier = 1000;
849 info->is_multiplier_set = TRUE;
850 }
851 }
852
853 if ((len=xsltUTF8Size(*format)) < 1)
854 return -1;
855 count += len;
856 *format += len;
857 }
858 }
859
860 /**
861 * xsltFormatNumberConversion:
862 * @self: the decimal format
863 * @format: the format requested
864 * @number: the value to format
865 * @result: the place to ouput the result
866 *
867 * format-number() uses the JDK 1.1 DecimalFormat class:
868 *
869 * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
870 *
871 * Structure:
872 *
873 * pattern := subpattern{;subpattern}
874 * subpattern := {prefix}integer{.fraction}{suffix}
875 * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
876 * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
877 * integer := '#'* '0'* '0'
878 * fraction := '0'* '#'*
879 *
880 * Notation:
881 * X* 0 or more instances of X
882 * (X | Y) either X or Y.
883 * X..Y any character from X up to Y, inclusive.
884 * S - T characters in S, except those in T
885 *
886 * Special Characters:
887 *
888 * Symbol Meaning
889 * 0 a digit
890 * # a digit, zero shows as absent
891 * . placeholder for decimal separator
892 * , placeholder for grouping separator.
893 * ; separates formats.
894 * - default negative prefix.
895 * % multiply by 100 and show as percentage
896 * ? multiply by 1000 and show as per mille
897 * X any other characters can be used in the prefix or suffix
898 * ' used to quote special characters in a prefix or suffix.
899 *
900 * Returns a possible XPath error
901 */
902 xmlXPathError
903 xsltFormatNumberConversion(xsltDecimalFormatPtr self,
904 xmlChar *format,
905 double number,
906 xmlChar **result)
907 {
908 xmlXPathError status = XPATH_EXPRESSION_OK;
909 xmlBufferPtr buffer;
910 xmlChar *the_format, *prefix = NULL, *suffix = NULL;
911 xmlChar *nprefix, *nsuffix = NULL;
912 xmlChar pchar;
913 int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
914 double scale;
915 int j, len;
916 int self_grouping_len;
917 xsltFormatNumberInfo format_info;
918 /*
919 * delayed_multiplier allows a 'trailing' percent or
920 * permille to be treated as suffix
921 */
922 int delayed_multiplier = 0;
923 /* flag to show no -ve format present for -ve number */
924 char default_sign = 0;
925 /* flag to show error found, should use default format */
926 char found_error = 0;
927
928 if (xmlStrlen(format) <= 0) {
929 xsltTransformError(NULL, NULL, NULL,
930 "xsltFormatNumberConversion : "
931 "Invalid format (0-length)\n");
932 }
933 *result = NULL;
934 switch (xmlXPathIsInf(number)) {
935 case -1:
936 if (self->minusSign == NULL)
937 *result = xmlStrdup(BAD_CAST "-");
938 else
939 *result = xmlStrdup(self->minusSign);
940 /* no-break on purpose */
941 case 1:
942 if ((self == NULL) || (self->infinity == NULL))
943 *result = xmlStrcat(*result, BAD_CAST "Infinity");
944 else
945 *result = xmlStrcat(*result, self->infinity);
946 return(status);
947 default:
948 if (xmlXPathIsNaN(number)) {
949 if ((self == NULL) || (self->noNumber == NULL))
950 *result = xmlStrdup(BAD_CAST "NaN");
951 else
952 *result = xmlStrdup(self->noNumber);
953 return(status);
954 }
955 }
956
957 buffer = xmlBufferCreate();
958 if (buffer == NULL) {
959 return XPATH_MEMORY_ERROR;
960 }
961
962 format_info.integer_hash = 0;
963 format_info.integer_digits = 0;
964 format_info.frac_digits = 0;
965 format_info.frac_hash = 0;
966 format_info.group = -1;
967 format_info.multiplier = 1;
968 format_info.add_decimal = FALSE;
969 format_info.is_multiplier_set = FALSE;
970 format_info.is_negative_pattern = FALSE;
971
972 the_format = format;
973
974 /*
975 * First we process the +ve pattern to get percent / permille,
976 * as well as main format
977 */
978 prefix = the_format;
979 prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
980 if (prefix_length < 0) {
981 found_error = 1;
982 goto OUTPUT_NUMBER;
983 }
984
985 /*
986 * Here we process the "number" part of the format. It gets
987 * a little messy because of the percent/per-mille - if that
988 * appears at the end, it may be part of the suffix instead
989 * of part of the number, so the variable delayed_multiplier
990 * is used to handle it
991 */
992 self_grouping_len = xmlStrlen(self->grouping);
993 while ((*the_format != 0) &&
994 (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
995 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
996
997 if (delayed_multiplier != 0) {
998 format_info.multiplier = delayed_multiplier;
999 format_info.is_multiplier_set = TRUE;
1000 delayed_multiplier = 0;
1001 }
1002 if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1003 if (format_info.integer_digits > 0) {
1004 found_error = 1;
1005 goto OUTPUT_NUMBER;
1006 }
1007 format_info.integer_hash++;
1008 if (format_info.group >= 0)
1009 format_info.group++;
1010 } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1011 format_info.integer_digits++;
1012 if (format_info.group >= 0)
1013 format_info.group++;
1014 } else if ((self_grouping_len > 0) &&
1015 (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
1016 /* Reset group count */
1017 format_info.group = 0;
1018 the_format += self_grouping_len;
1019 continue;
1020 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1021 if (format_info.is_multiplier_set) {
1022 found_error = 1;
1023 goto OUTPUT_NUMBER;
1024 }
1025 delayed_multiplier = 100;
1026 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1027 if (format_info.is_multiplier_set) {
1028 found_error = 1;
1029 goto OUTPUT_NUMBER;
1030 }
1031 delayed_multiplier = 1000;
1032 } else
1033 break; /* while */
1034
1035 if ((len=xsltUTF8Size(the_format)) < 1) {
1036 found_error = 1;
1037 goto OUTPUT_NUMBER;
1038 }
1039 the_format += len;
1040
1041 }
1042
1043 /* We have finished the integer part, now work on fraction */
1044 if (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) {
1045 format_info.add_decimal = TRUE;
1046 the_format += xsltUTF8Size(the_format); /* Skip over the decimal */
1047 }
1048
1049 while (*the_format != 0) {
1050
1051 if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
1052 if (format_info.frac_hash != 0) {
1053 found_error = 1;
1054 goto OUTPUT_NUMBER;
1055 }
1056 format_info.frac_digits++;
1057 } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
1058 format_info.frac_hash++;
1059 } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
1060 if (format_info.is_multiplier_set) {
1061 found_error = 1;
1062 goto OUTPUT_NUMBER;
1063 }
1064 delayed_multiplier = 100;
1065 if ((len = xsltUTF8Size(the_format)) < 1) {
1066 found_error = 1;
1067 goto OUTPUT_NUMBER;
1068 }
1069 the_format += len;
1070 continue; /* while */
1071 } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
1072 if (format_info.is_multiplier_set) {
1073 found_error = 1;
1074 goto OUTPUT_NUMBER;
1075 }
1076 delayed_multiplier = 1000;
1077 if ((len = xsltUTF8Size(the_format)) < 1) {
1078 found_error = 1;
1079 goto OUTPUT_NUMBER;
1080 }
1081 the_format += len;
1082 continue; /* while */
1083 } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
1084 break; /* while */
1085 }
1086 if ((len = xsltUTF8Size(the_format)) < 1) {
1087 found_error = 1;
1088 goto OUTPUT_NUMBER;
1089 }
1090 the_format += len;
1091 if (delayed_multiplier != 0) {
1092 format_info.multiplier = delayed_multiplier;
1093 delayed_multiplier = 0;
1094 format_info.is_multiplier_set = TRUE;
1095 }
1096 }
1097
1098 /*
1099 * If delayed_multiplier is set after processing the
1100 * "number" part, should be in suffix
1101 */
1102 if (delayed_multiplier != 0) {
1103 the_format -= len;
1104 delayed_multiplier = 0;
1105 }
1106
1107 suffix = the_format;
1108 suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
1109 if ( (suffix_length < 0) ||
1110 ((*the_format != 0) &&
1111 (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
1112 found_error = 1;
1113 goto OUTPUT_NUMBER;
1114 }
1115
1116 /*
1117 * We have processed the +ve prefix, number part and +ve suffix.
1118 * If the number is -ve, we must substitute the -ve prefix / suffix
1119 */
1120 if (number < 0) {
1121 /*
1122 * Note that j is the number of UTF8 chars before the separator,
1123 * not the number of bytes! (bug 151975)
1124 */
1125 j = xmlUTF8Strloc(format, self->patternSeparator);
1126 if (j < 0) {
1127 /* No -ve pattern present, so use default signing */
1128 default_sign = 1;
1129 }
1130 else {
1131 /* Skip over pattern separator (accounting for UTF8) */
1132 the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
1133 /*
1134 * Flag changes interpretation of percent/permille
1135 * in -ve pattern
1136 */
1137 format_info.is_negative_pattern = TRUE;
1138 format_info.is_multiplier_set = FALSE;
1139
1140 /* First do the -ve prefix */
1141 nprefix = the_format;
1142 nprefix_length = xsltFormatNumberPreSuffix(self,
1143 &the_format, &format_info);
1144 if (nprefix_length<0) {
1145 found_error = 1;
1146 goto OUTPUT_NUMBER;
1147 }
1148
1149 while (*the_format != 0) {
1150 if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
1151 (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
1152 if (format_info.is_multiplier_set) {
1153 found_error = 1;
1154 goto OUTPUT_NUMBER;
1155 }
1156 format_info.is_multiplier_set = TRUE;
1157 delayed_multiplier = 1;
1158 }
1159 else if (IS_SPECIAL(self, the_format))
1160 delayed_multiplier = 0;
1161 else
1162 break; /* while */
1163 if ((len = xsltUTF8Size(the_format)) < 1) {
1164 found_error = 1;
1165 goto OUTPUT_NUMBER;
1166 }
1167 the_format += len;
1168 }
1169 if (delayed_multiplier != 0) {
1170 format_info.is_multiplier_set = FALSE;
1171 the_format -= len;
1172 }
1173
1174 /* Finally do the -ve suffix */
1175 if (*the_format != 0) {
1176 nsuffix = the_format;
1177 nsuffix_length = xsltFormatNumberPreSuffix(self,
1178 &the_format, &format_info);
1179 if (nsuffix_length < 0) {
1180 found_error = 1;
1181 goto OUTPUT_NUMBER;
1182 }
1183 }
1184 else
1185 nsuffix_length = 0;
1186 if (*the_format != 0) {
1187 found_error = 1;
1188 goto OUTPUT_NUMBER;
1189 }
1190 /*
1191 * Here's another Java peculiarity:
1192 * if -ve prefix/suffix == +ve ones, discard & use default
1193 */
1194 if ((nprefix_length != prefix_length) ||
1195 (nsuffix_length != suffix_length) ||
1196 ((nprefix_length > 0) &&
1197 (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
1198 ((nsuffix_length > 0) &&
1199 (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
1200 prefix = nprefix;
1201 prefix_length = nprefix_length;
1202 suffix = nsuffix;
1203 suffix_length = nsuffix_length;
1204 } /* else {
1205 default_sign = 1;
1206 }
1207 */
1208 }
1209 }
1210
1211 OUTPUT_NUMBER:
1212 if (found_error != 0) {
1213 xsltTransformError(NULL, NULL, NULL,
1214 "xsltFormatNumberConversion : "
1215 "error in format string '%s', using default\n", format);
1216 default_sign = (number < 0.0) ? 1 : 0;
1217 prefix_length = suffix_length = 0;
1218 format_info.integer_hash = 0;
1219 format_info.integer_digits = 1;
1220 format_info.frac_digits = 1;
1221 format_info.frac_hash = 4;
1222 format_info.group = -1;
1223 format_info.multiplier = 1;
1224 format_info.add_decimal = TRUE;
1225 }
1226
1227 /* Ready to output our number. First see if "default sign" is required */
1228 if (default_sign != 0)
1229 xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign));
1230
1231 /* Put the prefix into the buffer */
1232 for (j = 0; j < prefix_length; j++) {
1233 if ((pchar = *prefix++) == SYMBOL_QUOTE) {
1234 len = xsltUTF8Size(prefix);
1235 xmlBufferAdd(buffer, prefix, len);
1236 prefix += len;
1237 j += len - 1; /* length of symbol less length of quote */
1238 } else
1239 xmlBufferAdd(buffer, &pchar, 1);
1240 }
1241
1242 /* Next do the integer part of the number */
1243 number = fabs(number) * (double)format_info.multiplier;
1244 scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
1245 number = floor((scale * number + 0.5)) / scale;
1246 if ((self->grouping != NULL) &&
1247 (self->grouping[0] != 0)) {
1248
1249 len = xmlStrlen(self->grouping);
1250 pchar = xsltGetUTF8Char(self->grouping, &len);
1251 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1252 format_info.integer_digits,
1253 format_info.group,
1254 pchar, len);
1255 } else
1256 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1257 format_info.integer_digits,
1258 format_info.group,
1259 ',', 1);
1260
1261 /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
1262 if ((format_info.integer_digits + format_info.integer_hash +
1263 format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
1264 ++format_info.frac_digits;
1265 --format_info.frac_hash;
1266 }
1267
1268 /* Add leading zero, if required */
1269 if ((floor(number) == 0) &&
1270 (format_info.integer_digits + format_info.frac_digits == 0)) {
1271 xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit));
1272 }
1273
1274 /* Next the fractional part, if required */
1275 if (format_info.frac_digits + format_info.frac_hash == 0) {
1276 if (format_info.add_decimal)
1277 xmlBufferAdd(buffer, self->decimalPoint,
1278 xsltUTF8Size(self->decimalPoint));
1279 }
1280 else {
1281 number -= floor(number);
1282 if ((number != 0) || (format_info.frac_digits != 0)) {
1283 xmlBufferAdd(buffer, self->decimalPoint,
1284 xsltUTF8Size(self->decimalPoint));
1285 number = floor(scale * number + 0.5);
1286 for (j = format_info.frac_hash; j > 0; j--) {
1287 if (fmod(number, 10.0) >= 1.0)
1288 break; /* for */
1289 number /= 10.0;
1290 }
1291 xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
1292 format_info.frac_digits + j,
1293 0, 0, 0);
1294 }
1295 }
1296 /* Put the suffix into the buffer */
1297 for (j = 0; j < suffix_length; j++) {
1298 if ((pchar = *suffix++) == SYMBOL_QUOTE) {
1299 len = xsltUTF8Size(suffix);
1300 xmlBufferAdd(buffer, suffix, len);
1301 suffix += len;
1302 j += len - 1; /* length of symbol less length of escape */
1303 } else
1304 xmlBufferAdd(buffer, &pchar, 1);
1305 }
1306
1307 *result = xmlStrdup(xmlBufferContent(buffer));
1308 xmlBufferFree(buffer);
1309 return status;
1310 }
1311