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