/* * 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$ */ // --------------------------------------------------------------------------- // Includes // --------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include 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 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 XMLSize_t maxStackSize = 100; XMLSize_t 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])!=0) { 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 janStr(nptr, fMemoryManager); checkBoundary(nptr); } } } 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() { XMLSize_t rawDataLen = XMLString::stringLen(fRawData); fFormattedString = (XMLCh*) fMemoryManager->allocate ( (rawDataLen + 8) * sizeof(XMLCh) );//new XMLCh [ rawDataLen + 8]; for (XMLSize_t 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); } } 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++)!=0 && 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 { XMLSize_t strLen = XMLString::stringLen(rawData); XMLCh* manStr = (XMLCh*) memMgr->allocate((strLen + 1) * sizeof(XMLCh)); ArrayJanitor janManStr(manStr, memMgr); XMLCh* manBuf = (XMLCh*) memMgr->allocate((strLen + 1) * sizeof(XMLCh)); ArrayJanitor janManBuf(manBuf, memMgr); XMLCh* expStr = (XMLCh*) memMgr->allocate((strLen + 1) * sizeof(XMLCh)); ArrayJanitor janExpStr(expStr, memMgr); XMLCh* retBuffer = (XMLCh*) memMgr->allocate((strLen + 8) * sizeof(XMLCh)); ArrayJanitor 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 { XMLSize_t manLen = ePosition - rawData; XMLString::copyNString(manStr, rawData, manLen); *(manStr + manLen) = chNull; XMLBigDecimal::parseDecimal(manStr, manBuf, sign, totalDigits, fractDigits, memMgr); XMLSize_t 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--; } XMLSize_t 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