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