| /* |
| * 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 |