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