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