GME  13
XMLBigDecimal.cpp
Go to the documentation of this file.
00001 /*
00002  * Licensed to the Apache Software Foundation (ASF) under one or more
00003  * contributor license agreements.  See the NOTICE file distributed with
00004  * this work for additional information regarding copyright ownership.
00005  * The ASF licenses this file to You under the Apache License, Version 2.0
00006  * (the "License"); you may not use this file except in compliance with
00007  * the License.  You may obtain a copy of the License at
00008  * 
00009  *      http://www.apache.org/licenses/LICENSE-2.0
00010  * 
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /*
00019  * $Id: XMLBigDecimal.cpp 557254 2007-07-18 13:28:54Z amassari $
00020  */
00021 
00022 // ---------------------------------------------------------------------------
00023 //  Includes
00024 // ---------------------------------------------------------------------------
00025 #include <xercesc/util/XMLBigDecimal.hpp>
00026 #include <xercesc/util/XMLBigInteger.hpp>
00027 #include <xercesc/util/TransService.hpp>
00028 #include <xercesc/util/NumberFormatException.hpp>
00029 #include <xercesc/util/XMLChar.hpp>
00030 #include <xercesc/util/OutOfMemoryException.hpp>
00031 #include <xercesc/util/Janitor.hpp>
00032 
00033 XERCES_CPP_NAMESPACE_BEGIN
00034 
00051 typedef JanitorMemFunCall<XMLBigDecimal>    CleanupType;
00052 
00053 XMLBigDecimal::XMLBigDecimal(const XMLCh* const strValue,
00054                              MemoryManager* const manager)
00055 : fSign(0)
00056 , fTotalDigits(0)
00057 , fScale(0)
00058 , fRawDataLen(0)
00059 , fRawData(0)
00060 , fIntVal(0)
00061 , fMemoryManager(manager)
00062 {
00063     if ((!strValue) || (!*strValue))
00064         ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_emptyString, fMemoryManager);
00065 
00066     CleanupType cleanup(this, &XMLBigDecimal::cleanUp);
00067 
00068     try
00069     {
00070         fRawDataLen = XMLString::stringLen(strValue);
00071         fRawData = (XMLCh*) fMemoryManager->allocate
00072         (
00073             ((fRawDataLen*2) + 2) * sizeof(XMLCh) //fRawData and fIntVal
00074         );
00075         memcpy(fRawData, strValue, fRawDataLen * sizeof(XMLCh));
00076         fRawData[fRawDataLen] = chNull;
00077         fIntVal = fRawData + fRawDataLen + 1;
00078         parseDecimal(strValue, fIntVal, fSign, (int&) fTotalDigits, (int&) fScale, fMemoryManager);
00079     }
00080     catch(const OutOfMemoryException&)
00081     {
00082         cleanup.release();
00083 
00084         throw;
00085     }
00086 
00087     cleanup.release();
00088 }
00089 
00090 XMLBigDecimal::~XMLBigDecimal()
00091 {
00092     cleanUp();
00093 }
00094 
00095 void XMLBigDecimal::cleanUp()
00096 {
00097     if (fRawData)
00098         fMemoryManager->deallocate(fRawData); //XMLString::release(&fRawData);
00099 }
00100 
00101 void XMLBigDecimal::setDecimalValue(const XMLCh* const strValue)
00102 {
00103     fScale = fTotalDigits = 0;
00104     XMLSize_t valueLen = XMLString::stringLen(strValue);
00105 
00106     if (valueLen > fRawDataLen)
00107     {
00108         fMemoryManager->deallocate(fRawData);
00109         fRawData = (XMLCh*) fMemoryManager->allocate
00110         (
00111             ((valueLen * 2) + 4) * sizeof(XMLCh)
00112         );//XMLString::replicate(strValue, fMemoryManager);
00113     }
00114 
00115     memcpy(fRawData, strValue, valueLen * sizeof(XMLCh));
00116     fRawData[valueLen] = chNull;
00117     fRawDataLen = valueLen;
00118     fIntVal = fRawData + fRawDataLen + 1;
00119     parseDecimal(strValue, fIntVal, fSign, (int&) fTotalDigits, (int&) fScale, fMemoryManager);
00120 
00121 }
00122 
00123 /***
00124  * 3.2.3 decimal  
00125  *
00126  * . the preceding optional "+" sign is prohibited. 
00127  * . The decimal point is required. 
00128  * . Leading and trailing zeroes are prohibited subject to the following: 
00129  *   there must be at least one digit to the right and to the left of the decimal point which may be a zero.
00130  *
00131  ***/
00132 XMLCh* XMLBigDecimal::getCanonicalRepresentation(const XMLCh*         const rawData
00133                                                ,       MemoryManager* const memMgr)
00134 {
00135 
00136     XMLCh* retBuf = (XMLCh*) memMgr->allocate( (XMLString::stringLen(rawData)+1) * sizeof(XMLCh));
00137     ArrayJanitor<XMLCh> janName(retBuf, memMgr);
00138     int   sign, totalDigits, fractDigits;
00139 
00140     try
00141     {
00142         parseDecimal(rawData, retBuf, sign, totalDigits, fractDigits, memMgr);
00143     }
00144     catch (const NumberFormatException&)
00145     {
00146         return 0;
00147     }
00148 
00149 
00150     //Extra space reserved in case strLen is zero
00151     XMLSize_t strLen = XMLString::stringLen(retBuf);
00152     XMLCh* retBuffer = (XMLCh*) memMgr->allocate( (strLen + 4) * sizeof(XMLCh));
00153 
00154     if ( (sign == 0) || (totalDigits == 0))
00155     {
00156         retBuffer[0] = chDigit_0;
00157         retBuffer[1] = chPeriod;
00158         retBuffer[2] = chDigit_0;
00159         retBuffer[3] = chNull;
00160     }
00161     else
00162     {
00163         XMLCh* retPtr = retBuffer;
00164 
00165         if (sign == -1)
00166         {
00167             *retPtr++ = chDash;
00168         }
00169 
00170         if (fractDigits == totalDigits)   // no integer
00171         {           
00172             *retPtr++ = chDigit_0;
00173             *retPtr++ = chPeriod;
00174             XMLString::copyNString(retPtr, retBuf, strLen);
00175             retPtr += strLen;
00176             *retPtr = chNull;
00177         }
00178         else if (fractDigits == 0)        // no fraction
00179         {
00180             XMLString::copyNString(retPtr, retBuf, strLen);
00181             retPtr += strLen;
00182             *retPtr++ = chPeriod;
00183             *retPtr++ = chDigit_0;
00184             *retPtr   = chNull;
00185         }
00186         else  // normal
00187         {
00188             int intLen = totalDigits - fractDigits;
00189             XMLString::copyNString(retPtr, retBuf, intLen);
00190             retPtr += intLen;
00191             *retPtr++ = chPeriod;
00192             XMLString::copyNString(retPtr, &(retBuf[intLen]), fractDigits);
00193             retPtr += fractDigits;
00194             *retPtr = chNull;
00195         }
00196 
00197     }
00198             
00199     return retBuffer;
00200 }
00201 
00202 void  XMLBigDecimal::parseDecimal(const XMLCh* const toParse
00203                                ,        XMLCh* const retBuffer
00204                                ,        int&         sign
00205                                ,        int&         totalDigits
00206                                ,        int&         fractDigits
00207                                ,        MemoryManager* const manager)
00208 {
00209     //init
00210     retBuffer[0] = chNull;
00211     totalDigits = 0;
00212     fractDigits = 0;
00213 
00214     // Strip leading white space, if any. 
00215     const XMLCh* startPtr = toParse;
00216     while (XMLChar1_0::isWhitespace(*startPtr))
00217         startPtr++;
00218 
00219     // If we hit the end, then return failure
00220     if (!*startPtr)
00221         ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_WSString, manager);
00222 
00223     // Strip tailing white space, if any.
00224     const XMLCh* endPtr = toParse + XMLString::stringLen(toParse);
00225     while (XMLChar1_0::isWhitespace(*(endPtr - 1)))
00226         endPtr--;
00227 
00228     // '+' or '-' is allowed only at the first position
00229     // and is NOT included in the return parsed string
00230     sign = 1;
00231     if (*startPtr == chDash)
00232     {
00233         sign = -1;
00234         startPtr++;
00235         if (startPtr == endPtr)
00236         {
00237             ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
00238         }
00239     }
00240     else if (*startPtr == chPlus)
00241     {
00242         startPtr++;         
00243         if (startPtr == endPtr)
00244         {
00245             ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
00246         }
00247     }
00248 
00249     // Strip leading zeros
00250     while (*startPtr == chDigit_0)
00251         startPtr++;
00252 
00253     // containning zero, only zero, nothing but zero
00254     // it is a zero, indeed
00255     if (startPtr >= endPtr)
00256     {
00257         sign = 0;
00258         return;
00259     }
00260 
00261     XMLCh* retPtr = (XMLCh*) retBuffer;
00262 
00263     // Scan data
00264     bool   dotSignFound = false;
00265     while (startPtr < endPtr)
00266     {
00267         if (*startPtr == chPeriod)
00268         {
00269             if (!dotSignFound)
00270             {
00271                 dotSignFound = true;
00272                 fractDigits = (int)(endPtr - startPtr - 1);
00273                 startPtr++;
00274                 continue;
00275             }
00276             else  // '.' is allowed only once
00277                 ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_2ManyDecPoint, manager);
00278         }
00279 
00280         // If not valid decimal digit, then an error
00281         if ((*startPtr < chDigit_0) || (*startPtr > chDigit_9))
00282             ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
00283 
00284         // copy over
00285         *retPtr++ = *startPtr++;
00286         totalDigits++;
00287     }
00288 
00289     /***
00290     E2-44 totalDigits
00291 
00292      ... by restricting it to numbers that are expressible as i x 10^-n
00293      where i and n are integers such that |i| < 10^totalDigits and 0 <= n <= totalDigits. 
00294 
00295         normalization: remove all trailing zero after the '.'
00296                        and adjust the scaleValue as well.
00297     ***/
00298     while ((fractDigits > 0) && (*(retPtr-1) == chDigit_0))          
00299     {
00300         retPtr--;
00301         fractDigits--;
00302         totalDigits--;
00303     }
00304     // 0.0 got past the check for zero because of the decimal point, so we need to double check it here
00305     if(totalDigits==0)
00306         sign = 0;
00307 
00308     *retPtr = chNull;   //terminated
00309     return;
00310 }
00311 
00312 void  XMLBigDecimal::parseDecimal(const XMLCh*         const toParse
00313                                ,        MemoryManager* const manager)
00314 {
00315 
00316     // Strip leading white space, if any. 
00317     const XMLCh* startPtr = toParse;
00318     while (XMLChar1_0::isWhitespace(*startPtr))
00319         startPtr++;
00320 
00321     // If we hit the end, then return failure
00322     if (!*startPtr)
00323         ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_WSString, manager);
00324 
00325     // Strip tailing white space, if any.
00326     const XMLCh* endPtr = toParse + XMLString::stringLen(toParse);
00327     while (XMLChar1_0::isWhitespace(*(endPtr - 1)))
00328         endPtr--;
00329 
00330     // '+' or '-' is allowed only at the first position
00331     // and is NOT included in the return parsed string
00332 
00333     if (*startPtr == chDash)
00334     {
00335         startPtr++;
00336         if (startPtr == endPtr)
00337         {
00338             ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
00339         }
00340     }
00341     else if (*startPtr == chPlus)
00342     {
00343         startPtr++;
00344         if (startPtr == endPtr)
00345         {
00346             ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
00347         }
00348     }
00349 
00350     // Strip leading zeros
00351     while (*startPtr == chDigit_0)
00352         startPtr++;
00353 
00354     // containning zero, only zero, nothing but zero
00355     // it is a zero, indeed
00356     if (startPtr >= endPtr)
00357     {
00358         return;
00359     }
00360 
00361     // Scan data
00362     bool   dotSignFound = false;
00363     while (startPtr < endPtr)
00364     {
00365         if (*startPtr == chPeriod)
00366         {
00367             if (!dotSignFound)
00368             {
00369                 dotSignFound = true;
00370                 startPtr++;
00371                 continue;
00372             }
00373             else  // '.' is allowed only once
00374                 ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_2ManyDecPoint, manager);
00375         }
00376 
00377         // If not valid decimal digit, then an error
00378         if ((*startPtr < chDigit_0) || (*startPtr > chDigit_9))
00379             ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
00380 
00381         startPtr++;
00382 
00383     }
00384 
00385     return;
00386 }
00387 
00388 int XMLBigDecimal::compareValues( const XMLBigDecimal* const lValue
00389                                 , const XMLBigDecimal* const rValue
00390                                 , MemoryManager* const manager)
00391 {
00392     if ((!lValue) || (!rValue) )
00393         ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_null_ptr, manager);
00394                 
00395     return lValue->toCompare(*rValue);
00396 }                                
00397 
00405 int XMLBigDecimal::toCompare(const XMLBigDecimal& other) const
00406 {
00407     /***
00408      * different sign
00409      */
00410     int lSign = this->getSign();
00411     if (lSign != other.getSign())
00412         return (lSign > other.getSign() ? 1 : -1);
00413 
00414     /***
00415      * same sign, zero
00416      */
00417     if (lSign == 0)    // optimization
00418         return 0;
00419 
00420     /***
00421      * same sign, non-zero
00422      */
00423     unsigned int lIntDigit = this->getTotalDigit() - this->getScale();
00424     unsigned int rIntDigit = other.getTotalDigit() - other.getScale();
00425 
00426     if (lIntDigit > rIntDigit)
00427     {
00428         return 1 * lSign;
00429     }
00430     else if (lIntDigit < rIntDigit)
00431     {
00432         return -1 * lSign;
00433     }
00434     else  // compare fraction
00435     {
00436         int res = XMLString::compareString
00437         ( this->getValue()
00438         , other.getValue()
00439         );
00440 
00441         if (res > 0)
00442             return 1 * lSign;
00443         else if (res < 0)
00444             return -1 * lSign;
00445         else
00446             return 0;
00447     }
00448 
00449 }
00450 
00451 
00452 /***
00453  * Support for Serialization/De-serialization
00454  ***/
00455 
00456 IMPL_XSERIALIZABLE_TOCREATE(XMLBigDecimal)
00457 
00458 XMLBigDecimal::XMLBigDecimal(MemoryManager* const manager)
00459 : fSign(0)
00460 , fTotalDigits(0)
00461 , fScale(0)
00462 , fRawDataLen(0)
00463 , fRawData(0)
00464 , fIntVal(0)
00465 , fMemoryManager(manager)
00466 {
00467 }
00468 
00469 void XMLBigDecimal::serialize(XSerializeEngine& serEng)
00470 {
00471     //REVISIT: may not need to call base since it does nothing
00472     XMLNumber::serialize(serEng);
00473 
00474     if (serEng.isStoring())
00475     {
00476         serEng<<fSign;
00477         serEng<<fTotalDigits;
00478         serEng<<fScale;
00479 
00480         serEng.writeString(fRawData);
00481         serEng.writeString(fIntVal);
00482 
00483     }
00484     else
00485     {
00486         serEng>>fSign;
00487         serEng>>fTotalDigits;
00488         serEng>>fScale;
00489 
00490         XMLCh* rawdataStr;
00491         serEng.readString(rawdataStr);
00492         ArrayJanitor<XMLCh> rawdataName(rawdataStr, serEng.getMemoryManager());
00493         fRawDataLen = XMLString::stringLen(rawdataStr);
00494 
00495         XMLCh* intvalStr;
00496         serEng.readString(intvalStr);
00497         ArrayJanitor<XMLCh> intvalName(intvalStr, serEng.getMemoryManager());
00498         XMLSize_t intvalStrLen = XMLString::stringLen(intvalStr);
00499 
00500         if (fRawData)
00501             fMemoryManager->deallocate(fRawData);
00502 
00503         fRawData = (XMLCh*) fMemoryManager->allocate
00504         (
00505             ((fRawDataLen + intvalStrLen) + 4) * sizeof(XMLCh)
00506         );
00507 
00508         memcpy(fRawData, rawdataStr, fRawDataLen * sizeof(XMLCh));
00509         fRawData[fRawDataLen] = chNull;
00510         fIntVal = fRawData + fRawDataLen + 1;
00511         memcpy(fIntVal, intvalStr,  intvalStrLen * sizeof(XMLCh));
00512         fIntVal[intvalStrLen] = chNull;
00513 
00514     }
00515 
00516 }
00517 
00518 XERCES_CPP_NAMESPACE_END
00519