source: trunk/third/libxslt/libxslt/numbers.c @ 18543

Revision 18543, 31.0 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18542, which included commits to RCS files with non-trunk default branches.
Line 
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
42typedef struct _xsltFormatToken xsltFormatToken;
43typedef xsltFormatToken *xsltFormatTokenPtr;
44struct _xsltFormatToken {
45    xmlChar     *separator;
46    xmlChar      token;
47    int          width;
48};
49
50typedef struct _xsltFormat xsltFormat;
51typedef xsltFormat *xsltFormatPtr;
52struct _xsltFormat {
53    xmlChar             *start;
54    xsltFormatToken      tokens[MAX_TOKENS];
55    int                  nTokens;
56    xmlChar             *end;
57};
58
59static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
60static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
61static 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
80static int
81xsltIsDigitZero(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
98static void
99xsltNumberFormatDecimal(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
132static void
133xsltNumberFormatAlpha(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
158static void
159xsltNumberFormatRoman(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
220static void
221xsltNumberFormatTokenize(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
322static void
323xsltNumberFormatInsertNumbers(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
427static int
428xsltNumberFormatGetAnyLevel(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
510static int
511xsltNumberFormatGetMultipleLevel(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
580static int
581xsltNumberFormatGetValue(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 */
616void
617xsltNumberFormat(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   
728XSLT_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
738static int
739xsltFormatNumberPreSuffix(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 */
834xmlXPathError
835xsltFormatNumberConversion(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
1087OUTPUT_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}
Note: See TracBrowser for help on using the repository browser.