added Info.plist
[TestXSLT.git] / libsablot / src / engine / decimal.cpp
1 /* 
2  * The contents of this file are subject to the Mozilla Public
3  * License Version 1.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.mozilla.org/MPL/
6  * 
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  * 
12  * The Original Code is the Sablotron XSLT Processor.
13  * 
14  * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15  * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16  * Ginger Alliance Ltd. All Rights Reserved.
17  * 
18  * Contributor(s):
19  * 
20  * Alternatively, the contents of this file may be used under the
21  * terms of the GNU General Public License Version 2 or later (the
22  * "GPL"), in which case the provisions of the GPL are applicable 
23  * instead of those above.  If you wish to allow use of your 
24  * version of this file only under the terms of the GPL and not to
25  * allow others to use your version of this file under the MPL,
26  * indicate your decision by deleting the provisions above and
27  * replace them with the notice and other provisions required by
28  * the GPL.  If you do not delete the provisions above, a recipient
29  * may use your version of this file under either the MPL or the
30  * GPL.
31  */
32
33 // decimal.cpp
34
35 #include "decimal.h"
36 #include "expr.h"
37 #include "math.h"
38
39 DecimalFormat::DecimalFormat(const EQName& name_)
40     : name(name_),
41       decimalSeparator(".", TRUE, XSLA_DECIMAL_SEPARATOR),
42       groupingSeparator(",", TRUE, XSLA_GROUPING_SEPARATOR),
43       infinity("Infinity", FALSE, XSLA_INFINITY),
44       minusSign("-", TRUE, XSLA_MINUS_SIGN),
45       NaN("NaN", FALSE, XSLA_NAN),
46       percent("%", TRUE, XSLA_PERCENT),
47       perMille("\xe2\x80\x83", TRUE, XSLA_PER_MILLE), // U+2030
48       zeroDigit("0", TRUE, XSLA_ZERO_DIGIT),
49       digit("#", TRUE, XSLA_DIGIT),
50       patternSeparator(";", TRUE, XSLA_PATTERN_SEPARATOR)
51 {
52 }
53
54 DecimalFormat::~DecimalFormat()
55 {
56 }
57
58 eFlag DecimalFormat::setItem(Sit S, XSL_ATT itemId, const Str& value)
59 {
60     return NZ(findItem(itemId)) -> set(S, value);
61 }
62
63 const Str& DecimalFormat::getItem(XSL_ATT itemId)
64 {
65     return NZ(findItem(itemId)) -> get();
66 }
67
68 DefaultedStr* DecimalFormat::findItem(XSL_ATT itemId)
69 {
70     switch(itemId)
71     {
72     case XSLA_DECIMAL_SEPARATOR:
73         return &decimalSeparator;
74     case XSLA_GROUPING_SEPARATOR:
75         return &groupingSeparator;
76     case XSLA_INFINITY:
77         return &infinity;
78     case XSLA_MINUS_SIGN:
79         return &minusSign;
80     case XSLA_NAN:
81         return &NaN;
82     case XSLA_PERCENT:
83         return &percent;
84     case XSLA_PER_MILLE:
85         return &perMille;
86     case XSLA_ZERO_DIGIT:
87         return &zeroDigit;
88     case XSLA_DIGIT:
89         return &digit;
90     case XSLA_PATTERN_SEPARATOR:
91         return &patternSeparator;
92     default:
93         return NULL;
94     }
95 }
96
97 #define max(x,y) (x > y ? x : y)
98
99 eFlag DecimalFormat::format(Sit S, Number& num, const Str& fmt, Str& result)
100 {
101     if (num.isNaN())
102     {
103         result = getItem(XSLA_NAN);
104         return OK;
105     }
106     int factor = 1,
107         iDigitsMin = 0, 
108         fDigits = 0, 
109         fDigitsMin = 0,
110         gSize = 0;
111     Str prefix,
112         suffix;
113     E( parse(S, fmt, num < 0.0, prefix, suffix, factor, iDigitsMin, fDigits, fDigitsMin, gSize) );
114     
115     //    printf("pfx %s sfx %s factor %d imin %d fmax %d fmin %d gsize %d",
116     //      (char*)prefix, (char*)suffix, factor, iDigitsMin, fDigits, fDigitsMin, gSize);
117     
118     // start with the prefix (DO NOT we need to add it as the very last step)
119     //DStr tempResult = prefix;
120     DStr tempResult;
121     if (num.isInf())
122     {
123       tempResult = prefix;
124       tempResult += getItem(XSLA_INFINITY);
125       tempResult += suffix;
126       result = tempResult;
127       return OK;
128     }
129     // we have a legitimate number
130     // convert number to string form
131     
132     double theNumber = fabs((double) num * factor);
133     
134     char buff[64];
135     DStr auxFmt = "%";
136     if (iDigitsMin)
137         auxFmt += "0";
138     //auxFmt += iDigitsMin + fDigits + 1 /* + (num < 0.0 ? 1 : 0) */;
139     auxFmt += iDigitsMin + (fDigits ? fDigits + 1 : 0);
140     auxFmt += ".";
141     auxFmt += fDigits;
142     auxFmt += "f";
143     sprintf(buff, (char*) auxFmt, theNumber);
144
145     // buff now holds the number without sign, grouping marks and with possibly too many trailing zeroes
146     // scan the number for decimal point position and trailing zeroes count
147     //if #.00 was specified, we still have a leading zero (0.12 instead of .12)
148     char *buff1 = buff;
149     if ( ! iDigitsMin ) //remove leading zeros
150       while ( *buff1 == '0' ) buff1++;
151
152     int decPt = -1,
153         trailingCount = 0,
154         j;
155     for (j = 0; buff1[j]; j++)
156     {
157         if (buff1[j] == '.' && decPt < 0)
158             decPt = j;
159         else if (decPt >= 0)
160         {
161             if (buff1[j] != '0')
162                 trailingCount = 0;
163             else
164                 trailingCount++;
165         }
166     }
167     if (decPt == -1)
168         decPt = j;
169     
170     // insert separators into integer part
171     int block = gSize ? decPt % gSize : decPt;
172     if (!block) block = gSize;
173     for (int i = 0; i < decPt; i += block, block = gSize)
174     {
175         for (int k = 0; k < block; k++)
176         {
177             if (buff1[i + k] != 0)
178                 tempResult.nadd(buff1 + i + k, 1);
179             else
180                 tempResult += getItem(XSLA_ZERO_DIGIT);
181         }
182         if (i + block < decPt)
183             tempResult += getItem(XSLA_GROUPING_SEPARATOR);     
184     }
185     // add a decimal point if needed
186     if (fDigitsMin || !(trailingCount == fDigits))      
187         tempResult += getItem(XSLA_DECIMAL_SEPARATOR);
188
189     // copy decimal part, omitting the extra trailing zeroes
190     tempResult.nadd(buff1 + decPt + 1, max(fDigits - trailingCount, fDigitsMin));
191
192     //replace the zero character (tell me why for god's sakes)
193     char zd = *((char*)zeroDigit.get());
194     if (zd != '0')
195       {
196         char *aux = (char*)tempResult;
197         while ((aux = strchr(aux, '0')))
198           *aux = zd;
199       }
200
201     // add percent or per-mille sign
202     if (factor == 100)
203         tempResult += getItem(XSLA_PERCENT);
204     if (factor == 1000)
205         tempResult += getItem(XSLA_PER_MILLE );
206     
207     // add the prefix and suffix
208     DStr final = prefix + tempResult + suffix;
209     result = final;
210     return OK;
211 }
212
213 const EQName& DecimalFormat::getname()
214 {
215     return name;
216 }
217
218 #define readToken(STRG, PTR, LENGTH) STRG.nset(PTR, LENGTH)
219
220 XSL_ATT tokensList[] = 
221 {
222     XSLA_DIGIT, 
223     XSLA_ZERO_DIGIT,
224     XSLA_GROUPING_SEPARATOR,
225     XSLA_DECIMAL_SEPARATOR,
226     XSLA_PATTERN_SEPARATOR,
227     XSLA_PERCENT,
228     XSLA_PER_MILLE,
229     XSLA_NONE
230 };
231
232 XSL_ATT DecimalFormat::whichToken(const char* ptr, int len)
233 {
234     for (int i = 0; tokensList[i] != XSLA_NONE; i++)
235         if (!strncmp(ptr, getItem(tokensList[i]), len))
236             return tokensList[i];
237     return XSLA_NONE;
238 }
239
240 eFlag DecimalFormat::parseSubpattern(Sit S, const char *&ptr, Bool negative, 
241                                      Str& prefix, Str& suffix, int& factor,
242                                      int& iDigitsMin, int& fDigits, int& fDigitsMin, int& gSize)
243 {
244     int len;
245     int state = 0;
246     Bool wasDigit = FALSE;
247     XSL_ATT token;
248     prefix.empty();
249     suffix.empty();
250     iDigitsMin = fDigits = fDigitsMin = 0;
251     gSize = -1;
252     factor = 1;
253     for (; *ptr && state < 5; ptr += len)
254     {
255         if (*ptr == '\'')
256         {
257             // escaped special character; if non-special follows, throw error
258             ptr++;
259             if (!*ptr) break;
260             len = utf8SingleCharLength(ptr);
261             if (whichToken(ptr, len) == XSLA_NONE)
262                 Err(S, E_FORMAT_INVALID);
263             token = XSLA_NONE;      
264         }
265         else
266         {
267             len = utf8SingleCharLength(ptr);        
268             token = whichToken(ptr, len);
269         }
270         
271         // check for the forbidden currency sign U+00a4
272         if ((unsigned char)*ptr == 0xc2 && (unsigned char)(ptr[1]) == 0xa4)
273             Err(S, E_FORMAT_INVALID);
274         
275         switch(state)
276         {
277         case 0:
278             // the very start
279         {
280             if (token == XSLA_NONE)
281             {
282               //readToken(prefix, ptr, len);
283               Str foo;
284               readToken(foo, ptr, len);
285               prefix = prefix + foo;
286                 break;
287             }
288             state = 1;
289         }; 
290         case 1:
291             // after the prefix has been retrieved
292         {
293             switch(token)
294             {
295             case XSLA_DIGIT:
296             case XSLA_ZERO_DIGIT:
297             {
298                 if (gSize >= 0) gSize++;
299                 wasDigit = TRUE;
300                 if (token == XSLA_DIGIT)
301                 {
302                     if (iDigitsMin)
303                         Err(S, E_FORMAT_INVALID);
304                 }
305                 else
306                     iDigitsMin++;
307             }; break;
308             case XSLA_GROUPING_SEPARATOR:
309             {
310                 // forbid consecutive commas and comma at start
311                 if (!gSize || !wasDigit)
312                     Err(S, E_FORMAT_INVALID);
313                 gSize = 0;
314             }; break;
315             case XSLA_DECIMAL_SEPARATOR:
316                 state = 2; break;
317             case XSLA_PERCENT:
318             case XSLA_PER_MILLE:
319             {
320                 if (factor != 1)
321                     Err(S, E_FORMAT_INVALID);
322                 factor = (token == XSLA_PERCENT) ? 100 : 1000;
323                 //state = 3;
324             }; break;
325             case XSLA_NONE:
326             {
327                 readToken(suffix, ptr, len);
328                 state = 4;
329             }; break;
330             case XSLA_PATTERN_SEPARATOR:
331             {
332                 if (negative)
333                     Err(S, E_FORMAT_INVALID);
334                 state = 5; 
335             }; break;
336             default:
337                 Err(S, E_FORMAT_INVALID);
338             }
339         }; break;
340         case 2:
341             // after the decimal point
342         {
343             switch(token)
344             {
345             case XSLA_ZERO_DIGIT:
346             {
347                 if (fDigits > fDigitsMin)
348                     Err(S, E_FORMAT_INVALID);
349                 fDigitsMin++; 
350                 fDigits++;
351             }; break;
352             case XSLA_DIGIT:
353                 fDigits++; break;
354             case XSLA_PERCENT:
355             case XSLA_PER_MILLE:
356             {
357                 if (factor != 1)
358                     Err(S, E_FORMAT_INVALID);
359                 factor = (token == XSLA_PERCENT) ? 100 : 1000;
360                 //state = 3;
361             }; break;
362             case XSLA_NONE:
363             {
364                 readToken(suffix, ptr, len);
365                 state = 4;
366             }; break;
367             case XSLA_PATTERN_SEPARATOR:
368             {
369                 if (negative)
370                     Err(S, E_FORMAT_INVALID);
371                 state = 5; 
372             }; break;
373             default:
374                 Err(S, E_FORMAT_INVALID);
375             }
376         }; break;
377         case 3:
378             // after the percent/per-mille sign was retrieved
379         {
380             switch(token)
381             {
382             case XSLA_NONE:
383             {
384                 readToken(suffix, ptr, len);
385                 state = 4;
386             }; break;
387             case XSLA_PATTERN_SEPARATOR:
388             {
389                 if (negative)
390                     Err(S, E_FORMAT_INVALID);
391                 state = 5; 
392             }; break;
393             default:
394                 Err(S, E_FORMAT_INVALID);
395             }
396         }; break;
397         case 4:
398             // after the suffix was retrieved
399         {
400           if (token == XSLA_NONE) {
401             Str foo;
402             readToken(foo, ptr, len);
403             suffix = suffix + foo;
404             break;
405           }
406           if (token != XSLA_PATTERN_SEPARATOR || negative)
407             Err(S, E_FORMAT_INVALID);
408           state = 5;
409         }; break;
410         }
411     }
412     if ((negative && *ptr)) //|| !iDigitsMin)
413         Err(S, E_FORMAT_INVALID);
414     // forbid trailing ;
415     if (!negative && !*ptr && state == 5)
416         Err(S, E_FORMAT_INVALID);
417     if (gSize == -1)
418         gSize = 0;
419     return OK;
420 }
421
422 eFlag DecimalFormat::parse(Sit S, const Str &src, Bool negative,
423                            Str& prefix, Str& suffix, int& factor,
424                            int& iDigitsMin, int& fDigits, int& fDigitsMin, int& gSize)
425 {
426     const char *p = (const char*) src;
427     E( parseSubpattern(S, p, FALSE, prefix, suffix, factor, 
428                        iDigitsMin, fDigits, fDigitsMin, gSize) );
429     if (negative)
430     {
431         if (*p)
432         {
433             int iDigitsMin_, fDigits_, fDigitsMin_, gSize_;
434             // determine prefix, suffix and factor, rest given by positive subpattern
435             E( parseSubpattern(S, p, TRUE, prefix, suffix, factor, 
436                                iDigitsMin_, fDigits_, fDigitsMin_, gSize_) );
437         }
438         else
439         {
440             // defaults
441             prefix = DStr(getItem(XSLA_MINUS_SIGN)) + prefix;
442             // suffix and factor remain
443         }
444     }
445     return OK;
446 }
447
448 void DecimalFormat::report(Sit S, MsgType type, MsgCode code, 
449     const Str &arg1, const Str &arg2) const
450 {
451     S.message(type, code, arg1, arg2);
452 }
453
454 //
455 //
456 //  DefaultedStr
457 //
458 // 
459
460 DefaultedStr::DefaultedStr(const char *defaultValue_, Bool singleChar_, XSL_ATT id_)
461     : defaultValue(defaultValue_), specified(FALSE), singleChar(singleChar_), id(id_)
462 {
463 }
464
465 DefaultedStr::~DefaultedStr()
466 {
467 }
468
469 eFlag DefaultedStr::set(Sit S, const Str& value)
470 {
471     if (specified && value != specifiedValue)
472         Err1(S, E1_FORMAT_DUPLICIT_OPTION, ownName());
473     if (singleChar && utf8StrLength(value) != 1)
474         Err1(S, E1_FORMAT_OPTION_CHAR, ownName());
475     specifiedValue = value;
476     specified = TRUE;
477     return OK;
478 }
479
480 const Str& DefaultedStr::get()
481 {
482     if (specified)
483         return specifiedValue;
484     else
485         return defaultValue;
486 }
487
488 char* DefaultedStr::ownName()
489 {
490     return (char*)xslAttNames[id];
491 }
492
493 void DefaultedStr::report(Sit S, MsgType type, MsgCode code, 
494     const Str &arg1, const Str &arg2) const
495 {
496     S.message(type, code, arg1, arg2);
497 }
498
499 //
500 //
501 //  DecimalFormatList
502 //
503 //  
504
505 DecimalFormatList::DecimalFormatList()
506 {
507     initialize();
508 }
509
510 void DecimalFormatList::initialize()
511 {
512     freeall(FALSE);
513     EQName emptyName;
514     // append the default format
515     append(new DecimalFormat(emptyName));
516 }
517
518 DecimalFormatList::~DecimalFormatList()
519 {
520     freeall(FALSE);
521 }
522
523 eFlag DecimalFormatList::add(Sit S, const EQName& name, DecimalFormat*& result)
524 {
525     int ndx = findNdx(name);
526     if (ndx != -1)
527         result = (*this)[ndx];
528     else
529         append(result = new DecimalFormat(name));
530     return OK;
531 }
532
533 eFlag DecimalFormatList::format(Sit S, const EQName& name, Number& num, const Str& fmt, Str& result)
534 {
535     int ndx = findNdx(name);
536     if (ndx != -1)
537         E( (*this)[ndx] -> format(S, num, fmt, result) )
538     else
539     {
540         Str fullname;
541         name.getname(fullname);
542         Err1(S, E1_FORMAT_NOT_FOUND, fullname);
543     }
544     return OK;
545 }
546
547 int DecimalFormatList::findNdx(const EQName& name)
548 {
549     int i;
550     for (i = 0; i < number(); i++)
551         if ((*this)[i] -> getname() == name)
552             return i;
553     return -1;
554 }
555
556 void DecimalFormatList::report(Sit S, MsgType type, MsgCode code, 
557     const Str &arg1, const Str &arg2) const
558 {
559     S.message(type, code, arg1, arg2);
560 }
561