/*
*******************************************************************************
-* 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
#if !UCONFIG_NO_FORMATTING
+#include <math.h>
#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
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]
// 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<const Measure*>(a) != NULL;
}
/**
* @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; i<count; ++i) result[i] = array[i]; // Don't memcpy!
+ if (result != NULL) {
+ for (int32_t i=0; i<count; ++i)
+ result[i] = array[i]; // Don't memcpy!
+ }
return result;
}
/**
* Set 'ec' to 'err' only if 'ec' is not already set to a failing UErrorCode.
*/
-static inline void setError(UErrorCode& ec, UErrorCode err) {
+static void setError(UErrorCode& ec, UErrorCode err) {
if (U_SUCCESS(ec)) {
ec = err;
}
}
+//
+// Common initialization code, shared by constructors.
+// Put everything into a known state.
+//
+void Formattable::init() {
+ fValue.fInt64 = 0;
+ fType = kLong;
+ fDecimalStr = NULL;
+ fDecimalNum = NULL;
+ fBogus.setToBogus();
+}
+
// -------------------------------------
// default constructor.
// Creates a formattable object with a long value 0.
-Formattable::Formattable()
- : UObject(), fType(kLong)
-{
- fBogus.setToBogus();
- fValue.fInt64 = 0;
+Formattable::Formattable() {
+ init();
}
// -------------------------------------
// Creates a formattable object with a Date instance.
Formattable::Formattable(UDate date, ISDATE /*isDate*/)
- : UObject(), fType(kDate)
{
- fBogus.setToBogus();
+ init();
+ fType = kDate;
fValue.fDate = date;
}
// Creates a formattable object with a double value.
Formattable::Formattable(double value)
- : UObject(), fType(kDouble)
{
- fBogus.setToBogus();
+ init();
+ fType = kDouble;
fValue.fDouble = value;
}
// -------------------------------------
-// Creates a formattable object with a long value.
+// Creates a formattable object with an int32_t value.
Formattable::Formattable(int32_t value)
- : UObject(), fType(kLong)
{
- fBogus.setToBogus();
+ init();
fValue.fInt64 = value;
}
// -------------------------------------
-// Creates a formattable object with a long value.
+// Creates a formattable object with an int64_t value.
Formattable::Formattable(int64_t value)
- : UObject(), fType(kInt64)
{
- fBogus.setToBogus();
+ init();
+ fType = kInt64;
fValue.fInt64 = value;
}
+// -------------------------------------
+// Creates a formattable object with a decimal number value from a string.
+
+Formattable::Formattable(const StringPiece &number, UErrorCode &status) {
+ init();
+ setDecimalNumber(number, status);
+}
+
+
// -------------------------------------
// Creates a formattable object with a UnicodeString instance.
Formattable::Formattable(const UnicodeString& stringToCopy)
- : UObject(), fType(kString)
{
- fBogus.setToBogus();
+ init();
+ fType = kString;
fValue.fString = new UnicodeString(stringToCopy);
}
// (adopting symantics)
Formattable::Formattable(UnicodeString* stringToAdopt)
- : UObject(), fType(kString)
{
- fBogus.setToBogus();
+ init();
+ fType = kString;
fValue.fString = stringToAdopt;
}
Formattable::Formattable(UObject* objectToAdopt)
- : UObject(), fType(kObject)
{
- fBogus.setToBogus();
+ init();
+ fType = kObject;
fValue.fObject = objectToAdopt;
}
Formattable::Formattable(const Formattable* arrayToCopy, int32_t count)
: UObject(), fType(kArray)
{
- fBogus.setToBogus();
+ init();
+ fType = kArray;
fValue.fArrayAndCount.fArray = createArrayCopy(arrayToCopy, count);
fValue.fArrayAndCount.fCount = count;
}
// -------------------------------------
// copy constructor
+
Formattable::Formattable(const Formattable &source)
- : UObject(source), fType(kLong)
+ : UObject(*this)
{
- fBogus.setToBogus();
+ init();
*this = source;
}
fValue.fObject = objectClone(source.fValue.fObject);
break;
}
+
+ UErrorCode status = U_ZERO_ERROR;
+ if (source.fDecimalNum != NULL) {
+ fDecimalNum = new DigitList(*source.fDecimalNum); // TODO: use internal digit list
+ }
+ if (source.fDecimalStr != NULL) {
+ fDecimalStr = new CharString(*source.fDecimalStr, status);
+ if (U_FAILURE(status)) {
+ delete fDecimalStr;
+ fDecimalStr = NULL;
+ }
+ }
}
return *this;
}
}
break;
case kObject:
- equal = objectEquals(fValue.fObject, that.fValue.fObject);
+ if (fValue.fObject == NULL || that.fValue.fObject == NULL) {
+ equal = FALSE;
+ } else {
+ equal = objectEquals(fValue.fObject, that.fValue.fObject);
+ }
break;
}
+ // TODO: compare digit lists if numeric.
return equal;
}
default:
break;
}
+
+ fType = kLong;
+ fValue.fInt64 = 0;
+
+ delete fDecimalStr;
+ fDecimalStr = NULL;
+
+ FmtStackData *stackData = (FmtStackData*)fStackData;
+ if(fDecimalNum != &(stackData->stackDecimalNum)) {
+ delete fDecimalNum;
+ } else {
+ fDecimalNum->~DigitList(); // destruct, don't deallocate
+ }
+ fDecimalNum = NULL;
}
Formattable *
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;
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
{
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;
}
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;
}
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;
}
setError(status, U_INVALID_FORMAT_ERROR);
return *getBogus();
}
+ if (fValue.fString == NULL) {
+ setError(status, U_MEMORY_ALLOCATION_ERROR);
+ return *getBogus();
+ }
return *fValue.fString;
}
setError(status, U_INVALID_FORMAT_ERROR);
return *getBogus();
}
+ if (fValue.fString == NULL) {
+ setError(status, U_MEMORY_ALLOCATION_ERROR);
+ return *getBogus();
+ }
return *fValue.fString;
}
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 <iostream>
using namespace std;
-#elif U_IOSTREAM_SOURCE >= 198506
-#include <iostream.h>
-#endif
#include "unicode/datefmt.h"
#include "unistrm.h"
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