blob: 6c19479e1b96cb357feb2b738eee6998c1150361 [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: XMLAbstractDoubleFloat.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/XMLAbstractDoubleFloat.hpp>
#include <xercesc/util/XMLBigDecimal.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/NumberFormatException.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/Janitor.hpp>
#include <locale.h>
#include <float.h>
#include <errno.h>
XERCES_CPP_NAMESPACE_BEGIN
// ---------------------------------------------------------------------------
// local data member
// ---------------------------------------------------------------------------
static const int BUF_LEN = 64;
static XMLCh expSign[] = {chLatin_e, chLatin_E, chNull};
// ---------------------------------------------------------------------------
// ctor/dtor
// ---------------------------------------------------------------------------
XMLAbstractDoubleFloat::XMLAbstractDoubleFloat(MemoryManager* const manager)
: fValue(0)
, fType(Normal)
, fDataConverted(false)
, fDataOverflowed(false)
, fSign(0)
, fRawData(0)
, fFormattedString(0)
, fMemoryManager(manager)
{
}
XMLAbstractDoubleFloat::~XMLAbstractDoubleFloat()
{
fMemoryManager->deallocate(fRawData);//delete [] fRawData;
fMemoryManager->deallocate(fFormattedString);//delete [] fFormattedString;
}
void XMLAbstractDoubleFloat::init(const XMLCh* const strValue)
{
if ((!strValue) || (!*strValue))
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_emptyString, fMemoryManager);
fRawData = XMLString::replicate(strValue, fMemoryManager); // preserve the raw data form
XMLCh* tmpStrValue = XMLString::replicate(strValue, fMemoryManager);
ArrayJanitor<XMLCh> janTmpName(tmpStrValue, fMemoryManager);
XMLString::trim(tmpStrValue);
if (!*tmpStrValue)
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_emptyString, fMemoryManager);
normalizeZero(tmpStrValue);
if (XMLString::equals(tmpStrValue, XMLUni::fgNegINFString) )
{
fType = NegINF;
fSign = -1;
}
else if (XMLString::equals(tmpStrValue, XMLUni::fgPosINFString) )
{
fType = PosINF;
fSign = 1;
}
else if (XMLString::equals(tmpStrValue, XMLUni::fgNaNString) )
{
fType = NaN;
fSign = 1;
}
else
//
// Normal case
//
{
// Use a stack-based buffer when possible. Since all
// valid doubles or floats will only contain ASCII
// digits, a decimal point, or the exponent character,
// they will all be single byte characters, and this will
// work.
static const unsigned int maxStackSize = 100;
unsigned int lenTempStrValue = 0;
// Need to check that the string only contains valid schema characters
// since the call to strtod may allow other values. For example, AIX
// allows "infinity" and "+INF"
XMLCh curChar;
while (curChar = tmpStrValue[lenTempStrValue]) {
if (!((curChar >= chDigit_0 &&
curChar <= chDigit_9) ||
curChar == chPeriod ||
curChar == chDash ||
curChar == chPlus ||
curChar == chLatin_E ||
curChar == chLatin_e)) {
ThrowXMLwithMemMgr(
NumberFormatException,
XMLExcepts::XMLNUM_Inv_chars,
getMemoryManager());
}
lenTempStrValue++;
}
if (lenTempStrValue < maxStackSize)
{
char buffer[maxStackSize + 1];
XMLString::transcode(
tmpStrValue,
buffer,
sizeof(buffer) - 1,
getMemoryManager());
// Do this for safety, because we've
// no guarantee we didn't overrun the
// capacity of the buffer when transcoding
// a bogus value.
buffer[maxStackSize] = '\0';
// If they aren't the same length, then some
// non-ASCII multibyte character was present.
// This will only happen in the case where the
// string has a bogus character, and it's long
// enough to overrun this buffer, but we need
// to check, even if it's unlikely to happen.
if (XMLString::stringLen(buffer) != lenTempStrValue)
{
ThrowXMLwithMemMgr(
NumberFormatException,
XMLExcepts::XMLNUM_Inv_chars,
getMemoryManager());
}
checkBoundary(buffer);
}
else
{
char *nptr = XMLString::transcode(tmpStrValue, getMemoryManager());
const ArrayJanitor<char> janStr(nptr, fMemoryManager);
checkBoundary(nptr);
}
}
}
//
//
//
XMLCh* XMLAbstractDoubleFloat::toString() const
{
// Return data using global operator new
return XMLString::replicate(fRawData);
}
XMLCh* XMLAbstractDoubleFloat::getRawData() const
{
return fRawData;
}
const XMLCh* XMLAbstractDoubleFloat::getFormattedString() const
{
if (!fDataConverted)
{
return fRawData;
}
else
{
if (!fFormattedString)
{
XMLAbstractDoubleFloat *temp = (XMLAbstractDoubleFloat *) this;
temp->formatString();
}
return fFormattedString;
}
}
void XMLAbstractDoubleFloat::formatString()
{
unsigned int rawDataLen = XMLString::stringLen(fRawData);
fFormattedString = (XMLCh*) fMemoryManager->allocate
(
(rawDataLen + 8) * sizeof(XMLCh)
);//new XMLCh [ rawDataLen + 8];
for (unsigned int i = 0; i < rawDataLen + 8; i++)
fFormattedString[i] = chNull;
XMLString::copyString(fFormattedString, fRawData);
fFormattedString[rawDataLen] = chSpace;
fFormattedString[rawDataLen + 1] = chOpenParen;
switch (fType)
{
case NegINF:
XMLString::catString(fFormattedString, XMLUni::fgNegINFString);
break;
case PosINF:
XMLString::catString(fFormattedString, XMLUni::fgPosINFString);
break;
case NaN:
XMLString::catString(fFormattedString, XMLUni::fgNaNString);
break;
default:
// its zero
XMLString::catString(fFormattedString, XMLUni::fgPosZeroString);
break;
}
fFormattedString[XMLString::stringLen(fFormattedString)] = chCloseParen;
}
int XMLAbstractDoubleFloat::getSign() const
{
return fSign;
}
//
//
//
int XMLAbstractDoubleFloat::compareValues(const XMLAbstractDoubleFloat* const lValue
, const XMLAbstractDoubleFloat* const rValue
, MemoryManager* const manager)
{
//
// case#1: lValue normal
// rValue normal
//
if ((!lValue->isSpecialValue()) &&
(!rValue->isSpecialValue()) )
{
if (lValue->fValue == rValue->fValue)
return EQUAL;
else
return (lValue->fValue > rValue->fValue) ? GREATER_THAN : LESS_THAN;
}
//
// case#2: lValue special
// rValue special
//
// Schema Errata E2-40
//
// Positive Infinity is greater than all other non-NAN value.
// Nan equals itself but is not comparable with (neither greater than nor less than)
// any other value in the value space
// Negative Infinity is less than all other non-NAN values.
//
else
if ((lValue->isSpecialValue()) &&
(rValue->isSpecialValue()) )
{
if (lValue->fType == rValue->fType)
return EQUAL;
else
{
if ((lValue->fType == NaN) ||
(rValue->fType == NaN) )
{
return INDETERMINATE;
}
else
{
return (lValue->fType > rValue->fType) ? GREATER_THAN : LESS_THAN;
}
}
}
//
// case#3: lValue special
// rValue normal
//
else
if ((lValue->isSpecialValue()) &&
(!rValue->isSpecialValue()) )
{
return compareSpecial(lValue, manager);
}
//
// case#4: lValue normal
// rValue special
//
else
{
return (-1) * compareSpecial(rValue, manager);
}
return 0;
}
int XMLAbstractDoubleFloat::compareSpecial(const XMLAbstractDoubleFloat* const specialValue
, MemoryManager* const manager)
{
switch (specialValue->fType)
{
case NegINF:
return LESS_THAN;
case PosINF:
return GREATER_THAN;
case NaN:
// NaN is not comparable to any other value
return INDETERMINATE;
default:
XMLCh value1[BUF_LEN+1];
XMLString::binToText(specialValue->fType, value1, 16, 10, manager);
ThrowXMLwithMemMgr1(NumberFormatException
, XMLExcepts::XMLNUM_DBL_FLT_InvalidType
, value1, manager);
//internal error
return 0;
}
}
//
// Assumption: no leading space
//
// 1. The valid char set is "+-.0"
// 2. There shall be only one sign at the first position, if there is one.
// 3. There shall be only one dot '.', if there is one.
//
// Return:
//
// for input comforming to [+]? [0]* '.'? [0]*,
// normalize the input to positive zero string
// for input comforming to '-' [0]* '.'? [0]*,
// normalize the input to negative zero string
// otherwise, do nothing
//
void XMLAbstractDoubleFloat::normalizeZero(XMLCh* const inData)
{
// do a quick check
if (!inData ||
!*inData ||
(XMLString::equals(inData, XMLUni::fgNegZeroString) ) ||
(XMLString::equals(inData, XMLUni::fgPosZeroString) ) )
return;
XMLCh* srcStr = inData;
bool minusSeen = false;
bool dotSeen = false;
// process sign if any
if (*srcStr == chDash)
{
minusSeen = true;
srcStr++;
if (!*srcStr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, getMemoryManager());
}
}
else if (*srcStr == chPlus)
{
srcStr++;
if (!*srcStr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, getMemoryManager());
}
}
else if (*srcStr == chPeriod)
{
dotSeen = true;
srcStr++;
if (!*srcStr)
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, getMemoryManager());
}
}
// scan the string
bool isValidStr = true;
XMLCh theChar;
while ((theChar=*srcStr++) && isValidStr)
{
if ( theChar != chPeriod && theChar != chDigit_0 )
isValidStr = false; // invalid char
else if (theChar == chPeriod) // process dot
dotSeen ? isValidStr = false : dotSeen = true;
}
// need not to worry about the memory problem
// since either fgNegZeroString or fgPosZeroString
// is the canonical form (meaning the shortest in length)
// of their category respectively.
if (isValidStr)
{
if (minusSeen)
XMLString::copyString(inData, XMLUni::fgNegZeroString);
else
XMLString::copyString(inData, XMLUni::fgPosZeroString);
}
else
{
// we got to set the sign first, since this string may
// eventaully turn out to be beyond the minimum representable
// number and reduced to -0 or +0.
fSign = minusSeen ? -1 : 1;
}
return;
}
void XMLAbstractDoubleFloat::normalizeDecimalPoint(char* const toNormal)
{
// find the locale-specific decimal point delimiter
lconv* lc = localeconv();
char delimiter = *lc->decimal_point;
// replace '.' with the locale-specific decimal point delimiter
if ( delimiter != '.' )
{
char* period = strchr( toNormal, '.' );
if ( period )
{
*period = delimiter;
}
}
}
void
XMLAbstractDoubleFloat::convert(char* const strValue)
{
normalizeDecimalPoint(strValue);
char *endptr = 0;
errno = 0;
fValue = strtod(strValue, &endptr);
// check if all chars are valid char. If they are, endptr will
// pointer to the null terminator.
if (*endptr != '\0')
{
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, getMemoryManager());
}
// check if overflow/underflow occurs
if (errno == ERANGE)
{
fDataConverted = true;
if ( fValue < 0 )
{
if (fValue > (-1)*DBL_MIN)
{
fValue = 0;
}
else
{
fType = NegINF;
fDataOverflowed = true;
}
}
else if ( fValue > 0)
{
if (fValue < DBL_MIN )
{
fValue = 0;
}
else
{
fType = PosINF;
fDataOverflowed = true;
}
}
}
}
/***
* E2-40
*
* 3.2.4 float
* 3.2.5 double
*
* . the exponent must be indicated by "E".
* if the exponent is zero, it must be indicated by "E0".
*
* . For the mantissa,
* the preceding optional "+" sign is prohibited and
* the decimal point is required.
*
* . For the exponent,
* the preceding optional "+" sign is prohibited.
* Leading zeroes are prohibited.
*
* . Leading and trailing zeroes are prohibited subject to the following:
* number representations must be normalized such that
* . there is a single digit, which is non-zero, to the left of the decimal point and
* . at least a single digit to the right of the decimal point.
* . unless the value being represented is zero.
* The canonical representation for zero is 0.0E0
*
***/
XMLCh* XMLAbstractDoubleFloat::getCanonicalRepresentation(const XMLCh* const rawData
, MemoryManager* const memMgr)
{
// before anything, let's look for special tokens since that
// breaks the calls to parse below.
if(XMLString::equals(rawData, XMLUni::fgNegINFString) ||
XMLString::equals(rawData, XMLUni::fgPosINFString) ||
XMLString::equals(rawData, XMLUni::fgNaNString) )
{
return XMLString::replicate(rawData, memMgr);
}
try
{
int strLen = XMLString::stringLen(rawData);
XMLCh* manStr = (XMLCh*) memMgr->allocate((strLen + 1) * sizeof(XMLCh));
ArrayJanitor<XMLCh> janManStr(manStr, memMgr);
XMLCh* manBuf = (XMLCh*) memMgr->allocate((strLen + 1) * sizeof(XMLCh));
ArrayJanitor<XMLCh> janManBuf(manBuf, memMgr);
XMLCh* expStr = (XMLCh*) memMgr->allocate((strLen + 1) * sizeof(XMLCh));
ArrayJanitor<XMLCh> janExpStr(expStr, memMgr);
XMLCh* retBuffer = (XMLCh*) memMgr->allocate((strLen + 8) * sizeof(XMLCh));
ArrayJanitor<XMLCh> janRetBuffer(retBuffer, memMgr);
retBuffer[0] = 0;
int sign, totalDigits, fractDigits, expValue = 0;
const XMLCh* ePosition = XMLString::findAny(rawData, expSign);
/***
* parse mantissa and exp separately
***/
if (!ePosition)
{
XMLBigDecimal::parseDecimal(rawData, manBuf, sign, totalDigits, fractDigits, memMgr);
expValue = 0;
}
else
{
int manLen = ePosition - rawData;
XMLString::copyNString(manStr, rawData, manLen);
*(manStr + manLen) = chNull;
XMLBigDecimal::parseDecimal(manStr, manBuf, sign, totalDigits, fractDigits, memMgr);
int expLen = strLen - manLen - 1;
ePosition++;
XMLString::copyNString(expStr, ePosition, expLen);
*(expStr + expLen) = chNull;
expValue = XMLString::parseInt(expStr);
}
if ( (sign == 0) || (totalDigits == 0) )
{
retBuffer[0] = chDigit_0;
retBuffer[1] = chPeriod;
retBuffer[2] = chDigit_0;
retBuffer[3] = chLatin_E;
retBuffer[4] = chDigit_0;
retBuffer[5] = chNull;
}
else
{
XMLCh* retPtr = retBuffer;
if (sign == -1)
{
*retPtr++ = chDash;
}
*retPtr++ = manBuf[0];
*retPtr++ = chPeriod;
//XMLBigDecimal::parseDecimal() will eliminate trailing zeros
// iff there is a decimal points
// eg. 56.7800e0 -> manBuf = 5678, totalDigits = 4, fractDigits = 2
// we print it as 5.678e1
//
// but it wont remove trailing zeros if there is no decimal point.
// eg. 567800e0 -> manBuf = 567800, totalDigits = 6, fractDigits = 0
// we print it 5.67800e5
//
// for the latter, we need to print it as 5.678e5 instead
//
XMLCh* endPtr = manBuf + totalDigits;
if (fractDigits == 0)
{
while(*(endPtr - 1) == chDigit_0)
endPtr--;
}
int remainLen = endPtr - &(manBuf[1]);
if (remainLen)
{
XMLString::copyNString(retPtr, &(manBuf[1]), remainLen);
retPtr += remainLen;
}
else
{
*retPtr++ = chDigit_0;
}
/***
*
* . adjust expValue
*
* new_fractDigits = totalDigits - 1
* new_expValue = old_expValue + (new_fractDigits - fractDigits)
*
***/
expValue += (totalDigits - 1) - fractDigits ;
XMLString::binToText(expValue, expStr, strLen, 10, memMgr);
*retPtr++ = chLatin_E;
*retPtr = chNull;
XMLString::catString(&(retBuffer[0]), expStr);
}
janRetBuffer.release();
return retBuffer;
}
catch (const NumberFormatException&)
{
return 0;
}
}
/***
* Support for Serialization/De-serialization
***/
IMPL_XSERIALIZABLE_NOCREATE(XMLAbstractDoubleFloat)
void XMLAbstractDoubleFloat::serialize(XSerializeEngine& serEng)
{
//REVISIT: may not need to call base since it does nothing
XMLNumber::serialize(serEng);
if (serEng.isStoring())
{
serEng << fValue;
serEng << fType;
serEng << fDataConverted;
serEng << fDataOverflowed;
serEng << fSign;
serEng.writeString(fRawData);
// Do not serialize fFormattedString
}
else
{
serEng >> fValue;
int type = 0;
serEng >> type;
fType = (LiteralType) type;
serEng >> fDataConverted;
serEng >> fDataOverflowed;
serEng >> fSign;
serEng.readString(fRawData);
// Set it to 0 force it to re-format if needed
fFormattedString = 0;
}
}
XERCES_CPP_NAMESPACE_END