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