source: icXML/icXML-devel/src/xercesc/util/XMLBigDecimal.cpp @ 2722

Last change on this file since 2722 was 2722, checked in by cameron, 6 years ago

Original Xerces files with import mods for icxercesc

File size: 14.5 KB
Line 
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18/*
19 * $Id: XMLBigDecimal.cpp 557254 2007-07-18 13:28:54Z amassari $
20 */
21
22// ---------------------------------------------------------------------------
23//  Includes
24// ---------------------------------------------------------------------------
25#include <xercesc/util/XMLBigDecimal.hpp>
26#include <xercesc/util/XMLBigInteger.hpp>
27#include <icxercesc/util/TransService.hpp>
28#include <xercesc/util/NumberFormatException.hpp>
29#include <xercesc/util/XMLChar.hpp>
30#include <xercesc/util/OutOfMemoryException.hpp>
31#include <xercesc/util/Janitor.hpp>
32
33XERCES_CPP_NAMESPACE_BEGIN
34
35/**
36 * Constructs a BigDecimal from a string containing an optional (plus | minus)
37 * sign followed by a sequence of zero or more decimal digits, optionally
38 * followed by a fraction, which consists of a decimal point followed by
39 * zero or more decimal digits.  The string must contain at least one
40 * digit in the integer or fractional part.  The scale of the resulting
41 * BigDecimal will be the number of digits to the right of the decimal
42 * point in the string, or 0 if the string contains no decimal point.
43 * Any extraneous characters (including whitespace) will result in
44 * a NumberFormatException.
45
46 * since parseBigDecimal()  may throw exception,
47 * caller of XMLBigDecimal need to catch it.
48//
49**/
50
51typedef JanitorMemFunCall<XMLBigDecimal>    CleanupType;
52
53XMLBigDecimal::XMLBigDecimal(const XMLCh* const strValue,
54                             MemoryManager* const manager)
55: fSign(0)
56, fTotalDigits(0)
57, fScale(0)
58, fRawDataLen(0)
59, fRawData(0)
60, fIntVal(0)
61, fMemoryManager(manager)
62{
63    if ((!strValue) || (!*strValue))
64        ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_emptyString, fMemoryManager);
65
66    CleanupType cleanup(this, &XMLBigDecimal::cleanUp);
67
68    try
69    {
70        fRawDataLen = XMLString::stringLen(strValue);
71        fRawData = (XMLCh*) fMemoryManager->allocate
72        (
73            ((fRawDataLen*2) + 2) * sizeof(XMLCh) //fRawData and fIntVal
74        );
75        memcpy(fRawData, strValue, fRawDataLen * sizeof(XMLCh));
76        fRawData[fRawDataLen] = chNull;
77        fIntVal = fRawData + fRawDataLen + 1;
78        parseDecimal(strValue, fIntVal, fSign, (int&) fTotalDigits, (int&) fScale, fMemoryManager);
79    }
80    catch(const OutOfMemoryException&)
81    {
82        cleanup.release();
83
84        throw;
85    }
86
87    cleanup.release();
88}
89
90XMLBigDecimal::~XMLBigDecimal()
91{
92    cleanUp();
93}
94
95void XMLBigDecimal::cleanUp()
96{
97    if (fRawData)
98        fMemoryManager->deallocate(fRawData); //XMLString::release(&fRawData);
99}
100
101void XMLBigDecimal::setDecimalValue(const XMLCh* const strValue)
102{
103    fScale = fTotalDigits = 0;
104    XMLSize_t valueLen = XMLString::stringLen(strValue);
105
106    if (valueLen > fRawDataLen)
107    {
108        fMemoryManager->deallocate(fRawData);
109        fRawData = (XMLCh*) fMemoryManager->allocate
110        (
111            ((valueLen * 2) + 4) * sizeof(XMLCh)
112        );//XMLString::replicate(strValue, fMemoryManager);
113    }
114
115    memcpy(fRawData, strValue, valueLen * sizeof(XMLCh));
116    fRawData[valueLen] = chNull;
117    fRawDataLen = valueLen;
118    fIntVal = fRawData + fRawDataLen + 1;
119    parseDecimal(strValue, fIntVal, fSign, (int&) fTotalDigits, (int&) fScale, fMemoryManager);
120
121}
122
123/***
124 * 3.2.3 decimal 
125 *
126 * . the preceding optional "+" sign is prohibited.
127 * . The decimal point is required.
128 * . Leading and trailing zeroes are prohibited subject to the following:
129 *   there must be at least one digit to the right and to the left of the decimal point which may be a zero.
130 *
131 ***/
132XMLCh* XMLBigDecimal::getCanonicalRepresentation(const XMLCh*         const rawData
133                                               ,       MemoryManager* const memMgr)
134{
135
136    XMLCh* retBuf = (XMLCh*) memMgr->allocate( (XMLString::stringLen(rawData)+1) * sizeof(XMLCh));
137    ArrayJanitor<XMLCh> janName(retBuf, memMgr);
138    int   sign, totalDigits, fractDigits;
139
140    try
141    {
142        parseDecimal(rawData, retBuf, sign, totalDigits, fractDigits, memMgr);
143    }
144    catch (const NumberFormatException&)
145    {
146        return 0;
147    }
148
149
150    //Extra space reserved in case strLen is zero
151    XMLSize_t strLen = XMLString::stringLen(retBuf);
152    XMLCh* retBuffer = (XMLCh*) memMgr->allocate( (strLen + 4) * sizeof(XMLCh));
153
154    if ( (sign == 0) || (totalDigits == 0))
155    {
156        retBuffer[0] = chDigit_0;
157        retBuffer[1] = chPeriod;
158        retBuffer[2] = chDigit_0;
159        retBuffer[3] = chNull;
160    }
161    else
162    {
163        XMLCh* retPtr = retBuffer;
164
165        if (sign == -1)
166        {
167            *retPtr++ = chDash;
168        }
169
170        if (fractDigits == totalDigits)   // no integer
171        {           
172            *retPtr++ = chDigit_0;
173            *retPtr++ = chPeriod;
174            XMLString::copyNString(retPtr, retBuf, strLen);
175            retPtr += strLen;
176            *retPtr = chNull;
177        }
178        else if (fractDigits == 0)        // no fraction
179        {
180            XMLString::copyNString(retPtr, retBuf, strLen);
181            retPtr += strLen;
182            *retPtr++ = chPeriod;
183            *retPtr++ = chDigit_0;
184            *retPtr   = chNull;
185        }
186        else  // normal
187        {
188            int intLen = totalDigits - fractDigits;
189            XMLString::copyNString(retPtr, retBuf, intLen);
190            retPtr += intLen;
191            *retPtr++ = chPeriod;
192            XMLString::copyNString(retPtr, &(retBuf[intLen]), fractDigits);
193            retPtr += fractDigits;
194            *retPtr = chNull;
195        }
196
197    }
198           
199    return retBuffer;
200}
201
202void  XMLBigDecimal::parseDecimal(const XMLCh* const toParse
203                               ,        XMLCh* const retBuffer
204                               ,        int&         sign
205                               ,        int&         totalDigits
206                               ,        int&         fractDigits
207                               ,        MemoryManager* const manager)
208{
209    //init
210    retBuffer[0] = chNull;
211    totalDigits = 0;
212    fractDigits = 0;
213
214    // Strip leading white space, if any.
215    const XMLCh* startPtr = toParse;
216    while (XMLChar1_0::isWhitespace(*startPtr))
217        startPtr++;
218
219    // If we hit the end, then return failure
220    if (!*startPtr)
221        ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_WSString, manager);
222
223    // Strip tailing white space, if any.
224    const XMLCh* endPtr = toParse + XMLString::stringLen(toParse);
225    while (XMLChar1_0::isWhitespace(*(endPtr - 1)))
226        endPtr--;
227
228    // '+' or '-' is allowed only at the first position
229    // and is NOT included in the return parsed string
230    sign = 1;
231    if (*startPtr == chDash)
232    {
233        sign = -1;
234        startPtr++;
235        if (startPtr == endPtr)
236        {
237            ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
238        }
239    }
240    else if (*startPtr == chPlus)
241    {
242        startPtr++;         
243        if (startPtr == endPtr)
244        {
245            ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
246        }
247    }
248
249    // Strip leading zeros
250    while (*startPtr == chDigit_0)
251        startPtr++;
252
253    // containning zero, only zero, nothing but zero
254    // it is a zero, indeed
255    if (startPtr >= endPtr)
256    {
257        sign = 0;
258        return;
259    }
260
261    XMLCh* retPtr = (XMLCh*) retBuffer;
262
263    // Scan data
264    bool   dotSignFound = false;
265    while (startPtr < endPtr)
266    {
267        if (*startPtr == chPeriod)
268        {
269            if (!dotSignFound)
270            {
271                dotSignFound = true;
272                fractDigits = (int)(endPtr - startPtr - 1);
273                startPtr++;
274                continue;
275            }
276            else  // '.' is allowed only once
277                ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_2ManyDecPoint, manager);
278        }
279
280        // If not valid decimal digit, then an error
281        if ((*startPtr < chDigit_0) || (*startPtr > chDigit_9))
282            ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
283
284        // copy over
285        *retPtr++ = *startPtr++;
286        totalDigits++;
287    }
288
289    /***
290    E2-44 totalDigits
291
292     ... by restricting it to numbers that are expressible as i x 10^-n
293     where i and n are integers such that |i| < 10^totalDigits and 0 <= n <= totalDigits.
294
295        normalization: remove all trailing zero after the '.'
296                       and adjust the scaleValue as well.
297    ***/
298    while ((fractDigits > 0) && (*(retPtr-1) == chDigit_0))         
299    {
300        retPtr--;
301        fractDigits--;
302        totalDigits--;
303    }
304    // 0.0 got past the check for zero because of the decimal point, so we need to double check it here
305    if(totalDigits==0)
306        sign = 0;
307
308    *retPtr = chNull;   //terminated
309    return;
310}
311
312void  XMLBigDecimal::parseDecimal(const XMLCh*         const toParse
313                               ,        MemoryManager* const manager)
314{
315
316    // Strip leading white space, if any.
317    const XMLCh* startPtr = toParse;
318    while (XMLChar1_0::isWhitespace(*startPtr))
319        startPtr++;
320
321    // If we hit the end, then return failure
322    if (!*startPtr)
323        ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_WSString, manager);
324
325    // Strip tailing white space, if any.
326    const XMLCh* endPtr = toParse + XMLString::stringLen(toParse);
327    while (XMLChar1_0::isWhitespace(*(endPtr - 1)))
328        endPtr--;
329
330    // '+' or '-' is allowed only at the first position
331    // and is NOT included in the return parsed string
332
333    if (*startPtr == chDash)
334    {
335        startPtr++;
336        if (startPtr == endPtr)
337        {
338            ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
339        }
340    }
341    else if (*startPtr == chPlus)
342    {
343        startPtr++;
344        if (startPtr == endPtr)
345        {
346            ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
347        }
348    }
349
350    // Strip leading zeros
351    while (*startPtr == chDigit_0)
352        startPtr++;
353
354    // containning zero, only zero, nothing but zero
355    // it is a zero, indeed
356    if (startPtr >= endPtr)
357    {
358        return;
359    }
360
361    // Scan data
362    bool   dotSignFound = false;
363    while (startPtr < endPtr)
364    {
365        if (*startPtr == chPeriod)
366        {
367            if (!dotSignFound)
368            {
369                dotSignFound = true;
370                startPtr++;
371                continue;
372            }
373            else  // '.' is allowed only once
374                ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_2ManyDecPoint, manager);
375        }
376
377        // If not valid decimal digit, then an error
378        if ((*startPtr < chDigit_0) || (*startPtr > chDigit_9))
379            ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
380
381        startPtr++;
382
383    }
384
385    return;
386}
387
388int XMLBigDecimal::compareValues( const XMLBigDecimal* const lValue
389                                , const XMLBigDecimal* const rValue
390                                , MemoryManager* const manager)
391{
392    if ((!lValue) || (!rValue) )
393        ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_null_ptr, manager);
394               
395    return lValue->toCompare(*rValue);
396}                               
397
398/**
399 * Returns -1, 0 or 1 as this is less than, equal to, or greater
400 * than rValue. 
401 *
402 * This method is based on the fact, that parsebigDecimal() would eliminate
403 * unnecessary leading/trailing zeros.
404**/
405int XMLBigDecimal::toCompare(const XMLBigDecimal& other) const
406{
407    /***
408     * different sign
409     */
410    int lSign = this->getSign();
411    if (lSign != other.getSign())
412        return (lSign > other.getSign() ? 1 : -1);
413
414    /***
415     * same sign, zero
416     */
417    if (lSign == 0)    // optimization
418        return 0;
419
420    /***
421     * same sign, non-zero
422     */
423    unsigned int lIntDigit = this->getTotalDigit() - this->getScale();
424    unsigned int rIntDigit = other.getTotalDigit() - other.getScale();
425
426    if (lIntDigit > rIntDigit)
427    {
428        return 1 * lSign;
429    }
430    else if (lIntDigit < rIntDigit)
431    {
432        return -1 * lSign;
433    }
434    else  // compare fraction
435    {
436        int res = XMLString::compareString
437        ( this->getValue()
438        , other.getValue()
439        );
440
441        if (res > 0)
442            return 1 * lSign;
443        else if (res < 0)
444            return -1 * lSign;
445        else
446            return 0;
447    }
448
449}
450
451
452/***
453 * Support for Serialization/De-serialization
454 ***/
455
456IMPL_XSERIALIZABLE_TOCREATE(XMLBigDecimal)
457
458XMLBigDecimal::XMLBigDecimal(MemoryManager* const manager)
459: fSign(0)
460, fTotalDigits(0)
461, fScale(0)
462, fRawDataLen(0)
463, fRawData(0)
464, fIntVal(0)
465, fMemoryManager(manager)
466{
467}
468
469void XMLBigDecimal::serialize(XSerializeEngine& serEng)
470{
471    //REVISIT: may not need to call base since it does nothing
472    XMLNumber::serialize(serEng);
473
474    if (serEng.isStoring())
475    {
476        serEng<<fSign;
477        serEng<<fTotalDigits;
478        serEng<<fScale;
479
480        serEng.writeString(fRawData);
481        serEng.writeString(fIntVal);
482
483    }
484    else
485    {
486        serEng>>fSign;
487        serEng>>fTotalDigits;
488        serEng>>fScale;
489
490        XMLCh* rawdataStr;
491        serEng.readString(rawdataStr);
492        ArrayJanitor<XMLCh> rawdataName(rawdataStr, serEng.getMemoryManager());
493        fRawDataLen = XMLString::stringLen(rawdataStr);
494
495        XMLCh* intvalStr;
496        serEng.readString(intvalStr);
497        ArrayJanitor<XMLCh> intvalName(intvalStr, serEng.getMemoryManager());
498        XMLSize_t intvalStrLen = XMLString::stringLen(intvalStr);
499
500        if (fRawData)
501            fMemoryManager->deallocate(fRawData);
502
503        fRawData = (XMLCh*) fMemoryManager->allocate
504        (
505            ((fRawDataLen + intvalStrLen) + 4) * sizeof(XMLCh)
506        );
507
508        memcpy(fRawData, rawdataStr, fRawDataLen * sizeof(XMLCh));
509        fRawData[fRawDataLen] = chNull;
510        fIntVal = fRawData + fRawDataLen + 1;
511        memcpy(fIntVal, intvalStr,  intvalStrLen * sizeof(XMLCh));
512        fIntVal[intvalStrLen] = chNull;
513
514    }
515
516}
517
518XERCES_CPP_NAMESPACE_END
519
Note: See TracBrowser for help on using the repository browser.