diff --git a/icu4c/source/common/unicode/urename.h b/icu4c/source/common/unicode/urename.h index 071efe4d72a..34b9689dbe0 100644 --- a/icu4c/source/common/unicode/urename.h +++ b/icu4c/source/common/unicode/urename.h @@ -23,7 +23,7 @@ /* Uncomment the following line to disable renaming on platforms that do not use Autoconf. */ -/* #define U_DISABLE_RENAMING 1 */ +#define U_DISABLE_RENAMING 1 #if !U_DISABLE_RENAMING diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index 116f2a4beb7..fd91e9b4f2e 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -72,7 +72,7 @@ cpdtrans.o rbt.o rbt_data.o rbt_pars.o rbt_rule.o rbt_set.o \ nultrans.o remtrans.o titletrn.o tolowtrn.o toupptrn.o anytrans.o \ name2uni.o uni2name.o nortrans.o quant.o transreg.o \ regexcmp.o rematch.o repattrn.o regexst.o uregex.o ulocdata.o \ -measfmt.o currfmt.o +measfmt.o currfmt.o curramt.o currunit.o measure.o STATIC_OBJECTS = $(OBJECTS:.o=.$(STATIC_O)) diff --git a/icu4c/source/i18n/curramt.cpp b/icu4c/source/i18n/curramt.cpp new file mode 100644 index 00000000000..f4e1abb0572 --- /dev/null +++ b/icu4c/source/i18n/curramt.cpp @@ -0,0 +1,50 @@ +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/curramt.h" +#include "unicode/currunit.h" + +U_NAMESPACE_BEGIN + +CurrencyAmount::CurrencyAmount(const Formattable& amount, const UChar* isoCode, + UErrorCode& ec) : + Measure(amount, new CurrencyUnit(isoCode, ec), ec) { +} + +CurrencyAmount::CurrencyAmount(double amount, const UChar* isoCode, + UErrorCode& ec) : + Measure(Formattable(amount), new CurrencyUnit(isoCode, ec), ec) { +} + +CurrencyAmount::CurrencyAmount(const CurrencyAmount& other) : + Measure(other) { +} + +CurrencyAmount& CurrencyAmount::operator=(const CurrencyAmount& other) { + Measure::operator=(other); + return *this; +} + +UObject* CurrencyAmount::clone() const { + return new CurrencyAmount(*this); +} + +CurrencyAmount::~CurrencyAmount() { +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyAmount) + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/icu4c/source/i18n/currunit.cpp b/icu4c/source/i18n/currunit.cpp new file mode 100644 index 00000000000..3b36fb47ff1 --- /dev/null +++ b/icu4c/source/i18n/currunit.cpp @@ -0,0 +1,59 @@ +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/currunit.h" +#include "unicode/ustring.h" + +U_NAMESPACE_BEGIN + +CurrencyUnit::CurrencyUnit(const UChar* _isoCode, UErrorCode& ec) { + *isoCode = 0; + if (U_SUCCESS(ec)) { + if (_isoCode && u_strlen(_isoCode)==3) { + u_strcpy(isoCode, _isoCode); + } else { + ec = U_ILLEGAL_ARGUMENT_ERROR; + } + } +} + +CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) { + *this = other; +} + +CurrencyUnit& CurrencyUnit::operator=(const CurrencyUnit& other) { + if (this != &other) { + u_strcpy(isoCode, other.isoCode); + } + return *this; +} + +UObject* CurrencyUnit::clone() const { + return new CurrencyUnit(*this); +} + +CurrencyUnit::~CurrencyUnit() { +} + +UBool CurrencyUnit::operator==(const UObject& other) const { + const CurrencyUnit& c = (const CurrencyUnit&) other; + return other.getDynamicClassID() == CurrencyUnit::getStaticClassID() && + u_strcmp(isoCode, c.isoCode) == 0; +} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyUnit) + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 9bf281951e4..2bd696b4661 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -48,6 +48,7 @@ #include "unicode/dcfmtsym.h" #include "unicode/resbund.h" #include "unicode/uchar.h" +#include "unicode/curramt.h" #include "ucurrimp.h" #include "uprops.h" #include "digitlst.h" @@ -1253,59 +1254,58 @@ void DecimalFormat::parse(const UnicodeString& text, return; } - if (parseCurrency) { - result.setCurrency(curbuf); - } - // Handle infinity if (status[fgStatusInfinite]) { double inf = uprv_getInfinity(); result.setDouble(digits.fIsPositive ? inf : -inf); - return; } - // Do as much of the multiplier conversion as possible without - // losing accuracy. - int32_t mult = fMultiplier; // Don't modify this.multiplier - while (mult % 10 == 0) { - mult /= 10; - --digits.fDecimalAt; - } - - // Handle integral values. We want to return the most - // parsimonious type that will accommodate all of the result's - // precision. We therefore only return a long if the result fits - // entirely within a long (taking into account the multiplier) -- - // otherwise we fall through and return a double. When more - // numeric types are supported by Formattable (e.g., 64-bit - // integers, bignums) we will extend this logic to include them. - if (digits.fitsIntoLong(isParseIntegerOnly())) { - int32_t n = digits.getLong(); - if (n % mult == 0) { - result.setLong(n / mult); - return; - } - else { // else handle the remainder - result.setDouble(((double)n) / mult); - return; - } - } - else if (digits.fitsIntoInt64(isParseIntegerOnly())) { - int64_t n = digits.getInt64(); - if (n % mult == 0) { - result.setInt64(n / mult); - return; - } - else { // else handle the remainder - result.setDouble(((double)n) / mult); - return; - } - } else { - // Handle non-integral or very large values - // Dividing by one is okay and not that costly. - result.setDouble(digits.getDouble() / mult); - return; + // Do as much of the multiplier conversion as possible without + // losing accuracy. + int32_t mult = fMultiplier; // Don't modify this.multiplier + while (mult % 10 == 0) { + mult /= 10; + --digits.fDecimalAt; + } + + // Handle integral values. We want to return the most + // parsimonious type that will accommodate all of the result's + // precision. We therefore only return a long if the result fits + // entirely within a long (taking into account the multiplier) -- + // otherwise we fall through and return a double. When more + // numeric types are supported by Formattable (e.g., 64-bit + // integers, bignums) we will extend this logic to include them. + if (digits.fitsIntoLong(isParseIntegerOnly())) { + int32_t n = digits.getLong(); + if (n % mult == 0) { + result.setLong(n / mult); + } + else { // else handle the remainder + result.setDouble(((double)n) / mult); + } + } + else if (digits.fitsIntoInt64(isParseIntegerOnly())) { + int64_t n = digits.getInt64(); + if (n % mult == 0) { + result.setInt64(n / mult); + } + else { // else handle the remainder + result.setDouble(((double)n) / mult); + } + } + else { + // Handle non-integral or very large values + // Dividing by one is okay and not that costly. + result.setDouble(digits.getDouble() / mult); + } + } + + if (parseCurrency) { + UErrorCode ec = U_ZERO_ERROR; + Formattable n(result); + result.adoptObject(new CurrencyAmount(n, curbuf, ec)); + U_ASSERT(U_SUCCESS(ec)); // should always succeed } } diff --git a/icu4c/source/i18n/fmtable.cpp b/icu4c/source/i18n/fmtable.cpp index c5201d29ad7..111edb0eba0 100644 --- a/icu4c/source/i18n/fmtable.cpp +++ b/icu4c/source/i18n/fmtable.cpp @@ -19,6 +19,8 @@ #include "unicode/fmtable.h" #include "unicode/ustring.h" +#include "unicode/measure.h" +#include "unicode/curramt.h" #include "cmemory.h" // ***************************************************************************** @@ -29,6 +31,38 @@ 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 +// work around this, I implement some simple inlines here. Later +// these can be modified or removed. [alan] + +// NOTE: These inlines assume that all fObjects are in fact instances +// of the Measure class, which is true as of 3.0. [alan] + +// Return TRUE if *a == *b. +inline UBool objectEquals(const UObject* a, const UObject* b) { + // LATER: return *a == *b; + return *((const Measure*) a) == *((const Measure*) b); +} + +// Return a clone of *a. +inline UObject* objectClone(const UObject* a) { + // LATER: return a->clone(); + return ((const Measure*) a)->clone(); +} + +// Return TRUE if *a is an instance of Measure. +inline UBool instanceOfMeasure(const UObject* a) { + // LATER: return a->instanceof(Measure::getStaticClassID()); + return a->getDynamicClassID() == + CurrencyAmount::getStaticClassID(); +} + +//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. + /** * Set 'ec' to 'err' only if 'ec' is not already set to a failing UErrorCode. */ @@ -47,7 +81,6 @@ Formattable::Formattable() { fBogus.setToBogus(); fValue.fInt64 = 0; - fCurrency[0] = 0; } // ------------------------------------- @@ -58,7 +91,6 @@ Formattable::Formattable(UDate date, ISDATE /*isDate*/) { fBogus.setToBogus(); fValue.fDate = date; - fCurrency[0] = 0; } // ------------------------------------- @@ -69,7 +101,6 @@ Formattable::Formattable(double value) { fBogus.setToBogus(); fValue.fDouble = value; - fCurrency[0] = 0; } // ------------------------------------- @@ -80,7 +111,6 @@ Formattable::Formattable(int32_t value) { fBogus.setToBogus(); fValue.fInt64 = value; - fCurrency[0] = 0; } // ------------------------------------- @@ -91,25 +121,6 @@ Formattable::Formattable(int64_t value) { fBogus.setToBogus(); fValue.fInt64 = value; - fCurrency[0] = 0; -} - -Formattable::Formattable(double n, const UChar* currency) : UObject(), fType(kDouble) { - fBogus.setToBogus(); - fValue.fDouble = n; - setCurrency(currency); -} - -Formattable::Formattable(int32_t n, const UChar* currency) : UObject(), fType(kLong) { - fBogus.setToBogus(); - fValue.fInt64 = n; - setCurrency(currency); -} - -Formattable::Formattable(int64_t n, const UChar* currency) : UObject(), fType(kInt64) { - fBogus.setToBogus(); - fValue.fInt64 = n; - setCurrency(currency); } // ------------------------------------- @@ -120,7 +131,6 @@ Formattable::Formattable(const char* stringToCopy) { fBogus.setToBogus(); fValue.fString = new UnicodeString(stringToCopy); - fCurrency[0] = 0; } // ------------------------------------- @@ -131,7 +141,6 @@ Formattable::Formattable(const UnicodeString& stringToCopy) { fBogus.setToBogus(); fValue.fString = new UnicodeString(stringToCopy); - fCurrency[0] = 0; } // ------------------------------------- @@ -143,7 +152,13 @@ Formattable::Formattable(UnicodeString* stringToAdopt) { fBogus.setToBogus(); fValue.fString = stringToAdopt; - fCurrency[0] = 0; +} + +Formattable::Formattable(UObject* objectToAdopt) + : UObject(), fType(kObject) +{ + fBogus.setToBogus(); + fValue.fObject = objectToAdopt; } // ------------------------------------- @@ -154,7 +169,6 @@ Formattable::Formattable(const Formattable* arrayToCopy, int32_t count) fBogus.setToBogus(); fValue.fArrayAndCount.fArray = createArrayCopy(arrayToCopy, count); fValue.fArrayAndCount.fCount = count; - fCurrency[0] = 0; } // ------------------------------------- @@ -205,9 +219,10 @@ Formattable::operator=(const Formattable& source) // Sets the Date value. fValue.fDate = source.fValue.fDate; break; + case kObject: + fValue.fObject = objectClone(source.fValue.fObject); + break; } - - u_strcpy(fCurrency, source.fCurrency); } return *this; } @@ -251,10 +266,9 @@ Formattable::operator==(const Formattable& that) const } } break; - } - - if (equal) { - equal = (u_strcmp(fCurrency, that.fCurrency) == 0); + case kObject: + equal = objectEquals(fValue.fObject, that.fValue.fObject); + break; } return equal; @@ -279,6 +293,9 @@ void Formattable::dispose() case kArray: delete[] fValue.fArrayAndCount.fArray; break; + case kObject: + delete fValue.fObject; + break; default: break; } @@ -297,6 +314,18 @@ Formattable::getType() const return fType; } +UBool +Formattable::isNumeric() const { + switch (fType) { + case kDouble: + case kLong: + case kInt64: + return TRUE; + default: + return FALSE; + } +} + // ------------------------------------- int32_t //Formattable::getLong(UErrorCode* status) const @@ -329,6 +358,12 @@ Formattable::getLong(UErrorCode& status) const } else { return (int32_t)fValue.fDouble; // loses fraction } + case Formattable::kObject: + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getLong(status); + } default: status = U_INVALID_FORMAT_ERROR; return 0; @@ -357,6 +392,12 @@ Formattable::getInt64(UErrorCode& status) const } else { return (int64_t)fValue.fDouble; } + case Formattable::kObject: + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getInt64(status); + } default: status = U_INVALID_FORMAT_ERROR; return 0; @@ -377,15 +418,21 @@ Formattable::getDouble(UErrorCode& status) const return (double)fValue.fInt64; case Formattable::kDouble: return fValue.fDouble; + case Formattable::kObject: + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getDouble(status); + } default: status = U_INVALID_FORMAT_ERROR; return 0; } } -const UChar* -Formattable::getCurrency() const { - return (fCurrency[0] != 0) ? fCurrency : NULL; +const UObject* +Formattable::getObject() const { + return (fType == kObject) ? fValue.fObject : NULL; } // ------------------------------------- @@ -479,12 +526,10 @@ Formattable::adoptArray(Formattable* array, int32_t count) } void -Formattable::setCurrency(const UChar* currency) { - fCurrency[0] = 0; - if (currency != NULL) { - uprv_memcpy(&fCurrency[0], currency, 3*sizeof(fCurrency[0])); - fCurrency[3] = 0; - } +Formattable::adoptObject(UObject* objectToAdopt) { + dispose(); + fType = kObject; + fValue.fObject = objectToAdopt; } // ------------------------------------- diff --git a/icu4c/source/i18n/i18n.vcproj b/icu4c/source/i18n/i18n.vcproj index ba5780f9f9b..2661f9c56ff 100644 --- a/icu4c/source/i18n/i18n.vcproj +++ b/icu4c/source/i18n/i18n.vcproj @@ -457,12 +457,38 @@ Outputs="..\..\include\unicode\$(InputFileName)"/> + + + + + + + + + + + + + + @@ -673,11 +699,36 @@ RelativePath=".\unicode\measfmt.h"> + + + + + + + + + + + + clone(); + } + return *this; +} + +Measure::~Measure() { + delete unit; +} + +UBool Measure::operator==(const UObject& other) const { + const Measure* m = (const Measure*) &other; + return getDynamicClassID() == other.getDynamicClassID() && + number == m->getNumber() && + *unit == m->getUnit(); +} + +//---------------------------------------------------------------------- +// MeasureUnit implementation + +MeasureUnit:: MeasureUnit() {} + +MeasureUnit::~MeasureUnit() {} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING diff --git a/icu4c/source/i18n/numfmt.cpp b/icu4c/source/i18n/numfmt.cpp index 3a59f6306a0..5785b8aee88 100644 --- a/icu4c/source/i18n/numfmt.cpp +++ b/icu4c/source/i18n/numfmt.cpp @@ -33,6 +33,7 @@ #include "unicode/decimfmt.h" #include "unicode/ustring.h" #include "unicode/ucurr.h" +#include "unicode/curramt.h" #include "uhash.h" #include "iculserv.h" #include "ucln_in.h" @@ -222,29 +223,34 @@ NumberFormat::format(const Formattable& obj, if (U_FAILURE(status)) return appendTo; NumberFormat* nonconst = (NumberFormat*) this; + const Formattable* n = &obj; UChar save[4]; UBool setCurr = FALSE; - const UChar *curr = obj.getCurrency(); // most commonly curr==NULL - if (curr != NULL) { - // getCurrency() returns a pointer to internal storage, so we + const UObject* o = obj.getObject(); // most commonly o==NULL + if (o != NULL && + o->getDynamicClassID() == CurrencyAmount::getStaticClassID()) { + // getISOCurrency() returns a pointer to internal storage, so we // copy it to retain it across the call to setCurrency(). + const CurrencyAmount* amt = (const CurrencyAmount*) o; + const UChar* curr = amt->getISOCurrency(); u_strcpy(save, getCurrency()); setCurr = (u_strcmp(curr, save) != 0); if (setCurr) { nonconst->setCurrency(curr, status); } + n = &amt->getNumber(); } - switch (obj.getType()) { + switch (n->getType()) { case Formattable::kDouble: - format(obj.getDouble(), appendTo, pos); + format(n->getDouble(), appendTo, pos); break; case Formattable::kLong: - format(obj.getLong(), appendTo, pos); + format(n->getLong(), appendTo, pos); break; case Formattable::kInt64: - format(obj.getInt64(), appendTo, pos); + format(n->getInt64(), appendTo, pos); break; default: status = U_INVALID_FORMAT_ERROR; @@ -342,7 +348,11 @@ Formattable& NumberFormat::parseCurrency(const UnicodeString& text, UErrorCode ec = U_ZERO_ERROR; getEffectiveCurrency(curr, ec); if (U_SUCCESS(ec)) { - result.setCurrency(curr); + Formattable n(result); + result.adoptObject(new CurrencyAmount(n, curr, ec)); + if (U_FAILURE(ec)) { + pos.setIndex(start); // indicate failure + } } } return result; diff --git a/icu4c/source/i18n/unicode/curramt.h b/icu4c/source/i18n/unicode/curramt.h new file mode 100644 index 00000000000..66feeffd215 --- /dev/null +++ b/icu4c/source/i18n/unicode/curramt.h @@ -0,0 +1,124 @@ +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#ifndef __CURRENCYAMOUNT_H__ +#define __CURRENCYAMOUNT_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measure.h" +#include "unicode/currunit.h" + +U_NAMESPACE_BEGIN + +/** + * A currency together with a numeric amount, such as 200 USD. + * + * @author Alan Liu + * @internal + */ +class U_I18N_API CurrencyAmount: public Measure { + public: + /** + * Construct an object with the given numeric amount and the given + * ISO currency code. + * @param amount a numeric object; amount.isNumeric() must be TRUE + * @param isoCode the 3-letter ISO 4217 currency code; must not be + * NULL and must have length 3 + * @param ec input-output error code. If the amount or the isoCode + * is invalid, then this will be set to a failing value. + * @internal + */ + CurrencyAmount(const Formattable& amount, const UChar* isoCode, + UErrorCode &ec); + + /** + * Construct an object with the given numeric amount and the given + * ISO currency code. + * @param amount the amount of the given currency + * @param isoCode the 3-letter ISO 4217 currency code; must not be + * NULL and must have length 3 + * @param ec input-output error code. If the isoCode is invalid, + * then this will be set to a failing value. + * @internal + */ + CurrencyAmount(double amount, const UChar* isoCode, + UErrorCode &ec); + + /** + * Copy constructor + * @internal + */ + CurrencyAmount(const CurrencyAmount& other); + + /** + * Assignment operator + * @internal + */ + CurrencyAmount& operator=(const CurrencyAmount& other); + + /** + * Return a polymorphic clone of this object. The result will + * have the same class as returned by getDynamicClassID(). + * @internal + */ + virtual UObject* clone() const; + + /** + * Destructor + * @internal + */ + virtual ~CurrencyAmount(); + + /** + * Returns a unique class ID for this object POLYMORPHICALLY. + * This method implements a simple form of RTTI used by ICU. + * @return The class ID for this object. All objects of a given + * class have the same class ID. Objects of other classes have + * different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const; + + /** + * Returns the class ID for this class. This is used to compare to + * the return value of getDynamicClassID(). + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID getStaticClassID(); + + /** + * Return the currency unit object of this object. + * @internal + */ + inline const CurrencyUnit& getCurrency() const; + + /** + * Return the ISO currency code of this object. + * @internal + */ + inline const UChar* getISOCurrency() const; +}; + +inline const CurrencyUnit& CurrencyAmount::getCurrency() const { + return (const CurrencyUnit&) getUnit(); +} + +inline const UChar* CurrencyAmount::getISOCurrency() const { + return getCurrency().getISOCurrency(); +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING +#endif // __CURRENCYAMOUNT_H__ diff --git a/icu4c/source/i18n/unicode/currunit.h b/icu4c/source/i18n/unicode/currunit.h new file mode 100644 index 00000000000..17014b2cdd5 --- /dev/null +++ b/icu4c/source/i18n/unicode/currunit.h @@ -0,0 +1,112 @@ +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#ifndef __CURRENCYUNIT_H__ +#define __CURRENCYUNIT_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/measunit.h" + +U_NAMESPACE_BEGIN + +/** + * A unit of currency, such as USD (U.S. dollars) or JPY (Japanese + * yen). This class is a thin wrapper over a UChar string that + * subclasses MeasureUnit, for use with Measure and MeasureFormat. + * + * @author Alan Liu + * @internal + */ +class U_I18N_API CurrencyUnit: public MeasureUnit { + public: + /** + * Construct an object with the given ISO currency code. + * @param isoCode the 3-letter ISO 4217 currency code; must not be + * NULL and must have length 3 + * @param ec input-output error code. If the isoCode is invalid, + * then this will be set to a failing value. + * @internal + */ + CurrencyUnit(const UChar* isoCode, UErrorCode &ec); + + /** + * Copy constructor + * @internal + */ + CurrencyUnit(const CurrencyUnit& other); + + /** + * Assignment operator + * @internal + */ + CurrencyUnit& operator=(const CurrencyUnit& other); + + /** + * Return a polymorphic clone of this object. The result will + * have the same class as returned by getDynamicClassID(). + * @internal + */ + virtual UObject* clone() const; + + /** + * Destructor + * @internal + */ + virtual ~CurrencyUnit(); + + /** + * Equality operator. Return true if this object is equal + * to the given object. + * @internal + */ + UBool operator==(const UObject& other) const; + + /** + * Returns a unique class ID for this object POLYMORPHICALLY. + * This method implements a simple form of RTTI used by ICU. + * @return The class ID for this object. All objects of a given + * class have the same class ID. Objects of other classes have + * different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID() const; + + /** + * Returns the class ID for this class. This is used to compare to + * the return value of getDynamicClassID(). + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID getStaticClassID(); + + /** + * Return the ISO currency code of this object. + * @internal + */ + inline const UChar* getISOCurrency() const; + + private: + /** + * The ISO 4217 code of this object. + */ + UChar isoCode[4]; +}; + +inline const UChar* CurrencyUnit::getISOCurrency() const { + return isoCode; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING +#endif // __CURRENCYUNIT_H__ diff --git a/icu4c/source/i18n/unicode/fmtable.h b/icu4c/source/i18n/unicode/fmtable.h index 90afb5201b4..8db065ffee9 100644 --- a/icu4c/source/i18n/unicode/fmtable.h +++ b/icu4c/source/i18n/unicode/fmtable.h @@ -98,30 +98,6 @@ public: */ Formattable(int64_t ll); - /** - * Creates a Formattable for a currency amount. - * @param n the numeric value - * @param currency the currency code - * @draft ICU 3.0 - */ - Formattable(double n, const UChar* currency); - - /** - * Creates a Formattable for a currency amount. - * @param n the numeric value - * @param currency the currency code - * @draft ICU 3.0 - */ - Formattable(int32_t n, const UChar* currency); - - /** - * Creates a Formattable for a currency amount. - * @param n the numeric value - * @param currency the currency code - * @draft ICU 3.0 - */ - Formattable(int64_t n, const UChar* currency); - /** * Creates a Formattable object with a char string pointer. * Assumes that the char string is null terminated. @@ -152,6 +128,13 @@ public: */ Formattable(const Formattable* arrayToCopy, int32_t count); + /** + * Creates a Formattable object that adopts the given UObject. + * @param objectToAdopt the UObject to set this object to + * @draft ICU 3.0 + */ + Formattable(UObject* objectToAdopt); + /** * Copy constructor. * @stable ICU 2.0 @@ -202,22 +185,60 @@ public: Formattable *clone() const; /** - * The list of possible data types of this Formattable object. + * Selector for flavor of data type contained within a + * Formattable object. Formattable is a union of several + * different types, and at any time contains exactly one type. * @stable ICU 2.4 */ enum Type { - /** @stable ICU 2.4 */ - kDate, // Date - /** @stable ICU 2.4 */ - kDouble, // double - /** @stable ICU 2.4 */ - kLong, // long - /** @stable ICU 2.4 */ - kString, // UnicodeString - /** @stable ICU 2.4 */ - kArray, // Formattable[] - /** @draft ICU 2.8 */ - kInt64 // int64 + /** + * Selector indicating a UDate value. Use getDate to retrieve + * the value. + * @stable ICU 2.4 + */ + kDate, + + /** + * Selector indicating a double value. Use getDouble to + * retrieve the value. + * @stable ICU 2.4 + */ + kDouble, + + /** + * Selector indicating a 32-bit integer value. Use getLong to + * retrieve the value. + * @stable ICU 2.4 + */ + kLong, + + /** + * Selector indicating a UnicodeString value. Use getString + * to retrieve the value. + * @stable ICU 2.4 + */ + kString, + + /** + * Selector indicating an array of Formattables. Use getArray + * to retrieve the value. + * @stable ICU 2.4 + */ + kArray, + + /** + * Selector indicating a 64-bit integer value. Use getInt64 + * to retrieve the value. + * @draft ICU 2.8 + */ + kInt64, + + /** + * Selector indicating a UObject value. Use getObject to + * retrieve the value. + * @draft ICU 3.0 + */ + kObject }; /** @@ -227,6 +248,14 @@ public: */ Type getType(void) const; + /** + * Returns TRUE if the data type of this Formattable object + * is kDouble, kLong, or kInt64. + * @return TRUE if this is a pure numeric object + * @draft ICU 3.0 + */ + UBool isNumeric() const; + /** * Gets the double value of this object. If this object is not of type * kDouble then the result is undefined. @@ -238,8 +267,11 @@ public: /** * Gets the double value of this object. If this object is of type * long or int64 then a casting conversion is peformed, with - * possible loss of precision. If the type is not a numeric type, - * 0 is returned and the status is set to U_INVALID_FORMAT_ERROR. + * possible loss of precision. If the type is kObject and the + * object is a Measure, then the result of + * getNumber().getDouble(status) is returned. If this object is + * neither a numeric type nor a Measure, then 0 is returned and + * the status is set to U_INVALID_FORMAT_ERROR. * @param status the error code * @return the double value of this object. * @draft ICU 3.0 @@ -261,9 +293,11 @@ public: * U_INVALID_FORMAT_ERROR. If this object is of type kInt64 and * it fits within a long, then no precision is lost. If it is of * type kDouble, then a casting conversion is peformed, with - * truncation of any fractional part. If the type is not a - * numeric type, 0 is returned and the status is set to - * U_INVALID_FORMAT_ERROR. + * truncation of any fractional part. If the type is kObject and + * the object is a Measure, then the result of + * getNumber().getLong(status) is returned. If this object is + * neither a numeric type nor a Measure, then 0 is returned and + * the status is set to U_INVALID_FORMAT_ERROR. * @param status the error code * @return the long value of this object. * @draft ICU 3.0 @@ -285,8 +319,10 @@ public: * and the status is set to U_INVALID_FORMAT_ERROR. If the * magnitude fits in an int64, then a casting conversion is * peformed, with truncation of any fractional part. If the type - * is not a numeric type, 0 is returned and the status is set to - * U_INVALID_FORMAT_ERROR. + * is kObject and the object is a Measure, then the result of + * getNumber().getDouble(status) is returned. If this object is + * neither a numeric type nor a Measure, then 0 is returned and + * the status is set to U_INVALID_FORMAT_ERROR. * @param status the error code * @return the int64 value of this object. * @draft ICU 3.0 @@ -401,13 +437,12 @@ public: Formattable& operator[](int32_t index) { return fValue.fArrayAndCount.fArray[index]; } /** - * Returns the currency of this object, or NULL if this object has - * no associated currency. Any type may have a currency, although - * this is intended for use with numeric types. - * @return a null-terminated 3-letter ISO 4217 code, or NULL + * Returns a pointer to the UObject contained within this + * formattable, or NULL if this object does not contain a UObject. + * @return a UObject pointer, or NULL * @draft ICU 3.0 */ - const UChar* getCurrency() const; + const UObject* getObject() const; /** * Sets the double value of this object and changes the type to @@ -474,15 +509,13 @@ public: void adoptArray(Formattable* array, int32_t count); /** - * Sets the currency of this object. Only numeric types may have - * a currency. If isoCode is NULL then the currency is removed - * from this object. Any type may have a currency, although - * this is intended for use with numeric types. - * @param currency a null-terminated 3-letter ISO 4217 code, or NULL. - * If longer than 3 characters, the extra characters are ignored. + * Sets and adopts the UObject value of this object and changes + * the type to kObject. After this call, the caller must not + * delete the given object. + * @param objectToAdopt the UObject value to be adopted * @draft ICU 3.0 */ - void setCurrency(const UChar* currency); + void adoptObject(UObject* objectToAdopt); /** * ICU "poor man's RTTI", returns a UClassID for the actual class. @@ -525,19 +558,18 @@ private: UnicodeString* getBogus() const; union { + UObject* fObject; UnicodeString* fString; double fDouble; int64_t fInt64; UDate fDate; - struct - { - Formattable* fArray; - int32_t fCount; + struct { + Formattable* fArray; + int32_t fCount; } fArrayAndCount; - } fValue; + } fValue; Type fType; - UChar fCurrency[4]; UnicodeString fBogus; // Bogus string when it's needed. }; diff --git a/icu4c/source/i18n/unicode/measunit.h b/icu4c/source/i18n/unicode/measunit.h new file mode 100644 index 00000000000..fc4b92d9877 --- /dev/null +++ b/icu4c/source/i18n/unicode/measunit.h @@ -0,0 +1,65 @@ +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#ifndef __MEASUREUNIT_H__ +#define __MEASUREUNIT_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/fmtable.h" + +U_NAMESPACE_BEGIN + +/** + * A unit such as length, mass, volume, currency, etc. A unit is + * coupled with a numeric amount to produce a Measure. + * + *

This is an abstract class. + * + * @author Alan Liu + * @internal + */ +class U_I18N_API MeasureUnit: public UObject { + public: + /** + * Return a polymorphic clone of this object. The result will + * have the same class as returned by getDynamicClassID(). + * @internal + */ + virtual UObject* clone() const = 0; + + /** + * Destructor + * @internal + */ + virtual ~MeasureUnit(); + + /** + * Equality operator. Return true if this object is equal + * to the given object. + * @internal + */ + virtual UBool operator==(const UObject& other) const = 0; + + protected: + /** + * Default constructor. + */ + MeasureUnit(); +}; + +U_NAMESPACE_END + +// NOTE: There is no measunit.cpp. For implementation, see measure.cpp. [alan] + +#endif // !UCONFIG_NO_FORMATTING +#endif // __MEASUREUNIT_H__ diff --git a/icu4c/source/i18n/unicode/measure.h b/icu4c/source/i18n/unicode/measure.h new file mode 100644 index 00000000000..73ad0b8d1ca --- /dev/null +++ b/icu4c/source/i18n/unicode/measure.h @@ -0,0 +1,125 @@ +/* +********************************************************************** +* Copyright (c) 2004, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* Author: Alan Liu +* Created: April 26, 2004 +* Since: ICU 3.0 +********************************************************************** +*/ +#ifndef __MEASURE_H__ +#define __MEASURE_H__ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/fmtable.h" + +U_NAMESPACE_BEGIN + +class MeasureUnit; + +/** + * An amount of a specified unit, consisting of a number and a Unit. + * For example, a length measure consists of a number and a length + * unit, such as feet or meters. This is an abstract class. + * Subclasses specify a concrete Unit type. + * + *

Measure objects are parsed and formatted by subclasses of + * MeasureFormat. + * + *

Measure objects are immutable. + * + *

This is an abstract class. + * + * @author Alan Liu + * @draft ICU 3.0 + */ +class U_I18N_API Measure: public UObject { + public: + /** + * Construct an object with the given numeric amount and the given + * unit. After this call, the caller must not delete the given + * unit object. + * @param amount a numeric object; amount.isNumeric() must be TRUE + * @param adpoptedUnit the unit object, which must not be NULL + * @param ec input-output error code. If the amount or the unit + * is invalid, then this will be set to a failing value. + * @internal + */ + Measure(const Formattable& number, MeasureUnit* adoptedUnit, + UErrorCode& ec); + + /** + * Copy constructor + * @internal + */ + Measure(const Measure& other); + + /** + * Assignment operator + * @internal + */ + Measure& operator=(const Measure& other); + + /** + * Return a polymorphic clone of this object. The result will + * have the same class as returned by getDynamicClassID(). + * @internal + */ + virtual UObject* clone() const = 0; + + /** + * Destructor + * @internal + */ + virtual ~Measure(); + + /** + * Equality operator. Return true if this object is equal + * to the given object. + * @internal + */ + UBool operator==(const UObject& other) const; + + /** + * Return a reference to the numeric value of this object. The + * numeric value may be of any numeric type supported by + * Formattable. + * @internal + */ + inline const Formattable& getNumber() const; + + /** + * Return a reference to the unit of this object. + * @internal + */ + inline const MeasureUnit& getUnit() const; + + private: + /** + * The numeric value of this object, e.g. 2.54 or 100. + */ + Formattable number; + + /** + * The unit of this object, e.g., "millimeter" or "JPY". This is + * owned by this object. + */ + MeasureUnit* unit; +}; + +inline const Formattable& Measure::getNumber() const { + return number; +} + +inline const MeasureUnit& Measure::getUnit() const { + return *unit; +} + +U_NAMESPACE_END + +#endif // !UCONFIG_NO_FORMATTING +#endif // __MEASURE_H__ diff --git a/icu4c/source/i18n/unum.cpp b/icu4c/source/i18n/unum.cpp index 9224077514d..6415c3c96fc 100644 --- a/icu4c/source/i18n/unum.cpp +++ b/icu4c/source/i18n/unum.cpp @@ -23,6 +23,7 @@ #include "unicode/ustring.h" #include "unicode/fmtable.h" #include "unicode/dcfmtsym.h" +#include "unicode/curramt.h" #include "uassert.h" #include "cpputils.h" @@ -270,8 +271,8 @@ unum_formatDoubleCurrency(const UNumberFormat* fmt, if (pos != 0) { fp.setField(pos->field); } - - Formattable n(number, currency); + + Formattable n(new CurrencyAmount(number, currency, *status)); ((const NumberFormat*)fmt)->format(n, res, fp, *status); if (pos != 0) { @@ -363,8 +364,10 @@ unum_parseDoubleCurrency(const UNumberFormat* fmt, Formattable res; parseRes(res, fmt, text, textLength, parsePos, TRUE, status); currency[0] = 0; - if (res.getCurrency() != 0) { - u_strcpy(currency, res.getCurrency()); + if (res.getType() == Formattable::kObject && + res.getObject()->getDynamicClassID() == CurrencyAmount::getStaticClassID()) { + const CurrencyAmount* c = (const CurrencyAmount*) res.getObject(); + u_strcpy(currency, c->getISOCurrency()); } return res.getDouble(*status); } diff --git a/icu4c/source/test/intltest/intltest.cpp b/icu4c/source/test/intltest/intltest.cpp index 55d5159d3c0..5acb716e78e 100644 --- a/icu4c/source/test/intltest/intltest.cpp +++ b/icu4c/source/test/intltest/intltest.cpp @@ -23,6 +23,7 @@ #include "unicode/ucnv.h" #include "unicode/uclean.h" #include "unicode/timezone.h" +#include "unicode/curramt.h" #include "intltest.h" #include "caltztst.h" @@ -115,17 +116,10 @@ operator+(const UnicodeString& left, #if !UCONFIG_NO_FORMATTING /** - * Originally coded this as operator+, but that makes the expression - * + char* ambiguous. - liu + * Return a string display for for this, without surrounding braces. */ -UnicodeString toString(const Formattable& f) { +UnicodeString _toString(const Formattable& f) { UnicodeString s; - UnicodeString close; - const UChar* currency = f.getCurrency(); - if (currency != NULL) { - close.append((UChar)0x2f/*/*/).append(currency); - } - close.append((UChar)0x5d/*]*/); switch (f.getType()) { case Formattable::kDate: { @@ -134,48 +128,68 @@ UnicodeString toString(const Formattable& f) { if (U_SUCCESS(status)) { FieldPosition pos; fmt.format(f.getDate(), s, pos); - s.insert(0, "[Date:"); - s.append(close); + s.insert(0, "Date:"); } else { - s = UnicodeString("[Error creating date format]"); + s = UnicodeString("Error creating date format]"); } } break; case Formattable::kDouble: - s = UnicodeString("[double:") + f.getDouble() + close; + s = UnicodeString("double:") + f.getDouble(); break; case Formattable::kLong: - s = UnicodeString("[long:") + f.getLong() + close; + s = UnicodeString("long:") + f.getLong(); break; case Formattable::kInt64: - s = UnicodeString("[int64:") + Int64ToUnicodeString(f.getInt64()) + close; + s = UnicodeString("int64:") + Int64ToUnicodeString(f.getInt64()); break; case Formattable::kString: f.getString(s); - s.insert(0, "[String:"); - s.append(close); + s.insert(0, "String:"); break; case Formattable::kArray: { int32_t i, n; const Formattable* array = f.getArray(n); - s.insert(0, UnicodeString("[Array:")); + s.insert(0, UnicodeString("Array:")); UnicodeString delim(", "); for (i=0; i 0) { s.append(delim); } - s = s + toString(array[i]); + s = s + _toString(array[i]); } - s.append(close); } break; + case Formattable::kObject: + if (f.getObject()->getDynamicClassID() == + CurrencyAmount::getStaticClassID()) { + const CurrencyAmount& c = (const CurrencyAmount&) *f.getObject(); + s = _toString(c.getNumber()) + " " + UnicodeString(c.getISOCurrency()); + } else { + s = UnicodeString("Unknown UObject"); + } + break; + default: + s = UnicodeString("Unknown Formattable type=") + (int32_t)f.getType(); + break; } return s; } +/** + * Originally coded this as operator+, but that makes the expression + * + char* ambiguous. - liu + */ +UnicodeString toString(const Formattable& f) { + UnicodeString s((UChar)91/*[*/); + s.append(_toString(f)); + s.append((UChar)0x5d/*]*/); + return s; +} + #endif // useful when operator+ won't cooperate diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index cc1e054806d..629c4d123c6 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -18,6 +18,7 @@ #include "unicode/ucurr.h" #include "unicode/ustring.h" #include "unicode/measfmt.h" +#include "unicode/curramt.h" #include "digitlst.h" #include "textfile.h" #include "tokiter.h" @@ -1569,8 +1570,9 @@ static void parseCurrencyAmount(const UnicodeString& str, int32_t i = str.indexOf(delim); str.extractBetween(0, i, num); str.extractBetween(i+1, INT32_MAX, cur); - fmt.parse(num, result, ec); - result.setCurrency(cur.getTerminatedBuffer()); + Formattable n; + fmt.parse(num, n, ec); + result.adoptObject(new CurrencyAmount(n, cur.getTerminatedBuffer(), ec)); } void NumberFormatTest::TestCases() {