X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/73c04bcfe1096173b00431f0cdc742894b15eef0..2ca993e82fb37b597a3c73ecd1586a139a6579c5:/icuSources/i18n/fmtable.cpp diff --git a/icuSources/i18n/fmtable.cpp b/icuSources/i18n/fmtable.cpp index b767b332..9fdef20c 100644 --- a/icuSources/i18n/fmtable.cpp +++ b/icuSources/i18n/fmtable.cpp @@ -1,7 +1,7 @@ /* ******************************************************************************* -* Copyright (C) 1997-2006, International Business Machines Corporation and * -* others. All Rights Reserved. * +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. ******************************************************************************* * * File FMTABLE.CPP @@ -17,11 +17,18 @@ #if !UCONFIG_NO_FORMATTING +#include #include "unicode/fmtable.h" #include "unicode/ustring.h" #include "unicode/measure.h" #include "unicode/curramt.h" +#include "unicode/uformattable.h" +#include "charstr.h" #include "cmemory.h" +#include "cstring.h" +#include "decNumber.h" +#include "digitlst.h" +#include "fmtableimp.h" // ***************************************************************************** // class Formattable @@ -31,11 +38,11 @@ U_NAMESPACE_BEGIN UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable) + //-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. // NOTE: As of 3.0, there are limitations to the UObject API. It does -// not (yet) support cloning, operator=, nor operator==. RTTI is also -// restricted in that subtype testing is not (yet) implemented. To +// not (yet) support cloning, operator=, nor operator==. To // work around this, I implement some simple inlines here. Later // these can be modified or removed. [alan] @@ -56,9 +63,7 @@ static inline UObject* objectClone(const UObject* a) { // Return TRUE if *a is an instance of Measure. static inline UBool instanceOfMeasure(const UObject* a) { - // LATER: return a->instanceof(Measure::getStaticClassID()); - return a->getDynamicClassID() == - CurrencyAmount::getStaticClassID(); + return dynamic_cast(a) != NULL; } /** @@ -68,9 +73,12 @@ static inline UBool instanceOfMeasure(const UObject* a) { * @param count the original array count * @return the new Formattable array. */ -static inline Formattable* createArrayCopy(const Formattable* array, int32_t count) { +static Formattable* createArrayCopy(const Formattable* array, int32_t count) { Formattable *result = new Formattable[count]; - for (int32_t i=0; istackDecimalNum)) { + delete fDecimalNum; + } else { + fDecimalNum->~DigitList(); // destruct, don't deallocate + } + fDecimalNum = NULL; } Formattable * @@ -343,7 +401,7 @@ Formattable::getLong(UErrorCode& status) const switch (fType) { case Formattable::kLong: return (int32_t)fValue.fInt64; - case Formattable::kInt64: + case Formattable::kInt64: if (fValue.fInt64 > INT32_MAX) { status = U_INVALID_FORMAT_ERROR; return INT32_MAX; @@ -364,18 +422,29 @@ Formattable::getLong(UErrorCode& status) const return (int32_t)fValue.fDouble; // loses fraction } case Formattable::kObject: + if (fValue.fObject == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } // TODO Later replace this with instanceof call if (instanceOfMeasure(fValue.fObject)) { return ((const Measure*) fValue.fObject)-> getNumber().getLong(status); } - default: + U_FALLTHROUGH; + default: status = U_INVALID_FORMAT_ERROR; return 0; } } // ------------------------------------- +// Maximum int that can be represented exactly in a double. (53 bits) +// Larger ints may be rounded to a near-by value as not all are representable. +// TODO: move this constant elsewhere, possibly configure it for different +// floating point formats, if any non-standard ones are still in use. +static const int64_t U_DOUBLE_MAX_EXACT_INT = 9007199254740992LL; + int64_t Formattable::getInt64(UErrorCode& status) const { @@ -388,22 +457,34 @@ Formattable::getInt64(UErrorCode& status) const case Formattable::kInt64: return fValue.fInt64; case Formattable::kDouble: - if (fValue.fDouble > U_INT64_MAX) { + if (fValue.fDouble > (double)U_INT64_MAX) { status = U_INVALID_FORMAT_ERROR; return U_INT64_MAX; - } else if (fValue.fDouble < U_INT64_MIN) { + } else if (fValue.fDouble < (double)U_INT64_MIN) { status = U_INVALID_FORMAT_ERROR; return U_INT64_MIN; + } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalNum != NULL) { + int64_t val = fDecimalNum->getInt64(); + if (val != 0) { + return val; + } else { + status = U_INVALID_FORMAT_ERROR; + return fValue.fDouble > 0 ? U_INT64_MAX : U_INT64_MIN; + } } else { return (int64_t)fValue.fDouble; - } + } case Formattable::kObject: - // TODO Later replace this with instanceof call + if (fValue.fObject == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } if (instanceOfMeasure(fValue.fObject)) { return ((const Measure*) fValue.fObject)-> getNumber().getInt64(status); } - default: + U_FALLTHROUGH; + default: status = U_INVALID_FORMAT_ERROR; return 0; } @@ -424,12 +505,17 @@ Formattable::getDouble(UErrorCode& status) const case Formattable::kDouble: return fValue.fDouble; case Formattable::kObject: + if (fValue.fObject == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } // TODO Later replace this with instanceof call if (instanceOfMeasure(fValue.fObject)) { return ((const Measure*) fValue.fObject)-> getNumber().getDouble(status); } - default: + U_FALLTHROUGH; + default: status = U_INVALID_FORMAT_ERROR; return 0; } @@ -545,7 +631,11 @@ Formattable::getString(UnicodeString& result, UErrorCode& status) const setError(status, U_INVALID_FORMAT_ERROR); result.setToBogus(); } else { - result = *fValue.fString; + if (fValue.fString == NULL) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + } else { + result = *fValue.fString; + } } return result; } @@ -558,6 +648,10 @@ Formattable::getString(UErrorCode& status) const setError(status, U_INVALID_FORMAT_ERROR); return *getBogus(); } + if (fValue.fString == NULL) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + return *getBogus(); + } return *fValue.fString; } @@ -569,6 +663,10 @@ Formattable::getString(UErrorCode& status) setError(status, U_INVALID_FORMAT_ERROR); return *getBogus(); } + if (fValue.fString == NULL) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + return *getBogus(); + } return *fValue.fString; } @@ -594,18 +692,144 @@ Formattable::getBogus() const return (UnicodeString*)&fBogus; /* cast away const :-( */ } + +// -------------------------------------- +StringPiece Formattable::getDecimalNumber(UErrorCode &status) { + if (U_FAILURE(status)) { + return ""; + } + if (fDecimalStr != NULL) { + return fDecimalStr->toStringPiece(); + } + + CharString *decimalStr = internalGetCharString(status); + if(decimalStr == NULL) { + return ""; // getDecimalNumber returns "" for error cases + } else { + return decimalStr->toStringPiece(); + } +} + +CharString *Formattable::internalGetCharString(UErrorCode &status) { + if(fDecimalStr == NULL) { + if (fDecimalNum == NULL) { + // No decimal number for the formattable yet. Which means the value was + // set directly by the user as an int, int64 or double. If the value came + // from parsing, or from the user setting a decimal number, fDecimalNum + // would already be set. + // + fDecimalNum = new DigitList; // TODO: use internal digit list + if (fDecimalNum == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + + switch (fType) { + case kDouble: + fDecimalNum->set(this->getDouble()); + break; + case kLong: + fDecimalNum->set(this->getLong()); + break; + case kInt64: + fDecimalNum->set(this->getInt64()); + break; + default: + // The formattable's value is not a numeric type. + status = U_INVALID_STATE_ERROR; + return NULL; + } + } + + fDecimalStr = new CharString; + if (fDecimalStr == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + fDecimalNum->getDecimal(*fDecimalStr, status); + } + return fDecimalStr; +} + + +DigitList * +Formattable::getInternalDigitList() { + FmtStackData *stackData = (FmtStackData*)fStackData; + if(fDecimalNum != &(stackData->stackDecimalNum)) { + delete fDecimalNum; + fDecimalNum = new (&(stackData->stackDecimalNum), kOnStack) DigitList(); + } else { + fDecimalNum->clear(); + } + return fDecimalNum; +} + +// --------------------------------------- +void +Formattable::adoptDigitList(DigitList *dl) { + if(fDecimalNum==dl) { + fDecimalNum = NULL; // don't delete + } + dispose(); + + fDecimalNum = dl; + + if(dl==NULL) { // allow adoptDigitList(NULL) to clear + return; + } + + // Set the value into the Union of simple type values. + // Cannot use the set() functions because they would delete the fDecimalNum value, + + if (fDecimalNum->fitsIntoLong(FALSE)) { + fType = kLong; + fValue.fInt64 = fDecimalNum->getLong(); + } else if (fDecimalNum->fitsIntoInt64(FALSE)) { + fType = kInt64; + fValue.fInt64 = fDecimalNum->getInt64(); + } else { + fType = kDouble; + fValue.fDouble = fDecimalNum->getDouble(); + } +} + + +// --------------------------------------- +void +Formattable::setDecimalNumber(const StringPiece &numberString, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + dispose(); + + // Copy the input string and nul-terminate it. + // The decNumber library requires nul-terminated input. StringPiece input + // is not guaranteed nul-terminated. Too bad. + // CharString automatically adds the nul. + DigitList *dnum = new DigitList(); // TODO: use getInternalDigitList + if (dnum == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + dnum->set(CharString(numberString, status).toStringPiece(), status); + if (U_FAILURE(status)) { + delete dnum; + return; // String didn't contain a decimal number. + } + adoptDigitList(dnum); + + // Note that we do not hang on to the caller's input string. + // If we are asked for the string, we will regenerate one from fDecimalNum. +} + #if 0 //---------------------------------------------------- // console I/O //---------------------------------------------------- #ifdef _DEBUG -#if U_IOSTREAM_SOURCE >= 199711 #include using namespace std; -#elif U_IOSTREAM_SOURCE >= 198506 -#include -#endif #include "unicode/datefmt.h" #include "unistrm.h" @@ -674,6 +898,152 @@ FormattableStreamer::streamOut(ostream& stream, const Formattable& obj) U_NAMESPACE_END +/* ---- UFormattable implementation ---- */ + +U_NAMESPACE_USE + +U_DRAFT UFormattable* U_EXPORT2 +ufmt_open(UErrorCode *status) { + if( U_FAILURE(*status) ) { + return NULL; + } + UFormattable *fmt = (new Formattable())->toUFormattable(); + + if( fmt == NULL ) { + *status = U_MEMORY_ALLOCATION_ERROR; + } + return fmt; +} + +U_DRAFT void U_EXPORT2 +ufmt_close(UFormattable *fmt) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + delete obj; +} + +U_INTERNAL UFormattableType U_EXPORT2 +ufmt_getType(const UFormattable *fmt, UErrorCode *status) { + if(U_FAILURE(*status)) { + return (UFormattableType)UFMT_COUNT; + } + const Formattable *obj = Formattable::fromUFormattable(fmt); + return (UFormattableType)obj->getType(); +} + + +U_INTERNAL UBool U_EXPORT2 +ufmt_isNumeric(const UFormattable *fmt) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + return obj->isNumeric(); +} + +U_DRAFT UDate U_EXPORT2 +ufmt_getDate(const UFormattable *fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getDate(*status); +} + +U_DRAFT double U_EXPORT2 +ufmt_getDouble(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getDouble(*status); +} + +U_DRAFT int32_t U_EXPORT2 +ufmt_getLong(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getLong(*status); +} + + +U_DRAFT const void *U_EXPORT2 +ufmt_getObject(const UFormattable *fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + const void *ret = obj->getObject(); + if( ret==NULL && + (obj->getType() != Formattable::kObject) && + U_SUCCESS( *status )) { + *status = U_INVALID_FORMAT_ERROR; + } + return ret; +} + +U_DRAFT const UChar* U_EXPORT2 +ufmt_getUChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + // avoid bogosity by checking the type first. + if( obj->getType() != Formattable::kString ) { + if( U_SUCCESS(*status) ){ + *status = U_INVALID_FORMAT_ERROR; + } + return NULL; + } + + // This should return a valid string + UnicodeString &str = obj->getString(*status); + if( U_SUCCESS(*status) && len != NULL ) { + *len = str.length(); + } + return str.getTerminatedBuffer(); +} + +U_DRAFT int32_t U_EXPORT2 +ufmt_getArrayLength(const UFormattable* fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + int32_t count; + (void)obj->getArray(count, *status); + return count; +} + +U_DRAFT UFormattable * U_EXPORT2 +ufmt_getArrayItemByIndex(UFormattable* fmt, int32_t n, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + int32_t count; + (void)obj->getArray(count, *status); + if(U_FAILURE(*status)) { + return NULL; + } else if(n<0 || n>=count) { + setError(*status, U_INDEX_OUTOFBOUNDS_ERROR); + return NULL; + } else { + return (*obj)[n].toUFormattable(); // returns non-const Formattable + } +} + +U_DRAFT const char * U_EXPORT2 +ufmt_getDecNumChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { + if(U_FAILURE(*status)) { + return ""; + } + Formattable *obj = Formattable::fromUFormattable(fmt); + CharString *charString = obj->internalGetCharString(*status); + if(U_FAILURE(*status)) { + return ""; + } + if(charString == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return ""; + } else { + if(len!=NULL) { + *len = charString->length(); + } + return charString->data(); + } +} + +U_DRAFT int64_t U_EXPORT2 +ufmt_getInt64(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + return obj->getInt64(*status); +} + #endif /* #if !UCONFIG_NO_FORMATTING */ //eof