diff options
Diffstat (limited to 'Build/source/libs/icu/icu-4.6/i18n/numfmt.cpp')
-rw-r--r-- | Build/source/libs/icu/icu-4.6/i18n/numfmt.cpp | 1299 |
1 files changed, 1299 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-4.6/i18n/numfmt.cpp b/Build/source/libs/icu/icu-4.6/i18n/numfmt.cpp new file mode 100644 index 00000000000..dcd24a7406b --- /dev/null +++ b/Build/source/libs/icu/icu-4.6/i18n/numfmt.cpp @@ -0,0 +1,1299 @@ +/* +******************************************************************************* +* Copyright (C) 1997-2010, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File NUMFMT.CPP +* +* Modification History: +* +* Date Name Description +* 02/19/97 aliu Converted from java. +* 03/18/97 clhuang Implemented with C++ APIs. +* 04/17/97 aliu Enlarged MAX_INTEGER_DIGITS to fully accomodate the +* largest double, by default. +* Changed DigitCount to int per code review. +* 07/20/98 stephen Changed operator== to check for grouping +* Changed setMaxIntegerDigits per Java implementation. +* Changed setMinIntegerDigits per Java implementation. +* Changed setMinFractionDigits per Java implementation. +* Changed setMaxFractionDigits per Java implementation. +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/numfmt.h" +#include "unicode/locid.h" +#include "unicode/dcfmtsym.h" +#include "unicode/decimfmt.h" +#include "unicode/ustring.h" +#include "unicode/ucurr.h" +#include "unicode/curramt.h" +#include "unicode/numsys.h" +#include "unicode/rbnf.h" +#include "winnmfmt.h" +#include "uresimp.h" +#include "uhash.h" +#include "cmemory.h" +#include "servloc.h" +#include "ucln_in.h" +#include "cstring.h" +#include "putilimp.h" +#include "umutex.h" +#include "digitlst.h" +#include <float.h> + +//#define FMT_DEBUG + +#ifdef FMT_DEBUG +#include <stdio.h> +static void debugout(UnicodeString s) { + char buf[2000]; + s.extract((int32_t) 0, s.length(), buf); + printf("%s", buf); +} +#define debug(x) printf("%s", x); +#else +#define debugout(x) +#define debug(x) +#endif + +// If no number pattern can be located for a locale, this is the last +// resort. +static const UChar gLastResortDecimalPat[] = { + 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x3B, 0x2D, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#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)" */ +}; +static const UChar gLastResortPercentPat[] = { + 0x23, 0x30, 0x25, 0 /* "#0%" */ +}; +static const UChar gLastResortScientificPat[] = { + 0x23, 0x45, 0x30, 0 /* "#E0" */ +}; +static const UChar gLastResortIsoCurrencyPat[] = { + 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)" */ +}; +static const UChar gLastResortPluralCurrencyPat[] = { + 0x23, 0x30, 0x2E, 0x30, 0x30, 0xA0, 0xA4, 0xA4, 0xA4, 0 /* "#0.00\u00A0\u00A4\u00A4\u00A4*/ +}; + +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. +// On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit +static const int32_t gMaxIntegerDigits = DBL_MAX_10_EXP + DBL_DIG + 1; +static const int32_t gMinIntegerDigits = 127; + +static const UChar * const gLastResortNumberPatterns[] = +{ + gLastResortDecimalPat, + gLastResortCurrencyPat, + gLastResortPercentPat, + gLastResortScientificPat, + gLastResortIsoCurrencyPat, + gLastResortPluralCurrencyPat, +}; + +// 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[] = { "decimalFormat", "currencyFormat", "percentFormat", "scientificFormat" }; + +// Static hashtable cache of NumberingSystem objects used by NumberFormat +static UHashtable * NumberingSystem_cache = NULL; + +static UMTX nscacheMutex = NULL; + +#if !UCONFIG_NO_SERVICE +static U_NAMESPACE_QUALIFIER ICULocaleService* gService = NULL; +#endif + +/** + * Release all static memory held by Number Format. + */ +U_CDECL_BEGIN +static void U_CALLCONV +deleteNumberingSystem(void *obj) { + delete (U_NAMESPACE_QUALIFIER NumberingSystem *)obj; +} + +static UBool U_CALLCONV numfmt_cleanup(void) { +#if !UCONFIG_NO_SERVICE + if (gService) { + delete gService; + gService = NULL; + } +#endif + if (NumberingSystem_cache) { + // delete NumberingSystem_cache; + uhash_close(NumberingSystem_cache); + NumberingSystem_cache = NULL; + } + + return TRUE; +} +U_CDECL_END + +// ***************************************************************************** +// class NumberFormat +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(NumberFormat) + +#if !UCONFIG_NO_SERVICE +// ------------------------------------- +// SimpleNumberFormatFactory implementation +NumberFormatFactory::~NumberFormatFactory() {} +SimpleNumberFormatFactory::SimpleNumberFormatFactory(const Locale& locale, UBool visible) + : _visible(visible) +{ + LocaleUtility::initNameFromLocale(locale, _id); +} + +SimpleNumberFormatFactory::~SimpleNumberFormatFactory() {} + +UBool SimpleNumberFormatFactory::visible(void) const { + return _visible; +} + +const UnicodeString * +SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + count = 1; + return &_id; + } + count = 0; + return NULL; +} +#endif /* #if !UCONFIG_NO_SERVICE */ + +// ------------------------------------- +// default constructor +NumberFormat::NumberFormat() +: fGroupingUsed(TRUE), + fMaxIntegerDigits(gMaxIntegerDigits), + fMinIntegerDigits(1), + fMaxFractionDigits(3), // invariant, >= minFractionDigits + fMinFractionDigits(0), + fParseIntegerOnly(FALSE) +{ + fCurrency[0] = 0; +} + +// ------------------------------------- + +NumberFormat::~NumberFormat() +{ +} + +// ------------------------------------- +// copy constructor + +NumberFormat::NumberFormat(const NumberFormat &source) +: Format(source) +{ + *this = source; +} + +// ------------------------------------- +// assignment operator + +NumberFormat& +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); + } + return *this; +} + +// ------------------------------------- + +UBool +NumberFormat::operator==(const Format& that) const +{ + // Format::operator== guarantees this cast is safe + NumberFormat* other = (NumberFormat*)&that; + +#ifdef FMT_DEBUG + // This code makes it easy to determine why two format objects that should + // be equal aren't. + UBool first = TRUE; + if (!Format::operator==(that)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("Format::!="); + } + if (!(fMaxIntegerDigits == other->fMaxIntegerDigits && + fMinIntegerDigits == other->fMinIntegerDigits)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("Integer digits !="); + } + if (!(fMaxFractionDigits == other->fMaxFractionDigits && + fMinFractionDigits == other->fMinFractionDigits)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("Fraction digits !="); + } + if (!(fGroupingUsed == other->fGroupingUsed)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fGroupingUsed != "); + } + if (!(fParseIntegerOnly == other->fParseIntegerOnly)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fParseIntegerOnly != "); + } + if (!(u_strcmp(fCurrency, other->fCurrency) == 0)) { + if (first) { printf("[ "); first = FALSE; } else { printf(", "); } + debug("fCurrency !="); + } + if (!first) { printf(" ]"); } +#endif + + return ((this == &that) || + ((Format::operator==(that) && + fMaxIntegerDigits == other->fMaxIntegerDigits && + fMinIntegerDigits == other->fMinIntegerDigits && + fMaxFractionDigits == other->fMaxFractionDigits && + fMinFractionDigits == other->fMinFractionDigits && + fGroupingUsed == other->fGroupingUsed && + fParseIntegerOnly == other->fParseIntegerOnly && + u_strcmp(fCurrency, other->fCurrency) == 0))); +} + +// ------------------------------------- +// 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; +} + +// ------------------------------------- +// 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. + +class ArgExtractor { + NumberFormat *ncnf; + const Formattable* num; + UBool setCurr; + UChar save[4]; + + public: + ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status); + ~ArgExtractor(); + + const Formattable* number(void) const; +}; + +inline const Formattable* +ArgExtractor::number(void) const { + return num; +} + +ArgExtractor::ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status) + : ncnf((NumberFormat*) &nf), num(&obj), setCurr(FALSE) { + + const UObject* o = obj.getObject(); // most commonly o==NULL + const CurrencyAmount* amt; + if (o != NULL && (amt = dynamic_cast<const CurrencyAmount*>(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, nf.getCurrency()); + setCurr = (u_strcmp(curr, save) != 0); + if (setCurr) { + ncnf->setCurrency(curr, status); + } + num = &amt->getNumber(); + } +} + +ArgExtractor::~ArgExtractor() { + if (setCurr) { + UErrorCode ok = U_ZERO_ERROR; + ncnf->setCurrency(save, ok); // always restore currency + } +} + +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, + FieldPosition& pos, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return appendTo; + + ArgExtractor arg(*this, obj, status); + const Formattable *n = arg.number(); + + 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; + } + } + + 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(); + + 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& +NumberFormat::format(int64_t number, + UnicodeString& appendTo, + FieldPosition& pos) const +{ + // default so we don't introduce a new abstract method + return format((int32_t)number, appendTo, pos); +} + +// ------------------------------------- +// Parses the string and save the result object as well +// as the final parsed position. + +void +NumberFormat::parseObject(const UnicodeString& source, + Formattable& result, + ParsePosition& parse_pos) const +{ + parse(source, result, parse_pos); +} + +// ------------------------------------- +// Formats a double number and save the result in a string. + +UnicodeString& +NumberFormat::format(double number, UnicodeString& appendTo) const +{ + FieldPosition pos(0); + return format(number, appendTo, pos); +} + +// ------------------------------------- +// Formats a long number and save the result in a string. + +UnicodeString& +NumberFormat::format(int32_t number, UnicodeString& appendTo) const +{ + FieldPosition pos(0); + return format(number, appendTo, pos); +} + +// ------------------------------------- +// Formats a long number and save the result in a string. + +UnicodeString& +NumberFormat::format(int64_t number, UnicodeString& appendTo) const +{ + FieldPosition pos(0); + return format(number, appendTo, pos); +} + +// ------------------------------------- +// Parses the text and save the result object. If the returned +// parse position is 0, that means the parsing failed, the status +// code needs to be set to failure. Ignores the returned parse +// position, otherwise. + +void +NumberFormat::parse(const UnicodeString& text, + Formattable& result, + UErrorCode& status) const +{ + if (U_FAILURE(status)) return; + + ParsePosition parsePosition(0); + parse(text, result, parsePosition); + if (parsePosition.getIndex() == 0) { + status = U_INVALID_FORMAT_ERROR; + } +} + +Formattable& NumberFormat::parseCurrency(const UnicodeString& text, + Formattable& result, + ParsePosition& pos) const { + // Default implementation only -- subclasses should override + int32_t start = pos.getIndex(); + parse(text, result, pos); + if (pos.getIndex() != start) { + UChar curr[4]; + UErrorCode ec = U_ZERO_ERROR; + getEffectiveCurrency(curr, ec); + if (U_SUCCESS(ec)) { + Formattable n(result); + CurrencyAmount *tempCurAmnt = new CurrencyAmount(n, curr, ec); // Use for null testing. + if (U_FAILURE(ec) || tempCurAmnt == NULL) { + pos.setIndex(start); // indicate failure + } else { + result.adoptObject(tempCurAmnt); + } + } + } + return result; +} + +// ------------------------------------- +// Sets to only parse integers. + +void +NumberFormat::setParseIntegerOnly(UBool value) +{ + fParseIntegerOnly = value; +} + +// ------------------------------------- +// Create a number style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(UErrorCode& status) +{ + return createInstance(Locale::getDefault(), kNumberStyle, status); +} + +// ------------------------------------- +// Create a number style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, kNumberStyle, status); +} + +// ------------------------------------- +// Create a currency style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createCurrencyInstance(UErrorCode& status) +{ + return createCurrencyInstance(Locale::getDefault(), status); +} + +// ------------------------------------- +// Create a currency style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, kCurrencyStyle, status); +} + +// ------------------------------------- +// Create a percent style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createPercentInstance(UErrorCode& status) +{ + return createInstance(Locale::getDefault(), kPercentStyle, status); +} + +// ------------------------------------- +// Create a percent style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, kPercentStyle, status); +} + +// ------------------------------------- +// Create a scientific style NumberFormat instance with the default locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createScientificInstance(UErrorCode& status) +{ + return createInstance(Locale::getDefault(), kScientificStyle, status); +} + +// ------------------------------------- +// Create a scientific style NumberFormat instance with the inLocale locale. + +NumberFormat* U_EXPORT2 +NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) +{ + return createInstance(inLocale, kScientificStyle, status); +} + +// ------------------------------------- + +const Locale* U_EXPORT2 +NumberFormat::getAvailableLocales(int32_t& count) +{ + return Locale::getAvailableLocales(count); +} + +// ------------------------------------------ +// +// Registration +// +//------------------------------------------- + +#if !UCONFIG_NO_SERVICE + +// ------------------------------------- + +class ICUNumberFormatFactory : public ICUResourceBundleFactory { +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); + } +}; + +// ------------------------------------- + +class NFFactory : public LocaleKeyFactory { +private: + NumberFormatFactory* _delegate; + Hashtable* _ids; + +public: + NFFactory(NumberFormatFactory* delegate) + : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) + , _delegate(delegate) + , _ids(NULL) + { + } + + virtual ~NFFactory() + { + delete _delegate; + delete _ids; + } + + virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const + { + if (handlesKey(key, status)) { + const LocaleKey& lkey = (const LocaleKey&)key; + Locale loc; + lkey.canonicalLocale(loc); + int32_t kind = lkey.kind(); + + UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)(kind+1)); + if (result == NULL) { + result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status); + } + return result; + } + return NULL; + } + +protected: + /** + * 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. + */ + virtual const Hashtable* getSupportedIDs(UErrorCode& status) const + { + if (U_SUCCESS(status)) { + if (!_ids) { + int32_t count = 0; + const UnicodeString * const idlist = _delegate->getSupportedIDs(count, status); + ((NFFactory*)this)->_ids = new Hashtable(status); /* cast away const */ + if (_ids) { + for (int i = 0; i < count; ++i) { + _ids->put(idlist[i], (void*)this, status); + } + } + } + return _ids; + } + return NULL; + } +}; + +class ICUNumberFormatService : public ICULocaleService { +public: + ICUNumberFormatService() + : ICULocaleService(UNICODE_STRING_SIMPLE("Number Format")) + { + UErrorCode status = U_ZERO_ERROR; + registerFactory(new ICUNumberFormatFactory(), status); + } + + virtual UObject* cloneInstance(UObject* instance) const { + return ((NumberFormat*)instance)->clone(); + } + + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /* actualID */, UErrorCode& status) const { + LocaleKey& lkey = (LocaleKey&)key; + int32_t kind = lkey.kind(); + Locale loc; + lkey.currentLocale(loc); + return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); + } + + virtual UBool isDefault() const { + return countFactories() == 1; + } +}; + +// ------------------------------------- + +static ICULocaleService* +getNumberFormatService(void) +{ + UBool needInit; + UMTX_CHECK(NULL, (UBool)(gService == NULL), needInit); + if (needInit) { + ICULocaleService * newservice = new ICUNumberFormatService(); + if (newservice) { + umtx_lock(NULL); + if (gService == NULL) { + gService = newservice; + newservice = NULL; + } + umtx_unlock(NULL); + } + if (newservice) { + delete newservice; + } else { + // we won the contention, this thread can register cleanup. + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + } + } + return gService; +} + +// ------------------------------------- + +URegistryKey U_EXPORT2 +NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) +{ + ICULocaleService *service = getNumberFormatService(); + if (service) { + NFFactory *tempnnf = new NFFactory(toAdopt); + if (tempnnf != NULL) { + return service->registerFactory(tempnnf, status); + } + } + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; +} + +// ------------------------------------- + +UBool U_EXPORT2 +NumberFormat::unregister(URegistryKey key, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + UBool haveService; + UMTX_CHECK(NULL, gService != NULL, haveService); + if (haveService) { + return gService->unregister(key, status); + } + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return FALSE; +} + +// ------------------------------------- +StringEnumeration* U_EXPORT2 +NumberFormat::getAvailableLocales(void) +{ + ICULocaleService *service = getNumberFormatService(); + if (service) { + 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) +{ +#if !UCONFIG_NO_SERVICE + UBool haveService; + UMTX_CHECK(NULL, gService != NULL, haveService); + if (haveService) { + return (NumberFormat*)gService->get(loc, kind, status); + } + else +#endif + { + return makeInstance(loc, kind, status); + } +} + + +// ------------------------------------- +// Checks if the thousand/10 thousand grouping is used in the +// NumberFormat instance. + +UBool +NumberFormat::isGroupingUsed() const +{ + return fGroupingUsed; +} + +// ------------------------------------- +// Sets to use the thousand/10 thousand grouping in the +// NumberFormat instance. + +void +NumberFormat::setGroupingUsed(UBool newValue) +{ + fGroupingUsed = newValue; +} + +// ------------------------------------- +// Gets the maximum number of digits for the integral part for +// this NumberFormat instance. + +int32_t NumberFormat::getMaximumIntegerDigits() const +{ + return fMaxIntegerDigits; +} + +// ------------------------------------- +// Sets the maximum number of digits for the integral part for +// this NumberFormat instance. + +void +NumberFormat::setMaximumIntegerDigits(int32_t newValue) +{ + fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); + if(fMinIntegerDigits > fMaxIntegerDigits) + fMinIntegerDigits = fMaxIntegerDigits; +} + +// ------------------------------------- +// Gets the minimum number of digits for the integral part for +// this NumberFormat instance. + +int32_t +NumberFormat::getMinimumIntegerDigits() const +{ + return fMinIntegerDigits; +} + +// ------------------------------------- +// Sets the minimum number of digits for the integral part for +// this NumberFormat instance. + +void +NumberFormat::setMinimumIntegerDigits(int32_t newValue) +{ + fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); + if(fMinIntegerDigits > fMaxIntegerDigits) + fMaxIntegerDigits = fMinIntegerDigits; +} + +// ------------------------------------- +// Gets the maximum number of digits for the fractional part for +// this NumberFormat instance. + +int32_t +NumberFormat::getMaximumFractionDigits() const +{ + return fMaxFractionDigits; +} + +// ------------------------------------- +// Sets the maximum number of digits for the fractional part for +// this NumberFormat instance. + +void +NumberFormat::setMaximumFractionDigits(int32_t newValue) +{ + fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); + if(fMaxFractionDigits < fMinFractionDigits) + fMinFractionDigits = fMaxFractionDigits; +} + +// ------------------------------------- +// Gets the minimum number of digits for the fractional part for +// this NumberFormat instance. + +int32_t +NumberFormat::getMinimumFractionDigits() const +{ + return fMinFractionDigits; +} + +// ------------------------------------- +// Sets the minimum number of digits for the fractional part for +// this NumberFormat instance. + +void +NumberFormat::setMinimumFractionDigits(int32_t newValue) +{ + fMinFractionDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); + if (fMaxFractionDigits < fMinFractionDigits) + fMaxFractionDigits = fMinFractionDigits; +} + +// ------------------------------------- + +void NumberFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { + if (U_FAILURE(ec)) { + return; + } + if (theCurrency) { + u_strncpy(fCurrency, theCurrency, 3); + fCurrency[3] = 0; + } else { + fCurrency[0] = 0; + } +} + +const UChar* NumberFormat::getCurrency() const { + return fCurrency; +} + +void NumberFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { + const UChar* c = getCurrency(); + if (*c != 0) { + u_strncpy(result, c, 3); + result[3] = 0; + } else { + const char* loc = getLocaleID(ULOC_VALID_LOCALE, ec); + if (loc == NULL) { + loc = uloc_getDefault(); + } + ucurr_forLocale(loc, result, 4, &ec); + } +} + +// ------------------------------------- +// Creates the NumberFormat instance of the specified style (number, currency, +// or percent) for the desired locale. + +NumberFormat* +NumberFormat::makeInstance(const Locale& desiredLocale, + EStyles style, + UErrorCode& status) +{ + if (U_FAILURE(status)) return NULL; + + if (style < 0 || style >= kStyleCount) { + 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 + + case kCurrencyStyle: + case kIsoCurrencyStyle: // do not support plural formatting here + case kPluralCurrencyStyle: + f = new Win32NumberFormat(desiredLocale, curr, status); + + if (U_SUCCESS(status)) { + return f; + } + + delete f; + break; + + default: + break; + } + } +#endif + + NumberFormat* f = NULL; + DecimalFormatSymbols* symbolsToAdopt = NULL; + UnicodeString pattern; + UResourceBundle *resource = ures_open(NULL, desiredLocale.getName(), &status); + NumberingSystem *ns = NULL; + UBool deleteSymbols = TRUE; + UHashtable * cache = NULL; + int32_t hashKey; + UBool getCache = FALSE; + UBool deleteNS = FALSE; + + 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); + + // Creates a DecimalFormat instance with the last resort number patterns. + pattern.setTo(TRUE, gLastResortNumberPatterns[style], -1); + } + else { + // Loads the decimal symbols of the desired locale. + symbolsToAdopt = new DecimalFormatSymbols(desiredLocale, status); + + int32_t patLen = 0; + + /* for ISOCURRENCYSTYLE and PLURALCURRENCYSTYLE, + * the pattern is the same as the pattern of CURRENCYSTYLE + * but by replacing the single currency sign with + * double currency sign or triple currency sign. + */ + int styleInNumberPattern = ((style == kIsoCurrencyStyle || + style == kPluralCurrencyStyle) ? + kCurrencyStyle : style); + + resource = ures_getByKeyWithFallback(resource, gNumberElements, resource, &status); + // TODO : Get patterns on a per numbering system basis, for right now assumes "latn" for patterns + resource = ures_getByKeyWithFallback(resource, gLatn, resource, &status); + resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); + + const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[styleInNumberPattern], &patLen, &status); + + // Creates the specified decimal format style of the desired locale. + pattern.setTo(TRUE, patResStr, patLen); + } + if (U_FAILURE(status) || symbolsToAdopt == NULL) { + goto cleanup; + } + if(style==kCurrencyStyle || style == kIsoCurrencyStyle){ + const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); + if(currPattern!=NULL){ + pattern.setTo(currPattern, u_strlen(currPattern)); + } + } + + // Use numbering system cache hashtable + UMTX_CHECK(&nscacheMutex, (UBool)(cache != NumberingSystem_cache), getCache); + if (getCache) { + umtx_lock(&nscacheMutex); + cache = NumberingSystem_cache; + umtx_unlock(&nscacheMutex); + } + + // Check cache we got, create if non-existant + status = U_ZERO_ERROR; + if (cache == NULL) { + cache = uhash_open(uhash_hashLong, + uhash_compareLong, + NULL, + &status); + + if (cache == NULL || U_FAILURE(status)) { + // cache not created - out of memory + cache = NULL; + } + else { + // cache created + uhash_setValueDeleter(cache, deleteNumberingSystem); + + // set final NumberingSystem_cache value + UHashtable* h = NULL; + + UMTX_CHECK(&nscacheMutex, (UBool)(h != NumberingSystem_cache), getCache); + if (getCache) { + umtx_lock(&nscacheMutex); + h = NumberingSystem_cache; + umtx_unlock(&nscacheMutex); + } + if (h == NULL) { + umtx_lock(&nscacheMutex); + NumberingSystem_cache = h = cache; + cache = NULL; + ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); + umtx_unlock(&nscacheMutex); + } + + if(cache != NULL) { + uhash_close(cache); + } + cache = h; + } + } + + // Get cached numbering system + if (cache != NULL) { + hashKey = desiredLocale.hashCode(); + + umtx_lock(&nscacheMutex); + ns = (NumberingSystem *)uhash_iget(cache, hashKey); + if (ns == NULL) { + ns = NumberingSystem::createInstance(desiredLocale,status); + uhash_iput(cache, hashKey, (void*)ns, &status); + } + umtx_unlock(&nscacheMutex); + } + else { + ns = NumberingSystem::createInstance(desiredLocale,status); + deleteNS = TRUE; + } + + // check results of getting a numbering system + if ((ns == NULL) || (U_FAILURE(status))) { + goto cleanup; + } + + 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 ) { + char nsLocID[ULOC_FULLNAME_CAPACITY]; + + nsDesc.extract(0,firstSlash,nsLocID,ULOC_FULLNAME_CAPACITY,US_INV); + nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); + nsRuleSetName.setTo(nsDesc,lastSlash+1); + + nsLoc = Locale::createFromName(nsLocID); + + UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); + if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { + desiredRulesType = URBNF_SPELLOUT; + } + } else { + nsLoc = desiredLocale; + nsRuleSetName.setTo(nsDesc); + } + + RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); + + if (U_FAILURE(status) || r == NULL) { + goto cleanup; + } + r->setDefaultRuleSet(nsRuleSetName,status); + f = (NumberFormat *) r; + + } else { + // replace single currency sign in the pattern with double currency sign + // if the style is kIsoCurrencyStyle + if (style == kIsoCurrencyStyle) { + pattern.findAndReplace(gSingleCurrencySign, gDoubleCurrencySign); + } + + f = new DecimalFormat(pattern, symbolsToAdopt, style, status); + if (U_FAILURE(status) || f == NULL) { + goto cleanup; + } + deleteSymbols = FALSE; + } + + f->setLocaleIDs(ures_getLocaleByType(resource, ULOC_VALID_LOCALE, &status), + ures_getLocaleByType(resource, ULOC_ACTUAL_LOCALE, &status)); + +cleanup: + ures_close(resource); + + if (deleteNS && ns) { + delete ns; + } + + if (U_FAILURE(status)) { + /* If f exists, then it will delete the symbols */ + if (f==NULL) { + delete symbolsToAdopt; + } + else { + delete f; + } + return NULL; + } + if (f == NULL || symbolsToAdopt == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + f = NULL; + } + if (deleteSymbols && symbolsToAdopt != NULL) { + delete symbolsToAdopt; + } + return f; +} + +U_NAMESPACE_END + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof |