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