intl/icu/source/i18n/ucurr.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /*
     2 **********************************************************************
     3 * Copyright (c) 2002-2013, International Business Machines
     4 * Corporation and others.  All Rights Reserved.
     5 **********************************************************************
     6 */
     8 #include "unicode/utypes.h"
    10 #if !UCONFIG_NO_FORMATTING
    12 #include "unicode/ucurr.h"
    13 #include "unicode/locid.h"
    14 #include "unicode/ures.h"
    15 #include "unicode/ustring.h"
    16 #include "unicode/choicfmt.h"
    17 #include "unicode/parsepos.h"
    18 #include "ustr_imp.h"
    19 #include "cmemory.h"
    20 #include "cstring.h"
    21 #include "uassert.h"
    22 #include "umutex.h"
    23 #include "ucln_in.h"
    24 #include "uenumimp.h"
    25 #include "uhash.h"
    26 #include "hash.h"
    27 #include "uresimp.h"
    28 #include "ulist.h"
    29 #include "ureslocs.h"
    31 //#define UCURR_DEBUG_EQUIV 1
    32 #ifdef UCURR_DEBUG_EQUIV
    33 #include "stdio.h"
    34 #endif
    35 //#define UCURR_DEBUG 1
    36 #ifdef UCURR_DEBUG
    37 #include "stdio.h"
    38 #endif
    40 typedef struct IsoCodeEntry {
    41     const UChar *isoCode; /* const because it's a reference to a resource bundle string. */
    42     UDate from;
    43     UDate to;
    44 } IsoCodeEntry;
    46 //------------------------------------------------------------
    47 // Constants
    49 // Default currency meta data of last resort.  We try to use the
    50 // defaults encoded in the meta data resource bundle.  If there is a
    51 // configuration/build error and these are not available, we use these
    52 // hard-coded defaults (which should be identical).
    53 static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 };
    55 // POW10[i] = 10^i, i=0..MAX_POW10
    56 static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
    57                                  1000000, 10000000, 100000000, 1000000000 };
    59 static const int32_t MAX_POW10 = (sizeof(POW10)/sizeof(POW10[0])) - 1;
    61 // Defines equivalent currency symbols.
    62 static const char *EQUIV_CURRENCY_SYMBOLS[][2] = {
    63     {"\\u00a5", "\\uffe5"},
    64     {"$", "\\ufe69"},
    65     {"$", "\\uff04"},
    66     {"\\u20a8", "\\u20b9"},
    67     {"\\u00a3", "\\u20a4"}};
    69 #define ISO_CURRENCY_CODE_LENGTH 3
    71 //------------------------------------------------------------
    72 // Resource tags
    73 //
    75 static const char CURRENCY_DATA[] = "supplementalData";
    76 // Tag for meta-data, in root.
    77 static const char CURRENCY_META[] = "CurrencyMeta";
    79 // Tag for map from countries to currencies, in root.
    80 static const char CURRENCY_MAP[] = "CurrencyMap";
    82 // Tag for default meta-data, in CURRENCY_META
    83 static const char DEFAULT_META[] = "DEFAULT";
    85 // Variant for legacy pre-euro mapping in CurrencyMap
    86 static const char VAR_PRE_EURO[] = "PREEURO";
    88 // Variant for legacy euro mapping in CurrencyMap
    89 static const char VAR_EURO[] = "EURO";
    91 // Variant delimiter
    92 static const char VAR_DELIM = '_';
    93 static const char VAR_DELIM_STR[] = "_";
    95 // Variant for legacy euro mapping in CurrencyMap
    96 //static const char VAR_DELIM_EURO[] = "_EURO";
    98 #define VARIANT_IS_EMPTY    0
    99 #define VARIANT_IS_EURO     0x1
   100 #define VARIANT_IS_PREEURO  0x2
   102 // Tag for localized display names (symbols) of currencies
   103 static const char CURRENCIES[] = "Currencies";
   104 static const char CURRENCYPLURALS[] = "CurrencyPlurals";
   106 // Marker character indicating that a display name is a ChoiceFormat
   107 // pattern.  Strings that start with one mark are ChoiceFormat
   108 // patterns.  Strings that start with 2 marks are static strings, and
   109 // the first mark is deleted.
   110 static const UChar CHOICE_FORMAT_MARK = 0x003D; // Equals sign
   112 static const UChar EUR_STR[] = {0x0045,0x0055,0x0052,0};
   114 // ISO codes mapping table
   115 static const UHashtable* gIsoCodes = NULL;
   116 static icu::UInitOnce gIsoCodesInitOnce = U_INITONCE_INITIALIZER;
   118 // Currency symbol equivalances
   119 static const icu::Hashtable* gCurrSymbolsEquiv = NULL;
   120 static icu::UInitOnce gCurrSymbolsEquivInitOnce = U_INITONCE_INITIALIZER;
   122 // EquivIterator iterates over all strings that are equivalent to a given
   123 // string, s. Note that EquivIterator will never yield s itself.
   124 class EquivIterator : icu::UMemory {
   125 public:
   126     // Constructor. hash stores the equivalence relationships; s is the string
   127     // for which we find equivalent strings.
   128     inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s)
   129         : _hash(hash) { 
   130         _start = _current = &s;
   131     }
   132     inline ~EquivIterator() { }
   134     // next returns the next equivalent string or NULL if there are no more.
   135     // If s has no equivalent strings, next returns NULL on the first call.
   136     const icu::UnicodeString *next();
   137 private:
   138     const icu::Hashtable& _hash;
   139     const icu::UnicodeString* _start;
   140     const icu::UnicodeString* _current;
   141 };
   143 const icu::UnicodeString *
   144 EquivIterator::next() {
   145     const icu::UnicodeString* _next = (const icu::UnicodeString*) _hash.get(*_current);
   146     if (_next == NULL) {
   147         U_ASSERT(_current == _start);
   148         return NULL;
   149     }
   150     if (*_next == *_start) {
   151         return NULL;
   152     }
   153     _current = _next;
   154     return _next;
   155 }
   157 // makeEquivalent makes lhs and rhs equivalent by updating the equivalence
   158 // relations in hash accordingly.
   159 static void makeEquivalent(
   160     const icu::UnicodeString &lhs,
   161     const icu::UnicodeString &rhs,
   162     icu::Hashtable* hash, UErrorCode &status) {
   163     if (U_FAILURE(status)) {
   164         return;
   165     }
   166     if (lhs == rhs) {
   167         // already equivalent
   168         return;
   169     }
   170     EquivIterator leftIter(*hash, lhs);
   171     EquivIterator rightIter(*hash, rhs);
   172     const icu::UnicodeString *firstLeft = leftIter.next();
   173     const icu::UnicodeString *firstRight = rightIter.next();
   174     const icu::UnicodeString *nextLeft = firstLeft;
   175     const icu::UnicodeString *nextRight = firstRight;
   176     while (nextLeft != NULL && nextRight != NULL) {
   177         if (*nextLeft == rhs || *nextRight == lhs) {
   178             // Already equivalent
   179             return;
   180         }
   181         nextLeft = leftIter.next();
   182         nextRight = rightIter.next();
   183     }
   184     // Not equivalent. Must join.
   185     icu::UnicodeString *newFirstLeft;
   186     icu::UnicodeString *newFirstRight;
   187     if (firstRight == NULL && firstLeft == NULL) {
   188         // Neither lhs or rhs belong to an equivalence circle, so we form
   189         // a new equivalnce circle of just lhs and rhs.
   190         newFirstLeft = new icu::UnicodeString(rhs);
   191         newFirstRight = new icu::UnicodeString(lhs);
   192     } else if (firstRight == NULL) {
   193         // lhs belongs to an equivalence circle, but rhs does not, so we link
   194         // rhs into lhs' circle.
   195         newFirstLeft = new icu::UnicodeString(rhs);
   196         newFirstRight = new icu::UnicodeString(*firstLeft);
   197     } else if (firstLeft == NULL) {
   198         // rhs belongs to an equivlance circle, but lhs does not, so we link
   199         // lhs into rhs' circle.
   200         newFirstLeft = new icu::UnicodeString(*firstRight);
   201         newFirstRight = new icu::UnicodeString(lhs);
   202     } else {
   203         // Both lhs and rhs belong to different equivalnce circles. We link
   204         // them together to form one single, larger equivalnce circle.
   205         newFirstLeft = new icu::UnicodeString(*firstRight);
   206         newFirstRight = new icu::UnicodeString(*firstLeft);
   207     }
   208     if (newFirstLeft == NULL || newFirstRight == NULL) {
   209         delete newFirstLeft;
   210         delete newFirstRight;
   211         status = U_MEMORY_ALLOCATION_ERROR;
   212         return;
   213     }
   214     hash->put(lhs, (void *) newFirstLeft, status);
   215     hash->put(rhs, (void *) newFirstRight, status);
   216 }
   218 // countEquivalent counts how many strings are equivalent to s.
   219 // hash stores all the equivalnce relations.
   220 // countEquivalent does not include s itself in the count.
   221 static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) {
   222     int32_t result = 0;
   223     EquivIterator iter(hash, s);
   224     while (iter.next() != NULL) {
   225         ++result;
   226     }
   227 #ifdef UCURR_DEBUG_EQUIV
   228  {
   229    char tmp[200];
   230    s.extract(0,s.length(),tmp, "UTF-8");
   231    printf("CountEquivalent('%s') = %d\n", tmp, result);
   232  }
   233 #endif
   234     return result;
   235 }
   237 static const icu::Hashtable* getCurrSymbolsEquiv();
   239 //------------------------------------------------------------
   240 // Code
   242 /**
   243  * Cleanup callback func
   244  */
   245 static UBool U_CALLCONV 
   246 isoCodes_cleanup(void)
   247 {
   248     if (gIsoCodes != NULL) {
   249         uhash_close(const_cast<UHashtable *>(gIsoCodes));
   250         gIsoCodes = NULL;
   251     }
   252     gIsoCodesInitOnce.reset();
   253     return TRUE;
   254 }
   256 /**
   257  * Cleanup callback func
   258  */
   259 static UBool U_CALLCONV 
   260 currSymbolsEquiv_cleanup(void)
   261 {
   262     delete const_cast<icu::Hashtable *>(gCurrSymbolsEquiv);
   263     gCurrSymbolsEquiv = NULL;
   264     gCurrSymbolsEquivInitOnce.reset();
   265     return TRUE;
   266 }
   268 /**
   269  * Deleter for OlsonToMetaMappingEntry
   270  */
   271 static void U_CALLCONV
   272 deleteIsoCodeEntry(void *obj) {
   273     IsoCodeEntry *entry = (IsoCodeEntry*)obj;
   274     uprv_free(entry);
   275 }
   277 /**
   278  * Deleter for gCurrSymbolsEquiv.
   279  */
   280 static void U_CALLCONV
   281 deleteUnicode(void *obj) {
   282     icu::UnicodeString *entry = (icu::UnicodeString*)obj;
   283     delete entry;
   284 }
   286 /**
   287  * Unfortunately, we have to convert the UChar* currency code to char*
   288  * to use it as a resource key.
   289  */
   290 static inline char*
   291 myUCharsToChars(char* resultOfLen4, const UChar* currency) {
   292     u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH);
   293     resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0;
   294     return resultOfLen4;
   295 }
   297 /**
   298  * Internal function to look up currency data.  Result is an array of
   299  * four integers.  The first is the fraction digits.  The second is the
   300  * rounding increment, or 0 if none.  The rounding increment is in
   301  * units of 10^(-fraction_digits).  The third and fourth are the same
   302  * except that they are those used in cash transations ( cashDigits
   303  * and cashRounding ).
   304  */
   305 static const int32_t*
   306 _findMetaData(const UChar* currency, UErrorCode& ec) {
   308     if (currency == 0 || *currency == 0) {
   309         if (U_SUCCESS(ec)) {
   310             ec = U_ILLEGAL_ARGUMENT_ERROR;
   311         }
   312         return LAST_RESORT_DATA;
   313     }
   315     // Get CurrencyMeta resource out of root locale file.  [This may
   316     // move out of the root locale file later; if it does, update this
   317     // code.]
   318     UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec);
   319     UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec);
   321     if (U_FAILURE(ec)) {
   322         ures_close(currencyMeta);
   323         // Config/build error; return hard-coded defaults
   324         return LAST_RESORT_DATA;
   325     }
   327     // Look up our currency, or if that's not available, then DEFAULT
   328     char buf[ISO_CURRENCY_CODE_LENGTH+1];
   329     UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure
   330     UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), NULL, &ec2);
   331       if (U_FAILURE(ec2)) {
   332         ures_close(rb);
   333         rb = ures_getByKey(currencyMeta,DEFAULT_META, NULL, &ec);
   334         if (U_FAILURE(ec)) {
   335             ures_close(currencyMeta);
   336             ures_close(rb);
   337             // Config/build error; return hard-coded defaults
   338             return LAST_RESORT_DATA;
   339         }
   340     }
   342     int32_t len;
   343     const int32_t *data = ures_getIntVector(rb, &len, &ec);
   344     if (U_FAILURE(ec) || len != 4) {
   345         // Config/build error; return hard-coded defaults
   346         if (U_SUCCESS(ec)) {
   347             ec = U_INVALID_FORMAT_ERROR;
   348         }
   349         ures_close(currencyMeta);
   350         ures_close(rb);
   351         return LAST_RESORT_DATA;
   352     }
   354     ures_close(currencyMeta);
   355     ures_close(rb);
   356     return data;
   357 }
   359 // -------------------------------------
   361 /**
   362  * @see VARIANT_IS_EURO
   363  * @see VARIANT_IS_PREEURO
   364  */
   365 static uint32_t
   366 idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec)
   367 {
   368     uint32_t variantType = 0;
   369     // !!! this is internal only, assumes buffer is not null and capacity is sufficient
   370     // Extract the country name and variant name.  We only
   371     // recognize two variant names, EURO and PREEURO.
   372     char variant[ULOC_FULLNAME_CAPACITY];
   373     uloc_getCountry(locale, countryAndVariant, capacity, ec);
   374     uloc_getVariant(locale, variant, sizeof(variant), ec);
   375     if (variant[0] != 0) {
   376         variantType = (uint32_t)(0 == uprv_strcmp(variant, VAR_EURO))
   377                    | ((uint32_t)(0 == uprv_strcmp(variant, VAR_PRE_EURO)) << 1);
   378         if (variantType)
   379         {
   380             uprv_strcat(countryAndVariant, VAR_DELIM_STR);
   381             uprv_strcat(countryAndVariant, variant);
   382         }
   383     }
   384     return variantType;
   385 }
   387 // ------------------------------------------
   388 //
   389 // Registration
   390 //
   391 //-------------------------------------------
   393 // don't use ICUService since we don't need fallback
   395 U_CDECL_BEGIN
   396 static UBool U_CALLCONV currency_cleanup(void);
   397 U_CDECL_END
   399 #if !UCONFIG_NO_SERVICE
   400 struct CReg;
   402 static UMutex gCRegLock = U_MUTEX_INITIALIZER;
   403 static CReg* gCRegHead = 0;
   405 struct CReg : public icu::UMemory {
   406     CReg *next;
   407     UChar iso[ISO_CURRENCY_CODE_LENGTH+1];
   408     char  id[ULOC_FULLNAME_CAPACITY];
   410     CReg(const UChar* _iso, const char* _id)
   411         : next(0)
   412     {
   413         int32_t len = (int32_t)uprv_strlen(_id);
   414         if (len > (int32_t)(sizeof(id)-1)) {
   415             len = (sizeof(id)-1);
   416         }
   417         uprv_strncpy(id, _id, len);
   418         id[len] = 0;
   419         uprv_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH * sizeof(const UChar));
   420         iso[ISO_CURRENCY_CODE_LENGTH] = 0;
   421     }
   423     static UCurrRegistryKey reg(const UChar* _iso, const char* _id, UErrorCode* status)
   424     {
   425         if (status && U_SUCCESS(*status) && _iso && _id) {
   426             CReg* n = new CReg(_iso, _id);
   427             if (n) {
   428                 umtx_lock(&gCRegLock);
   429                 if (!gCRegHead) {
   430                     /* register for the first time */
   431                     ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
   432                 }
   433                 n->next = gCRegHead;
   434                 gCRegHead = n;
   435                 umtx_unlock(&gCRegLock);
   436                 return n;
   437             }
   438             *status = U_MEMORY_ALLOCATION_ERROR;
   439         }
   440         return 0;
   441     }
   443     static UBool unreg(UCurrRegistryKey key) {
   444         UBool found = FALSE;
   445         umtx_lock(&gCRegLock);
   447         CReg** p = &gCRegHead;
   448         while (*p) {
   449             if (*p == key) {
   450                 *p = ((CReg*)key)->next;
   451                 delete (CReg*)key;
   452                 found = TRUE;
   453                 break;
   454             }
   455             p = &((*p)->next);
   456         }
   458         umtx_unlock(&gCRegLock);
   459         return found;
   460     }
   462     static const UChar* get(const char* id) {
   463         const UChar* result = NULL;
   464         umtx_lock(&gCRegLock);
   465         CReg* p = gCRegHead;
   467         /* register cleanup of the mutex */
   468         ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
   469         while (p) {
   470             if (uprv_strcmp(id, p->id) == 0) {
   471                 result = p->iso;
   472                 break;
   473             }
   474             p = p->next;
   475         }
   476         umtx_unlock(&gCRegLock);
   477         return result;
   478     }
   480     /* This doesn't need to be thread safe. It's for u_cleanup only. */
   481     static void cleanup(void) {
   482         while (gCRegHead) {
   483             CReg* n = gCRegHead;
   484             gCRegHead = gCRegHead->next;
   485             delete n;
   486         }
   487     }
   488 };
   490 // -------------------------------------
   492 U_CAPI UCurrRegistryKey U_EXPORT2
   493 ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status)
   494 {
   495     if (status && U_SUCCESS(*status)) {
   496         char id[ULOC_FULLNAME_CAPACITY];
   497         idForLocale(locale, id, sizeof(id), status);
   498         return CReg::reg(isoCode, id, status);
   499     }
   500     return NULL;
   501 }
   503 // -------------------------------------
   505 U_CAPI UBool U_EXPORT2
   506 ucurr_unregister(UCurrRegistryKey key, UErrorCode* status)
   507 {
   508     if (status && U_SUCCESS(*status)) {
   509         return CReg::unreg(key);
   510     }
   511     return FALSE;
   512 }
   513 #endif /* UCONFIG_NO_SERVICE */
   515 // -------------------------------------
   517 /**
   518  * Release all static memory held by currency.
   519  */
   520 /*The declaration here is needed so currency_cleanup(void)
   521  * can call this function.
   522  */
   523 static UBool U_CALLCONV
   524 currency_cache_cleanup(void);
   526 U_CDECL_BEGIN
   527 static UBool U_CALLCONV currency_cleanup(void) {
   528 #if !UCONFIG_NO_SERVICE
   529     CReg::cleanup();
   530 #endif
   531     /*
   532      * There might be some cached currency data or isoCodes data.
   533      */
   534     currency_cache_cleanup();
   535     isoCodes_cleanup();
   536     currSymbolsEquiv_cleanup();
   538     return TRUE;
   539 }
   540 U_CDECL_END
   542 // -------------------------------------
   544 U_CAPI int32_t U_EXPORT2
   545 ucurr_forLocale(const char* locale,
   546                 UChar* buff,
   547                 int32_t buffCapacity,
   548                 UErrorCode* ec)
   549 {
   550     int32_t resLen = 0;
   551     const UChar* s = NULL;
   552     if (ec != NULL && U_SUCCESS(*ec)) {
   553         if ((buff && buffCapacity) || !buffCapacity) {
   554             UErrorCode localStatus = U_ZERO_ERROR;
   555             char id[ULOC_FULLNAME_CAPACITY];
   556             if ((resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus))) {
   557                 // there is a currency keyword. Try to see if it's valid
   558                 if(buffCapacity > resLen) {
   559                     /* Normalize the currency keyword value to upper case. */
   560                     T_CString_toUpperCase(id);
   561                     u_charsToUChars(id, buff, resLen);
   562                 }
   563             } else {
   564                 // get country or country_variant in `id'
   565                 uint32_t variantType = idForLocale(locale, id, sizeof(id), ec);
   567                 if (U_FAILURE(*ec)) {
   568                     return 0;
   569                 }
   571 #if !UCONFIG_NO_SERVICE
   572                 const UChar* result = CReg::get(id);
   573                 if (result) {
   574                     if(buffCapacity > u_strlen(result)) {
   575                         u_strcpy(buff, result);
   576                     }
   577                     return u_strlen(result);
   578                 }
   579 #endif
   580                 // Remove variants, which is only needed for registration.
   581                 char *idDelim = strchr(id, VAR_DELIM);
   582                 if (idDelim) {
   583                     idDelim[0] = 0;
   584                 }
   586                 // Look up the CurrencyMap element in the root bundle.
   587                 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
   588                 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
   589                 UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
   590                 UResourceBundle *currencyReq = ures_getByIndex(countryArray, 0, NULL, &localStatus);
   591                 s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
   593                 /*
   594                 Get the second item when PREEURO is requested, and this is a known Euro country.
   595                 If the requested variant is PREEURO, and this isn't a Euro country, assume
   596                 that the country changed over to the Euro in the future. This is probably
   597                 an old version of ICU that hasn't been updated yet. The latest currency is
   598                 probably correct.
   599                 */
   600                 if (U_SUCCESS(localStatus)) {
   601                     if ((variantType & VARIANT_IS_PREEURO) && u_strcmp(s, EUR_STR) == 0) {
   602                         currencyReq = ures_getByIndex(countryArray, 1, currencyReq, &localStatus);
   603                         s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
   604                     }
   605                     else if ((variantType & VARIANT_IS_EURO)) {
   606                         s = EUR_STR;
   607                     }
   608                 }
   609                 ures_close(countryArray);
   610                 ures_close(currencyReq);
   612                 if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0)
   613                 {
   614                     // We don't know about it.  Check to see if we support the variant.
   615                     uloc_getParent(locale, id, sizeof(id), ec);
   616                     *ec = U_USING_FALLBACK_WARNING;
   617                     return ucurr_forLocale(id, buff, buffCapacity, ec);
   618                 }
   619                 else if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
   620                     // There is nothing to fallback to. Report the failure/warning if possible.
   621                     *ec = localStatus;
   622                 }
   623                 if (U_SUCCESS(*ec)) {
   624                     if(buffCapacity > resLen) {
   625                         u_strcpy(buff, s);
   626                     }
   627                 }
   628             }
   629             return u_terminateUChars(buff, buffCapacity, resLen, ec);
   630         } else {
   631             *ec = U_ILLEGAL_ARGUMENT_ERROR;
   632         }
   633     }
   634     return resLen;
   635 }
   637 // end registration
   639 /**
   640  * Modify the given locale name by removing the rightmost _-delimited
   641  * element.  If there is none, empty the string ("" == root).
   642  * NOTE: The string "root" is not recognized; do not use it.
   643  * @return TRUE if the fallback happened; FALSE if locale is already
   644  * root ("").
   645  */
   646 static UBool fallback(char *loc) {
   647     if (!*loc) {
   648         return FALSE;
   649     }
   650     UErrorCode status = U_ZERO_ERROR;
   651     uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status);
   652  /*
   653     char *i = uprv_strrchr(loc, '_');
   654     if (i == NULL) {
   655         i = loc;
   656     }
   657     *i = 0;
   658  */
   659     return TRUE;
   660 }
   663 U_CAPI const UChar* U_EXPORT2
   664 ucurr_getName(const UChar* currency,
   665               const char* locale,
   666               UCurrNameStyle nameStyle,
   667               UBool* isChoiceFormat, // fillin
   668               int32_t* len, // fillin
   669               UErrorCode* ec) {
   671     // Look up the Currencies resource for the given locale.  The
   672     // Currencies locale data looks like this:
   673     //|en {
   674     //|  Currencies {
   675     //|    USD { "US$", "US Dollar" }
   676     //|    CHF { "Sw F", "Swiss Franc" }
   677     //|    INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
   678     //|    //...
   679     //|  }
   680     //|}
   682     if (U_FAILURE(*ec)) {
   683         return 0;
   684     }
   686     int32_t choice = (int32_t) nameStyle;
   687     if (choice < 0 || choice > 1) {
   688         *ec = U_ILLEGAL_ARGUMENT_ERROR;
   689         return 0;
   690     }
   692     // In the future, resource bundles may implement multi-level
   693     // fallback.  That is, if a currency is not found in the en_US
   694     // Currencies data, then the en Currencies data will be searched.
   695     // Currently, if a Currencies datum exists in en_US and en, the
   696     // en_US entry hides that in en.
   698     // We want multi-level fallback for this resource, so we implement
   699     // it manually.
   701     // Use a separate UErrorCode here that does not propagate out of
   702     // this function.
   703     UErrorCode ec2 = U_ZERO_ERROR;
   705     char loc[ULOC_FULLNAME_CAPACITY];
   706     uloc_getName(locale, loc, sizeof(loc), &ec2);
   707     if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
   708         *ec = U_ILLEGAL_ARGUMENT_ERROR;
   709         return 0;
   710     }
   712     char buf[ISO_CURRENCY_CODE_LENGTH+1];
   713     myUCharsToChars(buf, currency);
   715     /* Normalize the keyword value to uppercase */
   716     T_CString_toUpperCase(buf);
   718     const UChar* s = NULL;
   719     ec2 = U_ZERO_ERROR;
   720     UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
   722     rb = ures_getByKey(rb, CURRENCIES, rb, &ec2);
   724     // Fetch resource with multi-level resource inheritance fallback
   725     rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2);
   727     s = ures_getStringByIndex(rb, choice, len, &ec2);
   728     ures_close(rb);
   730     // If we've succeeded we're done.  Otherwise, try to fallback.
   731     // If that fails (because we are already at root) then exit.
   732     if (U_SUCCESS(ec2)) {
   733         if (ec2 == U_USING_DEFAULT_WARNING
   734             || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
   735             *ec = ec2;
   736         }
   737     }
   739     // Determine if this is a ChoiceFormat pattern.  One leading mark
   740     // indicates a ChoiceFormat.  Two indicates a static string that
   741     // starts with a mark.  In either case, the first mark is ignored,
   742     // if present.  Marks in the rest of the string have no special
   743     // meaning.
   744     *isChoiceFormat = FALSE;
   745     if (U_SUCCESS(ec2)) {
   746         U_ASSERT(s != NULL);
   747         int32_t i=0;
   748         while (i < *len && s[i] == CHOICE_FORMAT_MARK && i < 2) {
   749             ++i;
   750         }
   751         *isChoiceFormat = (i == 1);
   752         if (i != 0) ++s; // Skip over first mark
   753         return s;
   754     }
   756     // If we fail to find a match, use the ISO 4217 code
   757     *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
   758     *ec = U_USING_DEFAULT_WARNING;
   759     return currency;
   760 }
   762 U_CAPI const UChar* U_EXPORT2
   763 ucurr_getPluralName(const UChar* currency,
   764                     const char* locale,
   765                     UBool* isChoiceFormat,
   766                     const char* pluralCount,
   767                     int32_t* len, // fillin
   768                     UErrorCode* ec) {
   769     // Look up the Currencies resource for the given locale.  The
   770     // Currencies locale data looks like this:
   771     //|en {
   772     //|  CurrencyPlurals {
   773     //|    USD{
   774     //|      one{"US dollar"}
   775     //|      other{"US dollars"}
   776     //|    }
   777     //|  }
   778     //|}
   780     if (U_FAILURE(*ec)) {
   781         return 0;
   782     }
   784     // Use a separate UErrorCode here that does not propagate out of
   785     // this function.
   786     UErrorCode ec2 = U_ZERO_ERROR;
   788     char loc[ULOC_FULLNAME_CAPACITY];
   789     uloc_getName(locale, loc, sizeof(loc), &ec2);
   790     if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
   791         *ec = U_ILLEGAL_ARGUMENT_ERROR;
   792         return 0;
   793     }
   795     char buf[ISO_CURRENCY_CODE_LENGTH+1];
   796     myUCharsToChars(buf, currency);
   798     const UChar* s = NULL;
   799     ec2 = U_ZERO_ERROR;
   800     UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
   802     rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2);
   804     // Fetch resource with multi-level resource inheritance fallback
   805     rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2);
   807     s = ures_getStringByKeyWithFallback(rb, pluralCount, len, &ec2);
   808     if (U_FAILURE(ec2)) {
   809         //  fall back to "other"
   810         ec2 = U_ZERO_ERROR;
   811         s = ures_getStringByKeyWithFallback(rb, "other", len, &ec2);     
   812         if (U_FAILURE(ec2)) {
   813             ures_close(rb);
   814             // fall back to long name in Currencies
   815             return ucurr_getName(currency, locale, UCURR_LONG_NAME, 
   816                                  isChoiceFormat, len, ec);
   817         }
   818     }
   819     ures_close(rb);
   821     // If we've succeeded we're done.  Otherwise, try to fallback.
   822     // If that fails (because we are already at root) then exit.
   823     if (U_SUCCESS(ec2)) {
   824         if (ec2 == U_USING_DEFAULT_WARNING
   825             || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
   826             *ec = ec2;
   827         }
   828         U_ASSERT(s != NULL);
   829         return s;
   830     }
   832     // If we fail to find a match, use the ISO 4217 code
   833     *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
   834     *ec = U_USING_DEFAULT_WARNING;
   835     return currency;
   836 }
   839 //========================================================================
   840 // Following are structure and function for parsing currency names
   842 #define NEED_TO_BE_DELETED 0x1
   844 // TODO: a better way to define this?
   845 #define MAX_CURRENCY_NAME_LEN 100
   847 typedef struct {
   848     const char* IsoCode;  // key
   849     UChar* currencyName;  // value
   850     int32_t currencyNameLen;  // value length
   851     int32_t flag;  // flags
   852 } CurrencyNameStruct;
   855 #ifndef MIN
   856 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
   857 #endif
   859 #ifndef MAX
   860 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
   861 #endif
   864 // Comparason function used in quick sort.
   865 static int U_CALLCONV currencyNameComparator(const void* a, const void* b) {
   866     const CurrencyNameStruct* currName_1 = (const CurrencyNameStruct*)a;
   867     const CurrencyNameStruct* currName_2 = (const CurrencyNameStruct*)b;
   868     for (int32_t i = 0; 
   869          i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen);
   870          ++i) {
   871         if (currName_1->currencyName[i] < currName_2->currencyName[i]) {
   872             return -1;
   873         }
   874         if (currName_1->currencyName[i] > currName_2->currencyName[i]) {
   875             return 1;
   876         }
   877     }
   878     if (currName_1->currencyNameLen < currName_2->currencyNameLen) {
   879         return -1;
   880     } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) {
   881         return 1;
   882     }
   883     return 0;
   884 }
   887 // Give a locale, return the maximum number of currency names associated with
   888 // this locale.
   889 // It gets currency names from resource bundles using fallback.
   890 // It is the maximum number because in the fallback chain, some of the 
   891 // currency names are duplicated.
   892 // For example, given locale as "en_US", the currency names get from resource
   893 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
   894 // all currency names in "en_US" and "en".
   895 static void
   896 getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) {
   897     U_NAMESPACE_USE
   898     *total_currency_name_count = 0;
   899     *total_currency_symbol_count = 0;
   900     const UChar* s = NULL;
   901     char locale[ULOC_FULLNAME_CAPACITY];
   902     uprv_strcpy(locale, loc);
   903     const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
   904     for (;;) {
   905         UErrorCode ec2 = U_ZERO_ERROR;
   906         // TODO: ures_openDirect?
   907         UResourceBundle* rb = ures_open(U_ICUDATA_CURR, locale, &ec2);
   908         UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
   909         int32_t n = ures_getSize(curr);
   910         for (int32_t i=0; i<n; ++i) {
   911             UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2);
   912             int32_t len;
   913             s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
   914             UBool isChoice = FALSE;
   915             if (len > 0 && s[0] == CHOICE_FORMAT_MARK) {
   916                 ++s;
   917                 --len;
   918                 if (len > 0 && s[0] != CHOICE_FORMAT_MARK) {
   919                     isChoice = TRUE;
   920                 }
   921             }
   922             if (isChoice) {
   923                 ChoiceFormat fmt(UnicodeString(TRUE, s, len), ec2);
   924                 int32_t fmt_count;
   925                 fmt.getFormats(fmt_count);
   926                 *total_currency_symbol_count += fmt_count;
   927             } else {
   928                 ++(*total_currency_symbol_count);  // currency symbol
   929                 if (currencySymbolsEquiv != NULL) {
   930                     *total_currency_symbol_count += countEquivalent(*currencySymbolsEquiv, UnicodeString(TRUE, s, len));
   931                 }
   932             }
   934             ++(*total_currency_symbol_count); // iso code
   935             ++(*total_currency_name_count); // long name
   936             ures_close(names);
   937         }
   939         // currency plurals
   940         UErrorCode ec3 = U_ZERO_ERROR;
   941         UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3);
   942         n = ures_getSize(curr_p);
   943         for (int32_t i=0; i<n; ++i) {
   944             UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3);
   945             *total_currency_name_count += ures_getSize(names);
   946             ures_close(names);
   947         }
   948         ures_close(curr_p);
   949         ures_close(curr);
   950         ures_close(rb);
   952         if (!fallback(locale)) {
   953             break;
   954         }
   955     }
   956 }
   958 static UChar* 
   959 toUpperCase(const UChar* source, int32_t len, const char* locale) {
   960     UChar* dest = NULL;
   961     UErrorCode ec = U_ZERO_ERROR;
   962     int32_t destLen = u_strToUpper(dest, 0, source, len, locale, &ec);
   964     ec = U_ZERO_ERROR;
   965     dest = (UChar*)uprv_malloc(sizeof(UChar) * MAX(destLen, len));
   966     u_strToUpper(dest, destLen, source, len, locale, &ec);
   967     if (U_FAILURE(ec)) {
   968         uprv_memcpy(dest, source, sizeof(UChar) * len);
   969     } 
   970     return dest;
   971 }
   974 // Collect all available currency names associated with the given locale
   975 // (enable fallback chain).
   976 // Read currenc names defined in resource bundle "Currencies" and
   977 // "CurrencyPlural", enable fallback chain.
   978 // return the malloc-ed currency name arrays and the total number of currency
   979 // names in the array.
   980 static void
   981 collectCurrencyNames(const char* locale, 
   982                      CurrencyNameStruct** currencyNames, 
   983                      int32_t* total_currency_name_count, 
   984                      CurrencyNameStruct** currencySymbols, 
   985                      int32_t* total_currency_symbol_count, 
   986                      UErrorCode& ec) {
   987     U_NAMESPACE_USE
   988     const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
   989     // Look up the Currencies resource for the given locale.
   990     UErrorCode ec2 = U_ZERO_ERROR;
   992     char loc[ULOC_FULLNAME_CAPACITY];
   993     uloc_getName(locale, loc, sizeof(loc), &ec2);
   994     if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
   995         ec = U_ILLEGAL_ARGUMENT_ERROR;
   996     }
   998     // Get maximum currency name count first.
   999     getCurrencyNameCount(loc, total_currency_name_count, total_currency_symbol_count);
  1001     *currencyNames = (CurrencyNameStruct*)uprv_malloc
  1002         (sizeof(CurrencyNameStruct) * (*total_currency_name_count));
  1003     *currencySymbols = (CurrencyNameStruct*)uprv_malloc
  1004         (sizeof(CurrencyNameStruct) * (*total_currency_symbol_count));
  1006     const UChar* s = NULL;  // currency name
  1007     char* iso = NULL;  // currency ISO code
  1009     *total_currency_name_count = 0;
  1010     *total_currency_symbol_count = 0;
  1012     UErrorCode ec3 = U_ZERO_ERROR;
  1013     UErrorCode ec4 = U_ZERO_ERROR;
  1015     // Using hash to remove duplicates caused by locale fallback
  1016     UHashtable* currencyIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec3);
  1017     UHashtable* currencyPluralIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec4);
  1018     for (int32_t localeLevel = 0; ; ++localeLevel) {
  1019         ec2 = U_ZERO_ERROR;
  1020         // TODO: ures_openDirect
  1021         UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
  1022         UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
  1023         int32_t n = ures_getSize(curr);
  1024         for (int32_t i=0; i<n; ++i) {
  1025             UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2);
  1026             int32_t len;
  1027             s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
  1028             // TODO: uhash_put wont change key/value?
  1029             iso = (char*)ures_getKey(names);
  1030             if (localeLevel == 0) {
  1031                 uhash_put(currencyIsoCodes, iso, iso, &ec3); 
  1032             } else {
  1033                 if (uhash_get(currencyIsoCodes, iso) != NULL) {
  1034                     ures_close(names);
  1035                     continue;
  1036                 } else {
  1037                     uhash_put(currencyIsoCodes, iso, iso, &ec3); 
  1040             UBool isChoice = FALSE;
  1041             if (len > 0 && s[0] == CHOICE_FORMAT_MARK) {
  1042                 ++s;
  1043                 --len;
  1044                 if (len > 0 && s[0] != CHOICE_FORMAT_MARK) {
  1045                     isChoice = TRUE;
  1048             if (isChoice) {
  1049                 ChoiceFormat fmt(UnicodeString(TRUE, s, len), ec2);
  1050                 int32_t fmt_count;
  1051                 const UnicodeString* formats = fmt.getFormats(fmt_count);
  1052                 for (int i = 0; i < fmt_count; ++i) {
  1053                     // put iso, formats[i]; into array
  1054                     int32_t length = formats[i].length();
  1055                     UChar* name = (UChar*)uprv_malloc(sizeof(UChar)*length);
  1056                     formats[i].extract(0, length, name);
  1057                     (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
  1058                     (*currencySymbols)[*total_currency_symbol_count].currencyName = name;
  1059                     (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
  1060                     (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = length;
  1062             } else {
  1063                 // Add currency symbol.
  1064                 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
  1065                 (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)s;
  1066                 (*currencySymbols)[*total_currency_symbol_count].flag = 0;
  1067                 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len;
  1068                 // Add equivalent symbols
  1069                 if (currencySymbolsEquiv != NULL) {
  1070                     EquivIterator iter(*currencySymbolsEquiv, UnicodeString(TRUE, s, len));
  1071                     const UnicodeString *symbol;
  1072                     while ((symbol = iter.next()) != NULL) {
  1073                         (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
  1074                         (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*) symbol->getBuffer();
  1075                         (*currencySymbols)[*total_currency_symbol_count].flag = 0;
  1076                         (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length();
  1081             // Add currency long name.
  1082             s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2);
  1083             (*currencyNames)[*total_currency_name_count].IsoCode = iso;
  1084             UChar* upperName = toUpperCase(s, len, locale);
  1085             (*currencyNames)[*total_currency_name_count].currencyName = upperName;
  1086             (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
  1087             (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
  1089             // put (iso, 3, and iso) in to array
  1090             // Add currency ISO code.
  1091             (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
  1092             (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)uprv_malloc(sizeof(UChar)*3);
  1093             // Must convert iso[] into Unicode
  1094             u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3);
  1095             (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
  1096             (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3;
  1098             ures_close(names);
  1101         // currency plurals
  1102         UErrorCode ec3 = U_ZERO_ERROR;
  1103         UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3);
  1104         n = ures_getSize(curr_p);
  1105         for (int32_t i=0; i<n; ++i) {
  1106             UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3);
  1107             iso = (char*)ures_getKey(names);
  1108             // Using hash to remove duplicated ISO codes in fallback chain.
  1109             if (localeLevel == 0) {
  1110                 uhash_put(currencyPluralIsoCodes, iso, iso, &ec4); 
  1111             } else {
  1112                 if (uhash_get(currencyPluralIsoCodes, iso) != NULL) {
  1113                     ures_close(names);
  1114                     continue;
  1115                 } else {
  1116                     uhash_put(currencyPluralIsoCodes, iso, iso, &ec4); 
  1119             int32_t num = ures_getSize(names);
  1120             int32_t len;
  1121             for (int32_t j = 0; j < num; ++j) {
  1122                 // TODO: remove duplicates between singular name and 
  1123                 // currency long name?
  1124                 s = ures_getStringByIndex(names, j, &len, &ec3);
  1125                 (*currencyNames)[*total_currency_name_count].IsoCode = iso;
  1126                 UChar* upperName = toUpperCase(s, len, locale);
  1127                 (*currencyNames)[*total_currency_name_count].currencyName = upperName;
  1128                 (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
  1129                 (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
  1131             ures_close(names);
  1133         ures_close(curr_p);
  1134         ures_close(curr);
  1135         ures_close(rb);
  1137         if (!fallback(loc)) {
  1138             break;
  1142     uhash_close(currencyIsoCodes);
  1143     uhash_close(currencyPluralIsoCodes);
  1145     // quick sort the struct
  1146     qsort(*currencyNames, *total_currency_name_count, 
  1147           sizeof(CurrencyNameStruct), currencyNameComparator);
  1148     qsort(*currencySymbols, *total_currency_symbol_count, 
  1149           sizeof(CurrencyNameStruct), currencyNameComparator);
  1151 #ifdef UCURR_DEBUG
  1152     printf("currency name count: %d\n", *total_currency_name_count);
  1153     for (int32_t index = 0; index < *total_currency_name_count; ++index) {
  1154         printf("index: %d\n", index);
  1155         printf("iso: %s\n", (*currencyNames)[index].IsoCode);
  1156         char curNameBuf[1024];
  1157         memset(curNameBuf, 0, 1024);
  1158         u_austrncpy(curNameBuf, (*currencyNames)[index].currencyName, (*currencyNames)[index].currencyNameLen);
  1159         printf("currencyName: %s\n", curNameBuf);
  1160         printf("len: %d\n", (*currencyNames)[index].currencyNameLen);
  1162     printf("currency symbol count: %d\n", *total_currency_symbol_count);
  1163     for (int32_t index = 0; index < *total_currency_symbol_count; ++index) {
  1164         printf("index: %d\n", index);
  1165         printf("iso: %s\n", (*currencySymbols)[index].IsoCode);
  1166         char curNameBuf[1024];
  1167         memset(curNameBuf, 0, 1024);
  1168         u_austrncpy(curNameBuf, (*currencySymbols)[index].currencyName, (*currencySymbols)[index].currencyNameLen);
  1169         printf("currencySymbol: %s\n", curNameBuf);
  1170         printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
  1172 #endif
  1175 // @param  currencyNames: currency names array
  1176 // @param  indexInCurrencyNames: the index of the character in currency names 
  1177 //         array against which the comparison is done
  1178 // @param  key: input text char to compare against
  1179 // @param  begin(IN/OUT): the begin index of matching range in currency names array
  1180 // @param  end(IN/OUT): the end index of matching range in currency names array.
  1181 static int32_t
  1182 binarySearch(const CurrencyNameStruct* currencyNames, 
  1183              int32_t indexInCurrencyNames,
  1184              const UChar key,
  1185              int32_t* begin, int32_t* end) {
  1186 #ifdef UCURR_DEBUG
  1187     printf("key = %x\n", key);
  1188 #endif
  1189    int32_t first = *begin;
  1190    int32_t last = *end;
  1191    while (first <= last) {
  1192        int32_t mid = (first + last) / 2;  // compute mid point.
  1193        if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) {
  1194            first = mid + 1;
  1195        } else {
  1196            if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) {
  1197                first = mid + 1;
  1199            else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) {
  1200                last = mid - 1;
  1202            else {
  1203                 // Find a match, and looking for ranges
  1204                 // Now do two more binary searches. First, on the left side for
  1205                 // the greatest L such that CurrencyNameStruct[L] < key.
  1206                 int32_t L = *begin;
  1207                 int32_t R = mid;
  1209 #ifdef UCURR_DEBUG
  1210                 printf("mid = %d\n", mid);
  1211 #endif
  1212                 while (L < R) {
  1213                     int32_t M = (L + R) / 2;
  1214 #ifdef UCURR_DEBUG
  1215                     printf("L = %d, R = %d, M = %d\n", L, R, M);
  1216 #endif
  1217                     if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) {
  1218                         L = M + 1;
  1219                     } else {
  1220                         if (currencyNames[M].currencyName[indexInCurrencyNames] < key) {
  1221                             L = M + 1;
  1222                         } else {
  1223 #ifdef UCURR_DEBUG
  1224                             U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
  1225 #endif
  1226                             R = M;
  1230 #ifdef UCURR_DEBUG
  1231                 U_ASSERT(L == R);
  1232 #endif
  1233                 *begin = L;
  1234 #ifdef UCURR_DEBUG
  1235                 printf("begin = %d\n", *begin);
  1236                 U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key);
  1237 #endif
  1239                 // Now for the second search, finding the least R such that
  1240                 // key < CurrencyNameStruct[R].
  1241                 L = mid;
  1242                 R = *end;
  1243                 while (L < R) {
  1244                     int32_t M = (L + R) / 2;
  1245 #ifdef UCURR_DEBUG
  1246                     printf("L = %d, R = %d, M = %d\n", L, R, M);
  1247 #endif
  1248                     if (currencyNames[M].currencyNameLen < indexInCurrencyNames) {
  1249                         L = M + 1;
  1250                     } else {
  1251                         if (currencyNames[M].currencyName[indexInCurrencyNames] > key) {
  1252                             R = M;
  1253                         } else {
  1254 #ifdef UCURR_DEBUG
  1255                             U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
  1256 #endif
  1257                             L = M + 1;
  1261 #ifdef UCURR_DEBUG
  1262                 U_ASSERT(L == R);
  1263 #endif
  1264                 if (currencyNames[R].currencyName[indexInCurrencyNames] > key) {
  1265                     *end = R - 1;
  1266                 } else {
  1267                     *end = R;
  1269 #ifdef UCURR_DEBUG
  1270                 printf("end = %d\n", *end);
  1271 #endif
  1273                 // now, found the range. check whether there is exact match
  1274                 if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) {
  1275                     return *begin;  // find range and exact match.
  1277                 return -1;  // find range, but no exact match.
  1281    *begin = -1;
  1282    *end = -1;
  1283    return -1;    // failed to find range.
  1287 // Linear search "text" in "currencyNames".
  1288 // @param  begin, end: the begin and end index in currencyNames, within which
  1289 //         range should the search be performed.
  1290 // @param  textLen: the length of the text to be compared
  1291 // @param  maxMatchLen(IN/OUT): passing in the computed max matching length
  1292 //                              pass out the new max  matching length
  1293 // @param  maxMatchIndex: the index in currencyName which has the longest
  1294 //                        match with input text.
  1295 static void
  1296 linearSearch(const CurrencyNameStruct* currencyNames, 
  1297              int32_t begin, int32_t end,
  1298              const UChar* text, int32_t textLen,
  1299              int32_t *maxMatchLen, int32_t* maxMatchIndex) {
  1300     for (int32_t index = begin; index <= end; ++index) {
  1301         int32_t len = currencyNames[index].currencyNameLen;
  1302         if (len > *maxMatchLen && len <= textLen &&
  1303             uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) {
  1304             *maxMatchIndex = index;
  1305             *maxMatchLen = len;
  1306 #ifdef UCURR_DEBUG
  1307             printf("maxMatchIndex = %d, maxMatchLen = %d\n",
  1308                    *maxMatchIndex, *maxMatchLen);
  1309 #endif
  1314 #define LINEAR_SEARCH_THRESHOLD 10
  1316 // Find longest match between "text" and currency names in "currencyNames".
  1317 // @param  total_currency_count: total number of currency names in CurrencyNames.
  1318 // @param  textLen: the length of the text to be compared
  1319 // @param  maxMatchLen: passing in the computed max matching length
  1320 //                              pass out the new max  matching length
  1321 // @param  maxMatchIndex: the index in currencyName which has the longest
  1322 //                        match with input text.
  1323 static void
  1324 searchCurrencyName(const CurrencyNameStruct* currencyNames, 
  1325                    int32_t total_currency_count,
  1326                    const UChar* text, int32_t textLen, 
  1327                    int32_t* maxMatchLen, int32_t* maxMatchIndex) {
  1328     *maxMatchIndex = -1;
  1329     *maxMatchLen = 0;
  1330     int32_t matchIndex = -1;
  1331     int32_t binarySearchBegin = 0;
  1332     int32_t binarySearchEnd = total_currency_count - 1;
  1333     // It is a variant of binary search.
  1334     // For example, given the currency names in currencyNames array are:
  1335     // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
  1336     // and the input text is BBEXST
  1337     // The first round binary search search "B" in the text against
  1338     // the first char in currency names, and find the first char matching range
  1339     // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
  1340     // The 2nd round binary search search the second "B" in the text against
  1341     // the 2nd char in currency names, and narrow the matching range to
  1342     // "BB BBEX BBEXYZ" (and the maximum matching "BB").
  1343     // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
  1344     // maximum matching).
  1345     // The 4th round returns the same range (the maximum matching is "BBEX").
  1346     // The 5th round returns no matching range.
  1347     for (int32_t index = 0; index < textLen; ++index) {
  1348         // matchIndex saves the one with exact match till the current point.
  1349         // [binarySearchBegin, binarySearchEnd] saves the matching range.
  1350         matchIndex = binarySearch(currencyNames, index,
  1351                                   text[index],
  1352                                   &binarySearchBegin, &binarySearchEnd);
  1353         if (binarySearchBegin == -1) { // did not find the range
  1354             break;
  1356         if (matchIndex != -1) { 
  1357             // find an exact match for text from text[0] to text[index] 
  1358             // in currencyNames array.
  1359             *maxMatchLen = index + 1;
  1360             *maxMatchIndex = matchIndex;
  1362         if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) {
  1363             // linear search if within threshold.
  1364             linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
  1365                          text, textLen,
  1366                          maxMatchLen, maxMatchIndex);
  1367             break;
  1370     return;
  1373 //========================= currency name cache =====================
  1374 typedef struct {
  1375     char locale[ULOC_FULLNAME_CAPACITY];  //key
  1376     // currency names, case insensitive
  1377     CurrencyNameStruct* currencyNames;  // value
  1378     int32_t totalCurrencyNameCount;  // currency name count
  1379     // currency symbols and ISO code, case sensitive
  1380     CurrencyNameStruct* currencySymbols; // value
  1381     int32_t totalCurrencySymbolCount;  // count
  1382     // reference count.
  1383     // reference count is set to 1 when an entry is put to cache.
  1384     // it increases by 1 before accessing, and decreased by 1 after accessing.
  1385     // The entry is deleted when ref count is zero, which means 
  1386     // the entry is replaced out of cache and no process is accessing it.
  1387     int32_t refCount;
  1388 } CurrencyNameCacheEntry;
  1391 #define CURRENCY_NAME_CACHE_NUM 10
  1393 // Reserve 10 cache entries.
  1394 static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {NULL};
  1395 // Using an index to indicate which entry to be replaced when cache is full.
  1396 // It is a simple round-robin replacement strategy.
  1397 static int8_t currentCacheEntryIndex = 0;
  1399 static UMutex gCurrencyCacheMutex = U_MUTEX_INITIALIZER;
  1401 // Cache deletion
  1402 static void
  1403 deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) {
  1404     for (int32_t index = 0; index < count; ++index) {
  1405         if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) {
  1406             uprv_free(currencyNames[index].currencyName);
  1409     uprv_free(currencyNames);
  1413 static void
  1414 deleteCacheEntry(CurrencyNameCacheEntry* entry) {
  1415     deleteCurrencyNames(entry->currencyNames, entry->totalCurrencyNameCount);
  1416     deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount);
  1417     uprv_free(entry);
  1421 // Cache clean up
  1422 static UBool U_CALLCONV
  1423 currency_cache_cleanup(void) {
  1424     for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
  1425         if (currCache[i]) {
  1426             deleteCacheEntry(currCache[i]);
  1427             currCache[i] = 0;
  1430     return TRUE;
  1434 U_CFUNC void
  1435 uprv_parseCurrency(const char* locale,
  1436                    const icu::UnicodeString& text,
  1437                    icu::ParsePosition& pos,
  1438                    int8_t type,
  1439                    UChar* result,
  1440                    UErrorCode& ec)
  1442     U_NAMESPACE_USE
  1444     if (U_FAILURE(ec)) {
  1445         return;
  1448     int32_t total_currency_name_count = 0;
  1449     CurrencyNameStruct* currencyNames = NULL;
  1450     int32_t total_currency_symbol_count = 0;
  1451     CurrencyNameStruct* currencySymbols = NULL;
  1452     CurrencyNameCacheEntry* cacheEntry = NULL;
  1454     umtx_lock(&gCurrencyCacheMutex);
  1455     // in order to handle racing correctly,
  1456     // not putting 'search' in a separate function.
  1457     int8_t  found = -1;
  1458     for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
  1459         if (currCache[i]!= NULL &&
  1460             uprv_strcmp(locale, currCache[i]->locale) == 0) {
  1461             found = i;
  1462             break;
  1465     if (found != -1) {
  1466         cacheEntry = currCache[found];
  1467         currencyNames = cacheEntry->currencyNames;
  1468         total_currency_name_count = cacheEntry->totalCurrencyNameCount;
  1469         currencySymbols = cacheEntry->currencySymbols;
  1470         total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
  1471         ++(cacheEntry->refCount);
  1473     umtx_unlock(&gCurrencyCacheMutex);
  1474     if (found == -1) {
  1475         collectCurrencyNames(locale, &currencyNames, &total_currency_name_count, &currencySymbols, &total_currency_symbol_count, ec);
  1476         if (U_FAILURE(ec)) {
  1477             return;
  1479         umtx_lock(&gCurrencyCacheMutex);
  1480         // check again.
  1481         int8_t  found = -1;
  1482         for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
  1483             if (currCache[i]!= NULL &&
  1484                 uprv_strcmp(locale, currCache[i]->locale) == 0) {
  1485                 found = i;
  1486                 break;
  1489         if (found == -1) {
  1490             // insert new entry to 
  1491             // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
  1492             // and remove the existing entry 
  1493             // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
  1494             // from cache.
  1495             cacheEntry = currCache[currentCacheEntryIndex];
  1496             if (cacheEntry) {
  1497                 --(cacheEntry->refCount);
  1498                 // delete if the ref count is zero
  1499                 if (cacheEntry->refCount == 0) {
  1500                     deleteCacheEntry(cacheEntry);
  1503             cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry));
  1504             currCache[currentCacheEntryIndex] = cacheEntry;
  1505             uprv_strcpy(cacheEntry->locale, locale);
  1506             cacheEntry->currencyNames = currencyNames;
  1507             cacheEntry->totalCurrencyNameCount = total_currency_name_count;
  1508             cacheEntry->currencySymbols = currencySymbols;
  1509             cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count;
  1510             cacheEntry->refCount = 2; // one for cache, one for reference
  1511             currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM;
  1512             ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cache_cleanup);
  1514         } else {
  1515             deleteCurrencyNames(currencyNames, total_currency_name_count);
  1516             deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
  1517             cacheEntry = currCache[found];
  1518             currencyNames = cacheEntry->currencyNames;
  1519             total_currency_name_count = cacheEntry->totalCurrencyNameCount;
  1520             currencySymbols = cacheEntry->currencySymbols;
  1521             total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
  1522             ++(cacheEntry->refCount);
  1524         umtx_unlock(&gCurrencyCacheMutex);
  1527     int32_t start = pos.getIndex();
  1529     UChar inputText[MAX_CURRENCY_NAME_LEN];  
  1530     UChar upperText[MAX_CURRENCY_NAME_LEN];  
  1531     int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start);
  1532     text.extract(start, textLen, inputText);
  1533     UErrorCode ec1 = U_ZERO_ERROR;
  1534     textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1);
  1536     int32_t max = 0;
  1537     int32_t matchIndex = -1;
  1538     // case in-sensitive comparision against currency names
  1539     searchCurrencyName(currencyNames, total_currency_name_count, 
  1540                        upperText, textLen, &max, &matchIndex);
  1542 #ifdef UCURR_DEBUG
  1543     printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
  1544 #endif
  1546     int32_t maxInSymbol = 0;
  1547     int32_t matchIndexInSymbol = -1;
  1548     if (type != UCURR_LONG_NAME) {  // not name only
  1549         // case sensitive comparison against currency symbols and ISO code.
  1550         searchCurrencyName(currencySymbols, total_currency_symbol_count, 
  1551                            inputText, textLen, 
  1552                            &maxInSymbol, &matchIndexInSymbol);
  1555 #ifdef UCURR_DEBUG
  1556     printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol);
  1557     if(matchIndexInSymbol != -1) {
  1558       printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode);
  1560 #endif
  1562     if (max >= maxInSymbol && matchIndex != -1) {
  1563         u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4);
  1564         pos.setIndex(start + max);
  1565     } else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
  1566         u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
  1567         pos.setIndex(start + maxInSymbol);
  1570     // decrease reference count
  1571     umtx_lock(&gCurrencyCacheMutex);
  1572     --(cacheEntry->refCount);
  1573     if (cacheEntry->refCount == 0) {  // remove 
  1574         deleteCacheEntry(cacheEntry);
  1576     umtx_unlock(&gCurrencyCacheMutex);
  1580 /**
  1581  * Internal method.  Given a currency ISO code and a locale, return
  1582  * the "static" currency name.  This is usually the same as the
  1583  * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
  1584  * format is applied to the number 2.0 (to yield the more common
  1585  * plural) to return a static name.
  1587  * This is used for backward compatibility with old currency logic in
  1588  * DecimalFormat and DecimalFormatSymbols.
  1589  */
  1590 U_CFUNC void
  1591 uprv_getStaticCurrencyName(const UChar* iso, const char* loc,
  1592                            icu::UnicodeString& result, UErrorCode& ec)
  1594     U_NAMESPACE_USE
  1596     UBool isChoiceFormat;
  1597     int32_t len;
  1598     const UChar* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME,
  1599                                           &isChoiceFormat, &len, &ec);
  1600     if (U_SUCCESS(ec)) {
  1601         // If this is a ChoiceFormat currency, then format an
  1602         // arbitrary value; pick something != 1; more common.
  1603         result.truncate(0);
  1604         if (isChoiceFormat) {
  1605             ChoiceFormat f(UnicodeString(TRUE, currname, len), ec);
  1606             if (U_SUCCESS(ec)) {
  1607                 f.format(2.0, result);
  1608             } else {
  1609                 result.setTo(iso, -1);
  1611         } else {
  1612             result.setTo(currname, -1);
  1617 U_CAPI int32_t U_EXPORT2
  1618 ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) {
  1619     return (_findMetaData(currency, *ec))[0];
  1622 U_CAPI double U_EXPORT2
  1623 ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) {
  1624     const int32_t *data = _findMetaData(currency, *ec);
  1626     // If the meta data is invalid, return 0.0.
  1627     if (data[0] < 0 || data[0] > MAX_POW10) {
  1628         if (U_SUCCESS(*ec)) {
  1629             *ec = U_INVALID_FORMAT_ERROR;
  1631         return 0.0;
  1634     // If there is no rounding, return 0.0 to indicate no rounding.  A
  1635     // rounding value (data[1]) of 0 or 1 indicates no rounding.
  1636     if (data[1] < 2) {
  1637         return 0.0;
  1640     // Return data[1] / 10^(data[0]).  The only actual rounding data,
  1641     // as of this writing, is CHF { 2, 5 }.
  1642     return double(data[1]) / POW10[data[0]];
  1645 U_CDECL_BEGIN
  1647 typedef struct UCurrencyContext {
  1648     uint32_t currType; /* UCurrCurrencyType */
  1649     uint32_t listIdx;
  1650 } UCurrencyContext;
  1652 /*
  1653 Please keep this list in alphabetical order.
  1654 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
  1655 of these items.
  1656 ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
  1657 */
  1658 static const struct CurrencyList {
  1659     const char *currency;
  1660     uint32_t currType;
  1661 } gCurrencyList[] = {
  1662     {"ADP", UCURR_COMMON|UCURR_DEPRECATED},
  1663     {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1664     {"AFA", UCURR_COMMON|UCURR_DEPRECATED},
  1665     {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1666     {"ALK", UCURR_COMMON|UCURR_DEPRECATED},
  1667     {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1668     {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1669     {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1670     {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1671     {"AOK", UCURR_COMMON|UCURR_DEPRECATED},
  1672     {"AON", UCURR_COMMON|UCURR_DEPRECATED},
  1673     {"AOR", UCURR_COMMON|UCURR_DEPRECATED},
  1674     {"ARA", UCURR_COMMON|UCURR_DEPRECATED},
  1675     {"ARL", UCURR_COMMON|UCURR_DEPRECATED},
  1676     {"ARM", UCURR_COMMON|UCURR_DEPRECATED},
  1677     {"ARP", UCURR_COMMON|UCURR_DEPRECATED},
  1678     {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1679     {"ATS", UCURR_COMMON|UCURR_DEPRECATED},
  1680     {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1681     {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1682     {"AZM", UCURR_COMMON|UCURR_DEPRECATED},
  1683     {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1684     {"BAD", UCURR_COMMON|UCURR_DEPRECATED},
  1685     {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1686     {"BAN", UCURR_COMMON|UCURR_DEPRECATED},
  1687     {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1688     {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1689     {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1690     {"BEF", UCURR_COMMON|UCURR_DEPRECATED},
  1691     {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1692     {"BGL", UCURR_COMMON|UCURR_DEPRECATED},
  1693     {"BGM", UCURR_COMMON|UCURR_DEPRECATED},
  1694     {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1695     {"BGO", UCURR_COMMON|UCURR_DEPRECATED},
  1696     {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1697     {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1698     {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1699     {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1700     {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1701     {"BOL", UCURR_COMMON|UCURR_DEPRECATED},
  1702     {"BOP", UCURR_COMMON|UCURR_DEPRECATED},
  1703     {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1704     {"BRB", UCURR_COMMON|UCURR_DEPRECATED},
  1705     {"BRC", UCURR_COMMON|UCURR_DEPRECATED},
  1706     {"BRE", UCURR_COMMON|UCURR_DEPRECATED},
  1707     {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1708     {"BRN", UCURR_COMMON|UCURR_DEPRECATED},
  1709     {"BRR", UCURR_COMMON|UCURR_DEPRECATED},
  1710     {"BRZ", UCURR_COMMON|UCURR_DEPRECATED},
  1711     {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1712     {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1713     {"BUK", UCURR_COMMON|UCURR_DEPRECATED},
  1714     {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1715     {"BYB", UCURR_COMMON|UCURR_DEPRECATED},
  1716     {"BYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1717     {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1718     {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1719     {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1720     {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1721     {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1722     {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1723     {"CLE", UCURR_COMMON|UCURR_DEPRECATED},
  1724     {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1725     {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1726     {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1727     {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1728     {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1729     {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1730     {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1731     {"CSD", UCURR_COMMON|UCURR_DEPRECATED},
  1732     {"CSK", UCURR_COMMON|UCURR_DEPRECATED},
  1733     {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1734     {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1735     {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1736     {"CYP", UCURR_COMMON|UCURR_DEPRECATED},
  1737     {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1738     {"DDM", UCURR_COMMON|UCURR_DEPRECATED},
  1739     {"DEM", UCURR_COMMON|UCURR_DEPRECATED},
  1740     {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1741     {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1742     {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1743     {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1744     {"ECS", UCURR_COMMON|UCURR_DEPRECATED},
  1745     {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1746     {"EEK", UCURR_COMMON|UCURR_DEPRECATED},
  1747     {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1748     {"EQE", UCURR_COMMON|UCURR_DEPRECATED},
  1749     {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1750     {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1751     {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1752     {"ESP", UCURR_COMMON|UCURR_DEPRECATED},
  1753     {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1754     {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1755     {"FIM", UCURR_COMMON|UCURR_DEPRECATED},
  1756     {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1757     {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1758     {"FRF", UCURR_COMMON|UCURR_DEPRECATED},
  1759     {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1760     {"GEK", UCURR_COMMON|UCURR_DEPRECATED},
  1761     {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1762     {"GHC", UCURR_COMMON|UCURR_DEPRECATED},
  1763     {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1764     {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1765     {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1766     {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1767     {"GNS", UCURR_COMMON|UCURR_DEPRECATED},
  1768     {"GQE", UCURR_COMMON|UCURR_DEPRECATED},
  1769     {"GRD", UCURR_COMMON|UCURR_DEPRECATED},
  1770     {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1771     {"GWE", UCURR_COMMON|UCURR_DEPRECATED},
  1772     {"GWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1773     {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1774     {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1775     {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1776     {"HRD", UCURR_COMMON|UCURR_DEPRECATED},
  1777     {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1778     {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1779     {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1780     {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1781     {"IEP", UCURR_COMMON|UCURR_DEPRECATED},
  1782     {"ILP", UCURR_COMMON|UCURR_DEPRECATED},
  1783     {"ILR", UCURR_COMMON|UCURR_DEPRECATED},
  1784     {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1785     {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1786     {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1787     {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1788     {"ISJ", UCURR_COMMON|UCURR_DEPRECATED},
  1789     {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1790     {"ITL", UCURR_COMMON|UCURR_DEPRECATED},
  1791     {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1792     {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1793     {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1794     {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1795     {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1796     {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1797     {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1798     {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1799     {"KRH", UCURR_COMMON|UCURR_DEPRECATED},
  1800     {"KRO", UCURR_COMMON|UCURR_DEPRECATED},
  1801     {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1802     {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1803     {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1804     {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1805     {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1806     {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1807     {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1808     {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1809     {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1810     {"LSM", UCURR_COMMON|UCURR_DEPRECATED},
  1811     {"LTL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1812     {"LTT", UCURR_COMMON|UCURR_DEPRECATED},
  1813     {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1814     {"LUF", UCURR_COMMON|UCURR_DEPRECATED},
  1815     {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1816     {"LVL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1817     {"LVR", UCURR_COMMON|UCURR_DEPRECATED},
  1818     {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1819     {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1820     {"MAF", UCURR_COMMON|UCURR_DEPRECATED},
  1821     {"MCF", UCURR_COMMON|UCURR_DEPRECATED},
  1822     {"MDC", UCURR_COMMON|UCURR_DEPRECATED},
  1823     {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1824     {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1825     {"MGF", UCURR_COMMON|UCURR_DEPRECATED},
  1826     {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1827     {"MKN", UCURR_COMMON|UCURR_DEPRECATED},
  1828     {"MLF", UCURR_COMMON|UCURR_DEPRECATED},
  1829     {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1830     {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1831     {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1832     {"MRO", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1833     {"MTL", UCURR_COMMON|UCURR_DEPRECATED},
  1834     {"MTP", UCURR_COMMON|UCURR_DEPRECATED},
  1835     {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1836     {"MVP", UCURR_COMMON|UCURR_DEPRECATED},
  1837     {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1838     {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1839     {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1840     {"MXP", UCURR_COMMON|UCURR_DEPRECATED},
  1841     {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1842     {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1843     {"MZE", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1844     {"MZM", UCURR_COMMON|UCURR_DEPRECATED},
  1845     {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1846     {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1847     {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1848     {"NIC", UCURR_COMMON|UCURR_DEPRECATED},
  1849     {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1850     {"NLG", UCURR_COMMON|UCURR_DEPRECATED},
  1851     {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1852     {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1853     {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1854     {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1855     {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1856     {"PEI", UCURR_COMMON|UCURR_DEPRECATED},
  1857     {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1858     {"PES", UCURR_COMMON|UCURR_DEPRECATED},
  1859     {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1860     {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1861     {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1862     {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1863     {"PLZ", UCURR_COMMON|UCURR_DEPRECATED},
  1864     {"PTE", UCURR_COMMON|UCURR_DEPRECATED},
  1865     {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1866     {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1867     {"RHD", UCURR_COMMON|UCURR_DEPRECATED},
  1868     {"ROL", UCURR_COMMON|UCURR_DEPRECATED},
  1869     {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1870     {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1871     {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1872     {"RUR", UCURR_COMMON|UCURR_DEPRECATED},
  1873     {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1874     {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1875     {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1876     {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1877     {"SDD", UCURR_COMMON|UCURR_DEPRECATED},
  1878     {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1879     {"SDP", UCURR_COMMON|UCURR_DEPRECATED},
  1880     {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1881     {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1882     {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1883     {"SIT", UCURR_COMMON|UCURR_DEPRECATED},
  1884     {"SKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1885     {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1886     {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1887     {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1888     {"SRG", UCURR_COMMON|UCURR_DEPRECATED},
  1889     {"SSP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1890     {"STD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1891     {"SUR", UCURR_COMMON|UCURR_DEPRECATED},
  1892     {"SVC", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1893     {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1894     {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1895     {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1896     {"TJR", UCURR_COMMON|UCURR_DEPRECATED},
  1897     {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1898     {"TMM", UCURR_COMMON|UCURR_DEPRECATED},
  1899     {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1900     {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1901     {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1902     {"TPE", UCURR_COMMON|UCURR_DEPRECATED},
  1903     {"TRL", UCURR_COMMON|UCURR_DEPRECATED},
  1904     {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1905     {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1906     {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1907     {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1908     {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1909     {"UAK", UCURR_COMMON|UCURR_DEPRECATED},
  1910     {"UGS", UCURR_COMMON|UCURR_DEPRECATED},
  1911     {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1912     {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1913     {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1914     {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1915     {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1916     {"UYP", UCURR_COMMON|UCURR_DEPRECATED},
  1917     {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1918     {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1919     {"VEB", UCURR_COMMON|UCURR_DEPRECATED},
  1920     {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1921     {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1922     {"VNN", UCURR_COMMON|UCURR_DEPRECATED},
  1923     {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1924     {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1925     {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1926     {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1927     {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1928     {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1929     {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1930     {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1931     {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1932     {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1933     {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1934     {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED},
  1935     {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1936     {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1937     {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1938     {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1939     {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1940     {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1941     {"XRE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1942     {"XSU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1943     {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1944     {"XUA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1945     {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1946     {"YDD", UCURR_COMMON|UCURR_DEPRECATED},
  1947     {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1948     {"YUD", UCURR_COMMON|UCURR_DEPRECATED},
  1949     {"YUM", UCURR_COMMON|UCURR_DEPRECATED},
  1950     {"YUN", UCURR_COMMON|UCURR_DEPRECATED},
  1951     {"YUR", UCURR_COMMON|UCURR_DEPRECATED},
  1952     {"ZAL", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
  1953     {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1954     {"ZMK", UCURR_COMMON|UCURR_DEPRECATED},
  1955     {"ZMW", UCURR_COMMON|UCURR_NON_DEPRECATED},
  1956     {"ZRN", UCURR_COMMON|UCURR_DEPRECATED},
  1957     {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED},
  1958     {"ZWL", UCURR_COMMON|UCURR_DEPRECATED},
  1959     {"ZWR", UCURR_COMMON|UCURR_DEPRECATED},
  1960     {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
  1961     { NULL, 0 } // Leave here to denote the end of the list.
  1962 };
  1964 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
  1965     ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
  1967 static int32_t U_CALLCONV
  1968 ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
  1969     UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
  1970     uint32_t currType = myContext->currType;
  1971     int32_t count = 0;
  1973     /* Count the number of items matching the type we are looking for. */
  1974     for (int32_t idx = 0; gCurrencyList[idx].currency != NULL; idx++) {
  1975         if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) {
  1976             count++;
  1979     return count;
  1982 static const char* U_CALLCONV
  1983 ucurr_nextCurrencyList(UEnumeration *enumerator,
  1984                         int32_t* resultLength,
  1985                         UErrorCode * /*pErrorCode*/)
  1987     UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
  1989     /* Find the next in the list that matches the type we are looking for. */
  1990     while (myContext->listIdx < (sizeof(gCurrencyList)/sizeof(gCurrencyList[0]))-1) {
  1991         const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++];
  1992         if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType))
  1994             if (resultLength) {
  1995                 *resultLength = 3; /* Currency codes are only 3 chars long */
  1997             return currItem->currency;
  2000     /* We enumerated too far. */
  2001     if (resultLength) {
  2002         *resultLength = 0;
  2004     return NULL;
  2007 static void U_CALLCONV
  2008 ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
  2009     ((UCurrencyContext *)(enumerator->context))->listIdx = 0;
  2012 static void U_CALLCONV
  2013 ucurr_closeCurrencyList(UEnumeration *enumerator) {
  2014     uprv_free(enumerator->context);
  2015     uprv_free(enumerator);
  2018 static void U_CALLCONV
  2019 ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){
  2020     UErrorCode localStatus = U_ZERO_ERROR;
  2022     // Look up the CurrencyMap element in the root bundle.
  2023     UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
  2024     UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
  2026     if (U_SUCCESS(localStatus)) {
  2027         // process each entry in currency map 
  2028         for (int32_t i=0; i<ures_getSize(currencyMapArray); i++) {
  2029             // get the currency resource
  2030             UResourceBundle *currencyArray = ures_getByIndex(currencyMapArray, i, NULL, &localStatus);
  2031             // process each currency 
  2032             if (U_SUCCESS(localStatus)) {
  2033                 for (int32_t j=0; j<ures_getSize(currencyArray); j++) {
  2034                     // get the currency resource
  2035                     UResourceBundle *currencyRes = ures_getByIndex(currencyArray, j, NULL, &localStatus);
  2036                     IsoCodeEntry *entry = (IsoCodeEntry*)uprv_malloc(sizeof(IsoCodeEntry));
  2037                     if (entry == NULL) {
  2038                         *status = U_MEMORY_ALLOCATION_ERROR;
  2039                         return;
  2042                     // get the ISO code
  2043                     int32_t isoLength = 0;
  2044                     UResourceBundle *idRes = ures_getByKey(currencyRes, "id", NULL, &localStatus);
  2045                     if (idRes == NULL) {
  2046                         continue;
  2048                     const UChar *isoCode = ures_getString(idRes, &isoLength, &localStatus);
  2050                     // get from date
  2051                     UDate fromDate = U_DATE_MIN;
  2052                     UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
  2054                     if (U_SUCCESS(localStatus)) {
  2055                         int32_t fromLength = 0;
  2056                         const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
  2057                         int64_t currDate64 = (int64_t)fromArray[0] << 32;
  2058                         currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
  2059                         fromDate = (UDate)currDate64;
  2061                     ures_close(fromRes);
  2063                     // get to date
  2064                     UDate toDate = U_DATE_MAX;
  2065                     localStatus = U_ZERO_ERROR;
  2066                     UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
  2068                     if (U_SUCCESS(localStatus)) {
  2069                         int32_t toLength = 0;
  2070                         const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
  2071                         int64_t currDate64 = (int64_t)toArray[0] << 32;
  2072                         currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
  2073                         toDate = (UDate)currDate64;
  2075                     ures_close(toRes);
  2077                     ures_close(idRes);
  2078                     ures_close(currencyRes);
  2080                     entry->isoCode = isoCode;
  2081                     entry->from = fromDate;
  2082                     entry->to = toDate;
  2084                     localStatus = U_ZERO_ERROR;
  2085                     uhash_put(isoCodes, (UChar *)isoCode, entry, &localStatus);
  2087             } else {
  2088                 *status = localStatus;
  2090             ures_close(currencyArray);
  2092     } else {
  2093         *status = localStatus;
  2096     ures_close(currencyMapArray);
  2099 static const UEnumeration gEnumCurrencyList = {
  2100     NULL,
  2101     NULL,
  2102     ucurr_closeCurrencyList,
  2103     ucurr_countCurrencyList,
  2104     uenum_unextDefault,
  2105     ucurr_nextCurrencyList,
  2106     ucurr_resetCurrencyList
  2107 };
  2108 U_CDECL_END
  2111 static void U_CALLCONV initIsoCodes(UErrorCode &status) {
  2112     U_ASSERT(gIsoCodes == NULL);
  2113     ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
  2115     UHashtable *isoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
  2116     if (U_FAILURE(status)) {
  2117         return;
  2119     uhash_setValueDeleter(isoCodes, deleteIsoCodeEntry);
  2121     ucurr_createCurrencyList(isoCodes, &status);
  2122     if (U_FAILURE(status)) {
  2123         uhash_close(isoCodes);
  2124         return;
  2126     gIsoCodes = isoCodes;  // Note: gIsoCodes is const. Once set up here it is never altered,
  2127                            //       and read only access is safe without synchronization.
  2130 static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) {
  2131     if (U_FAILURE(status)) {
  2132         return;
  2134     int32_t length = sizeof(EQUIV_CURRENCY_SYMBOLS) / sizeof(EQUIV_CURRENCY_SYMBOLS[0]);
  2135     for (int32_t i = 0; i < length; ++i) {
  2136         icu::UnicodeString lhs(EQUIV_CURRENCY_SYMBOLS[i][0], -1, US_INV);
  2137         icu::UnicodeString rhs(EQUIV_CURRENCY_SYMBOLS[i][1], -1, US_INV);
  2138         makeEquivalent(lhs.unescape(), rhs.unescape(), hash, status);
  2139         if (U_FAILURE(status)) {
  2140             return;
  2145 static void U_CALLCONV initCurrSymbolsEquiv() {
  2146     U_ASSERT(gCurrSymbolsEquiv == NULL);
  2147     UErrorCode status = U_ZERO_ERROR;
  2148     ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
  2149     icu::Hashtable *temp = new icu::Hashtable(status);
  2150     if (temp == NULL) {
  2151         return;
  2153     if (U_FAILURE(status)) {
  2154         delete temp;
  2155         return;
  2157     temp->setValueDeleter(deleteUnicode);
  2158     populateCurrSymbolsEquiv(temp, status);
  2159     if (U_FAILURE(status)) {
  2160         delete temp;
  2161         return;
  2163     gCurrSymbolsEquiv = temp;
  2166 U_CAPI UBool U_EXPORT2
  2167 ucurr_isAvailable(const UChar* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) {
  2168     umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode);
  2169     if (U_FAILURE(*eErrorCode)) {
  2170         return FALSE;
  2173     IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode);
  2174     if (result == NULL) {
  2175         return FALSE;
  2176     } else if (from > to) {
  2177         *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
  2178         return FALSE;
  2179     } else if  ((from > result->to) || (to < result->from)) {
  2180         return FALSE;
  2182     return TRUE;
  2185 static const icu::Hashtable* getCurrSymbolsEquiv() {
  2186     umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv);
  2187     return gCurrSymbolsEquiv;
  2190 U_CAPI UEnumeration * U_EXPORT2
  2191 ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) {
  2192     UEnumeration *myEnum = NULL;
  2193     UCurrencyContext *myContext;
  2195     myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration));
  2196     if (myEnum == NULL) {
  2197         *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
  2198         return NULL;
  2200     uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration));
  2201     myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext));
  2202     if (myContext == NULL) {
  2203         *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
  2204         uprv_free(myEnum);
  2205         return NULL;
  2207     myContext->currType = currType;
  2208     myContext->listIdx = 0;
  2209     myEnum->context = myContext;
  2210     return myEnum;
  2213 U_CAPI int32_t U_EXPORT2
  2214 ucurr_countCurrencies(const char* locale, 
  2215                  UDate date, 
  2216                  UErrorCode* ec)
  2218     int32_t currCount = 0;
  2220     if (ec != NULL && U_SUCCESS(*ec)) 
  2222         // local variables
  2223         UErrorCode localStatus = U_ZERO_ERROR;
  2224         char id[ULOC_FULLNAME_CAPACITY];
  2225         uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
  2226         // get country or country_variant in `id'
  2227         /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
  2229         if (U_FAILURE(*ec))
  2231             return 0;
  2234         // Remove variants, which is only needed for registration.
  2235         char *idDelim = strchr(id, VAR_DELIM);
  2236         if (idDelim)
  2238             idDelim[0] = 0;
  2241         // Look up the CurrencyMap element in the root bundle.
  2242         UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
  2243         UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
  2245         // Using the id derived from the local, get the currency data
  2246         UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
  2248         // process each currency to see which one is valid for the given date
  2249         if (U_SUCCESS(localStatus))
  2251             for (int32_t i=0; i<ures_getSize(countryArray); i++)
  2253                 // get the currency resource
  2254                 UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus);
  2256                 // get the from date
  2257                 int32_t fromLength = 0;
  2258                 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
  2259                 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
  2261                 int64_t currDate64 = (int64_t)fromArray[0] << 32;
  2262                 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
  2263                 UDate fromDate = (UDate)currDate64;
  2265                 if (ures_getSize(currencyRes)> 2)
  2267                     int32_t toLength = 0;
  2268                     UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
  2269                     const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
  2271                     currDate64 = (int64_t)toArray[0] << 32;
  2272                     currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
  2273                     UDate toDate = (UDate)currDate64;
  2275                     if ((fromDate <= date) && (date < toDate))
  2277                         currCount++;
  2280                     ures_close(toRes);
  2282                 else
  2284                     if (fromDate <= date)
  2286                         currCount++;
  2290                 // close open resources
  2291                 ures_close(currencyRes);
  2292                 ures_close(fromRes);
  2294             } // end For loop
  2295         } // end if (U_SUCCESS(localStatus))
  2297         ures_close(countryArray);
  2299         // Check for errors
  2300         if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
  2302             // There is nothing to fallback to. 
  2303             // Report the failure/warning if possible.
  2304             *ec = localStatus;
  2307         if (U_SUCCESS(*ec))
  2309             // no errors
  2310             return currCount;
  2315     // If we got here, either error code is invalid or
  2316     // some argument passed is no good.
  2317     return 0;
  2320 U_CAPI int32_t U_EXPORT2 
  2321 ucurr_forLocaleAndDate(const char* locale, 
  2322                 UDate date, 
  2323                 int32_t index,
  2324                 UChar* buff, 
  2325                 int32_t buffCapacity, 
  2326                 UErrorCode* ec)
  2328     int32_t resLen = 0;
  2329 	int32_t currIndex = 0;
  2330     const UChar* s = NULL;
  2332     if (ec != NULL && U_SUCCESS(*ec))
  2334         // check the arguments passed
  2335         if ((buff && buffCapacity) || !buffCapacity )
  2337             // local variables
  2338             UErrorCode localStatus = U_ZERO_ERROR;
  2339             char id[ULOC_FULLNAME_CAPACITY];
  2340             resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
  2342             // get country or country_variant in `id'
  2343             /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
  2344             if (U_FAILURE(*ec))
  2346                 return 0;
  2349             // Remove variants, which is only needed for registration.
  2350             char *idDelim = strchr(id, VAR_DELIM);
  2351             if (idDelim)
  2353                 idDelim[0] = 0;
  2356             // Look up the CurrencyMap element in the root bundle.
  2357             UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
  2358             UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
  2360             // Using the id derived from the local, get the currency data
  2361             UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
  2363             // process each currency to see which one is valid for the given date
  2364             bool matchFound = false;
  2365             if (U_SUCCESS(localStatus))
  2367                 if ((index <= 0) || (index> ures_getSize(countryArray)))
  2369                     // requested index is out of bounds
  2370                     ures_close(countryArray);
  2371                     return 0;
  2374                 for (int32_t i=0; i<ures_getSize(countryArray); i++)
  2376                     // get the currency resource
  2377                     UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus);
  2378                     s = ures_getStringByKey(currencyRes, "id", &resLen, &localStatus);
  2380                     // get the from date
  2381                     int32_t fromLength = 0;
  2382                     UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
  2383                     const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
  2385                     int64_t currDate64 = (int64_t)fromArray[0] << 32;
  2386                     currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
  2387                     UDate fromDate = (UDate)currDate64;
  2389                     if (ures_getSize(currencyRes)> 2)
  2391                         int32_t toLength = 0;
  2392                         UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
  2393                         const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
  2395                         currDate64 = (int64_t)toArray[0] << 32;
  2396                         currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
  2397                         UDate toDate = (UDate)currDate64;
  2399                         if ((fromDate <= date) && (date < toDate))
  2401                             currIndex++;
  2402                             if (currIndex == index)
  2404                                 matchFound = true;
  2408                         ures_close(toRes);
  2410                     else
  2412                         if (fromDate <= date)
  2414                             currIndex++;
  2415                             if (currIndex == index)
  2417                                 matchFound = true;
  2422                     // close open resources
  2423                     ures_close(currencyRes);
  2424                     ures_close(fromRes);
  2426                     // check for loop exit
  2427                     if (matchFound)
  2429                         break;
  2432                 } // end For loop
  2435             ures_close(countryArray);
  2437             // Check for errors
  2438             if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
  2440                 // There is nothing to fallback to. 
  2441                 // Report the failure/warning if possible.
  2442                 *ec = localStatus;
  2445             if (U_SUCCESS(*ec))
  2447                 // no errors
  2448                 if((buffCapacity> resLen) && matchFound)
  2450                     // write out the currency value
  2451                     u_strcpy(buff, s);
  2453                 else
  2455                     return 0;
  2459             // return null terminated currency string
  2460             return u_terminateUChars(buff, buffCapacity, resLen, ec);
  2462         else
  2464             // illegal argument encountered
  2465             *ec = U_ILLEGAL_ARGUMENT_ERROR;
  2470     // If we got here, either error code is invalid or
  2471     // some argument passed is no good.
  2472     return resLen;
  2475 static const UEnumeration defaultKeywordValues = {
  2476     NULL,
  2477     NULL,
  2478     ulist_close_keyword_values_iterator,
  2479     ulist_count_keyword_values,
  2480     uenum_unextDefault,
  2481     ulist_next_keyword_value, 
  2482     ulist_reset_keyword_values_iterator
  2483 };
  2485 U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) {
  2486     // Resolve region
  2487     char prefRegion[ULOC_FULLNAME_CAPACITY] = "";
  2488     int32_t prefRegionLength = 0;
  2489     prefRegionLength = uloc_getCountry(locale, prefRegion, sizeof(prefRegion), status);
  2490     if (prefRegionLength == 0) {
  2491         char loc[ULOC_FULLNAME_CAPACITY] = "";
  2492         uloc_addLikelySubtags(locale, loc, sizeof(loc), status);
  2494         prefRegionLength = uloc_getCountry(loc, prefRegion, sizeof(prefRegion), status);
  2497     // Read value from supplementalData
  2498     UList *values = ulist_createEmptyList(status);
  2499     UList *otherValues = ulist_createEmptyList(status);
  2500     UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
  2501     if (U_FAILURE(*status) || en == NULL) {
  2502         if (en == NULL) {
  2503             *status = U_MEMORY_ALLOCATION_ERROR;
  2504         } else {
  2505             uprv_free(en);
  2507         ulist_deleteList(values);
  2508         ulist_deleteList(otherValues);
  2509         return NULL;
  2511     memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
  2512     en->context = values;
  2514     UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status);
  2515     ures_getByKey(bundle, "CurrencyMap", bundle, status);
  2516     UResourceBundle bundlekey, regbndl, curbndl, to;
  2517     ures_initStackObject(&bundlekey);
  2518     ures_initStackObject(&regbndl);
  2519     ures_initStackObject(&curbndl);
  2520     ures_initStackObject(&to);
  2522     while (U_SUCCESS(*status) && ures_hasNext(bundle)) {
  2523         ures_getNextResource(bundle, &bundlekey, status);
  2524         if (U_FAILURE(*status)) {
  2525             break;
  2527         const char *region = ures_getKey(&bundlekey);
  2528         UBool isPrefRegion = uprv_strcmp(region, prefRegion) == 0 ? TRUE : FALSE;
  2529         if (!isPrefRegion && commonlyUsed) {
  2530             // With commonlyUsed=true, we do not put
  2531             // currencies for other regions in the
  2532             // result list.
  2533             continue;
  2535         ures_getByKey(bundle, region, &regbndl, status);
  2536         if (U_FAILURE(*status)) {
  2537             break;
  2539         while (U_SUCCESS(*status) && ures_hasNext(&regbndl)) {
  2540             ures_getNextResource(&regbndl, &curbndl, status);
  2541             if (ures_getType(&curbndl) != URES_TABLE) {
  2542                 // Currently, an empty ARRAY is mixed in.
  2543                 continue;
  2545             char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
  2546             int32_t curIDLength = ULOC_KEYWORDS_CAPACITY;
  2547             if (curID == NULL) {
  2548                 *status = U_MEMORY_ALLOCATION_ERROR;
  2549                 break;
  2552 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
  2553             ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, TRUE, status);
  2554             /* optimize - use the utf-8 string */
  2555 #else
  2557                        const UChar* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status);
  2558                        if(U_SUCCESS(*status)) {
  2559 			   if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) {
  2560 				*status = U_BUFFER_OVERFLOW_ERROR;
  2561 			   } else {
  2562                            	u_UCharsToChars(defString, curID, curIDLength+1);
  2566 #endif	
  2568             if (U_FAILURE(*status)) {
  2569                 break;
  2571             UBool hasTo = FALSE;
  2572             ures_getByKey(&curbndl, "to", &to, status);
  2573             if (U_FAILURE(*status)) {
  2574                 // Do nothing here...
  2575                 *status = U_ZERO_ERROR;
  2576             } else {
  2577                 hasTo = TRUE;
  2579             if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) {
  2580                 // Currently active currency for the target country
  2581                 ulist_addItemEndList(values, curID, TRUE, status);
  2582             } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) {
  2583                 ulist_addItemEndList(otherValues, curID, TRUE, status);
  2584             } else {
  2585                 uprv_free(curID);
  2590     if (U_SUCCESS(*status)) {
  2591         if (commonlyUsed) {
  2592             if (ulist_getListSize(values) == 0) {
  2593                 // This could happen if no valid region is supplied in the input
  2594                 // locale. In this case, we use the CLDR's default.
  2595                 uenum_close(en);
  2596                 en = ucurr_getKeywordValuesForLocale(key, "und", TRUE, status);
  2598         } else {
  2599             // Consolidate the list
  2600             char *value = NULL;
  2601             ulist_resetList(otherValues);
  2602             while ((value = (char *)ulist_getNext(otherValues)) != NULL) {
  2603                 if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) {
  2604                     char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
  2605                     uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1);
  2606                     ulist_addItemEndList(values, tmpValue, TRUE, status);
  2607                     if (U_FAILURE(*status)) {
  2608                         break;
  2614         ulist_resetList((UList *)(en->context));
  2615     } else {
  2616         ulist_deleteList(values);
  2617         uprv_free(en);
  2618         values = NULL;
  2619         en = NULL;
  2621     ures_close(&to);
  2622     ures_close(&curbndl);
  2623     ures_close(&regbndl);
  2624     ures_close(&bundlekey);
  2625     ures_close(bundle);
  2627     ulist_deleteList(otherValues);
  2629     return en;
  2633 U_CAPI int32_t U_EXPORT2
  2634 ucurr_getNumericCode(const UChar* currency) {
  2635     int32_t code = 0;
  2636     if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) {
  2637         UErrorCode status = U_ZERO_ERROR;
  2639         UResourceBundle *bundle = ures_openDirect(0, "currencyNumericCodes", &status);
  2640         ures_getByKey(bundle, "codeMap", bundle, &status);
  2641         if (U_SUCCESS(status)) {
  2642             char alphaCode[ISO_CURRENCY_CODE_LENGTH+1];
  2643             myUCharsToChars(alphaCode, currency);
  2644             T_CString_toUpperCase(alphaCode);
  2645             ures_getByKey(bundle, alphaCode, bundle, &status);
  2646             int tmpCode = ures_getInt(bundle, &status);
  2647             if (U_SUCCESS(status)) {
  2648                 code = tmpCode;
  2651         ures_close(bundle);
  2653     return code;
  2655 #endif /* #if !UCONFIG_NO_FORMATTING */
  2657 //eof

mercurial