|  | // © 2016 and later: Unicode, Inc. and others. | 
|  | // License & terms of use: http://www.unicode.org/copyright.html | 
|  | /* | 
|  | ********************************************************************** | 
|  | * Copyright (c) 2002-2016, International Business Machines | 
|  | * Corporation and others.  All Rights Reserved. | 
|  | ********************************************************************** | 
|  | */ | 
|  |  | 
|  | #include "unicode/utypes.h" | 
|  |  | 
|  | #if !UCONFIG_NO_FORMATTING | 
|  |  | 
|  | #include "unicode/ucurr.h" | 
|  | #include "unicode/locid.h" | 
|  | #include "unicode/ures.h" | 
|  | #include "unicode/ustring.h" | 
|  | #include "unicode/parsepos.h" | 
|  | #include "unicode/uniset.h" | 
|  | #include "unicode/usetiter.h" | 
|  | #include "unicode/utf16.h" | 
|  | #include "ustr_imp.h" | 
|  | #include "charstr.h" | 
|  | #include "cmemory.h" | 
|  | #include "cstring.h" | 
|  | #include "static_unicode_sets.h" | 
|  | #include "uassert.h" | 
|  | #include "umutex.h" | 
|  | #include "ucln_cmn.h" | 
|  | #include "uenumimp.h" | 
|  | #include "uhash.h" | 
|  | #include "hash.h" | 
|  | #include "uinvchar.h" | 
|  | #include "uresimp.h" | 
|  | #include "ulist.h" | 
|  | #include "uresimp.h" | 
|  | #include "ureslocs.h" | 
|  | #include "ulocimp.h" | 
|  |  | 
|  | using namespace icu; | 
|  |  | 
|  | //#define UCURR_DEBUG_EQUIV 1 | 
|  | #ifdef UCURR_DEBUG_EQUIV | 
|  | #include "stdio.h" | 
|  | #endif | 
|  | //#define UCURR_DEBUG 1 | 
|  | #ifdef UCURR_DEBUG | 
|  | #include "stdio.h" | 
|  | #endif | 
|  |  | 
|  | typedef struct IsoCodeEntry { | 
|  | const UChar *isoCode; /* const because it's a reference to a resource bundle string. */ | 
|  | UDate from; | 
|  | UDate to; | 
|  | } IsoCodeEntry; | 
|  |  | 
|  | //------------------------------------------------------------ | 
|  | // Constants | 
|  |  | 
|  | // Default currency meta data of last resort.  We try to use the | 
|  | // defaults encoded in the meta data resource bundle.  If there is a | 
|  | // configuration/build error and these are not available, we use these | 
|  | // hard-coded defaults (which should be identical). | 
|  | static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 }; | 
|  |  | 
|  | // POW10[i] = 10^i, i=0..MAX_POW10 | 
|  | static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000, | 
|  | 1000000, 10000000, 100000000, 1000000000 }; | 
|  |  | 
|  | static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1; | 
|  |  | 
|  | #define ISO_CURRENCY_CODE_LENGTH 3 | 
|  |  | 
|  | //------------------------------------------------------------ | 
|  | // Resource tags | 
|  | // | 
|  |  | 
|  | static const char CURRENCY_DATA[] = "supplementalData"; | 
|  | // Tag for meta-data, in root. | 
|  | static const char CURRENCY_META[] = "CurrencyMeta"; | 
|  |  | 
|  | // Tag for map from countries to currencies, in root. | 
|  | static const char CURRENCY_MAP[] = "CurrencyMap"; | 
|  |  | 
|  | // Tag for default meta-data, in CURRENCY_META | 
|  | static const char DEFAULT_META[] = "DEFAULT"; | 
|  |  | 
|  | // Variant delimiter | 
|  | static const char VAR_DELIM = '_'; | 
|  |  | 
|  | // Tag for localized display names (symbols) of currencies | 
|  | static const char CURRENCIES[] = "Currencies"; | 
|  | static const char CURRENCIES_NARROW[] = "Currencies%narrow"; | 
|  | static const char CURRENCIES_FORMAL[] = "Currencies%formal"; | 
|  | static const char CURRENCIES_VARIANT[] = "Currencies%variant"; | 
|  | static const char CURRENCYPLURALS[] = "CurrencyPlurals"; | 
|  |  | 
|  | // ISO codes mapping table | 
|  | static const UHashtable* gIsoCodes = NULL; | 
|  | static icu::UInitOnce gIsoCodesInitOnce = U_INITONCE_INITIALIZER; | 
|  |  | 
|  | // Currency symbol equivalances | 
|  | static const icu::Hashtable* gCurrSymbolsEquiv = NULL; | 
|  | static icu::UInitOnce gCurrSymbolsEquivInitOnce = U_INITONCE_INITIALIZER; | 
|  |  | 
|  | U_NAMESPACE_BEGIN | 
|  |  | 
|  | // EquivIterator iterates over all strings that are equivalent to a given | 
|  | // string, s. Note that EquivIterator will never yield s itself. | 
|  | class EquivIterator : public icu::UMemory { | 
|  | public: | 
|  | // Constructor. hash stores the equivalence relationships; s is the string | 
|  | // for which we find equivalent strings. | 
|  | inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s) | 
|  | : _hash(hash) { | 
|  | _start = _current = &s; | 
|  | } | 
|  | inline ~EquivIterator() { } | 
|  |  | 
|  | // next returns the next equivalent string or NULL if there are no more. | 
|  | // If s has no equivalent strings, next returns NULL on the first call. | 
|  | const icu::UnicodeString *next(); | 
|  | private: | 
|  | const icu::Hashtable& _hash; | 
|  | const icu::UnicodeString* _start; | 
|  | const icu::UnicodeString* _current; | 
|  | }; | 
|  |  | 
|  | const icu::UnicodeString * | 
|  | EquivIterator::next() { | 
|  | const icu::UnicodeString* _next = (const icu::UnicodeString*) _hash.get(*_current); | 
|  | if (_next == NULL) { | 
|  | U_ASSERT(_current == _start); | 
|  | return NULL; | 
|  | } | 
|  | if (*_next == *_start) { | 
|  | return NULL; | 
|  | } | 
|  | _current = _next; | 
|  | return _next; | 
|  | } | 
|  |  | 
|  | U_NAMESPACE_END | 
|  |  | 
|  | // makeEquivalent makes lhs and rhs equivalent by updating the equivalence | 
|  | // relations in hash accordingly. | 
|  | static void makeEquivalent( | 
|  | const icu::UnicodeString &lhs, | 
|  | const icu::UnicodeString &rhs, | 
|  | icu::Hashtable* hash, UErrorCode &status) { | 
|  | if (U_FAILURE(status)) { | 
|  | return; | 
|  | } | 
|  | if (lhs == rhs) { | 
|  | // already equivalent | 
|  | return; | 
|  | } | 
|  | icu::EquivIterator leftIter(*hash, lhs); | 
|  | icu::EquivIterator rightIter(*hash, rhs); | 
|  | const icu::UnicodeString *firstLeft = leftIter.next(); | 
|  | const icu::UnicodeString *firstRight = rightIter.next(); | 
|  | const icu::UnicodeString *nextLeft = firstLeft; | 
|  | const icu::UnicodeString *nextRight = firstRight; | 
|  | while (nextLeft != NULL && nextRight != NULL) { | 
|  | if (*nextLeft == rhs || *nextRight == lhs) { | 
|  | // Already equivalent | 
|  | return; | 
|  | } | 
|  | nextLeft = leftIter.next(); | 
|  | nextRight = rightIter.next(); | 
|  | } | 
|  | // Not equivalent. Must join. | 
|  | icu::UnicodeString *newFirstLeft; | 
|  | icu::UnicodeString *newFirstRight; | 
|  | if (firstRight == NULL && firstLeft == NULL) { | 
|  | // Neither lhs or rhs belong to an equivalence circle, so we form | 
|  | // a new equivalnce circle of just lhs and rhs. | 
|  | newFirstLeft = new icu::UnicodeString(rhs); | 
|  | newFirstRight = new icu::UnicodeString(lhs); | 
|  | } else if (firstRight == NULL) { | 
|  | // lhs belongs to an equivalence circle, but rhs does not, so we link | 
|  | // rhs into lhs' circle. | 
|  | newFirstLeft = new icu::UnicodeString(rhs); | 
|  | newFirstRight = new icu::UnicodeString(*firstLeft); | 
|  | } else if (firstLeft == NULL) { | 
|  | // rhs belongs to an equivlance circle, but lhs does not, so we link | 
|  | // lhs into rhs' circle. | 
|  | newFirstLeft = new icu::UnicodeString(*firstRight); | 
|  | newFirstRight = new icu::UnicodeString(lhs); | 
|  | } else { | 
|  | // Both lhs and rhs belong to different equivalnce circles. We link | 
|  | // them together to form one single, larger equivalnce circle. | 
|  | newFirstLeft = new icu::UnicodeString(*firstRight); | 
|  | newFirstRight = new icu::UnicodeString(*firstLeft); | 
|  | } | 
|  | if (newFirstLeft == NULL || newFirstRight == NULL) { | 
|  | delete newFirstLeft; | 
|  | delete newFirstRight; | 
|  | status = U_MEMORY_ALLOCATION_ERROR; | 
|  | return; | 
|  | } | 
|  | hash->put(lhs, (void *) newFirstLeft, status); | 
|  | hash->put(rhs, (void *) newFirstRight, status); | 
|  | } | 
|  |  | 
|  | // countEquivalent counts how many strings are equivalent to s. | 
|  | // hash stores all the equivalnce relations. | 
|  | // countEquivalent does not include s itself in the count. | 
|  | static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) { | 
|  | int32_t result = 0; | 
|  | icu::EquivIterator iter(hash, s); | 
|  | while (iter.next() != NULL) { | 
|  | ++result; | 
|  | } | 
|  | #ifdef UCURR_DEBUG_EQUIV | 
|  | { | 
|  | char tmp[200]; | 
|  | s.extract(0,s.length(),tmp, "UTF-8"); | 
|  | printf("CountEquivalent('%s') = %d\n", tmp, result); | 
|  | } | 
|  | #endif | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static const icu::Hashtable* getCurrSymbolsEquiv(); | 
|  |  | 
|  | //------------------------------------------------------------ | 
|  | // Code | 
|  |  | 
|  | /** | 
|  | * Cleanup callback func | 
|  | */ | 
|  | static UBool U_CALLCONV | 
|  | isoCodes_cleanup(void) | 
|  | { | 
|  | if (gIsoCodes != NULL) { | 
|  | uhash_close(const_cast<UHashtable *>(gIsoCodes)); | 
|  | gIsoCodes = NULL; | 
|  | } | 
|  | gIsoCodesInitOnce.reset(); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Cleanup callback func | 
|  | */ | 
|  | static UBool U_CALLCONV | 
|  | currSymbolsEquiv_cleanup(void) | 
|  | { | 
|  | delete const_cast<icu::Hashtable *>(gCurrSymbolsEquiv); | 
|  | gCurrSymbolsEquiv = NULL; | 
|  | gCurrSymbolsEquivInitOnce.reset(); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Deleter for OlsonToMetaMappingEntry | 
|  | */ | 
|  | static void U_CALLCONV | 
|  | deleteIsoCodeEntry(void *obj) { | 
|  | IsoCodeEntry *entry = (IsoCodeEntry*)obj; | 
|  | uprv_free(entry); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Deleter for gCurrSymbolsEquiv. | 
|  | */ | 
|  | static void U_CALLCONV | 
|  | deleteUnicode(void *obj) { | 
|  | icu::UnicodeString *entry = (icu::UnicodeString*)obj; | 
|  | delete entry; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Unfortunately, we have to convert the UChar* currency code to char* | 
|  | * to use it as a resource key. | 
|  | */ | 
|  | static inline char* | 
|  | myUCharsToChars(char* resultOfLen4, const UChar* currency) { | 
|  | u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH); | 
|  | resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0; | 
|  | return resultOfLen4; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Internal function to look up currency data.  Result is an array of | 
|  | * four integers.  The first is the fraction digits.  The second is the | 
|  | * rounding increment, or 0 if none.  The rounding increment is in | 
|  | * units of 10^(-fraction_digits).  The third and fourth are the same | 
|  | * except that they are those used in cash transations ( cashDigits | 
|  | * and cashRounding ). | 
|  | */ | 
|  | static const int32_t* | 
|  | _findMetaData(const UChar* currency, UErrorCode& ec) { | 
|  |  | 
|  | if (currency == 0 || *currency == 0) { | 
|  | if (U_SUCCESS(ec)) { | 
|  | ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | } | 
|  | return LAST_RESORT_DATA; | 
|  | } | 
|  |  | 
|  | // Get CurrencyMeta resource out of root locale file.  [This may | 
|  | // move out of the root locale file later; if it does, update this | 
|  | // code.] | 
|  | UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec); | 
|  | UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec); | 
|  |  | 
|  | if (U_FAILURE(ec)) { | 
|  | ures_close(currencyMeta); | 
|  | // Config/build error; return hard-coded defaults | 
|  | return LAST_RESORT_DATA; | 
|  | } | 
|  |  | 
|  | // Look up our currency, or if that's not available, then DEFAULT | 
|  | char buf[ISO_CURRENCY_CODE_LENGTH+1]; | 
|  | UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure | 
|  | UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), NULL, &ec2); | 
|  | if (U_FAILURE(ec2)) { | 
|  | ures_close(rb); | 
|  | rb = ures_getByKey(currencyMeta,DEFAULT_META, NULL, &ec); | 
|  | if (U_FAILURE(ec)) { | 
|  | ures_close(currencyMeta); | 
|  | ures_close(rb); | 
|  | // Config/build error; return hard-coded defaults | 
|  | return LAST_RESORT_DATA; | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t len; | 
|  | const int32_t *data = ures_getIntVector(rb, &len, &ec); | 
|  | if (U_FAILURE(ec) || len != 4) { | 
|  | // Config/build error; return hard-coded defaults | 
|  | if (U_SUCCESS(ec)) { | 
|  | ec = U_INVALID_FORMAT_ERROR; | 
|  | } | 
|  | ures_close(currencyMeta); | 
|  | ures_close(rb); | 
|  | return LAST_RESORT_DATA; | 
|  | } | 
|  |  | 
|  | ures_close(currencyMeta); | 
|  | ures_close(rb); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | // ------------------------------------- | 
|  |  | 
|  | static void | 
|  | idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec) | 
|  | { | 
|  | ulocimp_getRegionForSupplementalData(locale, FALSE, countryAndVariant, capacity, ec); | 
|  | } | 
|  |  | 
|  | // ------------------------------------------ | 
|  | // | 
|  | // Registration | 
|  | // | 
|  | //------------------------------------------- | 
|  |  | 
|  | // don't use ICUService since we don't need fallback | 
|  |  | 
|  | U_CDECL_BEGIN | 
|  | static UBool U_CALLCONV currency_cleanup(void); | 
|  | U_CDECL_END | 
|  |  | 
|  | #if !UCONFIG_NO_SERVICE | 
|  | struct CReg; | 
|  |  | 
|  | static UMutex gCRegLock; | 
|  | static CReg* gCRegHead = 0; | 
|  |  | 
|  | struct CReg : public icu::UMemory { | 
|  | CReg *next; | 
|  | UChar iso[ISO_CURRENCY_CODE_LENGTH+1]; | 
|  | char  id[ULOC_FULLNAME_CAPACITY]; | 
|  |  | 
|  | CReg(const UChar* _iso, const char* _id) | 
|  | : next(0) | 
|  | { | 
|  | int32_t len = (int32_t)uprv_strlen(_id); | 
|  | if (len > (int32_t)(sizeof(id)-1)) { | 
|  | len = (sizeof(id)-1); | 
|  | } | 
|  | uprv_strncpy(id, _id, len); | 
|  | id[len] = 0; | 
|  | u_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH); | 
|  | iso[ISO_CURRENCY_CODE_LENGTH] = 0; | 
|  | } | 
|  |  | 
|  | static UCurrRegistryKey reg(const UChar* _iso, const char* _id, UErrorCode* status) | 
|  | { | 
|  | if (status && U_SUCCESS(*status) && _iso && _id) { | 
|  | CReg* n = new CReg(_iso, _id); | 
|  | if (n) { | 
|  | umtx_lock(&gCRegLock); | 
|  | if (!gCRegHead) { | 
|  | /* register for the first time */ | 
|  | ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); | 
|  | } | 
|  | n->next = gCRegHead; | 
|  | gCRegHead = n; | 
|  | umtx_unlock(&gCRegLock); | 
|  | return n; | 
|  | } | 
|  | *status = U_MEMORY_ALLOCATION_ERROR; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static UBool unreg(UCurrRegistryKey key) { | 
|  | UBool found = FALSE; | 
|  | umtx_lock(&gCRegLock); | 
|  |  | 
|  | CReg** p = &gCRegHead; | 
|  | while (*p) { | 
|  | if (*p == key) { | 
|  | *p = ((CReg*)key)->next; | 
|  | delete (CReg*)key; | 
|  | found = TRUE; | 
|  | break; | 
|  | } | 
|  | p = &((*p)->next); | 
|  | } | 
|  |  | 
|  | umtx_unlock(&gCRegLock); | 
|  | return found; | 
|  | } | 
|  |  | 
|  | static const UChar* get(const char* id) { | 
|  | const UChar* result = NULL; | 
|  | umtx_lock(&gCRegLock); | 
|  | CReg* p = gCRegHead; | 
|  |  | 
|  | /* register cleanup of the mutex */ | 
|  | ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); | 
|  | while (p) { | 
|  | if (uprv_strcmp(id, p->id) == 0) { | 
|  | result = p->iso; | 
|  | break; | 
|  | } | 
|  | p = p->next; | 
|  | } | 
|  | umtx_unlock(&gCRegLock); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* This doesn't need to be thread safe. It's for u_cleanup only. */ | 
|  | static void cleanup(void) { | 
|  | while (gCRegHead) { | 
|  | CReg* n = gCRegHead; | 
|  | gCRegHead = gCRegHead->next; | 
|  | delete n; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // ------------------------------------- | 
|  |  | 
|  | U_CAPI UCurrRegistryKey U_EXPORT2 | 
|  | ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status) | 
|  | { | 
|  | if (status && U_SUCCESS(*status)) { | 
|  | char id[ULOC_FULLNAME_CAPACITY]; | 
|  | idForLocale(locale, id, sizeof(id), status); | 
|  | return CReg::reg(isoCode, id, status); | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | // ------------------------------------- | 
|  |  | 
|  | U_CAPI UBool U_EXPORT2 | 
|  | ucurr_unregister(UCurrRegistryKey key, UErrorCode* status) | 
|  | { | 
|  | if (status && U_SUCCESS(*status)) { | 
|  | return CReg::unreg(key); | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  | #endif /* UCONFIG_NO_SERVICE */ | 
|  |  | 
|  | // ------------------------------------- | 
|  |  | 
|  | /** | 
|  | * Release all static memory held by currency. | 
|  | */ | 
|  | /*The declaration here is needed so currency_cleanup(void) | 
|  | * can call this function. | 
|  | */ | 
|  | static UBool U_CALLCONV | 
|  | currency_cache_cleanup(void); | 
|  |  | 
|  | U_CDECL_BEGIN | 
|  | static UBool U_CALLCONV currency_cleanup(void) { | 
|  | #if !UCONFIG_NO_SERVICE | 
|  | CReg::cleanup(); | 
|  | #endif | 
|  | /* | 
|  | * There might be some cached currency data or isoCodes data. | 
|  | */ | 
|  | currency_cache_cleanup(); | 
|  | isoCodes_cleanup(); | 
|  | currSymbolsEquiv_cleanup(); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  | U_CDECL_END | 
|  |  | 
|  | // ------------------------------------- | 
|  |  | 
|  | U_CAPI int32_t U_EXPORT2 | 
|  | ucurr_forLocale(const char* locale, | 
|  | UChar* buff, | 
|  | int32_t buffCapacity, | 
|  | UErrorCode* ec) { | 
|  | if (U_FAILURE(*ec)) { return 0; } | 
|  | if (buffCapacity < 0 || (buff == nullptr && buffCapacity > 0)) { | 
|  | *ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | char currency[4];  // ISO currency codes are alpha3 codes. | 
|  | UErrorCode localStatus = U_ZERO_ERROR; | 
|  | int32_t resLen = uloc_getKeywordValue(locale, "currency", | 
|  | currency, UPRV_LENGTHOF(currency), &localStatus); | 
|  | if (U_SUCCESS(localStatus) && resLen == 3 && uprv_isInvariantString(currency, resLen)) { | 
|  | if (resLen < buffCapacity) { | 
|  | T_CString_toUpperCase(currency); | 
|  | u_charsToUChars(currency, buff, resLen); | 
|  | } | 
|  | return u_terminateUChars(buff, buffCapacity, resLen, ec); | 
|  | } | 
|  |  | 
|  | // get country or country_variant in `id' | 
|  | char id[ULOC_FULLNAME_CAPACITY]; | 
|  | idForLocale(locale, id, UPRV_LENGTHOF(id), ec); | 
|  | if (U_FAILURE(*ec)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #if !UCONFIG_NO_SERVICE | 
|  | const UChar* result = CReg::get(id); | 
|  | if (result) { | 
|  | if(buffCapacity > u_strlen(result)) { | 
|  | u_strcpy(buff, result); | 
|  | } | 
|  | resLen = u_strlen(result); | 
|  | return u_terminateUChars(buff, buffCapacity, resLen, ec); | 
|  | } | 
|  | #endif | 
|  | // Remove variants, which is only needed for registration. | 
|  | char *idDelim = uprv_strchr(id, VAR_DELIM); | 
|  | if (idDelim) { | 
|  | idDelim[0] = 0; | 
|  | } | 
|  |  | 
|  | const UChar* s = NULL;  // Currency code from data file. | 
|  | if (id[0] == 0) { | 
|  | // No point looking in the data for an empty string. | 
|  | // This is what we would get. | 
|  | localStatus = U_MISSING_RESOURCE_ERROR; | 
|  | } else { | 
|  | // Look up the CurrencyMap element in the root bundle. | 
|  | localStatus = U_ZERO_ERROR; | 
|  | UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); | 
|  | UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); | 
|  | UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); | 
|  | UResourceBundle *currencyReq = ures_getByIndex(countryArray, 0, NULL, &localStatus); | 
|  | s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus); | 
|  | ures_close(currencyReq); | 
|  | ures_close(countryArray); | 
|  | } | 
|  |  | 
|  | if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0) { | 
|  | // We don't know about it.  Check to see if we support the variant. | 
|  | uloc_getParent(locale, id, UPRV_LENGTHOF(id), ec); | 
|  | *ec = U_USING_FALLBACK_WARNING; | 
|  | // TODO: Loop over the shortened id rather than recursing and | 
|  | // looking again for a currency keyword. | 
|  | return ucurr_forLocale(id, buff, buffCapacity, ec); | 
|  | } | 
|  | if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) { | 
|  | // There is nothing to fallback to. Report the failure/warning if possible. | 
|  | *ec = localStatus; | 
|  | } | 
|  | if (U_SUCCESS(*ec)) { | 
|  | if(buffCapacity > resLen) { | 
|  | u_strcpy(buff, s); | 
|  | } | 
|  | } | 
|  | return u_terminateUChars(buff, buffCapacity, resLen, ec); | 
|  | } | 
|  |  | 
|  | // end registration | 
|  |  | 
|  | /** | 
|  | * Modify the given locale name by removing the rightmost _-delimited | 
|  | * element.  If there is none, empty the string ("" == root). | 
|  | * NOTE: The string "root" is not recognized; do not use it. | 
|  | * @return TRUE if the fallback happened; FALSE if locale is already | 
|  | * root (""). | 
|  | */ | 
|  | static UBool fallback(char *loc) { | 
|  | if (!*loc) { | 
|  | return FALSE; | 
|  | } | 
|  | UErrorCode status = U_ZERO_ERROR; | 
|  | if (uprv_strcmp(loc, "en_GB") == 0) { | 
|  | // HACK: See #13368.  We need "en_GB" to fall back to "en_001" instead of "en" | 
|  | // in order to consume the correct data strings.  This hack will be removed | 
|  | // when proper data sink loading is implemented here. | 
|  | // NOTE: "001" adds 1 char over "GB".  However, both call sites allocate | 
|  | // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001). | 
|  | uprv_strcpy(loc + 3, "001"); | 
|  | } else { | 
|  | uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status); | 
|  | } | 
|  | /* | 
|  | char *i = uprv_strrchr(loc, '_'); | 
|  | if (i == NULL) { | 
|  | i = loc; | 
|  | } | 
|  | *i = 0; | 
|  | */ | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | U_CAPI const UChar* U_EXPORT2 | 
|  | ucurr_getName(const UChar* currency, | 
|  | const char* locale, | 
|  | UCurrNameStyle nameStyle, | 
|  | UBool* isChoiceFormat, // fillin | 
|  | int32_t* len, // fillin | 
|  | UErrorCode* ec) { | 
|  |  | 
|  | // Look up the Currencies resource for the given locale.  The | 
|  | // Currencies locale data looks like this: | 
|  | //|en { | 
|  | //|  Currencies { | 
|  | //|    USD { "US$", "US Dollar" } | 
|  | //|    CHF { "Sw F", "Swiss Franc" } | 
|  | //|    INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" } | 
|  | //|    //... | 
|  | //|  } | 
|  | //|} | 
|  |  | 
|  | if (U_FAILURE(*ec)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t choice = (int32_t) nameStyle; | 
|  | if (choice < 0 || choice > 4) { | 
|  | *ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // In the future, resource bundles may implement multi-level | 
|  | // fallback.  That is, if a currency is not found in the en_US | 
|  | // Currencies data, then the en Currencies data will be searched. | 
|  | // Currently, if a Currencies datum exists in en_US and en, the | 
|  | // en_US entry hides that in en. | 
|  |  | 
|  | // We want multi-level fallback for this resource, so we implement | 
|  | // it manually. | 
|  |  | 
|  | // Use a separate UErrorCode here that does not propagate out of | 
|  | // this function. | 
|  | UErrorCode ec2 = U_ZERO_ERROR; | 
|  |  | 
|  | char loc[ULOC_FULLNAME_CAPACITY]; | 
|  | uloc_getName(locale, loc, sizeof(loc), &ec2); | 
|  | if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { | 
|  | *ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | char buf[ISO_CURRENCY_CODE_LENGTH+1]; | 
|  | myUCharsToChars(buf, currency); | 
|  |  | 
|  | /* Normalize the keyword value to uppercase */ | 
|  | T_CString_toUpperCase(buf); | 
|  |  | 
|  | const UChar* s = NULL; | 
|  | ec2 = U_ZERO_ERROR; | 
|  | LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc, &ec2)); | 
|  |  | 
|  | if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) { | 
|  | CharString key; | 
|  | switch (nameStyle) { | 
|  | case UCURR_NARROW_SYMBOL_NAME: | 
|  | key.append(CURRENCIES_NARROW, ec2); | 
|  | break; | 
|  | case UCURR_FORMAL_SYMBOL_NAME: | 
|  | key.append(CURRENCIES_FORMAL, ec2); | 
|  | break; | 
|  | case UCURR_VARIANT_SYMBOL_NAME: | 
|  | key.append(CURRENCIES_VARIANT, ec2); | 
|  | break; | 
|  | default: | 
|  | *ec = U_UNSUPPORTED_ERROR; | 
|  | return 0; | 
|  | } | 
|  | key.append("/", ec2); | 
|  | key.append(buf, ec2); | 
|  | s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2); | 
|  | if (ec2 == U_MISSING_RESOURCE_ERROR) { | 
|  | *ec = U_USING_FALLBACK_WARNING; | 
|  | ec2 = U_ZERO_ERROR; | 
|  | choice = UCURR_SYMBOL_NAME; | 
|  | } | 
|  | } | 
|  | if (s == NULL) { | 
|  | ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2); | 
|  | ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2); | 
|  | s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2); | 
|  | } | 
|  |  | 
|  | // If we've succeeded we're done.  Otherwise, try to fallback. | 
|  | // If that fails (because we are already at root) then exit. | 
|  | if (U_SUCCESS(ec2)) { | 
|  | if (ec2 == U_USING_DEFAULT_WARNING | 
|  | || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) { | 
|  | *ec = ec2; | 
|  | } | 
|  | } | 
|  |  | 
|  | // We no longer support choice format data in names.  Data should not contain | 
|  | // choice patterns. | 
|  | if (isChoiceFormat != NULL) { | 
|  | *isChoiceFormat = FALSE; | 
|  | } | 
|  | if (U_SUCCESS(ec2)) { | 
|  | U_ASSERT(s != NULL); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | // If we fail to find a match, use the ISO 4217 code | 
|  | *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...? | 
|  | *ec = U_USING_DEFAULT_WARNING; | 
|  | return currency; | 
|  | } | 
|  |  | 
|  | U_CAPI const UChar* U_EXPORT2 | 
|  | ucurr_getPluralName(const UChar* currency, | 
|  | const char* locale, | 
|  | UBool* isChoiceFormat, | 
|  | const char* pluralCount, | 
|  | int32_t* len, // fillin | 
|  | UErrorCode* ec) { | 
|  | // Look up the Currencies resource for the given locale.  The | 
|  | // Currencies locale data looks like this: | 
|  | //|en { | 
|  | //|  CurrencyPlurals { | 
|  | //|    USD{ | 
|  | //|      one{"US dollar"} | 
|  | //|      other{"US dollars"} | 
|  | //|    } | 
|  | //|  } | 
|  | //|} | 
|  |  | 
|  | if (U_FAILURE(*ec)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Use a separate UErrorCode here that does not propagate out of | 
|  | // this function. | 
|  | UErrorCode ec2 = U_ZERO_ERROR; | 
|  |  | 
|  | char loc[ULOC_FULLNAME_CAPACITY]; | 
|  | uloc_getName(locale, loc, sizeof(loc), &ec2); | 
|  | if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { | 
|  | *ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | char buf[ISO_CURRENCY_CODE_LENGTH+1]; | 
|  | myUCharsToChars(buf, currency); | 
|  |  | 
|  | const UChar* s = NULL; | 
|  | ec2 = U_ZERO_ERROR; | 
|  | UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2); | 
|  |  | 
|  | rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2); | 
|  |  | 
|  | // Fetch resource with multi-level resource inheritance fallback | 
|  | rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2); | 
|  |  | 
|  | s = ures_getStringByKeyWithFallback(rb, pluralCount, len, &ec2); | 
|  | if (U_FAILURE(ec2)) { | 
|  | //  fall back to "other" | 
|  | ec2 = U_ZERO_ERROR; | 
|  | s = ures_getStringByKeyWithFallback(rb, "other", len, &ec2); | 
|  | if (U_FAILURE(ec2)) { | 
|  | ures_close(rb); | 
|  | // fall back to long name in Currencies | 
|  | return ucurr_getName(currency, locale, UCURR_LONG_NAME, | 
|  | isChoiceFormat, len, ec); | 
|  | } | 
|  | } | 
|  | ures_close(rb); | 
|  |  | 
|  | // If we've succeeded we're done.  Otherwise, try to fallback. | 
|  | // If that fails (because we are already at root) then exit. | 
|  | if (U_SUCCESS(ec2)) { | 
|  | if (ec2 == U_USING_DEFAULT_WARNING | 
|  | || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) { | 
|  | *ec = ec2; | 
|  | } | 
|  | U_ASSERT(s != NULL); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | // If we fail to find a match, use the ISO 4217 code | 
|  | *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...? | 
|  | *ec = U_USING_DEFAULT_WARNING; | 
|  | return currency; | 
|  | } | 
|  |  | 
|  |  | 
|  | //======================================================================== | 
|  | // Following are structure and function for parsing currency names | 
|  |  | 
|  | #define NEED_TO_BE_DELETED 0x1 | 
|  |  | 
|  | // TODO: a better way to define this? | 
|  | #define MAX_CURRENCY_NAME_LEN 100 | 
|  |  | 
|  | typedef struct { | 
|  | const char* IsoCode;  // key | 
|  | UChar* currencyName;  // value | 
|  | int32_t currencyNameLen;  // value length | 
|  | int32_t flag;  // flags | 
|  | } CurrencyNameStruct; | 
|  |  | 
|  |  | 
|  | #ifndef MIN | 
|  | #define MIN(a,b) (((a)<(b)) ? (a) : (b)) | 
|  | #endif | 
|  |  | 
|  | #ifndef MAX | 
|  | #define MAX(a,b) (((a)<(b)) ? (b) : (a)) | 
|  | #endif | 
|  |  | 
|  |  | 
|  | // Comparison function used in quick sort. | 
|  | static int U_CALLCONV currencyNameComparator(const void* a, const void* b) { | 
|  | const CurrencyNameStruct* currName_1 = (const CurrencyNameStruct*)a; | 
|  | const CurrencyNameStruct* currName_2 = (const CurrencyNameStruct*)b; | 
|  | for (int32_t i = 0; | 
|  | i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen); | 
|  | ++i) { | 
|  | if (currName_1->currencyName[i] < currName_2->currencyName[i]) { | 
|  | return -1; | 
|  | } | 
|  | if (currName_1->currencyName[i] > currName_2->currencyName[i]) { | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | if (currName_1->currencyNameLen < currName_2->currencyNameLen) { | 
|  | return -1; | 
|  | } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) { | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Give a locale, return the maximum number of currency names associated with | 
|  | // this locale. | 
|  | // It gets currency names from resource bundles using fallback. | 
|  | // It is the maximum number because in the fallback chain, some of the | 
|  | // currency names are duplicated. | 
|  | // For example, given locale as "en_US", the currency names get from resource | 
|  | // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count | 
|  | // all currency names in "en_US" and "en". | 
|  | static void | 
|  | getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) { | 
|  | U_NAMESPACE_USE | 
|  | *total_currency_name_count = 0; | 
|  | *total_currency_symbol_count = 0; | 
|  | const UChar* s = NULL; | 
|  | char locale[ULOC_FULLNAME_CAPACITY] = ""; | 
|  | uprv_strcpy(locale, loc); | 
|  | const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv(); | 
|  | for (;;) { | 
|  | UErrorCode ec2 = U_ZERO_ERROR; | 
|  | // TODO: ures_openDirect? | 
|  | UResourceBundle* rb = ures_open(U_ICUDATA_CURR, locale, &ec2); | 
|  | UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2); | 
|  | int32_t n = ures_getSize(curr); | 
|  | for (int32_t i=0; i<n; ++i) { | 
|  | UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2); | 
|  | int32_t len; | 
|  | s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2); | 
|  | ++(*total_currency_symbol_count);  // currency symbol | 
|  | if (currencySymbolsEquiv != NULL) { | 
|  | *total_currency_symbol_count += countEquivalent(*currencySymbolsEquiv, UnicodeString(TRUE, s, len)); | 
|  | } | 
|  | ++(*total_currency_symbol_count); // iso code | 
|  | ++(*total_currency_name_count); // long name | 
|  | ures_close(names); | 
|  | } | 
|  |  | 
|  | // currency plurals | 
|  | UErrorCode ec3 = U_ZERO_ERROR; | 
|  | UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3); | 
|  | n = ures_getSize(curr_p); | 
|  | for (int32_t i=0; i<n; ++i) { | 
|  | UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3); | 
|  | *total_currency_name_count += ures_getSize(names); | 
|  | ures_close(names); | 
|  | } | 
|  | ures_close(curr_p); | 
|  | ures_close(curr); | 
|  | ures_close(rb); | 
|  |  | 
|  | if (!fallback(locale)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static UChar* | 
|  | toUpperCase(const UChar* source, int32_t len, const char* locale) { | 
|  | UChar* dest = NULL; | 
|  | UErrorCode ec = U_ZERO_ERROR; | 
|  | int32_t destLen = u_strToUpper(dest, 0, source, len, locale, &ec); | 
|  |  | 
|  | ec = U_ZERO_ERROR; | 
|  | dest = (UChar*)uprv_malloc(sizeof(UChar) * MAX(destLen, len)); | 
|  | u_strToUpper(dest, destLen, source, len, locale, &ec); | 
|  | if (U_FAILURE(ec)) { | 
|  | u_memcpy(dest, source, len); | 
|  | } | 
|  | return dest; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Collect all available currency names associated with the given locale | 
|  | // (enable fallback chain). | 
|  | // Read currenc names defined in resource bundle "Currencies" and | 
|  | // "CurrencyPlural", enable fallback chain. | 
|  | // return the malloc-ed currency name arrays and the total number of currency | 
|  | // names in the array. | 
|  | static void | 
|  | collectCurrencyNames(const char* locale, | 
|  | CurrencyNameStruct** currencyNames, | 
|  | int32_t* total_currency_name_count, | 
|  | CurrencyNameStruct** currencySymbols, | 
|  | int32_t* total_currency_symbol_count, | 
|  | UErrorCode& ec) { | 
|  | U_NAMESPACE_USE | 
|  | const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv(); | 
|  | // Look up the Currencies resource for the given locale. | 
|  | UErrorCode ec2 = U_ZERO_ERROR; | 
|  |  | 
|  | char loc[ULOC_FULLNAME_CAPACITY] = ""; | 
|  | uloc_getName(locale, loc, sizeof(loc), &ec2); | 
|  | if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) { | 
|  | ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | } | 
|  |  | 
|  | // Get maximum currency name count first. | 
|  | getCurrencyNameCount(loc, total_currency_name_count, total_currency_symbol_count); | 
|  |  | 
|  | *currencyNames = (CurrencyNameStruct*)uprv_malloc | 
|  | (sizeof(CurrencyNameStruct) * (*total_currency_name_count)); | 
|  | *currencySymbols = (CurrencyNameStruct*)uprv_malloc | 
|  | (sizeof(CurrencyNameStruct) * (*total_currency_symbol_count)); | 
|  |  | 
|  | if(currencyNames == NULL || currencySymbols == NULL) { | 
|  | ec = U_MEMORY_ALLOCATION_ERROR; | 
|  | } | 
|  |  | 
|  | if (U_FAILURE(ec)) return; | 
|  |  | 
|  | const UChar* s = NULL;  // currency name | 
|  | char* iso = NULL;  // currency ISO code | 
|  |  | 
|  | *total_currency_name_count = 0; | 
|  | *total_currency_symbol_count = 0; | 
|  |  | 
|  | UErrorCode ec3 = U_ZERO_ERROR; | 
|  | UErrorCode ec4 = U_ZERO_ERROR; | 
|  |  | 
|  | // Using hash to remove duplicates caused by locale fallback | 
|  | UHashtable* currencyIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec3); | 
|  | UHashtable* currencyPluralIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec4); | 
|  | for (int32_t localeLevel = 0; ; ++localeLevel) { | 
|  | ec2 = U_ZERO_ERROR; | 
|  | // TODO: ures_openDirect | 
|  | UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2); | 
|  | UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2); | 
|  | int32_t n = ures_getSize(curr); | 
|  | for (int32_t i=0; i<n; ++i) { | 
|  | UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2); | 
|  | int32_t len; | 
|  | s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2); | 
|  | // TODO: uhash_put wont change key/value? | 
|  | iso = (char*)ures_getKey(names); | 
|  | if (localeLevel == 0) { | 
|  | uhash_put(currencyIsoCodes, iso, iso, &ec3); | 
|  | } else { | 
|  | if (uhash_get(currencyIsoCodes, iso) != NULL) { | 
|  | ures_close(names); | 
|  | continue; | 
|  | } else { | 
|  | uhash_put(currencyIsoCodes, iso, iso, &ec3); | 
|  | } | 
|  | } | 
|  | // Add currency symbol. | 
|  | (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso; | 
|  | (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)s; | 
|  | (*currencySymbols)[*total_currency_symbol_count].flag = 0; | 
|  | (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len; | 
|  | // Add equivalent symbols | 
|  | if (currencySymbolsEquiv != NULL) { | 
|  | UnicodeString str(TRUE, s, len); | 
|  | icu::EquivIterator iter(*currencySymbolsEquiv, str); | 
|  | const UnicodeString *symbol; | 
|  | while ((symbol = iter.next()) != NULL) { | 
|  | (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso; | 
|  | (*currencySymbols)[*total_currency_symbol_count].currencyName = | 
|  | const_cast<UChar*>(symbol->getBuffer()); | 
|  | (*currencySymbols)[*total_currency_symbol_count].flag = 0; | 
|  | (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add currency long name. | 
|  | s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2); | 
|  | (*currencyNames)[*total_currency_name_count].IsoCode = iso; | 
|  | UChar* upperName = toUpperCase(s, len, locale); | 
|  | (*currencyNames)[*total_currency_name_count].currencyName = upperName; | 
|  | (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED; | 
|  | (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len; | 
|  |  | 
|  | // put (iso, 3, and iso) in to array | 
|  | // Add currency ISO code. | 
|  | (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso; | 
|  | (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)uprv_malloc(sizeof(UChar)*3); | 
|  | // Must convert iso[] into Unicode | 
|  | u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3); | 
|  | (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED; | 
|  | (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3; | 
|  |  | 
|  | ures_close(names); | 
|  | } | 
|  |  | 
|  | // currency plurals | 
|  | UErrorCode ec5 = U_ZERO_ERROR; | 
|  | UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec5); | 
|  | n = ures_getSize(curr_p); | 
|  | for (int32_t i=0; i<n; ++i) { | 
|  | UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec5); | 
|  | iso = (char*)ures_getKey(names); | 
|  | // Using hash to remove duplicated ISO codes in fallback chain. | 
|  | if (localeLevel == 0) { | 
|  | uhash_put(currencyPluralIsoCodes, iso, iso, &ec4); | 
|  | } else { | 
|  | if (uhash_get(currencyPluralIsoCodes, iso) != NULL) { | 
|  | ures_close(names); | 
|  | continue; | 
|  | } else { | 
|  | uhash_put(currencyPluralIsoCodes, iso, iso, &ec4); | 
|  | } | 
|  | } | 
|  | int32_t num = ures_getSize(names); | 
|  | int32_t len; | 
|  | for (int32_t j = 0; j < num; ++j) { | 
|  | // TODO: remove duplicates between singular name and | 
|  | // currency long name? | 
|  | s = ures_getStringByIndex(names, j, &len, &ec5); | 
|  | (*currencyNames)[*total_currency_name_count].IsoCode = iso; | 
|  | UChar* upperName = toUpperCase(s, len, locale); | 
|  | (*currencyNames)[*total_currency_name_count].currencyName = upperName; | 
|  | (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED; | 
|  | (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len; | 
|  | } | 
|  | ures_close(names); | 
|  | } | 
|  | ures_close(curr_p); | 
|  | ures_close(curr); | 
|  | ures_close(rb); | 
|  |  | 
|  | if (!fallback(loc)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | uhash_close(currencyIsoCodes); | 
|  | uhash_close(currencyPluralIsoCodes); | 
|  |  | 
|  | // quick sort the struct | 
|  | qsort(*currencyNames, *total_currency_name_count, | 
|  | sizeof(CurrencyNameStruct), currencyNameComparator); | 
|  | qsort(*currencySymbols, *total_currency_symbol_count, | 
|  | sizeof(CurrencyNameStruct), currencyNameComparator); | 
|  |  | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("currency name count: %d\n", *total_currency_name_count); | 
|  | for (int32_t index = 0; index < *total_currency_name_count; ++index) { | 
|  | printf("index: %d\n", index); | 
|  | printf("iso: %s\n", (*currencyNames)[index].IsoCode); | 
|  | char curNameBuf[1024]; | 
|  | memset(curNameBuf, 0, 1024); | 
|  | u_austrncpy(curNameBuf, (*currencyNames)[index].currencyName, (*currencyNames)[index].currencyNameLen); | 
|  | printf("currencyName: %s\n", curNameBuf); | 
|  | printf("len: %d\n", (*currencyNames)[index].currencyNameLen); | 
|  | } | 
|  | printf("currency symbol count: %d\n", *total_currency_symbol_count); | 
|  | for (int32_t index = 0; index < *total_currency_symbol_count; ++index) { | 
|  | printf("index: %d\n", index); | 
|  | printf("iso: %s\n", (*currencySymbols)[index].IsoCode); | 
|  | char curNameBuf[1024]; | 
|  | memset(curNameBuf, 0, 1024); | 
|  | u_austrncpy(curNameBuf, (*currencySymbols)[index].currencyName, (*currencySymbols)[index].currencyNameLen); | 
|  | printf("currencySymbol: %s\n", curNameBuf); | 
|  | printf("len: %d\n", (*currencySymbols)[index].currencyNameLen); | 
|  | } | 
|  | #endif | 
|  | // fail on hashtable errors | 
|  | if (U_FAILURE(ec3)) { | 
|  | ec = ec3; | 
|  | return; | 
|  | } | 
|  | if (U_FAILURE(ec4)) { | 
|  | ec = ec4; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // @param  currencyNames: currency names array | 
|  | // @param  indexInCurrencyNames: the index of the character in currency names | 
|  | //         array against which the comparison is done | 
|  | // @param  key: input text char to compare against | 
|  | // @param  begin(IN/OUT): the begin index of matching range in currency names array | 
|  | // @param  end(IN/OUT): the end index of matching range in currency names array. | 
|  | static int32_t | 
|  | binarySearch(const CurrencyNameStruct* currencyNames, | 
|  | int32_t indexInCurrencyNames, | 
|  | const UChar key, | 
|  | int32_t* begin, int32_t* end) { | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("key = %x\n", key); | 
|  | #endif | 
|  | int32_t first = *begin; | 
|  | int32_t last = *end; | 
|  | while (first <= last) { | 
|  | int32_t mid = (first + last) / 2;  // compute mid point. | 
|  | if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) { | 
|  | first = mid + 1; | 
|  | } else { | 
|  | if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) { | 
|  | first = mid + 1; | 
|  | } | 
|  | else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) { | 
|  | last = mid - 1; | 
|  | } | 
|  | else { | 
|  | // Find a match, and looking for ranges | 
|  | // Now do two more binary searches. First, on the left side for | 
|  | // the greatest L such that CurrencyNameStruct[L] < key. | 
|  | int32_t L = *begin; | 
|  | int32_t R = mid; | 
|  |  | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("mid = %d\n", mid); | 
|  | #endif | 
|  | while (L < R) { | 
|  | int32_t M = (L + R) / 2; | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("L = %d, R = %d, M = %d\n", L, R, M); | 
|  | #endif | 
|  | if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) { | 
|  | L = M + 1; | 
|  | } else { | 
|  | if (currencyNames[M].currencyName[indexInCurrencyNames] < key) { | 
|  | L = M + 1; | 
|  | } else { | 
|  | #ifdef UCURR_DEBUG | 
|  | U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); | 
|  | #endif | 
|  | R = M; | 
|  | } | 
|  | } | 
|  | } | 
|  | #ifdef UCURR_DEBUG | 
|  | U_ASSERT(L == R); | 
|  | #endif | 
|  | *begin = L; | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("begin = %d\n", *begin); | 
|  | U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key); | 
|  | #endif | 
|  |  | 
|  | // Now for the second search, finding the least R such that | 
|  | // key < CurrencyNameStruct[R]. | 
|  | L = mid; | 
|  | R = *end; | 
|  | while (L < R) { | 
|  | int32_t M = (L + R) / 2; | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("L = %d, R = %d, M = %d\n", L, R, M); | 
|  | #endif | 
|  | if (currencyNames[M].currencyNameLen < indexInCurrencyNames) { | 
|  | L = M + 1; | 
|  | } else { | 
|  | if (currencyNames[M].currencyName[indexInCurrencyNames] > key) { | 
|  | R = M; | 
|  | } else { | 
|  | #ifdef UCURR_DEBUG | 
|  | U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); | 
|  | #endif | 
|  | L = M + 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | #ifdef UCURR_DEBUG | 
|  | U_ASSERT(L == R); | 
|  | #endif | 
|  | if (currencyNames[R].currencyName[indexInCurrencyNames] > key) { | 
|  | *end = R - 1; | 
|  | } else { | 
|  | *end = R; | 
|  | } | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("end = %d\n", *end); | 
|  | #endif | 
|  |  | 
|  | // now, found the range. check whether there is exact match | 
|  | if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) { | 
|  | return *begin;  // find range and exact match. | 
|  | } | 
|  | return -1;  // find range, but no exact match. | 
|  | } | 
|  | } | 
|  | } | 
|  | *begin = -1; | 
|  | *end = -1; | 
|  | return -1;    // failed to find range. | 
|  | } | 
|  |  | 
|  |  | 
|  | // Linear search "text" in "currencyNames". | 
|  | // @param  begin, end: the begin and end index in currencyNames, within which | 
|  | //         range should the search be performed. | 
|  | // @param  textLen: the length of the text to be compared | 
|  | // @param  maxMatchLen(IN/OUT): passing in the computed max matching length | 
|  | //                              pass out the new max  matching length | 
|  | // @param  maxMatchIndex: the index in currencyName which has the longest | 
|  | //                        match with input text. | 
|  | static void | 
|  | linearSearch(const CurrencyNameStruct* currencyNames, | 
|  | int32_t begin, int32_t end, | 
|  | const UChar* text, int32_t textLen, | 
|  | int32_t *partialMatchLen, | 
|  | int32_t *maxMatchLen, int32_t* maxMatchIndex) { | 
|  | int32_t initialPartialMatchLen = *partialMatchLen; | 
|  | for (int32_t index = begin; index <= end; ++index) { | 
|  | int32_t len = currencyNames[index].currencyNameLen; | 
|  | if (len > *maxMatchLen && len <= textLen && | 
|  | uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) { | 
|  | *partialMatchLen = MAX(*partialMatchLen, len); | 
|  | *maxMatchIndex = index; | 
|  | *maxMatchLen = len; | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("maxMatchIndex = %d, maxMatchLen = %d\n", | 
|  | *maxMatchIndex, *maxMatchLen); | 
|  | #endif | 
|  | } else { | 
|  | // Check for partial matches. | 
|  | for (int32_t i=initialPartialMatchLen; i<MIN(len, textLen); i++) { | 
|  | if (currencyNames[index].currencyName[i] != text[i]) { | 
|  | break; | 
|  | } | 
|  | *partialMatchLen = MAX(*partialMatchLen, i + 1); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #define LINEAR_SEARCH_THRESHOLD 10 | 
|  |  | 
|  | // Find longest match between "text" and currency names in "currencyNames". | 
|  | // @param  total_currency_count: total number of currency names in CurrencyNames. | 
|  | // @param  textLen: the length of the text to be compared | 
|  | // @param  maxMatchLen: passing in the computed max matching length | 
|  | //                              pass out the new max  matching length | 
|  | // @param  maxMatchIndex: the index in currencyName which has the longest | 
|  | //                        match with input text. | 
|  | static void | 
|  | searchCurrencyName(const CurrencyNameStruct* currencyNames, | 
|  | int32_t total_currency_count, | 
|  | const UChar* text, int32_t textLen, | 
|  | int32_t *partialMatchLen, | 
|  | int32_t* maxMatchLen, int32_t* maxMatchIndex) { | 
|  | *maxMatchIndex = -1; | 
|  | *maxMatchLen = 0; | 
|  | int32_t matchIndex = -1; | 
|  | int32_t binarySearchBegin = 0; | 
|  | int32_t binarySearchEnd = total_currency_count - 1; | 
|  | // It is a variant of binary search. | 
|  | // For example, given the currency names in currencyNames array are: | 
|  | // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E.... | 
|  | // and the input text is BBEXST | 
|  | // The first round binary search search "B" in the text against | 
|  | // the first char in currency names, and find the first char matching range | 
|  | // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B"). | 
|  | // The 2nd round binary search search the second "B" in the text against | 
|  | // the 2nd char in currency names, and narrow the matching range to | 
|  | // "BB BBEX BBEXYZ" (and the maximum matching "BB"). | 
|  | // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing | 
|  | // maximum matching). | 
|  | // The 4th round returns the same range (the maximum matching is "BBEX"). | 
|  | // The 5th round returns no matching range. | 
|  | for (int32_t index = 0; index < textLen; ++index) { | 
|  | // matchIndex saves the one with exact match till the current point. | 
|  | // [binarySearchBegin, binarySearchEnd] saves the matching range. | 
|  | matchIndex = binarySearch(currencyNames, index, | 
|  | text[index], | 
|  | &binarySearchBegin, &binarySearchEnd); | 
|  | if (binarySearchBegin == -1) { // did not find the range | 
|  | break; | 
|  | } | 
|  | *partialMatchLen = MAX(*partialMatchLen, index + 1); | 
|  | if (matchIndex != -1) { | 
|  | // find an exact match for text from text[0] to text[index] | 
|  | // in currencyNames array. | 
|  | *maxMatchLen = index + 1; | 
|  | *maxMatchIndex = matchIndex; | 
|  | } | 
|  | if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) { | 
|  | // linear search if within threshold. | 
|  | linearSearch(currencyNames, binarySearchBegin, binarySearchEnd, | 
|  | text, textLen, | 
|  | partialMatchLen, | 
|  | maxMatchLen, maxMatchIndex); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | //========================= currency name cache ===================== | 
|  | typedef struct { | 
|  | char locale[ULOC_FULLNAME_CAPACITY];  //key | 
|  | // currency names, case insensitive | 
|  | CurrencyNameStruct* currencyNames;  // value | 
|  | int32_t totalCurrencyNameCount;  // currency name count | 
|  | // currency symbols and ISO code, case sensitive | 
|  | CurrencyNameStruct* currencySymbols; // value | 
|  | int32_t totalCurrencySymbolCount;  // count | 
|  | // reference count. | 
|  | // reference count is set to 1 when an entry is put to cache. | 
|  | // it increases by 1 before accessing, and decreased by 1 after accessing. | 
|  | // The entry is deleted when ref count is zero, which means | 
|  | // the entry is replaced out of cache and no process is accessing it. | 
|  | int32_t refCount; | 
|  | } CurrencyNameCacheEntry; | 
|  |  | 
|  |  | 
|  | #define CURRENCY_NAME_CACHE_NUM 10 | 
|  |  | 
|  | // Reserve 10 cache entries. | 
|  | static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {NULL}; | 
|  | // Using an index to indicate which entry to be replaced when cache is full. | 
|  | // It is a simple round-robin replacement strategy. | 
|  | static int8_t currentCacheEntryIndex = 0; | 
|  |  | 
|  | static UMutex gCurrencyCacheMutex; | 
|  |  | 
|  | // Cache deletion | 
|  | static void | 
|  | deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) { | 
|  | for (int32_t index = 0; index < count; ++index) { | 
|  | if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) { | 
|  | uprv_free(currencyNames[index].currencyName); | 
|  | } | 
|  | } | 
|  | uprv_free(currencyNames); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | deleteCacheEntry(CurrencyNameCacheEntry* entry) { | 
|  | deleteCurrencyNames(entry->currencyNames, entry->totalCurrencyNameCount); | 
|  | deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount); | 
|  | uprv_free(entry); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Cache clean up | 
|  | static UBool U_CALLCONV | 
|  | currency_cache_cleanup(void) { | 
|  | for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { | 
|  | if (currCache[i]) { | 
|  | deleteCacheEntry(currCache[i]); | 
|  | currCache[i] = 0; | 
|  | } | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Loads the currency name data from the cache, or from resource bundles if necessary. | 
|  | * The refCount is automatically incremented.  It is the caller's responsibility | 
|  | * to decrement it when done! | 
|  | */ | 
|  | static CurrencyNameCacheEntry* | 
|  | getCacheEntry(const char* locale, UErrorCode& ec) { | 
|  |  | 
|  | int32_t total_currency_name_count = 0; | 
|  | CurrencyNameStruct* currencyNames = NULL; | 
|  | int32_t total_currency_symbol_count = 0; | 
|  | CurrencyNameStruct* currencySymbols = NULL; | 
|  | CurrencyNameCacheEntry* cacheEntry = NULL; | 
|  |  | 
|  | umtx_lock(&gCurrencyCacheMutex); | 
|  | // in order to handle racing correctly, | 
|  | // not putting 'search' in a separate function. | 
|  | int8_t found = -1; | 
|  | for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { | 
|  | if (currCache[i]!= NULL && | 
|  | uprv_strcmp(locale, currCache[i]->locale) == 0) { | 
|  | found = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (found != -1) { | 
|  | cacheEntry = currCache[found]; | 
|  | ++(cacheEntry->refCount); | 
|  | } | 
|  | umtx_unlock(&gCurrencyCacheMutex); | 
|  | if (found == -1) { | 
|  | collectCurrencyNames(locale, ¤cyNames, &total_currency_name_count, ¤cySymbols, &total_currency_symbol_count, ec); | 
|  | if (U_FAILURE(ec)) { | 
|  | return NULL; | 
|  | } | 
|  | umtx_lock(&gCurrencyCacheMutex); | 
|  | // check again. | 
|  | for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { | 
|  | if (currCache[i]!= NULL && | 
|  | uprv_strcmp(locale, currCache[i]->locale) == 0) { | 
|  | found = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (found == -1) { | 
|  | // insert new entry to | 
|  | // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM | 
|  | // and remove the existing entry | 
|  | // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM | 
|  | // from cache. | 
|  | cacheEntry = currCache[currentCacheEntryIndex]; | 
|  | if (cacheEntry) { | 
|  | --(cacheEntry->refCount); | 
|  | // delete if the ref count is zero | 
|  | if (cacheEntry->refCount == 0) { | 
|  | deleteCacheEntry(cacheEntry); | 
|  | } | 
|  | } | 
|  | cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry)); | 
|  | currCache[currentCacheEntryIndex] = cacheEntry; | 
|  | uprv_strcpy(cacheEntry->locale, locale); | 
|  | cacheEntry->currencyNames = currencyNames; | 
|  | cacheEntry->totalCurrencyNameCount = total_currency_name_count; | 
|  | cacheEntry->currencySymbols = currencySymbols; | 
|  | cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count; | 
|  | cacheEntry->refCount = 2; // one for cache, one for reference | 
|  | currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM; | 
|  | ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); | 
|  | } else { | 
|  | deleteCurrencyNames(currencyNames, total_currency_name_count); | 
|  | deleteCurrencyNames(currencySymbols, total_currency_symbol_count); | 
|  | cacheEntry = currCache[found]; | 
|  | ++(cacheEntry->refCount); | 
|  | } | 
|  | umtx_unlock(&gCurrencyCacheMutex); | 
|  | } | 
|  |  | 
|  | return cacheEntry; | 
|  | } | 
|  |  | 
|  | static void releaseCacheEntry(CurrencyNameCacheEntry* cacheEntry) { | 
|  | umtx_lock(&gCurrencyCacheMutex); | 
|  | --(cacheEntry->refCount); | 
|  | if (cacheEntry->refCount == 0) {  // remove | 
|  | deleteCacheEntry(cacheEntry); | 
|  | } | 
|  | umtx_unlock(&gCurrencyCacheMutex); | 
|  | } | 
|  |  | 
|  | U_CAPI void | 
|  | uprv_parseCurrency(const char* locale, | 
|  | const icu::UnicodeString& text, | 
|  | icu::ParsePosition& pos, | 
|  | int8_t type, | 
|  | int32_t* partialMatchLen, | 
|  | UChar* result, | 
|  | UErrorCode& ec) { | 
|  | U_NAMESPACE_USE | 
|  | if (U_FAILURE(ec)) { | 
|  | return; | 
|  | } | 
|  | CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec); | 
|  | if (U_FAILURE(ec)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | int32_t total_currency_name_count = cacheEntry->totalCurrencyNameCount; | 
|  | CurrencyNameStruct* currencyNames = cacheEntry->currencyNames; | 
|  | int32_t total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount; | 
|  | CurrencyNameStruct* currencySymbols = cacheEntry->currencySymbols; | 
|  |  | 
|  | int32_t start = pos.getIndex(); | 
|  |  | 
|  | UChar inputText[MAX_CURRENCY_NAME_LEN]; | 
|  | UChar upperText[MAX_CURRENCY_NAME_LEN]; | 
|  | int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start); | 
|  | text.extract(start, textLen, inputText); | 
|  | UErrorCode ec1 = U_ZERO_ERROR; | 
|  | textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1); | 
|  |  | 
|  | // Make sure partialMatchLen is initialized | 
|  | *partialMatchLen = 0; | 
|  |  | 
|  | int32_t max = 0; | 
|  | int32_t matchIndex = -1; | 
|  | // case in-sensitive comparison against currency names | 
|  | searchCurrencyName(currencyNames, total_currency_name_count, | 
|  | upperText, textLen, partialMatchLen, &max, &matchIndex); | 
|  |  | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex); | 
|  | #endif | 
|  |  | 
|  | int32_t maxInSymbol = 0; | 
|  | int32_t matchIndexInSymbol = -1; | 
|  | if (type != UCURR_LONG_NAME) {  // not name only | 
|  | // case sensitive comparison against currency symbols and ISO code. | 
|  | searchCurrencyName(currencySymbols, total_currency_symbol_count, | 
|  | inputText, textLen, | 
|  | partialMatchLen, | 
|  | &maxInSymbol, &matchIndexInSymbol); | 
|  | } | 
|  |  | 
|  | #ifdef UCURR_DEBUG | 
|  | printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol); | 
|  | if(matchIndexInSymbol != -1) { | 
|  | printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (max >= maxInSymbol && matchIndex != -1) { | 
|  | u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4); | 
|  | pos.setIndex(start + max); | 
|  | } else if (maxInSymbol >= max && matchIndexInSymbol != -1) { | 
|  | u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4); | 
|  | pos.setIndex(start + maxInSymbol); | 
|  | } | 
|  |  | 
|  | // decrease reference count | 
|  | releaseCacheEntry(cacheEntry); | 
|  | } | 
|  |  | 
|  | void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) { | 
|  | U_NAMESPACE_USE | 
|  | if (U_FAILURE(ec)) { | 
|  | return; | 
|  | } | 
|  | CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec); | 
|  | if (U_FAILURE(ec)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (int32_t i=0; i<cacheEntry->totalCurrencySymbolCount; i++) { | 
|  | const CurrencyNameStruct& info = cacheEntry->currencySymbols[i]; | 
|  | UChar32 cp; | 
|  | U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp); | 
|  | result.add(cp); | 
|  | } | 
|  |  | 
|  | for (int32_t i=0; i<cacheEntry->totalCurrencyNameCount; i++) { | 
|  | const CurrencyNameStruct& info = cacheEntry->currencyNames[i]; | 
|  | UChar32 cp; | 
|  | U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp); | 
|  | result.add(cp); | 
|  | } | 
|  |  | 
|  | // decrease reference count | 
|  | releaseCacheEntry(cacheEntry); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Internal method.  Given a currency ISO code and a locale, return | 
|  | * the "static" currency name.  This is usually the same as the | 
|  | * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the | 
|  | * format is applied to the number 2.0 (to yield the more common | 
|  | * plural) to return a static name. | 
|  | * | 
|  | * This is used for backward compatibility with old currency logic in | 
|  | * DecimalFormat and DecimalFormatSymbols. | 
|  | */ | 
|  | U_CAPI void | 
|  | uprv_getStaticCurrencyName(const UChar* iso, const char* loc, | 
|  | icu::UnicodeString& result, UErrorCode& ec) | 
|  | { | 
|  | U_NAMESPACE_USE | 
|  |  | 
|  | int32_t len; | 
|  | const UChar* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME, | 
|  | nullptr /* isChoiceFormat */, &len, &ec); | 
|  | if (U_SUCCESS(ec)) { | 
|  | result.setTo(currname, len); | 
|  | } | 
|  | } | 
|  |  | 
|  | U_CAPI int32_t U_EXPORT2 | 
|  | ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) { | 
|  | return ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec); | 
|  | } | 
|  |  | 
|  | U_CAPI int32_t U_EXPORT2 | 
|  | ucurr_getDefaultFractionDigitsForUsage(const UChar* currency, const UCurrencyUsage usage, UErrorCode* ec) { | 
|  | int32_t fracDigits = 0; | 
|  | if (U_SUCCESS(*ec)) { | 
|  | switch (usage) { | 
|  | case UCURR_USAGE_STANDARD: | 
|  | fracDigits = (_findMetaData(currency, *ec))[0]; | 
|  | break; | 
|  | case UCURR_USAGE_CASH: | 
|  | fracDigits = (_findMetaData(currency, *ec))[2]; | 
|  | break; | 
|  | default: | 
|  | *ec = U_UNSUPPORTED_ERROR; | 
|  | } | 
|  | } | 
|  | return fracDigits; | 
|  | } | 
|  |  | 
|  | U_CAPI double U_EXPORT2 | 
|  | ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) { | 
|  | return ucurr_getRoundingIncrementForUsage(currency, UCURR_USAGE_STANDARD, ec); | 
|  | } | 
|  |  | 
|  | U_CAPI double U_EXPORT2 | 
|  | ucurr_getRoundingIncrementForUsage(const UChar* currency, const UCurrencyUsage usage, UErrorCode* ec) { | 
|  | double result = 0.0; | 
|  |  | 
|  | const int32_t *data = _findMetaData(currency, *ec); | 
|  | if (U_SUCCESS(*ec)) { | 
|  | int32_t fracDigits; | 
|  | int32_t increment; | 
|  | switch (usage) { | 
|  | case UCURR_USAGE_STANDARD: | 
|  | fracDigits = data[0]; | 
|  | increment = data[1]; | 
|  | break; | 
|  | case UCURR_USAGE_CASH: | 
|  | fracDigits = data[2]; | 
|  | increment = data[3]; | 
|  | break; | 
|  | default: | 
|  | *ec = U_UNSUPPORTED_ERROR; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // If the meta data is invalid, return 0.0 | 
|  | if (fracDigits < 0 || fracDigits > MAX_POW10) { | 
|  | *ec = U_INVALID_FORMAT_ERROR; | 
|  | } else { | 
|  | // A rounding value of 0 or 1 indicates no rounding. | 
|  | if (increment >= 2) { | 
|  | // Return (increment) / 10^(fracDigits).  The only actual rounding data, | 
|  | // as of this writing, is CHF { 2, 5 }. | 
|  | result = double(increment) / POW10[fracDigits]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | U_CDECL_BEGIN | 
|  |  | 
|  | typedef struct UCurrencyContext { | 
|  | uint32_t currType; /* UCurrCurrencyType */ | 
|  | uint32_t listIdx; | 
|  | } UCurrencyContext; | 
|  |  | 
|  | /* | 
|  | Please keep this list in alphabetical order. | 
|  | You can look at the CLDR supplemental data or ISO-4217 for the meaning of some | 
|  | of these items. | 
|  | ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html | 
|  | */ | 
|  | static const struct CurrencyList { | 
|  | const char *currency; | 
|  | uint32_t currType; | 
|  | } gCurrencyList[] = { | 
|  | {"ADP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"AFA", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ALK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"AOK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"AON", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"AOR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ARA", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ARL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ARM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ARP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ATS", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"AZM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BAD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BAN", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"BEF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"BGL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BGM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BGO", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BOL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BOP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BRB", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BRC", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BRE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BRN", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BRR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BRZ", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BUK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BYB", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BYN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"BYR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CLE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CNH", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CSD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"CSK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"CYP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"DDM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"DEM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ECS", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"EEK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"EQE", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove? | 
|  | {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"ESP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"FIM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"FRF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GEK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GHC", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GNS", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GQE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GRD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"GWE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GWP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"HRD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"IEP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ILP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ILR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ISJ", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ITL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KRH", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"KRO", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"LSM", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove? | 
|  | {"LTL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"LTT", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"LUF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"LVL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"LVR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MAF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MCF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MDC", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MGF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MKN", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MLF", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MRO", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MRU", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MTL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MTP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove? | 
|  | {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MXP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"MZE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MZM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"NIC", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"NLG", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PEI", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PES", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"PLZ", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"PTE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"RHD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ROL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"RUR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SDD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SDP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SIT", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SKK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SRG", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SSP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"STD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SUR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SVC", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TJR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TMM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TPE", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"TRL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"UAK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"UGS", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"UYP", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"VEB", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"VNN", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XRE", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"XSU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XUA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, | 
|  | {"YDD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"YUD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"YUM", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"YUN", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"YUR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ZAL", UCURR_UNCOMMON|UCURR_DEPRECATED}, | 
|  | {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ZMK", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ZMW", UCURR_COMMON|UCURR_NON_DEPRECATED}, | 
|  | {"ZRN", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ZWD", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ZWL", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | {"ZWR", UCURR_COMMON|UCURR_DEPRECATED}, | 
|  | { NULL, 0 } // Leave here to denote the end of the list. | 
|  | }; | 
|  |  | 
|  | #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \ | 
|  | ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch)) | 
|  |  | 
|  | static int32_t U_CALLCONV | 
|  | ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { | 
|  | UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context); | 
|  | uint32_t currType = myContext->currType; | 
|  | int32_t count = 0; | 
|  |  | 
|  | /* Count the number of items matching the type we are looking for. */ | 
|  | for (int32_t idx = 0; gCurrencyList[idx].currency != NULL; idx++) { | 
|  | if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) { | 
|  | count++; | 
|  | } | 
|  | } | 
|  | return count; | 
|  | } | 
|  |  | 
|  | static const char* U_CALLCONV | 
|  | ucurr_nextCurrencyList(UEnumeration *enumerator, | 
|  | int32_t* resultLength, | 
|  | UErrorCode * /*pErrorCode*/) | 
|  | { | 
|  | UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context); | 
|  |  | 
|  | /* Find the next in the list that matches the type we are looking for. */ | 
|  | while (myContext->listIdx < UPRV_LENGTHOF(gCurrencyList)-1) { | 
|  | const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++]; | 
|  | if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType)) | 
|  | { | 
|  | if (resultLength) { | 
|  | *resultLength = 3; /* Currency codes are only 3 chars long */ | 
|  | } | 
|  | return currItem->currency; | 
|  | } | 
|  | } | 
|  | /* We enumerated too far. */ | 
|  | if (resultLength) { | 
|  | *resultLength = 0; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void U_CALLCONV | 
|  | ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { | 
|  | ((UCurrencyContext *)(enumerator->context))->listIdx = 0; | 
|  | } | 
|  |  | 
|  | static void U_CALLCONV | 
|  | ucurr_closeCurrencyList(UEnumeration *enumerator) { | 
|  | uprv_free(enumerator->context); | 
|  | uprv_free(enumerator); | 
|  | } | 
|  |  | 
|  | static void U_CALLCONV | 
|  | ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){ | 
|  | UErrorCode localStatus = U_ZERO_ERROR; | 
|  |  | 
|  | // Look up the CurrencyMap element in the root bundle. | 
|  | UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); | 
|  | UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); | 
|  |  | 
|  | if (U_SUCCESS(localStatus)) { | 
|  | // process each entry in currency map | 
|  | for (int32_t i=0; i<ures_getSize(currencyMapArray); i++) { | 
|  | // get the currency resource | 
|  | UResourceBundle *currencyArray = ures_getByIndex(currencyMapArray, i, NULL, &localStatus); | 
|  | // process each currency | 
|  | if (U_SUCCESS(localStatus)) { | 
|  | for (int32_t j=0; j<ures_getSize(currencyArray); j++) { | 
|  | // get the currency resource | 
|  | UResourceBundle *currencyRes = ures_getByIndex(currencyArray, j, NULL, &localStatus); | 
|  | IsoCodeEntry *entry = (IsoCodeEntry*)uprv_malloc(sizeof(IsoCodeEntry)); | 
|  | if (entry == NULL) { | 
|  | *status = U_MEMORY_ALLOCATION_ERROR; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // get the ISO code | 
|  | int32_t isoLength = 0; | 
|  | UResourceBundle *idRes = ures_getByKey(currencyRes, "id", NULL, &localStatus); | 
|  | if (idRes == NULL) { | 
|  | continue; | 
|  | } | 
|  | const UChar *isoCode = ures_getString(idRes, &isoLength, &localStatus); | 
|  |  | 
|  | // get from date | 
|  | UDate fromDate = U_DATE_MIN; | 
|  | UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus); | 
|  |  | 
|  | if (U_SUCCESS(localStatus)) { | 
|  | int32_t fromLength = 0; | 
|  | const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus); | 
|  | int64_t currDate64 = (int64_t)fromArray[0] << 32; | 
|  | currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); | 
|  | fromDate = (UDate)currDate64; | 
|  | } | 
|  | ures_close(fromRes); | 
|  |  | 
|  | // get to date | 
|  | UDate toDate = U_DATE_MAX; | 
|  | localStatus = U_ZERO_ERROR; | 
|  | UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus); | 
|  |  | 
|  | if (U_SUCCESS(localStatus)) { | 
|  | int32_t toLength = 0; | 
|  | const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); | 
|  | int64_t currDate64 = (int64_t)toArray[0] << 32; | 
|  | currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); | 
|  | toDate = (UDate)currDate64; | 
|  | } | 
|  | ures_close(toRes); | 
|  |  | 
|  | ures_close(idRes); | 
|  | ures_close(currencyRes); | 
|  |  | 
|  | entry->isoCode = isoCode; | 
|  | entry->from = fromDate; | 
|  | entry->to = toDate; | 
|  |  | 
|  | localStatus = U_ZERO_ERROR; | 
|  | uhash_put(isoCodes, (UChar *)isoCode, entry, &localStatus); | 
|  | } | 
|  | } else { | 
|  | *status = localStatus; | 
|  | } | 
|  | ures_close(currencyArray); | 
|  | } | 
|  | } else { | 
|  | *status = localStatus; | 
|  | } | 
|  |  | 
|  | ures_close(currencyMapArray); | 
|  | } | 
|  |  | 
|  | static const UEnumeration gEnumCurrencyList = { | 
|  | NULL, | 
|  | NULL, | 
|  | ucurr_closeCurrencyList, | 
|  | ucurr_countCurrencyList, | 
|  | uenum_unextDefault, | 
|  | ucurr_nextCurrencyList, | 
|  | ucurr_resetCurrencyList | 
|  | }; | 
|  | U_CDECL_END | 
|  |  | 
|  |  | 
|  | static void U_CALLCONV initIsoCodes(UErrorCode &status) { | 
|  | U_ASSERT(gIsoCodes == NULL); | 
|  | ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); | 
|  |  | 
|  | UHashtable *isoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status); | 
|  | if (U_FAILURE(status)) { | 
|  | return; | 
|  | } | 
|  | uhash_setValueDeleter(isoCodes, deleteIsoCodeEntry); | 
|  |  | 
|  | ucurr_createCurrencyList(isoCodes, &status); | 
|  | if (U_FAILURE(status)) { | 
|  | uhash_close(isoCodes); | 
|  | return; | 
|  | } | 
|  | gIsoCodes = isoCodes;  // Note: gIsoCodes is const. Once set up here it is never altered, | 
|  | //       and read only access is safe without synchronization. | 
|  | } | 
|  |  | 
|  | static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) { | 
|  | if (U_FAILURE(status)) { return; } | 
|  | for (auto& entry : unisets::kCurrencyEntries) { | 
|  | UnicodeString exemplar(entry.exemplar); | 
|  | const UnicodeSet* set = unisets::get(entry.key); | 
|  | if (set == nullptr) { return; } | 
|  | UnicodeSetIterator it(*set); | 
|  | while (it.next()) { | 
|  | UnicodeString value = it.getString(); | 
|  | if (value == exemplar) { | 
|  | // No need to mark the exemplar character as an equivalent | 
|  | continue; | 
|  | } | 
|  | makeEquivalent(exemplar, value, hash, status); | 
|  | if (U_FAILURE(status)) { return; } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void U_CALLCONV initCurrSymbolsEquiv() { | 
|  | U_ASSERT(gCurrSymbolsEquiv == NULL); | 
|  | UErrorCode status = U_ZERO_ERROR; | 
|  | ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); | 
|  | icu::Hashtable *temp = new icu::Hashtable(status); | 
|  | if (temp == NULL) { | 
|  | return; | 
|  | } | 
|  | if (U_FAILURE(status)) { | 
|  | delete temp; | 
|  | return; | 
|  | } | 
|  | temp->setValueDeleter(deleteUnicode); | 
|  | populateCurrSymbolsEquiv(temp, status); | 
|  | if (U_FAILURE(status)) { | 
|  | delete temp; | 
|  | return; | 
|  | } | 
|  | gCurrSymbolsEquiv = temp; | 
|  | } | 
|  |  | 
|  | U_CAPI UBool U_EXPORT2 | 
|  | ucurr_isAvailable(const UChar* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) { | 
|  | umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode); | 
|  | if (U_FAILURE(*eErrorCode)) { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode); | 
|  | if (result == NULL) { | 
|  | return FALSE; | 
|  | } else if (from > to) { | 
|  | *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | return FALSE; | 
|  | } else if  ((from > result->to) || (to < result->from)) { | 
|  | return FALSE; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static const icu::Hashtable* getCurrSymbolsEquiv() { | 
|  | umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv); | 
|  | return gCurrSymbolsEquiv; | 
|  | } | 
|  |  | 
|  | U_CAPI UEnumeration * U_EXPORT2 | 
|  | ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) { | 
|  | UEnumeration *myEnum = NULL; | 
|  | UCurrencyContext *myContext; | 
|  |  | 
|  | myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration)); | 
|  | if (myEnum == NULL) { | 
|  | *pErrorCode = U_MEMORY_ALLOCATION_ERROR; | 
|  | return NULL; | 
|  | } | 
|  | uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration)); | 
|  | myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext)); | 
|  | if (myContext == NULL) { | 
|  | *pErrorCode = U_MEMORY_ALLOCATION_ERROR; | 
|  | uprv_free(myEnum); | 
|  | return NULL; | 
|  | } | 
|  | myContext->currType = currType; | 
|  | myContext->listIdx = 0; | 
|  | myEnum->context = myContext; | 
|  | return myEnum; | 
|  | } | 
|  |  | 
|  | U_CAPI int32_t U_EXPORT2 | 
|  | ucurr_countCurrencies(const char* locale, | 
|  | UDate date, | 
|  | UErrorCode* ec) | 
|  | { | 
|  | int32_t currCount = 0; | 
|  |  | 
|  | if (ec != NULL && U_SUCCESS(*ec)) | 
|  | { | 
|  | // local variables | 
|  | UErrorCode localStatus = U_ZERO_ERROR; | 
|  | char id[ULOC_FULLNAME_CAPACITY]; | 
|  |  | 
|  | // get country or country_variant in `id' | 
|  | idForLocale(locale, id, sizeof(id), ec); | 
|  |  | 
|  | if (U_FAILURE(*ec)) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Remove variants, which is only needed for registration. | 
|  | char *idDelim = strchr(id, VAR_DELIM); | 
|  | if (idDelim) | 
|  | { | 
|  | idDelim[0] = 0; | 
|  | } | 
|  |  | 
|  | // Look up the CurrencyMap element in the root bundle. | 
|  | UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); | 
|  | UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); | 
|  |  | 
|  | // Using the id derived from the local, get the currency data | 
|  | UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); | 
|  |  | 
|  | // process each currency to see which one is valid for the given date | 
|  | if (U_SUCCESS(localStatus)) | 
|  | { | 
|  | for (int32_t i=0; i<ures_getSize(countryArray); i++) | 
|  | { | 
|  | // get the currency resource | 
|  | UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus); | 
|  |  | 
|  | // get the from date | 
|  | int32_t fromLength = 0; | 
|  | UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus); | 
|  | const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus); | 
|  |  | 
|  | int64_t currDate64 = (int64_t)fromArray[0] << 32; | 
|  | currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); | 
|  | UDate fromDate = (UDate)currDate64; | 
|  |  | 
|  | if (ures_getSize(currencyRes)> 2) | 
|  | { | 
|  | int32_t toLength = 0; | 
|  | UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus); | 
|  | const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); | 
|  |  | 
|  | currDate64 = (int64_t)toArray[0] << 32; | 
|  | currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); | 
|  | UDate toDate = (UDate)currDate64; | 
|  |  | 
|  | if ((fromDate <= date) && (date < toDate)) | 
|  | { | 
|  | currCount++; | 
|  | } | 
|  |  | 
|  | ures_close(toRes); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (fromDate <= date) | 
|  | { | 
|  | currCount++; | 
|  | } | 
|  | } | 
|  |  | 
|  | // close open resources | 
|  | ures_close(currencyRes); | 
|  | ures_close(fromRes); | 
|  |  | 
|  | } // end For loop | 
|  | } // end if (U_SUCCESS(localStatus)) | 
|  |  | 
|  | ures_close(countryArray); | 
|  |  | 
|  | // Check for errors | 
|  | if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) | 
|  | { | 
|  | // There is nothing to fallback to. | 
|  | // Report the failure/warning if possible. | 
|  | *ec = localStatus; | 
|  | } | 
|  |  | 
|  | if (U_SUCCESS(*ec)) | 
|  | { | 
|  | // no errors | 
|  | return currCount; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | // If we got here, either error code is invalid or | 
|  | // some argument passed is no good. | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | U_CAPI int32_t U_EXPORT2 | 
|  | ucurr_forLocaleAndDate(const char* locale, | 
|  | UDate date, | 
|  | int32_t index, | 
|  | UChar* buff, | 
|  | int32_t buffCapacity, | 
|  | UErrorCode* ec) | 
|  | { | 
|  | int32_t resLen = 0; | 
|  | int32_t currIndex = 0; | 
|  | const UChar* s = NULL; | 
|  |  | 
|  | if (ec != NULL && U_SUCCESS(*ec)) | 
|  | { | 
|  | // check the arguments passed | 
|  | if ((buff && buffCapacity) || !buffCapacity ) | 
|  | { | 
|  | // local variables | 
|  | UErrorCode localStatus = U_ZERO_ERROR; | 
|  | char id[ULOC_FULLNAME_CAPACITY]; | 
|  |  | 
|  | // get country or country_variant in `id' | 
|  | idForLocale(locale, id, sizeof(id), ec); | 
|  | if (U_FAILURE(*ec)) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Remove variants, which is only needed for registration. | 
|  | char *idDelim = strchr(id, VAR_DELIM); | 
|  | if (idDelim) | 
|  | { | 
|  | idDelim[0] = 0; | 
|  | } | 
|  |  | 
|  | // Look up the CurrencyMap element in the root bundle. | 
|  | UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); | 
|  | UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); | 
|  |  | 
|  | // Using the id derived from the local, get the currency data | 
|  | UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); | 
|  |  | 
|  | // process each currency to see which one is valid for the given date | 
|  | bool matchFound = false; | 
|  | if (U_SUCCESS(localStatus)) | 
|  | { | 
|  | if ((index <= 0) || (index> ures_getSize(countryArray))) | 
|  | { | 
|  | // requested index is out of bounds | 
|  | ures_close(countryArray); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | for (int32_t i=0; i<ures_getSize(countryArray); i++) | 
|  | { | 
|  | // get the currency resource | 
|  | UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus); | 
|  | s = ures_getStringByKey(currencyRes, "id", &resLen, &localStatus); | 
|  |  | 
|  | // get the from date | 
|  | int32_t fromLength = 0; | 
|  | UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus); | 
|  | const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus); | 
|  |  | 
|  | int64_t currDate64 = (int64_t)fromArray[0] << 32; | 
|  | currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); | 
|  | UDate fromDate = (UDate)currDate64; | 
|  |  | 
|  | if (ures_getSize(currencyRes)> 2) | 
|  | { | 
|  | int32_t toLength = 0; | 
|  | UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus); | 
|  | const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus); | 
|  |  | 
|  | currDate64 = (int64_t)toArray[0] << 32; | 
|  | currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF)); | 
|  | UDate toDate = (UDate)currDate64; | 
|  |  | 
|  | if ((fromDate <= date) && (date < toDate)) | 
|  | { | 
|  | currIndex++; | 
|  | if (currIndex == index) | 
|  | { | 
|  | matchFound = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | ures_close(toRes); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (fromDate <= date) | 
|  | { | 
|  | currIndex++; | 
|  | if (currIndex == index) | 
|  | { | 
|  | matchFound = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // close open resources | 
|  | ures_close(currencyRes); | 
|  | ures_close(fromRes); | 
|  |  | 
|  | // check for loop exit | 
|  | if (matchFound) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | } // end For loop | 
|  | } | 
|  |  | 
|  | ures_close(countryArray); | 
|  |  | 
|  | // Check for errors | 
|  | if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) | 
|  | { | 
|  | // There is nothing to fallback to. | 
|  | // Report the failure/warning if possible. | 
|  | *ec = localStatus; | 
|  | } | 
|  |  | 
|  | if (U_SUCCESS(*ec)) | 
|  | { | 
|  | // no errors | 
|  | if((buffCapacity> resLen) && matchFound) | 
|  | { | 
|  | // write out the currency value | 
|  | u_strcpy(buff, s); | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // return null terminated currency string | 
|  | return u_terminateUChars(buff, buffCapacity, resLen, ec); | 
|  | } | 
|  | else | 
|  | { | 
|  | // illegal argument encountered | 
|  | *ec = U_ILLEGAL_ARGUMENT_ERROR; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | // If we got here, either error code is invalid or | 
|  | // some argument passed is no good. | 
|  | return resLen; | 
|  | } | 
|  |  | 
|  | static const UEnumeration defaultKeywordValues = { | 
|  | NULL, | 
|  | NULL, | 
|  | ulist_close_keyword_values_iterator, | 
|  | ulist_count_keyword_values, | 
|  | uenum_unextDefault, | 
|  | ulist_next_keyword_value, | 
|  | ulist_reset_keyword_values_iterator | 
|  | }; | 
|  |  | 
|  | U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) { | 
|  | // Resolve region | 
|  | char prefRegion[ULOC_COUNTRY_CAPACITY]; | 
|  | ulocimp_getRegionForSupplementalData(locale, TRUE, prefRegion, sizeof(prefRegion), status); | 
|  |  | 
|  | // Read value from supplementalData | 
|  | UList *values = ulist_createEmptyList(status); | 
|  | UList *otherValues = ulist_createEmptyList(status); | 
|  | UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration)); | 
|  | if (U_FAILURE(*status) || en == NULL) { | 
|  | if (en == NULL) { | 
|  | *status = U_MEMORY_ALLOCATION_ERROR; | 
|  | } else { | 
|  | uprv_free(en); | 
|  | } | 
|  | ulist_deleteList(values); | 
|  | ulist_deleteList(otherValues); | 
|  | return NULL; | 
|  | } | 
|  | memcpy(en, &defaultKeywordValues, sizeof(UEnumeration)); | 
|  | en->context = values; | 
|  |  | 
|  | UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status); | 
|  | ures_getByKey(bundle, "CurrencyMap", bundle, status); | 
|  | UResourceBundle bundlekey, regbndl, curbndl, to; | 
|  | ures_initStackObject(&bundlekey); | 
|  | ures_initStackObject(®bndl); | 
|  | ures_initStackObject(&curbndl); | 
|  | ures_initStackObject(&to); | 
|  |  | 
|  | while (U_SUCCESS(*status) && ures_hasNext(bundle)) { | 
|  | ures_getNextResource(bundle, &bundlekey, status); | 
|  | if (U_FAILURE(*status)) { | 
|  | break; | 
|  | } | 
|  | const char *region = ures_getKey(&bundlekey); | 
|  | UBool isPrefRegion = uprv_strcmp(region, prefRegion) == 0 ? TRUE : FALSE; | 
|  | if (!isPrefRegion && commonlyUsed) { | 
|  | // With commonlyUsed=true, we do not put | 
|  | // currencies for other regions in the | 
|  | // result list. | 
|  | continue; | 
|  | } | 
|  | ures_getByKey(bundle, region, ®bndl, status); | 
|  | if (U_FAILURE(*status)) { | 
|  | break; | 
|  | } | 
|  | while (U_SUCCESS(*status) && ures_hasNext(®bndl)) { | 
|  | ures_getNextResource(®bndl, &curbndl, status); | 
|  | if (ures_getType(&curbndl) != URES_TABLE) { | 
|  | // Currently, an empty ARRAY is mixed in. | 
|  | continue; | 
|  | } | 
|  | char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY); | 
|  | int32_t curIDLength = ULOC_KEYWORDS_CAPACITY; | 
|  | if (curID == NULL) { | 
|  | *status = U_MEMORY_ALLOCATION_ERROR; | 
|  | break; | 
|  | } | 
|  |  | 
|  | #if U_CHARSET_FAMILY==U_ASCII_FAMILY | 
|  | ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, TRUE, status); | 
|  | /* optimize - use the utf-8 string */ | 
|  | #else | 
|  | { | 
|  | const UChar* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status); | 
|  | if(U_SUCCESS(*status)) { | 
|  | if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) { | 
|  | *status = U_BUFFER_OVERFLOW_ERROR; | 
|  | } else { | 
|  | u_UCharsToChars(defString, curID, curIDLength+1); | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (U_FAILURE(*status)) { | 
|  | break; | 
|  | } | 
|  | UBool hasTo = FALSE; | 
|  | ures_getByKey(&curbndl, "to", &to, status); | 
|  | if (U_FAILURE(*status)) { | 
|  | // Do nothing here... | 
|  | *status = U_ZERO_ERROR; | 
|  | } else { | 
|  | hasTo = TRUE; | 
|  | } | 
|  | if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) { | 
|  | // Currently active currency for the target country | 
|  | ulist_addItemEndList(values, curID, TRUE, status); | 
|  | } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) { | 
|  | ulist_addItemEndList(otherValues, curID, TRUE, status); | 
|  | } else { | 
|  | uprv_free(curID); | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | if (U_SUCCESS(*status)) { | 
|  | if (commonlyUsed) { | 
|  | if (ulist_getListSize(values) == 0) { | 
|  | // This could happen if no valid region is supplied in the input | 
|  | // locale. In this case, we use the CLDR's default. | 
|  | uenum_close(en); | 
|  | en = ucurr_getKeywordValuesForLocale(key, "und", TRUE, status); | 
|  | } | 
|  | } else { | 
|  | // Consolidate the list | 
|  | char *value = NULL; | 
|  | ulist_resetList(otherValues); | 
|  | while ((value = (char *)ulist_getNext(otherValues)) != NULL) { | 
|  | if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) { | 
|  | char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY); | 
|  | uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1); | 
|  | ulist_addItemEndList(values, tmpValue, TRUE, status); | 
|  | if (U_FAILURE(*status)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ulist_resetList((UList *)(en->context)); | 
|  | } else { | 
|  | ulist_deleteList(values); | 
|  | uprv_free(en); | 
|  | values = NULL; | 
|  | en = NULL; | 
|  | } | 
|  | ures_close(&to); | 
|  | ures_close(&curbndl); | 
|  | ures_close(®bndl); | 
|  | ures_close(&bundlekey); | 
|  | ures_close(bundle); | 
|  |  | 
|  | ulist_deleteList(otherValues); | 
|  |  | 
|  | return en; | 
|  | } | 
|  |  | 
|  |  | 
|  | U_CAPI int32_t U_EXPORT2 | 
|  | ucurr_getNumericCode(const UChar* currency) { | 
|  | int32_t code = 0; | 
|  | if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) { | 
|  | UErrorCode status = U_ZERO_ERROR; | 
|  |  | 
|  | UResourceBundle *bundle = ures_openDirect(0, "currencyNumericCodes", &status); | 
|  | ures_getByKey(bundle, "codeMap", bundle, &status); | 
|  | if (U_SUCCESS(status)) { | 
|  | char alphaCode[ISO_CURRENCY_CODE_LENGTH+1]; | 
|  | myUCharsToChars(alphaCode, currency); | 
|  | T_CString_toUpperCase(alphaCode); | 
|  | ures_getByKey(bundle, alphaCode, bundle, &status); | 
|  | int tmpCode = ures_getInt(bundle, &status); | 
|  | if (U_SUCCESS(status)) { | 
|  | code = tmpCode; | 
|  | } | 
|  | } | 
|  | ures_close(bundle); | 
|  | } | 
|  | return code; | 
|  | } | 
|  | #endif /* #if !UCONFIG_NO_FORMATTING */ | 
|  |  | 
|  | //eof |