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