blob: 21406717756472242397504e0c0970185193d29d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: XMLBigDecimal.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/XMLBigDecimal.hpp>
#include <xercesc/util/XMLBigInteger.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/util/NumberFormatException.hpp>
#include <xercesc/util/XMLChar.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/util/Janitor.hpp>
XERCES_CPP_NAMESPACE_BEGIN
/**
* Constructs a BigDecimal from a string containing an optional (plus | minus)
* sign followed by a sequence of zero or more decimal digits, optionally
* followed by a fraction, which consists of a decimal point followed by
* zero or more decimal digits. The string must contain at least one
* digit in the integer or fractional part. The scale of the resulting
* BigDecimal will be the number of digits to the right of the decimal
* point in the string, or 0 if the string contains no decimal point.
* Any extraneous characters (including whitespace) will result in
* a NumberFormatException.
* since parseBigDecimal() may throw exception,
* caller of XMLBigDecimal need to catch it.
//
**/
typedef JanitorMemFunCall<XMLBigDecimal> CleanupType;
XMLBigDecimal::XMLBigDecimal(const XMLCh* const strValue,
MemoryManager* const manager)
: fSign(0)
, fTotalDigits(0)
, fScale(0)
, fRawDataLen(0)
, fRawData(0)
, fIntVal(0)
, fMemoryManager(manager)
{
if ((!strValue) || (!*strValue))
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_emptyString, fMemoryManager);
CleanupType cleanup(this, &XMLBigDecimal::cleanUp);
try
{
fRawDataLen = XMLString::stringLen(strValue);
fRawData = (XMLCh*) fMemoryManager->allocate
(
((fRawDataLen*2) + 2) * sizeof(XMLCh) //fRawData and fIntVal
);
memcpy(fRawData, strValue, fRawDataLen * sizeof(XMLCh));
fRawData[fRawDataLen] = chNull;
fIntVal = fRawData + fRawDataLen + 1;
parseDecimal(strValue, fIntVal, fSign, (int&) fTotalDigits, (int&) fScale, fMemoryManager);
}
catch(const OutOfMemoryException&)
{
cleanup.release();
throw;
}
cleanup.release();
}
XMLBigDecimal::~XMLBigDecimal()
{
cleanUp();
}
void XMLBigDecimal::cleanUp()
{
if (fRawData)
fMemoryManager->deallocate(fRawData); //XMLString::release(&fRawData);
}
void XMLBigDecimal::setDecimalValue(const XMLCh* const strValue)
{
fScale = fTotalDigits = 0;
unsigned int valueLen = XMLString::stringLen(strValue);
if (valueLen > fRawDataLen)
{
fMemoryManager->deallocate(fRawData);
fRawData = (XMLCh*) fMemoryManager->allocate
(
((valueLen * 2) + 4) * sizeof(XMLCh)
);//XMLString::replicate(strValue, fMemoryManager);
}
memcpy(fRawData, strValue, valueLen * sizeof(XMLCh));
fRawData[valueLen] = chNull;
fRawDataLen = valueLen;
fIntVal = fRawData + fRawDataLen + 1;
parseDecimal(strValue, fIntVal, fSign, (int&) fTotalDigits, (int&) fScale, fMemoryManager);
}
/***
* 3.2.3 decimal
*
* . the preceding optional "+" sign is prohibited.
* . The decimal point is required.
* . Leading and trailing zeroes are prohibited subject to the following:
* there must be at least one digit to the right and to the left of the decimal point which may be a zero.
*
***/
XMLCh* XMLBigDecimal::getCanonicalRepresentation(const XMLCh* const rawData
, MemoryManager* const memMgr)
{
XMLCh* retBuf = (XMLCh*) memMgr->allocate( (XMLString::stringLen(rawData)+1) * sizeof(XMLCh));
ArrayJanitor<XMLCh> janName(retBuf, memMgr);
int sign, totalDigits, fractDigits;
try
{
parseDecimal(rawData, retBuf, sign, totalDigits, fractDigits, memMgr);
}
catch (const NumberFormatException&)
{
return 0;
}
//Extra space reserved in case strLen is zero
int strLen = XMLString::stringLen(retBuf);
XMLCh* retBuffer = (XMLCh*) memMgr->allocate( (strLen + 4) * sizeof(XMLCh));
if ( (sign == 0) || (totalDigits == 0))
{
retBuffer[0] = chDigit_0;
retBuffer[1] = chPeriod;
retBuffer[2] = chDigit_0;
retBuffer[3] = chNull;
}
else
{
XMLCh* retPtr = retBuffer;
if (sign == -1)
{
*retPtr++ = chDash;
}
if (fractDigits == totalDigits) // no integer
{
*retPtr++ = chDigit_0;
*retPtr++ = chPeriod;
XMLString::copyNString(retPtr, retBuf, strLen);
retPtr += strLen;
*retPtr = chNull;
}
else if (fractDigits == 0) // no fraction
{
XMLString::copyNString(retPtr, retBuf, strLen);
retPtr += strLen;
*retPtr++ = chPeriod;
*retPtr++ = chDigit_0;
*retPtr = chNull;
}
else // normal
{
int intLen = totalDigits - fractDigits;
XMLString::copyNString(retPtr, retBuf, intLen);
retPtr += intLen;
*retPtr++ = chPeriod;
XMLString::copyNString(retPtr, &(retBuf[intLen]), fractDigits);
retPtr += fractDigits;
*retPtr = chNull;
}
}
return retBuffer;
}
void XMLBigDecimal::parseDecimal(const XMLCh* const toParse
, XMLCh* const retBuffer
, int& sign
, int& totalDigits
, int& fractDigits
, MemoryManager* const manager)
{
//init
retBuffer[0] = chNull;
totalDigits = 0;
fractDigits = 0;
// Strip leading white space, if any.
const XMLCh* startPtr = toParse;
while (XMLChar1_0::isWhitespace(*startPtr))
startPtr++;
// If we hit the end, then return failure
if (!*startPtr)
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_WSString, manager);
// Strip tailing white space, if any.
const XMLCh* endPtr = toParse + XMLString::stringLen(toParse);
while (XMLChar1_0::isWhitespace(*(endPtr - 1)))
endPtr--;
// '+' or '-' is allowed only at the first position
// and is NOT included in the return parsed string
sign = 1;
if (*startPtr == chDash)
{
sign = -1;
startPtr++;
if (startPtr == endPtr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
}
}
else if (*startPtr == chPlus)
{
startPtr++;
if (startPtr == endPtr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
}
}
// Strip leading zeros
while (*startPtr == chDigit_0)
startPtr++;
// containning zero, only zero, nothing but zero
// it is a zero, indeed
if (startPtr >= endPtr)
{
sign = 0;
return;
}
XMLCh* retPtr = (XMLCh*) retBuffer;
// Scan data
bool dotSignFound = false;
while (startPtr < endPtr)
{
if (*startPtr == chPeriod)
{
if (!dotSignFound)
{
dotSignFound = true;
fractDigits = endPtr - startPtr - 1;
startPtr++;
continue;
}
else // '.' is allowed only once
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_2ManyDecPoint, manager);
}
// If not valid decimal digit, then an error
if ((*startPtr < chDigit_0) || (*startPtr > chDigit_9))
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
// copy over
*retPtr++ = *startPtr++;
totalDigits++;
}
/***
E2-44 totalDigits
... by restricting it to numbers that are expressible as i x 10^-n
where i and n are integers such that |i| < 10^totalDigits and 0 <= n <= totalDigits.
normalization: remove all trailing zero after the '.'
and adjust the scaleValue as well.
***/
while ((fractDigits > 0) && (*(retPtr-1) == chDigit_0))
{
retPtr--;
fractDigits--;
totalDigits--;
}
// 0.0 got past the check for zero because of the decimal point, so we need to double check it here
if(totalDigits==0)
sign = 0;
*retPtr = chNull; //terminated
return;
}
void XMLBigDecimal::parseDecimal(const XMLCh* const toParse
, MemoryManager* const manager)
{
// Strip leading white space, if any.
const XMLCh* startPtr = toParse;
while (XMLChar1_0::isWhitespace(*startPtr))
startPtr++;
// If we hit the end, then return failure
if (!*startPtr)
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_WSString, manager);
// Strip tailing white space, if any.
const XMLCh* endPtr = toParse + XMLString::stringLen(toParse);
while (XMLChar1_0::isWhitespace(*(endPtr - 1)))
endPtr--;
// '+' or '-' is allowed only at the first position
// and is NOT included in the return parsed string
if (*startPtr == chDash)
{
startPtr++;
if (startPtr == endPtr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
}
}
else if (*startPtr == chPlus)
{
startPtr++;
if (startPtr == endPtr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
}
}
// Strip leading zeros
while (*startPtr == chDigit_0)
startPtr++;
// containning zero, only zero, nothing but zero
// it is a zero, indeed
if (startPtr >= endPtr)
{
return;
}
// Scan data
bool dotSignFound = false;
while (startPtr < endPtr)
{
if (*startPtr == chPeriod)
{
if (!dotSignFound)
{
dotSignFound = true;
startPtr++;
continue;
}
else // '.' is allowed only once
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_2ManyDecPoint, manager);
}
// If not valid decimal digit, then an error
if ((*startPtr < chDigit_0) || (*startPtr > chDigit_9))
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, manager);
startPtr++;
}
return;
}
int XMLBigDecimal::compareValues( const XMLBigDecimal* const lValue
, const XMLBigDecimal* const rValue
, MemoryManager* const manager)
{
if ((!lValue) || (!rValue) )
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_null_ptr, manager);
return lValue->toCompare(*rValue);
}
/**
* Returns -1, 0 or 1 as this is less than, equal to, or greater
* than rValue.
*
* This method is based on the fact, that parsebigDecimal() would eliminate
* unnecessary leading/trailing zeros.
**/
int XMLBigDecimal::toCompare(const XMLBigDecimal& other) const
{
/***
* different sign
*/
int lSign = this->getSign();
if (lSign != other.getSign())
return (lSign > other.getSign() ? 1 : -1);
/***
* same sign, zero
*/
if (lSign == 0) // optimization
return 0;
/***
* same sign, non-zero
*/
unsigned int lIntDigit = this->getTotalDigit() - this->getScale();
unsigned int rIntDigit = other.getTotalDigit() - other.getScale();
if (lIntDigit > rIntDigit)
{
return 1 * lSign;
}
else if (lIntDigit < rIntDigit)
{
return -1 * lSign;
}
else // compare fraction
{
int res = XMLString::compareString
( this->getValue()
, other.getValue()
);
if (res > 0)
return 1 * lSign;
else if (res < 0)
return -1 * lSign;
else
return 0;
}
}
/***
* Support for Serialization/De-serialization
***/
IMPL_XSERIALIZABLE_TOCREATE(XMLBigDecimal)
XMLBigDecimal::XMLBigDecimal(MemoryManager* const manager)
: fSign(0)
, fTotalDigits(0)
, fScale(0)
, fRawDataLen(0)
, fRawData(0)
, fIntVal(0)
, fMemoryManager(manager)
{
}
void XMLBigDecimal::serialize(XSerializeEngine& serEng)
{
//REVISIT: may not need to call base since it does nothing
XMLNumber::serialize(serEng);
if (serEng.isStoring())
{
serEng<<fSign;
serEng<<fTotalDigits;
serEng<<fScale;
serEng.writeString(fRawData);
serEng.writeString(fIntVal);
}
else
{
serEng>>fSign;
serEng>>fTotalDigits;
serEng>>fScale;
XMLCh* rawdataStr;
serEng.readString(rawdataStr);
ArrayJanitor<XMLCh> rawdataName(rawdataStr, serEng.getMemoryManager());
fRawDataLen = XMLString::stringLen(rawdataStr);
XMLCh* intvalStr;
serEng.readString(intvalStr);
ArrayJanitor<XMLCh> intvalName(intvalStr, serEng.getMemoryManager());
unsigned int intvalStrLen = XMLString::stringLen(intvalStr);
if (fRawData)
fMemoryManager->deallocate(fRawData);
fRawData = (XMLCh*) fMemoryManager->allocate
(
((fRawDataLen + intvalStrLen) + 4) * sizeof(XMLCh)
);
memcpy(fRawData, rawdataStr, fRawDataLen * sizeof(XMLCh));
fRawData[fRawDataLen] = chNull;
fIntVal = fRawData + fRawDataLen + 1;
memcpy(fIntVal, intvalStr, intvalStrLen * sizeof(XMLCh));
fIntVal[intvalStrLen] = chNull;
}
}
XERCES_CPP_NAMESPACE_END