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