michael@0: /* michael@0: ******************************************************************************* michael@0: * Copyright (C) 1997-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************************* michael@0: * michael@0: * File NUMFMT.CPP michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 02/19/97 aliu Converted from java. michael@0: * 03/18/97 clhuang Implemented with C++ APIs. michael@0: * 04/17/97 aliu Enlarged MAX_INTEGER_DIGITS to fully accomodate the michael@0: * largest double, by default. michael@0: * Changed DigitCount to int per code review. michael@0: * 07/20/98 stephen Changed operator== to check for grouping michael@0: * Changed setMaxIntegerDigits per Java implementation. michael@0: * Changed setMinIntegerDigits per Java implementation. michael@0: * Changed setMinFractionDigits per Java implementation. michael@0: * Changed setMaxFractionDigits per Java implementation. michael@0: ******************************************************************************** michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/numfmt.h" michael@0: #include "unicode/locid.h" michael@0: #include "unicode/dcfmtsym.h" michael@0: #include "unicode/decimfmt.h" michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/ucurr.h" michael@0: #include "unicode/curramt.h" michael@0: #include "unicode/numsys.h" michael@0: #include "unicode/rbnf.h" michael@0: #include "unicode/localpointer.h" michael@0: #include "charstr.h" michael@0: #include "winnmfmt.h" michael@0: #include "uresimp.h" michael@0: #include "uhash.h" michael@0: #include "cmemory.h" michael@0: #include "servloc.h" michael@0: #include "ucln_in.h" michael@0: #include "cstring.h" michael@0: #include "putilimp.h" michael@0: #include "uassert.h" michael@0: #include "umutex.h" michael@0: #include "mutex.h" michael@0: #include "digitlst.h" michael@0: #include michael@0: michael@0: //#define FMT_DEBUG michael@0: michael@0: #ifdef FMT_DEBUG michael@0: #include michael@0: static inline void debugout(UnicodeString s) { michael@0: char buf[2000]; michael@0: s.extract((int32_t) 0, s.length(), buf); michael@0: printf("%s", buf); michael@0: } michael@0: #define debug(x) printf("%s", x); michael@0: #else michael@0: #define debugout(x) michael@0: #define debug(x) michael@0: #endif michael@0: michael@0: // If no number pattern can be located for a locale, this is the last michael@0: // resort. michael@0: static const UChar gLastResortDecimalPat[] = { michael@0: 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x3B, 0x2D, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#0.###;-#0.###" */ michael@0: }; michael@0: static const UChar gLastResortCurrencyPat[] = { michael@0: 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x3B, 0x28, 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x29, 0 /* "$#0.00;($#0.00)" */ michael@0: }; michael@0: static const UChar gLastResortPercentPat[] = { michael@0: 0x23, 0x30, 0x25, 0 /* "#0%" */ michael@0: }; michael@0: static const UChar gLastResortScientificPat[] = { michael@0: 0x23, 0x45, 0x30, 0 /* "#E0" */ michael@0: }; michael@0: static const UChar gLastResortIsoCurrencyPat[] = { michael@0: 0xA4, 0xA4, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x3B, 0x28, 0xA4, 0xA4, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x29, 0 /* "\u00A4\u00A4#0.00;(\u00A4\u00A4#0.00)" */ michael@0: }; michael@0: static const UChar gLastResortPluralCurrencyPat[] = { michael@0: 0x23, 0x30, 0x2E, 0x30, 0x30, 0xA0, 0xA4, 0xA4, 0xA4, 0 /* "#0.00\u00A0\u00A4\u00A4\u00A4*/ michael@0: }; michael@0: michael@0: static const UChar gSingleCurrencySign[] = {0xA4, 0}; michael@0: static const UChar gDoubleCurrencySign[] = {0xA4, 0xA4, 0}; michael@0: michael@0: static const UChar gSlash = 0x2f; michael@0: michael@0: // If the maximum base 10 exponent were 4, then the largest number would michael@0: // be 99,999 which has 5 digits. michael@0: // On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit michael@0: // With big decimal, the max exponent is 999,999,999 and the max number of digits is the same, 999,999,999 michael@0: const int32_t icu::NumberFormat::gDefaultMaxIntegerDigits = 2000000000; michael@0: const int32_t icu::NumberFormat::gDefaultMinIntegerDigits = 127; michael@0: michael@0: static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = { michael@0: NULL, // UNUM_PATTERN_DECIMAL michael@0: gLastResortDecimalPat, // UNUM_DECIMAL michael@0: gLastResortCurrencyPat, // UNUM_CURRENCY michael@0: gLastResortPercentPat, // UNUM_PERCENT michael@0: gLastResortScientificPat, // UNUM_SCIENTIFIC michael@0: NULL, // UNUM_SPELLOUT michael@0: NULL, // UNUM_ORDINAL michael@0: NULL, // UNUM_DURATION michael@0: NULL, // UNUM_NUMBERING_SYSTEM michael@0: NULL, // UNUM_PATTERN_RULEBASED michael@0: gLastResortIsoCurrencyPat, // UNUM_CURRENCY_ISO michael@0: gLastResortPluralCurrencyPat // UNUM_CURRENCY_PLURAL michael@0: }; michael@0: michael@0: // Keys used for accessing resource bundles michael@0: michael@0: static const char *gNumberElements = "NumberElements"; michael@0: static const char *gLatn = "latn"; michael@0: static const char *gPatterns = "patterns"; michael@0: static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = { michael@0: NULL, // UNUM_PATTERN_DECIMAL michael@0: "decimalFormat", // UNUM_DECIMAL michael@0: "currencyFormat", // UNUM_CURRENCY michael@0: "percentFormat", // UNUM_PERCENT michael@0: "scientificFormat", // UNUM_SCIENTIFIC michael@0: NULL, // UNUM_SPELLOUT michael@0: NULL, // UNUM_ORDINAL michael@0: NULL, // UNUM_DURATION michael@0: NULL, // UNUM_NUMBERING_SYSTEM michael@0: NULL, // UNUM_PATTERN_RULEBASED michael@0: // For UNUM_CURRENCY_ISO and UNUM_CURRENCY_PLURAL, michael@0: // the pattern is the same as the pattern of UNUM_CURRENCY michael@0: // except for replacing the single currency sign with michael@0: // double currency sign or triple currency sign. michael@0: "currencyFormat", // UNUM_CURRENCY_ISO michael@0: "currencyFormat" // UNUM_CURRENCY_PLURAL michael@0: }; michael@0: michael@0: // Static hashtable cache of NumberingSystem objects used by NumberFormat michael@0: static UHashtable * NumberingSystem_cache = NULL; michael@0: static UMutex nscacheMutex = U_MUTEX_INITIALIZER; michael@0: static icu::UInitOnce gNSCacheInitOnce = U_INITONCE_INITIALIZER; michael@0: michael@0: #if !UCONFIG_NO_SERVICE michael@0: static icu::ICULocaleService* gService = NULL; michael@0: static icu::UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER; michael@0: #endif michael@0: michael@0: /** michael@0: * Release all static memory held by Number Format. michael@0: */ michael@0: U_CDECL_BEGIN michael@0: static void U_CALLCONV michael@0: deleteNumberingSystem(void *obj) { michael@0: delete (icu::NumberingSystem *)obj; michael@0: } michael@0: michael@0: static UBool U_CALLCONV numfmt_cleanup(void) { michael@0: #if !UCONFIG_NO_SERVICE michael@0: gServiceInitOnce.reset(); michael@0: if (gService) { michael@0: delete gService; michael@0: gService = NULL; michael@0: } michael@0: #endif michael@0: gNSCacheInitOnce.reset(); michael@0: if (NumberingSystem_cache) { michael@0: // delete NumberingSystem_cache; michael@0: uhash_close(NumberingSystem_cache); michael@0: NumberingSystem_cache = NULL; michael@0: } michael@0: michael@0: return TRUE; michael@0: } michael@0: U_CDECL_END michael@0: michael@0: // ***************************************************************************** michael@0: // class NumberFormat michael@0: // ***************************************************************************** michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(NumberFormat) michael@0: michael@0: #if !UCONFIG_NO_SERVICE michael@0: // ------------------------------------- michael@0: // SimpleNumberFormatFactory implementation michael@0: NumberFormatFactory::~NumberFormatFactory() {} michael@0: SimpleNumberFormatFactory::SimpleNumberFormatFactory(const Locale& locale, UBool visible) michael@0: : _visible(visible) michael@0: { michael@0: LocaleUtility::initNameFromLocale(locale, _id); michael@0: } michael@0: michael@0: SimpleNumberFormatFactory::~SimpleNumberFormatFactory() {} michael@0: michael@0: UBool SimpleNumberFormatFactory::visible(void) const { michael@0: return _visible; michael@0: } michael@0: michael@0: const UnicodeString * michael@0: SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const michael@0: { michael@0: if (U_SUCCESS(status)) { michael@0: count = 1; michael@0: return &_id; michael@0: } michael@0: count = 0; michael@0: return NULL; michael@0: } michael@0: #endif /* #if !UCONFIG_NO_SERVICE */ michael@0: michael@0: // ------------------------------------- michael@0: // default constructor michael@0: NumberFormat::NumberFormat() michael@0: : fGroupingUsed(TRUE), michael@0: fMaxIntegerDigits(gDefaultMaxIntegerDigits), michael@0: fMinIntegerDigits(1), michael@0: fMaxFractionDigits(3), // invariant, >= minFractionDigits michael@0: fMinFractionDigits(0), michael@0: fParseIntegerOnly(FALSE), michael@0: fLenient(FALSE) michael@0: { michael@0: fCurrency[0] = 0; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: NumberFormat::~NumberFormat() michael@0: { michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // copy constructor michael@0: michael@0: NumberFormat::NumberFormat(const NumberFormat &source) michael@0: : Format(source) michael@0: { michael@0: *this = source; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // assignment operator michael@0: michael@0: NumberFormat& michael@0: NumberFormat::operator=(const NumberFormat& rhs) michael@0: { michael@0: if (this != &rhs) michael@0: { michael@0: Format::operator=(rhs); michael@0: fGroupingUsed = rhs.fGroupingUsed; michael@0: fMaxIntegerDigits = rhs.fMaxIntegerDigits; michael@0: fMinIntegerDigits = rhs.fMinIntegerDigits; michael@0: fMaxFractionDigits = rhs.fMaxFractionDigits; michael@0: fMinFractionDigits = rhs.fMinFractionDigits; michael@0: fParseIntegerOnly = rhs.fParseIntegerOnly; michael@0: u_strncpy(fCurrency, rhs.fCurrency, 4); michael@0: fLenient = rhs.fLenient; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: UBool michael@0: NumberFormat::operator==(const Format& that) const michael@0: { michael@0: // Format::operator== guarantees this cast is safe michael@0: NumberFormat* other = (NumberFormat*)&that; michael@0: michael@0: #ifdef FMT_DEBUG michael@0: // This code makes it easy to determine why two format objects that should michael@0: // be equal aren't. michael@0: UBool first = TRUE; michael@0: if (!Format::operator==(that)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Format::!="); michael@0: } michael@0: if (!(fMaxIntegerDigits == other->fMaxIntegerDigits && michael@0: fMinIntegerDigits == other->fMinIntegerDigits)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Integer digits !="); michael@0: } michael@0: if (!(fMaxFractionDigits == other->fMaxFractionDigits && michael@0: fMinFractionDigits == other->fMinFractionDigits)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Fraction digits !="); michael@0: } michael@0: if (!(fGroupingUsed == other->fGroupingUsed)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("fGroupingUsed != "); michael@0: } michael@0: if (!(fParseIntegerOnly == other->fParseIntegerOnly)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("fParseIntegerOnly != "); michael@0: } michael@0: if (!(u_strcmp(fCurrency, other->fCurrency) == 0)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("fCurrency !="); michael@0: } michael@0: if (!(fLenient == other->fLenient)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("fLenient != "); michael@0: } michael@0: if (!first) { printf(" ]"); } michael@0: #endif michael@0: michael@0: return ((this == &that) || michael@0: ((Format::operator==(that) && michael@0: fMaxIntegerDigits == other->fMaxIntegerDigits && michael@0: fMinIntegerDigits == other->fMinIntegerDigits && michael@0: fMaxFractionDigits == other->fMaxFractionDigits && michael@0: fMinFractionDigits == other->fMinFractionDigits && michael@0: fGroupingUsed == other->fGroupingUsed && michael@0: fParseIntegerOnly == other->fParseIntegerOnly && michael@0: u_strcmp(fCurrency, other->fCurrency) == 0 && michael@0: fLenient == other->fLenient))); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Default implementation sets unsupported error; subclasses should michael@0: // override. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(double /* unused number */, michael@0: UnicodeString& toAppendTo, michael@0: FieldPositionIterator* /* unused posIter */, michael@0: UErrorCode& status) const michael@0: { michael@0: if (!U_FAILURE(status)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return toAppendTo; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Default implementation sets unsupported error; subclasses should michael@0: // override. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int32_t /* unused number */, michael@0: UnicodeString& toAppendTo, michael@0: FieldPositionIterator* /* unused posIter */, michael@0: UErrorCode& status) const michael@0: { michael@0: if (!U_FAILURE(status)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return toAppendTo; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Default implementation sets unsupported error; subclasses should michael@0: // override. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int64_t /* unused number */, michael@0: UnicodeString& toAppendTo, michael@0: FieldPositionIterator* /* unused posIter */, michael@0: UErrorCode& status) const michael@0: { michael@0: if (!U_FAILURE(status)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return toAppendTo; michael@0: } michael@0: michael@0: // ------------------------------------------ michael@0: // These functions add the status code, just fall back to the non-status versions michael@0: UnicodeString& michael@0: NumberFormat::format(double number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode &status) const { michael@0: if(U_SUCCESS(status)) { michael@0: return format(number,appendTo,pos); michael@0: } else { michael@0: return appendTo; michael@0: } michael@0: } michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int32_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode &status) const { michael@0: if(U_SUCCESS(status)) { michael@0: return format(number,appendTo,pos); michael@0: } else { michael@0: return appendTo; michael@0: } michael@0: } michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int64_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode &status) const { michael@0: if(U_SUCCESS(status)) { michael@0: return format(number,appendTo,pos); michael@0: } else { michael@0: return appendTo; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // Decimal Number format() default implementation michael@0: // Subclasses do not normally override this function, but rather the DigitList michael@0: // formatting functions.. michael@0: // The expected call chain from here is michael@0: // this function -> michael@0: // NumberFormat::format(Formattable -> michael@0: // DecimalFormat::format(DigitList michael@0: // michael@0: // Or, for subclasses of Formattable that do not know about DigitList, michael@0: // this Function -> michael@0: // NumberFormat::format(Formattable -> michael@0: // NumberFormat::format(DigitList -> michael@0: // XXXFormat::format(double michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(const StringPiece &decimalNum, michael@0: UnicodeString& toAppendTo, michael@0: FieldPositionIterator* fpi, michael@0: UErrorCode& status) const michael@0: { michael@0: Formattable f; michael@0: f.setDecimalNumber(decimalNum, status); michael@0: format(f, toAppendTo, fpi, status); michael@0: return toAppendTo; michael@0: } michael@0: michael@0: /** michael@0: * michael@0: // Formats the number object and save the format michael@0: // result in the toAppendTo string buffer. michael@0: michael@0: // utility to save/restore state, used in two overloads michael@0: // of format(const Formattable&...) below. michael@0: * michael@0: * Old purpose of ArgExtractor was to avoid const. Not thread safe! michael@0: * michael@0: * keeping it around as a shim. michael@0: */ michael@0: class ArgExtractor { michael@0: const Formattable* num; michael@0: UChar save[4]; michael@0: UBool fWasCurrency; michael@0: michael@0: public: michael@0: ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status); michael@0: ~ArgExtractor(); michael@0: michael@0: const Formattable* number(void) const; michael@0: const UChar *iso(void) const; michael@0: UBool wasCurrency(void) const; michael@0: }; michael@0: michael@0: inline const Formattable* michael@0: ArgExtractor::number(void) const { michael@0: return num; michael@0: } michael@0: michael@0: inline UBool michael@0: ArgExtractor::wasCurrency(void) const { michael@0: return fWasCurrency; michael@0: } michael@0: michael@0: inline const UChar * michael@0: ArgExtractor::iso(void) const { michael@0: return save; michael@0: } michael@0: michael@0: ArgExtractor::ArgExtractor(const NumberFormat& /*nf*/, const Formattable& obj, UErrorCode& /*status*/) michael@0: : num(&obj), fWasCurrency(FALSE) { michael@0: michael@0: const UObject* o = obj.getObject(); // most commonly o==NULL michael@0: const CurrencyAmount* amt; michael@0: if (o != NULL && (amt = dynamic_cast(o)) != NULL) { michael@0: // getISOCurrency() returns a pointer to internal storage, so we michael@0: // copy it to retain it across the call to setCurrency(). michael@0: //const UChar* curr = amt->getISOCurrency(); michael@0: u_strcpy(save, amt->getISOCurrency()); michael@0: num = &amt->getNumber(); michael@0: fWasCurrency=TRUE; michael@0: } else { michael@0: save[0]=0; michael@0: } michael@0: } michael@0: michael@0: ArgExtractor::~ArgExtractor() { michael@0: } michael@0: michael@0: UnicodeString& NumberFormat::format(const DigitList &number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionIterator* posIter, michael@0: UErrorCode& status) const { michael@0: // DecimalFormat overrides this function, and handles DigitList based big decimals. michael@0: // Other subclasses (ChoiceFormat, RuleBasedNumberFormat) do not (yet) handle DigitLists, michael@0: // so this default implementation falls back to formatting decimal numbers as doubles. michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: double dnum = number.getDouble(); michael@0: format(dnum, appendTo, posIter, status); michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(const DigitList &number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode &status) const { michael@0: // DecimalFormat overrides this function, and handles DigitList based big decimals. michael@0: // Other subclasses (ChoiceFormat, RuleBasedNumberFormat) do not (yet) handle DigitLists, michael@0: // so this default implementation falls back to formatting decimal numbers as doubles. michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: double dnum = number.getDouble(); michael@0: format(dnum, appendTo, pos, status); michael@0: return appendTo; michael@0: } michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(const Formattable& obj, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode& status) const michael@0: { michael@0: if (U_FAILURE(status)) return appendTo; michael@0: michael@0: ArgExtractor arg(*this, obj, status); michael@0: const Formattable *n = arg.number(); michael@0: const UChar *iso = arg.iso(); michael@0: michael@0: if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { michael@0: // trying to format a different currency. michael@0: // Right now, we clone. michael@0: LocalPointer cloneFmt((NumberFormat*)this->clone()); michael@0: cloneFmt->setCurrency(iso, status); michael@0: // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. michael@0: return cloneFmt->format(*n, appendTo, pos, status); michael@0: } michael@0: michael@0: if (n->isNumeric() && n->getDigitList() != NULL) { michael@0: // Decimal Number. We will have a DigitList available if the value was michael@0: // set to a decimal number, or if the value originated with a parse. michael@0: // michael@0: // The default implementation for formatting a DigitList converts it michael@0: // to a double, and formats that, allowing formatting classes that don't michael@0: // know about DigitList to continue to operate as they had. michael@0: // michael@0: // DecimalFormat overrides the DigitList formatting functions. michael@0: format(*n->getDigitList(), appendTo, pos, status); michael@0: } else { michael@0: switch (n->getType()) { michael@0: case Formattable::kDouble: michael@0: format(n->getDouble(), appendTo, pos); michael@0: break; michael@0: case Formattable::kLong: michael@0: format(n->getLong(), appendTo, pos); michael@0: break; michael@0: case Formattable::kInt64: michael@0: format(n->getInt64(), appendTo, pos); michael@0: break; michael@0: default: michael@0: status = U_INVALID_FORMAT_ERROR; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return appendTo; michael@0: } michael@0: michael@0: // -------------------------------------x michael@0: // Formats the number object and save the format michael@0: // result in the toAppendTo string buffer. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(const Formattable& obj, michael@0: UnicodeString& appendTo, michael@0: FieldPositionIterator* posIter, michael@0: UErrorCode& status) const michael@0: { michael@0: if (U_FAILURE(status)) return appendTo; michael@0: michael@0: ArgExtractor arg(*this, obj, status); michael@0: const Formattable *n = arg.number(); michael@0: const UChar *iso = arg.iso(); michael@0: michael@0: if(arg.wasCurrency() && u_strcmp(iso, getCurrency())) { michael@0: // trying to format a different currency. michael@0: // Right now, we clone. michael@0: LocalPointer cloneFmt((NumberFormat*)this->clone()); michael@0: cloneFmt->setCurrency(iso, status); michael@0: // next line should NOT recurse, because n is numeric whereas obj was a wrapper around currency amount. michael@0: return cloneFmt->format(*n, appendTo, posIter, status); michael@0: } michael@0: michael@0: if (n->isNumeric() && n->getDigitList() != NULL) { michael@0: // Decimal Number michael@0: format(*n->getDigitList(), appendTo, posIter, status); michael@0: } else { michael@0: switch (n->getType()) { michael@0: case Formattable::kDouble: michael@0: format(n->getDouble(), appendTo, posIter, status); michael@0: break; michael@0: case Formattable::kLong: michael@0: format(n->getLong(), appendTo, posIter, status); michael@0: break; michael@0: case Formattable::kInt64: michael@0: format(n->getInt64(), appendTo, posIter, status); michael@0: break; michael@0: default: michael@0: status = U_INVALID_FORMAT_ERROR; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return appendTo; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int64_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos) const michael@0: { michael@0: // default so we don't introduce a new abstract method michael@0: return format((int32_t)number, appendTo, pos); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Parses the string and save the result object as well michael@0: // as the final parsed position. michael@0: michael@0: void michael@0: NumberFormat::parseObject(const UnicodeString& source, michael@0: Formattable& result, michael@0: ParsePosition& parse_pos) const michael@0: { michael@0: parse(source, result, parse_pos); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Formats a double number and save the result in a string. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(double number, UnicodeString& appendTo) const michael@0: { michael@0: FieldPosition pos(0); michael@0: return format(number, appendTo, pos); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Formats a long number and save the result in a string. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int32_t number, UnicodeString& appendTo) const michael@0: { michael@0: FieldPosition pos(0); michael@0: return format(number, appendTo, pos); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Formats a long number and save the result in a string. michael@0: michael@0: UnicodeString& michael@0: NumberFormat::format(int64_t number, UnicodeString& appendTo) const michael@0: { michael@0: FieldPosition pos(0); michael@0: return format(number, appendTo, pos); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Parses the text and save the result object. If the returned michael@0: // parse position is 0, that means the parsing failed, the status michael@0: // code needs to be set to failure. Ignores the returned parse michael@0: // position, otherwise. michael@0: michael@0: void michael@0: NumberFormat::parse(const UnicodeString& text, michael@0: Formattable& result, michael@0: UErrorCode& status) const michael@0: { michael@0: if (U_FAILURE(status)) return; michael@0: michael@0: ParsePosition parsePosition(0); michael@0: parse(text, result, parsePosition); michael@0: if (parsePosition.getIndex() == 0) { michael@0: status = U_INVALID_FORMAT_ERROR; michael@0: } michael@0: } michael@0: michael@0: CurrencyAmount* NumberFormat::parseCurrency(const UnicodeString& text, michael@0: ParsePosition& pos) const { michael@0: // Default implementation only -- subclasses should override michael@0: Formattable parseResult; michael@0: int32_t start = pos.getIndex(); michael@0: parse(text, parseResult, pos); michael@0: if (pos.getIndex() != start) { michael@0: UChar curr[4]; michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: getEffectiveCurrency(curr, ec); michael@0: if (U_SUCCESS(ec)) { michael@0: LocalPointer currAmt(new CurrencyAmount(parseResult, curr, ec)); michael@0: if (U_FAILURE(ec)) { michael@0: pos.setIndex(start); // indicate failure michael@0: } else { michael@0: return currAmt.orphan(); michael@0: } michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets to only parse integers. michael@0: michael@0: void michael@0: NumberFormat::setParseIntegerOnly(UBool value) michael@0: { michael@0: fParseIntegerOnly = value; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets whether lenient parse is enabled. michael@0: michael@0: void michael@0: NumberFormat::setLenient(UBool enable) michael@0: { michael@0: fLenient = enable; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a number style NumberFormat instance with the default locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createInstance(UErrorCode& status) michael@0: { michael@0: return createInstance(Locale::getDefault(), UNUM_DECIMAL, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a number style NumberFormat instance with the inLocale locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) michael@0: { michael@0: return createInstance(inLocale, UNUM_DECIMAL, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a currency style NumberFormat instance with the default locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createCurrencyInstance(UErrorCode& status) michael@0: { michael@0: return createCurrencyInstance(Locale::getDefault(), status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a currency style NumberFormat instance with the inLocale locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) michael@0: { michael@0: return createInstance(inLocale, UNUM_CURRENCY, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a percent style NumberFormat instance with the default locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createPercentInstance(UErrorCode& status) michael@0: { michael@0: return createInstance(Locale::getDefault(), UNUM_PERCENT, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a percent style NumberFormat instance with the inLocale locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) michael@0: { michael@0: return createInstance(inLocale, UNUM_PERCENT, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a scientific style NumberFormat instance with the default locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createScientificInstance(UErrorCode& status) michael@0: { michael@0: return createInstance(Locale::getDefault(), UNUM_SCIENTIFIC, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Create a scientific style NumberFormat instance with the inLocale locale. michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) michael@0: { michael@0: return createInstance(inLocale, UNUM_SCIENTIFIC, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: const Locale* U_EXPORT2 michael@0: NumberFormat::getAvailableLocales(int32_t& count) michael@0: { michael@0: return Locale::getAvailableLocales(count); michael@0: } michael@0: michael@0: // ------------------------------------------ michael@0: // michael@0: // Registration michael@0: // michael@0: //------------------------------------------- michael@0: michael@0: #if !UCONFIG_NO_SERVICE michael@0: michael@0: // ------------------------------------- michael@0: michael@0: class ICUNumberFormatFactory : public ICUResourceBundleFactory { michael@0: public: michael@0: virtual ~ICUNumberFormatFactory(); michael@0: protected: michael@0: virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /* service */, UErrorCode& status) const { michael@0: return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); michael@0: } michael@0: }; michael@0: michael@0: ICUNumberFormatFactory::~ICUNumberFormatFactory() {} michael@0: michael@0: // ------------------------------------- michael@0: michael@0: class NFFactory : public LocaleKeyFactory { michael@0: private: michael@0: NumberFormatFactory* _delegate; michael@0: Hashtable* _ids; michael@0: michael@0: public: michael@0: NFFactory(NumberFormatFactory* delegate) michael@0: : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) michael@0: , _delegate(delegate) michael@0: , _ids(NULL) michael@0: { michael@0: } michael@0: michael@0: virtual ~NFFactory(); michael@0: michael@0: virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const michael@0: { michael@0: if (handlesKey(key, status)) { michael@0: const LocaleKey& lkey = (const LocaleKey&)key; michael@0: Locale loc; michael@0: lkey.canonicalLocale(loc); michael@0: int32_t kind = lkey.kind(); michael@0: michael@0: UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)kind); michael@0: if (result == NULL) { michael@0: result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status); michael@0: } michael@0: return result; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: protected: michael@0: /** michael@0: * Return the set of ids that this factory supports (visible or michael@0: * otherwise). This can be called often and might need to be michael@0: * cached if it is expensive to create. michael@0: */ michael@0: virtual const Hashtable* getSupportedIDs(UErrorCode& status) const michael@0: { michael@0: if (U_SUCCESS(status)) { michael@0: if (!_ids) { michael@0: int32_t count = 0; michael@0: const UnicodeString * const idlist = _delegate->getSupportedIDs(count, status); michael@0: ((NFFactory*)this)->_ids = new Hashtable(status); /* cast away const */ michael@0: if (_ids) { michael@0: for (int i = 0; i < count; ++i) { michael@0: _ids->put(idlist[i], (void*)this, status); michael@0: } michael@0: } michael@0: } michael@0: return _ids; michael@0: } michael@0: return NULL; michael@0: } michael@0: }; michael@0: michael@0: NFFactory::~NFFactory() michael@0: { michael@0: delete _delegate; michael@0: delete _ids; michael@0: } michael@0: michael@0: class ICUNumberFormatService : public ICULocaleService { michael@0: public: michael@0: ICUNumberFormatService() michael@0: : ICULocaleService(UNICODE_STRING_SIMPLE("Number Format")) michael@0: { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: registerFactory(new ICUNumberFormatFactory(), status); michael@0: } michael@0: michael@0: virtual ~ICUNumberFormatService(); michael@0: michael@0: virtual UObject* cloneInstance(UObject* instance) const { michael@0: return ((NumberFormat*)instance)->clone(); michael@0: } michael@0: michael@0: virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /* actualID */, UErrorCode& status) const { michael@0: LocaleKey& lkey = (LocaleKey&)key; michael@0: int32_t kind = lkey.kind(); michael@0: Locale loc; michael@0: lkey.currentLocale(loc); michael@0: return NumberFormat::makeInstance(loc, (UNumberFormatStyle)kind, status); michael@0: } michael@0: michael@0: virtual UBool isDefault() const { michael@0: return countFactories() == 1; michael@0: } michael@0: }; michael@0: michael@0: ICUNumberFormatService::~ICUNumberFormatService() {} michael@0: michael@0: // ------------------------------------- michael@0: michael@0: static void U_CALLCONV initNumberFormatService() { michael@0: U_ASSERT(gService == NULL); michael@0: ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); michael@0: gService = new ICUNumberFormatService(); michael@0: } michael@0: michael@0: static ICULocaleService* michael@0: getNumberFormatService(void) michael@0: { michael@0: umtx_initOnce(gServiceInitOnce, &initNumberFormatService); michael@0: return gService; michael@0: } michael@0: michael@0: static UBool haveService() { michael@0: return !gServiceInitOnce.isReset() && (getNumberFormatService() != NULL); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: URegistryKey U_EXPORT2 michael@0: NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) michael@0: { michael@0: ICULocaleService *service = getNumberFormatService(); michael@0: if (service) { michael@0: NFFactory *tempnnf = new NFFactory(toAdopt); michael@0: if (tempnnf != NULL) { michael@0: return service->registerFactory(tempnnf, status); michael@0: } michael@0: } michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: UBool U_EXPORT2 michael@0: NumberFormat::unregister(URegistryKey key, UErrorCode& status) michael@0: { michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: if (haveService()) { michael@0: return gService->unregister(key, status); michael@0: } else { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return FALSE; michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: StringEnumeration* U_EXPORT2 michael@0: NumberFormat::getAvailableLocales(void) michael@0: { michael@0: ICULocaleService *service = getNumberFormatService(); michael@0: if (service) { michael@0: return service->getAvailableLocales(); michael@0: } michael@0: return NULL; // no way to return error condition michael@0: } michael@0: #endif /* UCONFIG_NO_SERVICE */ michael@0: // ------------------------------------- michael@0: michael@0: NumberFormat* U_EXPORT2 michael@0: NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) { michael@0: #if !UCONFIG_NO_SERVICE michael@0: if (haveService()) { michael@0: return (NumberFormat*)gService->get(loc, kind, status); michael@0: } michael@0: #endif michael@0: return makeInstance(loc, kind, status); michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // Checks if the thousand/10 thousand grouping is used in the michael@0: // NumberFormat instance. michael@0: michael@0: UBool michael@0: NumberFormat::isGroupingUsed() const michael@0: { michael@0: return fGroupingUsed; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets to use the thousand/10 thousand grouping in the michael@0: // NumberFormat instance. michael@0: michael@0: void michael@0: NumberFormat::setGroupingUsed(UBool newValue) michael@0: { michael@0: fGroupingUsed = newValue; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Gets the maximum number of digits for the integral part for michael@0: // this NumberFormat instance. michael@0: michael@0: int32_t NumberFormat::getMaximumIntegerDigits() const michael@0: { michael@0: return fMaxIntegerDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets the maximum number of digits for the integral part for michael@0: // this NumberFormat instance. michael@0: michael@0: void michael@0: NumberFormat::setMaximumIntegerDigits(int32_t newValue) michael@0: { michael@0: fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); michael@0: if(fMinIntegerDigits > fMaxIntegerDigits) michael@0: fMinIntegerDigits = fMaxIntegerDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Gets the minimum number of digits for the integral part for michael@0: // this NumberFormat instance. michael@0: michael@0: int32_t michael@0: NumberFormat::getMinimumIntegerDigits() const michael@0: { michael@0: return fMinIntegerDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets the minimum number of digits for the integral part for michael@0: // this NumberFormat instance. michael@0: michael@0: void michael@0: NumberFormat::setMinimumIntegerDigits(int32_t newValue) michael@0: { michael@0: fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); michael@0: if(fMinIntegerDigits > fMaxIntegerDigits) michael@0: fMaxIntegerDigits = fMinIntegerDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Gets the maximum number of digits for the fractional part for michael@0: // this NumberFormat instance. michael@0: michael@0: int32_t michael@0: NumberFormat::getMaximumFractionDigits() const michael@0: { michael@0: return fMaxFractionDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets the maximum number of digits for the fractional part for michael@0: // this NumberFormat instance. michael@0: michael@0: void michael@0: NumberFormat::setMaximumFractionDigits(int32_t newValue) michael@0: { michael@0: fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMaxIntegerDigits)); michael@0: if(fMaxFractionDigits < fMinFractionDigits) michael@0: fMinFractionDigits = fMaxFractionDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Gets the minimum number of digits for the fractional part for michael@0: // this NumberFormat instance. michael@0: michael@0: int32_t michael@0: NumberFormat::getMinimumFractionDigits() const michael@0: { michael@0: return fMinFractionDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets the minimum number of digits for the fractional part for michael@0: // this NumberFormat instance. michael@0: michael@0: void michael@0: NumberFormat::setMinimumFractionDigits(int32_t newValue) michael@0: { michael@0: fMinFractionDigits = uprv_max(0, uprv_min(newValue, gDefaultMinIntegerDigits)); michael@0: if (fMaxFractionDigits < fMinFractionDigits) michael@0: fMaxFractionDigits = fMinFractionDigits; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void NumberFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { michael@0: if (U_FAILURE(ec)) { michael@0: return; michael@0: } michael@0: if (theCurrency) { michael@0: u_strncpy(fCurrency, theCurrency, 3); michael@0: fCurrency[3] = 0; michael@0: } else { michael@0: fCurrency[0] = 0; michael@0: } michael@0: } michael@0: michael@0: const UChar* NumberFormat::getCurrency() const { michael@0: return fCurrency; michael@0: } michael@0: michael@0: void NumberFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { michael@0: const UChar* c = getCurrency(); michael@0: if (*c != 0) { michael@0: u_strncpy(result, c, 3); michael@0: result[3] = 0; michael@0: } else { michael@0: const char* loc = getLocaleID(ULOC_VALID_LOCALE, ec); michael@0: if (loc == NULL) { michael@0: loc = uloc_getDefault(); michael@0: } michael@0: ucurr_forLocale(loc, result, 4, &ec); michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Creates the NumberFormat instance of the specified style (number, currency, michael@0: // or percent) for the desired locale. michael@0: michael@0: static void U_CALLCONV nscacheInit() { michael@0: U_ASSERT(NumberingSystem_cache == NULL); michael@0: ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: NumberingSystem_cache = uhash_open(uhash_hashLong, michael@0: uhash_compareLong, michael@0: NULL, michael@0: &status); michael@0: if (U_FAILURE(status)) { michael@0: // Number Format code will run with no cache if creation fails. michael@0: NumberingSystem_cache = NULL; michael@0: return; michael@0: } michael@0: uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem); michael@0: } michael@0: michael@0: UBool michael@0: NumberFormat::isStyleSupported(UNumberFormatStyle style) { michael@0: return gLastResortNumberPatterns[style] != NULL; michael@0: } michael@0: michael@0: NumberFormat* michael@0: NumberFormat::makeInstance(const Locale& desiredLocale, michael@0: UNumberFormatStyle style, michael@0: UErrorCode& status) { michael@0: return makeInstance(desiredLocale, style, false, status); michael@0: } michael@0: michael@0: NumberFormat* michael@0: NumberFormat::makeInstance(const Locale& desiredLocale, michael@0: UNumberFormatStyle style, michael@0: UBool mustBeDecimalFormat, michael@0: UErrorCode& status) { michael@0: if (U_FAILURE(status)) return NULL; michael@0: michael@0: if (style < 0 || style >= UNUM_FORMAT_STYLE_COUNT) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: // Some styles are not supported. This is a result of merging michael@0: // the @draft ICU 4.2 NumberFormat::EStyles into the long-existing UNumberFormatStyle. michael@0: // Ticket #8503 is for reviewing/fixing/merging the two relevant implementations: michael@0: // this one and unum_open(). michael@0: // The UNUM_PATTERN_ styles are not supported here michael@0: // because this method does not take a pattern string. michael@0: if (!isStyleSupported(style)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: #if U_PLATFORM_USES_ONLY_WIN32_API michael@0: if (!mustBeDecimalFormat) { michael@0: char buffer[8]; michael@0: int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); michael@0: michael@0: // if the locale has "@compat=host", create a host-specific NumberFormat michael@0: if (U_SUCCESS(status) && count > 0 && uprv_strcmp(buffer, "host") == 0) { michael@0: Win32NumberFormat *f = NULL; michael@0: UBool curr = TRUE; michael@0: michael@0: switch (style) { michael@0: case UNUM_DECIMAL: michael@0: curr = FALSE; michael@0: // fall-through michael@0: michael@0: case UNUM_CURRENCY: michael@0: case UNUM_CURRENCY_ISO: // do not support plural formatting here michael@0: case UNUM_CURRENCY_PLURAL: michael@0: f = new Win32NumberFormat(desiredLocale, curr, status); michael@0: michael@0: if (U_SUCCESS(status)) { michael@0: return f; michael@0: } michael@0: michael@0: delete f; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: // Use numbering system cache hashtable michael@0: umtx_initOnce(gNSCacheInitOnce, &nscacheInit); michael@0: michael@0: // Get cached numbering system michael@0: LocalPointer ownedNs; michael@0: NumberingSystem *ns = NULL; michael@0: if (NumberingSystem_cache != NULL) { michael@0: // TODO: Bad hash key usage, see ticket #8504. michael@0: int32_t hashKey = desiredLocale.hashCode(); michael@0: michael@0: Mutex lock(&nscacheMutex); michael@0: ns = (NumberingSystem *)uhash_iget(NumberingSystem_cache, hashKey); michael@0: if (ns == NULL) { michael@0: ns = NumberingSystem::createInstance(desiredLocale,status); michael@0: uhash_iput(NumberingSystem_cache, hashKey, (void*)ns, &status); michael@0: } michael@0: } else { michael@0: ownedNs.adoptInstead(NumberingSystem::createInstance(desiredLocale,status)); michael@0: ns = ownedNs.getAlias(); michael@0: } michael@0: michael@0: // check results of getting a numbering system michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (mustBeDecimalFormat && ns->isAlgorithmic()) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: LocalPointer symbolsToAdopt; michael@0: UnicodeString pattern; michael@0: LocalUResourceBundlePointer ownedResource(ures_open(NULL, desiredLocale.getName(), &status)); michael@0: if (U_FAILURE(status)) { michael@0: // We don't appear to have resource data available -- use the last-resort data michael@0: status = U_USING_FALLBACK_WARNING; michael@0: // When the data is unavailable, and locale isn't passed in, last resort data is used. michael@0: symbolsToAdopt.adoptInstead(new DecimalFormatSymbols(status)); michael@0: if (symbolsToAdopt.isNull()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: // Creates a DecimalFormat instance with the last resort number patterns. michael@0: pattern.setTo(TRUE, gLastResortNumberPatterns[style], -1); michael@0: } michael@0: else { michael@0: // Loads the decimal symbols of the desired locale. michael@0: symbolsToAdopt.adoptInstead(new DecimalFormatSymbols(desiredLocale, status)); michael@0: if (symbolsToAdopt.isNull()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: UResourceBundle *resource = ownedResource.orphan(); michael@0: UResourceBundle *numElements = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status); michael@0: resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &status); michael@0: resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); michael@0: ownedResource.adoptInstead(resource); michael@0: michael@0: int32_t patLen = 0; michael@0: const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); michael@0: michael@0: // Didn't find a pattern specific to the numbering system, so fall back to "latn" michael@0: if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(gLatn,ns->getName())) { michael@0: status = U_ZERO_ERROR; michael@0: resource = ures_getByKeyWithFallback(numElements, gLatn, resource, &status); michael@0: resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); michael@0: patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); michael@0: } michael@0: michael@0: ures_close(numElements); michael@0: michael@0: // Creates the specified decimal format style of the desired locale. michael@0: pattern.setTo(TRUE, patResStr, patLen); michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: if(style==UNUM_CURRENCY || style == UNUM_CURRENCY_ISO){ michael@0: const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); michael@0: if(currPattern!=NULL){ michael@0: pattern.setTo(currPattern, u_strlen(currPattern)); michael@0: } michael@0: } michael@0: michael@0: michael@0: NumberFormat *f; michael@0: if (ns->isAlgorithmic()) { michael@0: UnicodeString nsDesc; michael@0: UnicodeString nsRuleSetGroup; michael@0: UnicodeString nsRuleSetName; michael@0: Locale nsLoc; michael@0: URBNFRuleSetTag desiredRulesType = URBNF_NUMBERING_SYSTEM; michael@0: michael@0: nsDesc.setTo(ns->getDescription()); michael@0: int32_t firstSlash = nsDesc.indexOf(gSlash); michael@0: int32_t lastSlash = nsDesc.lastIndexOf(gSlash); michael@0: if ( lastSlash > firstSlash ) { michael@0: CharString nsLocID; michael@0: michael@0: nsLocID.appendInvariantChars(nsDesc.tempSubString(0, firstSlash), status); michael@0: nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); michael@0: nsRuleSetName.setTo(nsDesc,lastSlash+1); michael@0: michael@0: nsLoc = Locale::createFromName(nsLocID.data()); michael@0: michael@0: UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); michael@0: if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { michael@0: desiredRulesType = URBNF_SPELLOUT; michael@0: } michael@0: } else { michael@0: nsLoc = desiredLocale; michael@0: nsRuleSetName.setTo(nsDesc); michael@0: } michael@0: michael@0: RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); michael@0: if (r == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: r->setDefaultRuleSet(nsRuleSetName,status); michael@0: f = r; michael@0: } else { michael@0: // replace single currency sign in the pattern with double currency sign michael@0: // if the style is UNUM_CURRENCY_ISO michael@0: if (style == UNUM_CURRENCY_ISO) { michael@0: pattern.findAndReplace(UnicodeString(TRUE, gSingleCurrencySign, 1), michael@0: UnicodeString(TRUE, gDoubleCurrencySign, 2)); michael@0: } michael@0: michael@0: // "new DecimalFormat()" does not adopt the symbols if its memory allocation fails. michael@0: DecimalFormatSymbols *syms = symbolsToAdopt.orphan(); michael@0: f = new DecimalFormat(pattern, syms, style, status); michael@0: if (f == NULL) { michael@0: delete syms; michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: f->setLocaleIDs(ures_getLocaleByType(ownedResource.getAlias(), ULOC_VALID_LOCALE, &status), michael@0: ures_getLocaleByType(ownedResource.getAlias(), ULOC_ACTUAL_LOCALE, &status)); michael@0: if (U_FAILURE(status)) { michael@0: delete f; michael@0: return NULL; michael@0: } michael@0: return f; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */ michael@0: michael@0: //eof