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