X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/73c04bcfe1096173b00431f0cdc742894b15eef0..2ca993e82fb37b597a3c73ecd1586a139a6579c5:/icuSources/i18n/numfmt.cpp diff --git a/icuSources/i18n/numfmt.cpp b/icuSources/i18n/numfmt.cpp index d0f4109b..f59a1f60 100644 --- a/icuSources/i18n/numfmt.cpp +++ b/icuSources/i18n/numfmt.cpp @@ -1,7 +1,7 @@ /* ******************************************************************************* -* Copyright (C) 1997-2006, International Business Machines Corporation and * -* others. All Rights Reserved. * +* Copyright (C) 1997-2015, International Business Machines Corporation and +* others. All Rights Reserved. ******************************************************************************* * * File NUMFMT.CPP @@ -33,6 +33,11 @@ #include "unicode/ustring.h" #include "unicode/ucurr.h" #include "unicode/curramt.h" +#include "unicode/numsys.h" +#include "unicode/rbnf.h" +#include "unicode/localpointer.h" +#include "unicode/udisplaycontext.h" +#include "charstr.h" #include "winnmfmt.h" #include "uresimp.h" #include "uhash.h" @@ -41,13 +46,19 @@ #include "ucln_in.h" #include "cstring.h" #include "putilimp.h" +#include "uassert.h" +#include "umutex.h" +#include "mutex.h" +#include "digitlst.h" #include +#include "sharednumberformat.h" +#include "unifiedcache.h" //#define FMT_DEBUG #ifdef FMT_DEBUG #include -static void debugout(UnicodeString s) { +static inline void debugout(UnicodeString s) { char buf[2000]; s.extract((int32_t) 0, s.length(), buf); printf("%s", buf); @@ -59,33 +70,127 @@ static void debugout(UnicodeString s) { #endif // If no number pattern can be located for a locale, this is the last -// resort. +// resort. The patterns are same as the ones in root locale. static const UChar gLastResortDecimalPat[] = { - 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x3B, 0x2D, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#0.###;-#0.###" */ + 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#,##0.###" */ }; static const UChar gLastResortCurrencyPat[] = { - 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x3B, 0x28, 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x29, 0 /* "$#0.00;($#0.00)" */ + 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A0#,##0.00" */ }; static const UChar gLastResortPercentPat[] = { - 0x23, 0x30, 0x25, 0 /* "#0%" */ + 0x23, 0x2C, 0x23, 0x23, 0x30, 0x25, 0 /* "#,##0%" */ }; static const UChar gLastResortScientificPat[] = { 0x23, 0x45, 0x30, 0 /* "#E0" */ }; +static const UChar gLastResortIsoCurrencyPat[] = { + 0xA4, 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A4\u00A0#,##0.00" */ +}; +static const UChar gLastResortPluralCurrencyPat[] = { + 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x20, 0xA4, 0xA4, 0xA4, 0 /* "#,##0.### \u00A4\u00A4\u00A4*/ +}; +static const UChar gLastResortAccountingCurrencyPat[] = { + 0xA4, 0xA0, 0x23, 0x2C, 0x23, 0x23, 0x30, 0x2E, 0x30, 0x30, 0 /* "\u00A4\u00A0#,##0.00" */ +}; + +static const UChar gSingleCurrencySign[] = {0xA4, 0}; +static const UChar gDoubleCurrencySign[] = {0xA4, 0xA4, 0}; + +static const UChar gSlash = 0x2f; // If the maximum base 10 exponent were 4, then the largest number would // be 99,999 which has 5 digits. -static const int32_t gMaxIntegerDigits = DBL_MAX_10_EXP + 1; // Should be ~40 ? --srl -static const int32_t gMinIntegerDigits = 127; +// On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit +// With big decimal, the max exponent is 999,999,999 and the max number of digits is the same, 999,999,999 +const int32_t icu::NumberFormat::gDefaultMaxIntegerDigits = 2000000000; +const int32_t icu::NumberFormat::gDefaultMinIntegerDigits = 127; + +static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = { + NULL, // UNUM_PATTERN_DECIMAL + gLastResortDecimalPat, // UNUM_DECIMAL + gLastResortCurrencyPat, // UNUM_CURRENCY + gLastResortPercentPat, // UNUM_PERCENT + gLastResortScientificPat, // UNUM_SCIENTIFIC + NULL, // UNUM_SPELLOUT + NULL, // UNUM_ORDINAL + NULL, // UNUM_DURATION + NULL, // UNUM_NUMBERING_SYSTEM + NULL, // UNUM_PATTERN_RULEBASED + gLastResortIsoCurrencyPat, // UNUM_CURRENCY_ISO + gLastResortPluralCurrencyPat, // UNUM_CURRENCY_PLURAL + gLastResortAccountingCurrencyPat, // UNUM_CURRENCY_ACCOUNTING + gLastResortCurrencyPat, // UNUM_CASH_CURRENCY + NULL, // UNUM_DECIMAL_COMPACT_SHORT + NULL, // UNUM_DECIMAL_COMPACT_LONG + gLastResortCurrencyPat, // UNUM_CURRENCY_STANDARD +}; -static const UChar * const gLastResortNumberPatterns[] = -{ - gLastResortDecimalPat, - gLastResortCurrencyPat, - gLastResortPercentPat, - gLastResortScientificPat +// Keys used for accessing resource bundles + +static const char *gNumberElements = "NumberElements"; +static const char *gLatn = "latn"; +static const char *gPatterns = "patterns"; +static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = { + NULL, // UNUM_PATTERN_DECIMAL + "decimalFormat", // UNUM_DECIMAL + "currencyFormat", // UNUM_CURRENCY + "percentFormat", // UNUM_PERCENT + "scientificFormat", // UNUM_SCIENTIFIC + NULL, // UNUM_SPELLOUT + NULL, // UNUM_ORDINAL + NULL, // UNUM_DURATION + NULL, // UNUM_NUMBERING_SYSTEM + NULL, // UNUM_PATTERN_RULEBASED + // For UNUM_CURRENCY_ISO and UNUM_CURRENCY_PLURAL, + // the pattern is the same as the pattern of UNUM_CURRENCY + // except for replacing the single currency sign with + // double currency sign or triple currency sign. + "currencyFormat", // UNUM_CURRENCY_ISO + "currencyFormat", // UNUM_CURRENCY_PLURAL + "accountingFormat", // UNUM_CURRENCY_ACCOUNTING + "currencyFormat", // UNUM_CASH_CURRENCY + NULL, // UNUM_DECIMAL_COMPACT_SHORT + NULL, // UNUM_DECIMAL_COMPACT_LONG + "currencyFormat", // UNUM_CURRENCY_STANDARD }; +// Static hashtable cache of NumberingSystem objects used by NumberFormat +static UHashtable * NumberingSystem_cache = NULL; +static UMutex nscacheMutex = U_MUTEX_INITIALIZER; +static icu::UInitOnce gNSCacheInitOnce = U_INITONCE_INITIALIZER; + +#if !UCONFIG_NO_SERVICE +static icu::ICULocaleService* gService = NULL; +static icu::UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER; +#endif + +/** + * Release all static memory held by Number Format. + */ +U_CDECL_BEGIN +static void U_CALLCONV +deleteNumberingSystem(void *obj) { + delete (icu::NumberingSystem *)obj; +} + +static UBool U_CALLCONV numfmt_cleanup(void) { +#if !UCONFIG_NO_SERVICE + gServiceInitOnce.reset(); + if (gService) { + delete gService; + gService = NULL; + } +#endif + gNSCacheInitOnce.reset(); + if (NumberingSystem_cache) { + // delete NumberingSystem_cache; + uhash_close(NumberingSystem_cache); + NumberingSystem_cache = NULL; + } + return TRUE; +} +U_CDECL_END + // ***************************************************************************** // class NumberFormat // ***************************************************************************** @@ -111,7 +216,7 @@ UBool SimpleNumberFormatFactory::visible(void) const { } const UnicodeString * -SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const +SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const { if (U_SUCCESS(status)) { count = 1; @@ -126,11 +231,13 @@ SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) c // default constructor NumberFormat::NumberFormat() : fGroupingUsed(TRUE), - fMaxIntegerDigits(gMaxIntegerDigits), + fMaxIntegerDigits(gDefaultMaxIntegerDigits), fMinIntegerDigits(1), fMaxFractionDigits(3), // invariant, >= minFractionDigits fMinFractionDigits(0), - fParseIntegerOnly(FALSE) + fParseIntegerOnly(FALSE), + fLenient(FALSE), + fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) { fCurrency[0] = 0; } @@ -141,6 +248,10 @@ NumberFormat::~NumberFormat() { } +SharedNumberFormat::~SharedNumberFormat() { + delete ptr; +} + // ------------------------------------- // copy constructor @@ -158,13 +269,17 @@ NumberFormat::operator=(const NumberFormat& rhs) { if (this != &rhs) { + Format::operator=(rhs); fGroupingUsed = rhs.fGroupingUsed; fMaxIntegerDigits = rhs.fMaxIntegerDigits; fMinIntegerDigits = rhs.fMinIntegerDigits; fMaxFractionDigits = rhs.fMaxFractionDigits; fMinFractionDigits = rhs.fMinFractionDigits; fParseIntegerOnly = rhs.fParseIntegerOnly; - u_strncpy(fCurrency, rhs.fCurrency, 4); + u_strncpy(fCurrency, rhs.fCurrency, 3); + fCurrency[3] = 0; + fLenient = rhs.fLenient; + fCapitalizationContext = rhs.fCapitalizationContext; } return *this; } @@ -207,7 +322,15 @@ NumberFormat::operator==(const Format& that) const if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("fCurrency !="); } - if (!first) { printf(" ]"); } + if (!(fLenient == other->fLenient)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fLenient != "); + } + if (!(fCapitalizationContext == other->fCapitalizationContext)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fCapitalizationContext != "); + } + if (!first) { printf(" ]"); } #endif return ((this == &that) || @@ -218,13 +341,220 @@ NumberFormat::operator==(const Format& that) const fMinFractionDigits == other->fMinFractionDigits && fGroupingUsed == other->fGroupingUsed && fParseIntegerOnly == other->fParseIntegerOnly && - u_strcmp(fCurrency, other->fCurrency) == 0))); + u_strcmp(fCurrency, other->fCurrency) == 0 && + fLenient == other->fLenient && + fCapitalizationContext == other->fCapitalizationContext))); } -// -------------------------------------x +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +NumberFormat::format(double /* unused number */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +NumberFormat::format(int32_t /* unused number */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------- +// Default implementation sets unsupported error; subclasses should +// override. + +UnicodeString& +NumberFormat::format(int64_t /* unused number */, + UnicodeString& toAppendTo, + FieldPositionIterator* /* unused posIter */, + UErrorCode& status) const +{ + if (!U_FAILURE(status)) { + status = U_UNSUPPORTED_ERROR; + } + return toAppendTo; +} + +// ------------------------------------------ +// These functions add the status code, just fall back to the non-status versions +UnicodeString& +NumberFormat::format(double number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + return format(number,appendTo,pos); + } else { + return appendTo; + } +} + +UnicodeString& +NumberFormat::format(int32_t number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + return format(number,appendTo,pos); + } else { + return appendTo; + } +} + +UnicodeString& +NumberFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + if(U_SUCCESS(status)) { + return format(number,appendTo,pos); + } else { + return appendTo; + } +} + + + +// ------------------------------------- +// Decimal Number format() default implementation +// Subclasses do not normally override this function, but rather the DigitList +// formatting functions.. +// The expected call chain from here is +// this function -> +// NumberFormat::format(Formattable -> +// DecimalFormat::format(DigitList +// +// Or, for subclasses of Formattable that do not know about DigitList, +// this Function -> +// NumberFormat::format(Formattable -> +// NumberFormat::format(DigitList -> +// XXXFormat::format(double + +UnicodeString& +NumberFormat::format(const StringPiece &decimalNum, + UnicodeString& toAppendTo, + FieldPositionIterator* fpi, + UErrorCode& status) const +{ + Formattable f; + f.setDecimalNumber(decimalNum, status); + format(f, toAppendTo, fpi, status); + return toAppendTo; +} + +/** + * // Formats the number object and save the format // result in the toAppendTo string buffer. +// utility to save/restore state, used in two overloads +// of format(const Formattable&...) below. +* +* Old purpose of ArgExtractor was to avoid const. Not thread safe! +* +* keeping it around as a shim. +*/ +class ArgExtractor { + const Formattable* num; + UChar save[4]; + UBool fWasCurrency; + + public: + ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status); + ~ArgExtractor(); + + const Formattable* number(void) const; + const UChar *iso(void) const; + UBool wasCurrency(void) const; +}; + +inline const Formattable* +ArgExtractor::number(void) const { + return num; +} + +inline UBool +ArgExtractor::wasCurrency(void) const { + return fWasCurrency; +} + +inline const UChar * +ArgExtractor::iso(void) const { + return save; +} + +ArgExtractor::ArgExtractor(const NumberFormat& /*nf*/, const Formattable& obj, UErrorCode& /*status*/) + : num(&obj), fWasCurrency(FALSE) { + + const UObject* o = obj.getObject(); // most commonly o==NULL + const CurrencyAmount* amt; + if (o != NULL && (amt = dynamic_cast(o)) != NULL) { + // getISOCurrency() returns a pointer to internal storage, so we + // copy it to retain it across the call to setCurrency(). + //const UChar* curr = amt->getISOCurrency(); + u_strcpy(save, amt->getISOCurrency()); + num = &amt->getNumber(); + fWasCurrency=TRUE; + } else { + save[0]=0; + } +} + +ArgExtractor::~ArgExtractor() { +} + +UnicodeString& NumberFormat::format(const DigitList &number, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const { + // DecimalFormat overrides this function, and handles DigitList based big decimals. + // Other subclasses (ChoiceFormat, RuleBasedNumberFormat) do not (yet) handle DigitLists, + // so this default implementation falls back to formatting decimal numbers as doubles. + if (U_FAILURE(status)) { + return appendTo; + } + double dnum = number.getDouble(); + format(dnum, appendTo, posIter, status); + return appendTo; +} + + + +UnicodeString& +NumberFormat::format(const DigitList &number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode &status) const { + // DecimalFormat overrides this function, and handles DigitList based big decimals. + // Other subclasses (ChoiceFormat, RuleBasedNumberFormat) do not (yet) handle DigitLists, + // so this default implementation falls back to formatting decimal numbers as doubles. + if (U_FAILURE(status)) { + return appendTo; + } + double dnum = number.getDouble(); + format(dnum, appendTo, pos, status); + return appendTo; +} + UnicodeString& NumberFormat::format(const Formattable& obj, UnicodeString& appendTo, @@ -233,51 +563,100 @@ NumberFormat::format(const Formattable& obj, { if (U_FAILURE(status)) return appendTo; - NumberFormat* nonconst = (NumberFormat*) this; - const Formattable* n = &obj; + ArgExtractor arg(*this, obj, status); + const Formattable *n = arg.number(); + const UChar *iso = arg.iso(); + + if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { + // trying to format a different currency. + // Right now, we clone. + LocalPointer cloneFmt((NumberFormat*)this->clone()); + cloneFmt->setCurrency(iso, status); + // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. + return cloneFmt->format(*n, appendTo, pos, status); + } - UChar save[4]; - UBool setCurr = FALSE; - 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); + if (n->isNumeric() && n->getDigitList() != NULL) { + // Decimal Number. We will have a DigitList available if the value was + // set to a decimal number, or if the value originated with a parse. + // + // The default implementation for formatting a DigitList converts it + // to a double, and formats that, allowing formatting classes that don't + // know about DigitList to continue to operate as they had. + // + // DecimalFormat overrides the DigitList formatting functions. + format(*n->getDigitList(), appendTo, pos, status); + } else { + switch (n->getType()) { + case Formattable::kDouble: + format(n->getDouble(), appendTo, pos); + break; + case Formattable::kLong: + format(n->getLong(), appendTo, pos); + break; + case Formattable::kInt64: + format(n->getInt64(), appendTo, pos); + break; + default: + status = U_INVALID_FORMAT_ERROR; + break; } - n = &amt->getNumber(); - } - - switch (n->getType()) { - case Formattable::kDouble: - format(n->getDouble(), appendTo, pos); - break; - case Formattable::kLong: - format(n->getLong(), appendTo, pos); - break; - case Formattable::kInt64: - format(n->getInt64(), appendTo, pos); - break; - default: - status = U_INVALID_FORMAT_ERROR; - break; } - if (setCurr) { - UErrorCode ok = U_ZERO_ERROR; - nonconst->setCurrency(save, ok); // always restore currency + return appendTo; +} + +// -------------------------------------x +// Formats the number object and save the format +// result in the toAppendTo string buffer. + +UnicodeString& +NumberFormat::format(const Formattable& obj, + UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + ArgExtractor arg(*this, obj, status); + const Formattable *n = arg.number(); + const UChar *iso = arg.iso(); + + if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { + // trying to format a different currency. + // Right now, we clone. + LocalPointer cloneFmt((NumberFormat*)this->clone()); + cloneFmt->setCurrency(iso, status); + // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. + return cloneFmt->format(*n, appendTo, posIter, status); + } + + if (n->isNumeric() && n->getDigitList() != NULL) { + // Decimal Number + format(*n->getDigitList(), appendTo, posIter, status); + } else { + switch (n->getType()) { + case Formattable::kDouble: + format(n->getDouble(), appendTo, posIter, status); + break; + case Formattable::kLong: + format(n->getLong(), appendTo, posIter, status); + break; + case Formattable::kInt64: + format(n->getInt64(), appendTo, posIter, status); + break; + default: + status = U_INVALID_FORMAT_ERROR; + break; + } } + return appendTo; } // ------------------------------------- -UnicodeString& +UnicodeString& NumberFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const @@ -348,25 +727,26 @@ NumberFormat::parse(const UnicodeString& text, } } -Formattable& NumberFormat::parseCurrency(const UnicodeString& text, - Formattable& result, - ParsePosition& pos) const { +CurrencyAmount* NumberFormat::parseCurrency(const UnicodeString& text, + ParsePosition& pos) const { // Default implementation only -- subclasses should override + Formattable parseResult; int32_t start = pos.getIndex(); - parse(text, result, pos); + parse(text, parseResult, pos); if (pos.getIndex() != start) { UChar curr[4]; UErrorCode ec = U_ZERO_ERROR; getEffectiveCurrency(curr, ec); if (U_SUCCESS(ec)) { - Formattable n(result); - result.adoptObject(new CurrencyAmount(n, curr, ec)); + LocalPointer currAmt(new CurrencyAmount(parseResult, curr, ec), ec); if (U_FAILURE(ec)) { pos.setIndex(start); // indicate failure + } else { + return currAmt.orphan(); } } } - return result; + return NULL; } // ------------------------------------- @@ -378,13 +758,22 @@ NumberFormat::setParseIntegerOnly(UBool value) fParseIntegerOnly = value; } +// ------------------------------------- +// Sets whether lenient parse is enabled. + +void +NumberFormat::setLenient(UBool enable) +{ + fLenient = enable; +} + // ------------------------------------- // Create a number style NumberFormat instance with the default locale. NumberFormat* U_EXPORT2 NumberFormat::createInstance(UErrorCode& status) { - return createInstance(Locale::getDefault(), kNumberStyle, status); + return createInstance(Locale::getDefault(), UNUM_DECIMAL, status); } // ------------------------------------- @@ -393,7 +782,7 @@ NumberFormat::createInstance(UErrorCode& status) NumberFormat* U_EXPORT2 NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) { - return createInstance(inLocale, kNumberStyle, status); + return createInstance(inLocale, UNUM_DECIMAL, status); } // ------------------------------------- @@ -410,8 +799,8 @@ NumberFormat::createCurrencyInstance(UErrorCode& status) NumberFormat* U_EXPORT2 NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) -{ - return createInstance(inLocale, kCurrencyStyle, status); +{ + return createInstance(inLocale, UNUM_CURRENCY, status); } // ------------------------------------- @@ -420,7 +809,7 @@ NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) NumberFormat* U_EXPORT2 NumberFormat::createPercentInstance(UErrorCode& status) { - return createInstance(Locale::getDefault(), kPercentStyle, status); + return createInstance(Locale::getDefault(), UNUM_PERCENT, status); } // ------------------------------------- @@ -429,7 +818,7 @@ NumberFormat::createPercentInstance(UErrorCode& status) NumberFormat* U_EXPORT2 NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) { - return createInstance(inLocale, kPercentStyle, status); + return createInstance(inLocale, UNUM_PERCENT, status); } // ------------------------------------- @@ -438,7 +827,7 @@ NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) NumberFormat* U_EXPORT2 NumberFormat::createScientificInstance(UErrorCode& status) { - return createInstance(Locale::getDefault(), kScientificStyle, status); + return createInstance(Locale::getDefault(), UNUM_SCIENTIFIC, status); } // ------------------------------------- @@ -447,7 +836,7 @@ NumberFormat::createScientificInstance(UErrorCode& status) NumberFormat* U_EXPORT2 NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) { - return createInstance(inLocale, kScientificStyle, status); + return createInstance(inLocale, UNUM_SCIENTIFIC, status); } // ------------------------------------- @@ -465,31 +854,20 @@ NumberFormat::getAvailableLocales(int32_t& count) //------------------------------------------- #if !UCONFIG_NO_SERVICE -static ICULocaleService* gService = NULL; - -/** - * Release all static memory held by numberformat. - */ -U_CDECL_BEGIN -static UBool U_CALLCONV numfmt_cleanup(void) { - if (gService) { - delete gService; - gService = NULL; - } - return TRUE; -} -U_CDECL_END // ------------------------------------- class ICUNumberFormatFactory : public ICUResourceBundleFactory { +public: + virtual ~ICUNumberFormatFactory(); protected: virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /* service */, UErrorCode& status) const { - // !!! kind is not an EStyles, need to determine how to handle this - return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); + return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); } }; +ICUNumberFormatFactory::~ICUNumberFormatFactory() {} + // ------------------------------------- class NFFactory : public LocaleKeyFactory { @@ -498,18 +876,14 @@ private: Hashtable* _ids; public: - NFFactory(NumberFormatFactory* delegate) + NFFactory(NumberFormatFactory* delegate) : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) , _delegate(delegate) , _ids(NULL) { } - virtual ~NFFactory() - { - delete _delegate; - delete _ids; - } + virtual ~NFFactory(); virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const { @@ -519,7 +893,7 @@ public: lkey.canonicalLocale(loc); int32_t kind = lkey.kind(); - UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)(kind+1)); + UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)kind); if (result == NULL) { result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status); } @@ -530,7 +904,7 @@ public: protected: /** - * Return the set of ids that this factory supports (visible or + * Return the set of ids that this factory supports (visible or * otherwise). This can be called often and might need to be * cached if it is expensive to create. */ @@ -553,6 +927,12 @@ protected: } }; +NFFactory::~NFFactory() +{ + delete _delegate; + delete _ids; +} + class ICUNumberFormatService : public ICULocaleService { public: ICUNumberFormatService() @@ -562,6 +942,8 @@ public: registerFactory(new ICUNumberFormatFactory(), status); } + virtual ~ICUNumberFormatService(); + virtual UObject* cloneInstance(UObject* instance) const { return ((NumberFormat*)instance)->clone(); } @@ -571,7 +953,7 @@ public: int32_t kind = lkey.kind(); Locale loc; lkey.currentLocale(loc); - return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); + return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); } virtual UBool isDefault() const { @@ -579,37 +961,27 @@ public: } }; +ICUNumberFormatService::~ICUNumberFormatService() {} + // ------------------------------------- -static ICULocaleService* +static void U_CALLCONV initNumberFormatService() { + U_ASSERT(gService == NULL); + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + gService = new ICUNumberFormatService(); +} + +static ICULocaleService* getNumberFormatService(void) { - UBool needInit; - { - Mutex mutex; - needInit = (UBool)(gService == NULL); - } - if (needInit) { - ICULocaleService * newservice = new ICUNumberFormatService(); - if (newservice) { - Mutex mutex; - if (gService == NULL) { - gService = newservice; - newservice = NULL; - } - } - if (newservice) { - delete newservice; - } else { - // we won the contention, this thread can register cleanup. -#if !UCONFIG_NO_SERVICE - ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); -#endif - } - } + umtx_initOnce(gServiceInitOnce, &initNumberFormatService); return gService; } +static UBool haveService() { + return !gServiceInitOnce.isReset() && (getNumberFormatService() != NULL); +} + // ------------------------------------- URegistryKey U_EXPORT2 @@ -617,7 +989,10 @@ NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) { ICULocaleService *service = getNumberFormatService(); if (service) { - return service->registerFactory(new NFFactory(toAdopt), status); + NFFactory *tempnnf = new NFFactory(toAdopt); + if (tempnnf != NULL) { + return service->registerFactory(tempnnf, status); + } } status = U_MEMORY_ALLOCATION_ERROR; return NULL; @@ -628,16 +1003,15 @@ NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) UBool U_EXPORT2 NumberFormat::unregister(URegistryKey key, UErrorCode& status) { - if (U_SUCCESS(status)) { - umtx_lock(NULL); - UBool haveService = gService != NULL; - umtx_unlock(NULL); - if (haveService) { - return gService->unregister(key, status); - } + if (U_FAILURE(status)) { + return FALSE; + } + if (haveService()) { + return gService->unregister(key, status); + } else { status = U_ILLEGAL_ARGUMENT_ERROR; + return FALSE; } - return FALSE; } // ------------------------------------- @@ -646,30 +1020,50 @@ NumberFormat::getAvailableLocales(void) { ICULocaleService *service = getNumberFormatService(); if (service) { - return service->getAvailableLocales(); + return service->getAvailableLocales(); } return NULL; // no way to return error condition } #endif /* UCONFIG_NO_SERVICE */ // ------------------------------------- -NumberFormat* U_EXPORT2 -NumberFormat::createInstance(const Locale& loc, EStyles kind, UErrorCode& status) -{ +enum { kKeyValueLenMax = 32 }; + +NumberFormat* +NumberFormat::internalCreateInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (kind == UNUM_CURRENCY) { + char cfKeyValue[kKeyValueLenMax] = {0}; + UErrorCode kvStatus = U_ZERO_ERROR; + int32_t kLen = loc.getKeywordValue("cf", cfKeyValue, kKeyValueLenMax, kvStatus); + if (U_SUCCESS(kvStatus) && kLen > 0 && uprv_strcmp(cfKeyValue,"account")==0) { + kind = UNUM_CURRENCY_ACCOUNTING; + } + } #if !UCONFIG_NO_SERVICE - umtx_lock(NULL); - UBool haveService = gService != NULL; - umtx_unlock(NULL); - if (haveService) { + if (haveService()) { return (NumberFormat*)gService->get(loc, kind, status); } - else #endif - { - return makeInstance(loc, kind, status); - } + return makeInstance(loc, kind, status); } +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (kind != UNUM_DECIMAL) { + return internalCreateInstance(loc, kind, status); + } + const SharedNumberFormat *shared = createSharedInstance(loc, kind, status); + if (U_FAILURE(status)) { + return NULL; + } + NumberFormat *result = static_cast((*shared)->clone()); + shared->removeRef(); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return result; +} + // ------------------------------------- // Checks if the thousand/10 thousand grouping is used in the @@ -707,7 +1101,7 @@ int32_t NumberFormat::getMaximumIntegerDigits() const void NumberFormat::setMaximumIntegerDigits(int32_t newValue) { - fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); + fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); if(fMinIntegerDigits > fMaxIntegerDigits) fMinIntegerDigits = fMaxIntegerDigits; } @@ -729,7 +1123,7 @@ NumberFormat::getMinimumIntegerDigits() const void NumberFormat::setMinimumIntegerDigits(int32_t newValue) { - fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); + fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); if(fMinIntegerDigits > fMaxIntegerDigits) fMaxIntegerDigits = fMinIntegerDigits; } @@ -751,7 +1145,7 @@ NumberFormat::getMaximumFractionDigits() const void NumberFormat::setMaximumFractionDigits(int32_t newValue) { - fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); + fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); if(fMaxFractionDigits < fMinFractionDigits) fMinFractionDigits = fMaxFractionDigits; } @@ -773,7 +1167,7 @@ NumberFormat::getMinimumFractionDigits() const void NumberFormat::setMinimumFractionDigits(int32_t newValue) { - fMinFractionDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); + fMinFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); if (fMaxFractionDigits < fMinFractionDigits) fMaxFractionDigits = fMinFractionDigits; } @@ -810,115 +1204,305 @@ void NumberFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { } } +//---------------------------------------------------------------------- + + +void NumberFormat::setContext(UDisplayContext value, UErrorCode& status) +{ + if (U_FAILURE(status)) + return; + if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { + fCapitalizationContext = value; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + } +} + + +UDisplayContext NumberFormat::getContext(UDisplayContextType type, UErrorCode& status) const +{ + if (U_FAILURE(status)) + return (UDisplayContext)0; + if (type != UDISPCTX_TYPE_CAPITALIZATION) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return (UDisplayContext)0; + } + return fCapitalizationContext; +} + + // ------------------------------------- // Creates the NumberFormat instance of the specified style (number, currency, // or percent) for the desired locale. +static void U_CALLCONV nscacheInit() { + U_ASSERT(NumberingSystem_cache == NULL); + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + UErrorCode status = U_ZERO_ERROR; + NumberingSystem_cache = uhash_open(uhash_hashLong, + uhash_compareLong, + NULL, + &status); + if (U_FAILURE(status)) { + // Number Format code will run with no cache if creation fails. + NumberingSystem_cache = NULL; + return; + } + uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem); +} + +template<> U_I18N_API +const SharedNumberFormat *LocaleCacheKey::createObject( + const void * /*unused*/, UErrorCode &status) const { + const char *localeId = fLoc.getName(); + NumberFormat *nf = NumberFormat::internalCreateInstance( + localeId, UNUM_DECIMAL, status); + if (U_FAILURE(status)) { + return NULL; + } + SharedNumberFormat *result = new SharedNumberFormat(nf); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + delete nf; + return NULL; + } + result->addRef(); + return result; +} + +const SharedNumberFormat* U_EXPORT2 +NumberFormat::createSharedInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { + if (U_FAILURE(status)) { + return NULL; + } + if (kind != UNUM_DECIMAL) { + status = U_UNSUPPORTED_ERROR; + return NULL; + } + const SharedNumberFormat *result = NULL; + UnifiedCache::getByLocale(loc, result, status); + return result; +} + +UBool +NumberFormat::isStyleSupported(UNumberFormatStyle style) { + return gLastResortNumberPatterns[style] != NULL; +} + NumberFormat* NumberFormat::makeInstance(const Locale& desiredLocale, - EStyles style, - UErrorCode& status) -{ + UNumberFormatStyle style, + UErrorCode& status) { + return makeInstance(desiredLocale, style, false, status); +} + +NumberFormat* +NumberFormat::makeInstance(const Locale& desiredLocale, + UNumberFormatStyle style, + UBool mustBeDecimalFormat, + UErrorCode& status) { if (U_FAILURE(status)) return NULL; - if (style < 0 || style >= kStyleCount) { + if (style < 0 || style >= UNUM_FORMAT_STYLE_COUNT) { status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } -#ifdef U_WINDOWS - char buffer[8]; - int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); - - // if the locale has "@compat=host", create a host-specific NumberFormat - if (count > 0 && uprv_strcmp(buffer, "host") == 0) { - Win32NumberFormat *f = NULL; - UBool curr = TRUE; - - switch (style) { - case kNumberStyle: - curr = FALSE; - // fall-through + // Some styles are not supported. This is a result of merging + // the @draft ICU 4.2 NumberFormat::EStyles into the long-existing UNumberFormatStyle. + // Ticket #8503 is for reviewing/fixing/merging the two relevant implementations: + // this one and unum_open(). + // The UNUM_PATTERN_ styles are not supported here + // because this method does not take a pattern string. + if (!isStyleSupported(style)) { + status = U_UNSUPPORTED_ERROR; + return NULL; + } - case kCurrencyStyle: - f = new Win32NumberFormat(desiredLocale, curr, status); +#if U_PLATFORM_USES_ONLY_WIN32_API + if (!mustBeDecimalFormat) { + char buffer[8]; + int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); + + // if the locale has "@compat=host", create a host-specific NumberFormat + if (U_SUCCESS(status) && count > 0 && uprv_strcmp(buffer, "host") == 0) { + Win32NumberFormat *f = NULL; + UBool curr = TRUE; + + switch (style) { + case UNUM_DECIMAL: + curr = FALSE; + // fall-through + + case UNUM_CURRENCY: + case UNUM_CURRENCY_ISO: // do not support plural formatting here + case UNUM_CURRENCY_PLURAL: + case UNUM_CURRENCY_ACCOUNTING: + case UNUM_CASH_CURRENCY: + case UNUM_CURRENCY_STANDARD: + f = new Win32NumberFormat(desiredLocale, curr, status); + + if (U_SUCCESS(status)) { + return f; + } - if (U_SUCCESS(status)) { - return f; + delete f; + break; + default: + break; } - - delete f; - break; - - default: - break; } } #endif + // Use numbering system cache hashtable + umtx_initOnce(gNSCacheInitOnce, &nscacheInit); + + // Get cached numbering system + LocalPointer ownedNs; + NumberingSystem *ns = NULL; + if (NumberingSystem_cache != NULL) { + // TODO: Bad hash key usage, see ticket #8504. + int32_t hashKey = desiredLocale.hashCode(); + + Mutex lock(&nscacheMutex); + ns = (NumberingSystem *)uhash_iget(NumberingSystem_cache, hashKey); + if (ns == NULL) { + ns = NumberingSystem::createInstance(desiredLocale,status); + uhash_iput(NumberingSystem_cache, hashKey, (void*)ns, &status); + } + } else { + ownedNs.adoptInstead(NumberingSystem::createInstance(desiredLocale,status)); + ns = ownedNs.getAlias(); + } - NumberFormat* f = NULL; - DecimalFormatSymbols* symbolsToAdopt = NULL; - UnicodeString pattern; - UResourceBundle *resource = ures_open((char *)0, desiredLocale.getName(), &status); - UResourceBundle *numberPatterns = ures_getByKey(resource, DecimalFormat::fgNumberPatterns, NULL, &status); - + // check results of getting a numbering system if (U_FAILURE(status)) { - // We don't appear to have resource data available -- use the last-resort data - status = U_USING_FALLBACK_WARNING; - // When the data is unavailable, and locale isn't passed in, last resort data is used. - symbolsToAdopt = new DecimalFormatSymbols(status); + return NULL; + } + + if (mustBeDecimalFormat && ns->isAlgorithmic()) { + status = U_UNSUPPORTED_ERROR; + return NULL; + } - // Creates a DecimalFormat instance with the last resort number patterns. - pattern.setTo(TRUE, gLastResortNumberPatterns[style], -1); + LocalPointer symbolsToAdopt; + UnicodeString pattern; + LocalUResourceBundlePointer ownedResource(ures_open(NULL, desiredLocale.getName(), &status)); + if (U_FAILURE(status)) { + return NULL; } else { - // If not all the styled patterns exists for the NumberFormat in this locale, - // sets the status code to failure and returns nil. - if (ures_getSize(numberPatterns) < (int32_t)(sizeof(gLastResortNumberPatterns)/sizeof(gLastResortNumberPatterns[0]))) { - status = U_INVALID_FORMAT_ERROR; - goto cleanup; + // Loads the decimal symbols of the desired locale. + symbolsToAdopt.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(desiredLocale, status), status); + if (U_FAILURE(status)) { + return NULL; } - // Loads the decimal symbols of the desired locale. - symbolsToAdopt = new DecimalFormatSymbols(desiredLocale, status); + UResourceBundle *resource = ownedResource.orphan(); + UResourceBundle *numElements = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status); + resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &status); + resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); + ownedResource.adoptInstead(resource); int32_t patLen = 0; - const UChar *patResStr = ures_getStringByIndex(numberPatterns, (int32_t)style, &patLen, &status); + const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); + + // Didn't find a pattern specific to the numbering system, so fall back to "latn" + if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(gLatn,ns->getName())) { + status = U_ZERO_ERROR; + resource = ures_getByKeyWithFallback(numElements, gLatn, resource, &status); + resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); + patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); + } + + ures_close(numElements); + // Creates the specified decimal format style of the desired locale. pattern.setTo(TRUE, patResStr, patLen); } - if (U_FAILURE(status) || symbolsToAdopt == NULL) { - goto cleanup; + if (U_FAILURE(status)) { + return NULL; } - if(style==kCurrencyStyle){ + if(style==UNUM_CURRENCY || style == UNUM_CURRENCY_ISO || style == UNUM_CURRENCY_ACCOUNTING + || style == UNUM_CASH_CURRENCY || style == UNUM_CURRENCY_STANDARD){ const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); if(currPattern!=NULL){ pattern.setTo(currPattern, u_strlen(currPattern)); } } - f = new DecimalFormat(pattern, symbolsToAdopt, status); - if (U_FAILURE(status) || f == NULL) { - goto cleanup; - } - f->setLocaleIDs(ures_getLocaleByType(numberPatterns, ULOC_VALID_LOCALE, &status), - ures_getLocaleByType(numberPatterns, ULOC_ACTUAL_LOCALE, &status)); -cleanup: - ures_close(numberPatterns); - ures_close(resource); - if (U_FAILURE(status)) { - /* If f exists, then it will delete the symbols */ - if (f==NULL) { - delete symbolsToAdopt; + + NumberFormat *f; + if (ns->isAlgorithmic()) { + UnicodeString nsDesc; + UnicodeString nsRuleSetGroup; + UnicodeString nsRuleSetName; + Locale nsLoc; + URBNFRuleSetTag desiredRulesType = URBNF_NUMBERING_SYSTEM; + + nsDesc.setTo(ns->getDescription()); + int32_t firstSlash = nsDesc.indexOf(gSlash); + int32_t lastSlash = nsDesc.lastIndexOf(gSlash); + if ( lastSlash > firstSlash ) { + CharString nsLocID; + + nsLocID.appendInvariantChars(nsDesc.tempSubString(0, firstSlash), status); + nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); + nsRuleSetName.setTo(nsDesc,lastSlash+1); + + nsLoc = Locale::createFromName(nsLocID.data()); + + UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); + if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { + desiredRulesType = URBNF_SPELLOUT; + } + } else { + nsLoc = desiredLocale; + nsRuleSetName.setTo(nsDesc); } - else { - delete f; + + RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); + if (r == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + r->setDefaultRuleSet(nsRuleSetName,status); + f = r; + } else { + // replace single currency sign in the pattern with double currency sign + // if the style is UNUM_CURRENCY_ISO + if (style == UNUM_CURRENCY_ISO) { + pattern.findAndReplace(UnicodeString(TRUE, gSingleCurrencySign, 1), + UnicodeString(TRUE, gDoubleCurrencySign, 2)); + } + + // "new DecimalFormat()" does not adopt the symbols if its memory allocation fails. + DecimalFormatSymbols *syms = symbolsToAdopt.orphan(); + DecimalFormat* df = new DecimalFormat(pattern, syms, style, status); + + // if it is cash currency style, setCurrencyUsage with usage + if (style == UNUM_CASH_CURRENCY){ + df->setCurrencyUsage(UCURR_USAGE_CASH, &status); + } + + if (U_FAILURE(status)) { + delete df; + return NULL; + } + + f = df; + if (f == NULL) { + delete syms; + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; } - return NULL; } - if (f == NULL || symbolsToAdopt == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - f = NULL; + + f->setLocaleIDs(ures_getLocaleByType(ownedResource.getAlias(), ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(ownedResource.getAlias(), ULOC_ACTUAL_LOCALE, &status)); + if (U_FAILURE(status)) { + delete f; + return NULL; } return f; }