1 /*
2 **********************************************************************
3 * Copyright (c) 2002-2011, 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_COUNTRY_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
isoCodes_cleanup(void)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
deleteIsoCodeEntry(void * obj)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*
myUCharsToChars(char * resultOfLen4,const UChar * currency)143 myUCharsToChars(char* resultOfLen4, const UChar* currency) {
144 u_UCharsToChars(currency, resultOfLen4, ISO_COUNTRY_CODE_LENGTH);
145 resultOfLen4[ISO_COUNTRY_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*
_findMetaData(const UChar * currency,UErrorCode & ec)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_COUNTRY_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
idForLocale(const char * locale,char * countryAndVariant,int capacity,UErrorCode * ec)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 U_NAMESPACE_QUALIFIER UMemory {
256 CReg *next;
257 UChar iso[ISO_COUNTRY_CODE_LENGTH+1];
258 char id[ULOC_FULLNAME_CAPACITY];
259
CRegCReg260 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_COUNTRY_CODE_LENGTH * sizeof(const UChar));
270 iso[ISO_COUNTRY_CODE_LENGTH] = 0;
271 }
272
regCReg273 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
unregCReg293 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
getCReg312 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. */
cleanupCReg331 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
ucurr_register(const UChar * isoCode,const char * locale,UErrorCode * status)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
ucurr_unregister(UCurrRegistryKey key,UErrorCode * status)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
currency_cleanup(void)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
ucurr_forLocale(const char * locale,UChar * buff,int32_t buffCapacity,UErrorCode * ec)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 */
fallback(char * loc)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
ucurr_getName(const UChar * currency,const char * locale,UCurrNameStyle nameStyle,UBool * isChoiceFormat,int32_t * len,UErrorCode * ec)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_COUNTRY_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_COUNTRY_CODE_LENGTH, but maybe not...?
608 *ec = U_USING_DEFAULT_WARNING;
609 return currency;
610 }
611
612 U_CAPI const UChar* U_EXPORT2
ucurr_getPluralName(const UChar * currency,const char * locale,UBool * isChoiceFormat,const char * pluralCount,int32_t * len,UErrorCode * ec)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_COUNTRY_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_COUNTRY_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.
currencyNameComparator(const void * a,const void * b)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
getCurrencyNameCount(const char * loc,int32_t * total_currency_name_count,int32_t * total_currency_symbol_count)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(s, 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 // TODO: locale dependent
805 static UChar*
toUpperCase(const UChar * source,int32_t len)806 toUpperCase(const UChar* source, int32_t len) {
807 UChar* dest = NULL;
808 UErrorCode ec = U_ZERO_ERROR;
809 int32_t destLen = u_strToUpper(dest, 0, source, len, NULL, &ec);
810
811 ec = U_ZERO_ERROR;
812 dest = (UChar*)uprv_malloc(sizeof(UChar) * MAX(destLen, len));
813 u_strToUpper(dest, destLen, source, len, NULL, &ec);
814 if (U_FAILURE(ec)) {
815 uprv_memcpy(dest, source, sizeof(UChar) * len);
816 }
817 return dest;
818 }
819
820
821 // Collect all available currency names associated with the give locale
822 // (enable fallback chain).
823 // Read currenc names defined in resource bundle "Currencies" and
824 // "CurrencyPlural", enable fallback chain.
825 // return the malloc-ed currency name arrays and the total number of currency
826 // names in the array.
827 static void
collectCurrencyNames(const char * locale,CurrencyNameStruct ** currencyNames,int32_t * total_currency_name_count,CurrencyNameStruct ** currencySymbols,int32_t * total_currency_symbol_count,UErrorCode & ec)828 collectCurrencyNames(const char* locale,
829 CurrencyNameStruct** currencyNames,
830 int32_t* total_currency_name_count,
831 CurrencyNameStruct** currencySymbols,
832 int32_t* total_currency_symbol_count,
833 UErrorCode& ec) {
834 U_NAMESPACE_USE
835 // Look up the Currencies resource for the given locale.
836 UErrorCode ec2 = U_ZERO_ERROR;
837
838 char loc[ULOC_FULLNAME_CAPACITY];
839 uloc_getName(locale, loc, sizeof(loc), &ec2);
840 if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
841 ec = U_ILLEGAL_ARGUMENT_ERROR;
842 }
843
844 // Get maximum currency name count first.
845 getCurrencyNameCount(loc, total_currency_name_count, total_currency_symbol_count);
846
847 *currencyNames = (CurrencyNameStruct*)uprv_malloc
848 (sizeof(CurrencyNameStruct) * (*total_currency_name_count));
849 *currencySymbols = (CurrencyNameStruct*)uprv_malloc
850 (sizeof(CurrencyNameStruct) * (*total_currency_symbol_count));
851
852 const UChar* s = NULL; // currency name
853 char* iso = NULL; // currency ISO code
854
855 *total_currency_name_count = 0;
856 *total_currency_symbol_count = 0;
857
858 UErrorCode ec3 = U_ZERO_ERROR;
859 UErrorCode ec4 = U_ZERO_ERROR;
860
861 // Using hash to remove duplicates caused by locale fallback
862 UHashtable* currencyIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec3);
863 UHashtable* currencyPluralIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec4);
864 for (int32_t localeLevel = 0; ; ++localeLevel) {
865 ec2 = U_ZERO_ERROR;
866 // TODO: ures_openDirect
867 UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
868 UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
869 int32_t n = ures_getSize(curr);
870 for (int32_t i=0; i<n; ++i) {
871 UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2);
872 int32_t len;
873 s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
874 // TODO: uhash_put wont change key/value?
875 iso = (char*)ures_getKey(names);
876 if (localeLevel == 0) {
877 uhash_put(currencyIsoCodes, iso, iso, &ec3);
878 } else {
879 if (uhash_get(currencyIsoCodes, iso) != NULL) {
880 ures_close(names);
881 continue;
882 } else {
883 uhash_put(currencyIsoCodes, iso, iso, &ec3);
884 }
885 }
886 UBool isChoice = FALSE;
887 if (len > 0 && s[0] == CHOICE_FORMAT_MARK) {
888 ++s;
889 --len;
890 if (len > 0 && s[0] != CHOICE_FORMAT_MARK) {
891 isChoice = TRUE;
892 }
893 }
894 if (isChoice) {
895 ChoiceFormat fmt(s, ec2);
896 int32_t fmt_count;
897 const UnicodeString* formats = fmt.getFormats(fmt_count);
898 for (int i = 0; i < fmt_count; ++i) {
899 // put iso, formats[i]; into array
900 int32_t length = formats[i].length();
901 UChar* name = (UChar*)uprv_malloc(sizeof(UChar)*length);
902 formats[i].extract(0, length, name);
903 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
904 (*currencySymbols)[*total_currency_symbol_count].currencyName = name;
905 (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
906 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = length;
907 }
908 } else {
909 // Add currency symbol.
910 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
911 (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)s;
912 (*currencySymbols)[*total_currency_symbol_count].flag = 0;
913 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len;
914 }
915
916 // Add currency long name.
917 s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2);
918 (*currencyNames)[*total_currency_name_count].IsoCode = iso;
919 UChar* upperName = toUpperCase(s, len);
920 (*currencyNames)[*total_currency_name_count].currencyName = upperName;
921 (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
922 (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
923
924 // put (iso, 3, and iso) in to array
925 // Add currency ISO code.
926 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
927 (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)uprv_malloc(sizeof(UChar)*3);
928 // Must convert iso[] into Unicode
929 u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3);
930 (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
931 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3;
932
933 ures_close(names);
934 }
935
936 // currency plurals
937 UErrorCode ec3 = U_ZERO_ERROR;
938 UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3);
939 n = ures_getSize(curr_p);
940 for (int32_t i=0; i<n; ++i) {
941 UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3);
942 iso = (char*)ures_getKey(names);
943 // Using hash to remove duplicated ISO codes in fallback chain.
944 if (localeLevel == 0) {
945 uhash_put(currencyPluralIsoCodes, iso, iso, &ec4);
946 } else {
947 if (uhash_get(currencyPluralIsoCodes, iso) != NULL) {
948 ures_close(names);
949 continue;
950 } else {
951 uhash_put(currencyPluralIsoCodes, iso, iso, &ec4);
952 }
953 }
954 int32_t num = ures_getSize(names);
955 int32_t len;
956 for (int32_t j = 0; j < num; ++j) {
957 // TODO: remove duplicates between singular name and
958 // currency long name?
959 s = ures_getStringByIndex(names, j, &len, &ec3);
960 (*currencyNames)[*total_currency_name_count].IsoCode = iso;
961 UChar* upperName = toUpperCase(s, len);
962 (*currencyNames)[*total_currency_name_count].currencyName = upperName;
963 (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
964 (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
965 }
966 ures_close(names);
967 }
968 ures_close(curr_p);
969 ures_close(curr);
970 ures_close(rb);
971
972 if (!fallback(loc)) {
973 break;
974 }
975 }
976
977 uhash_close(currencyIsoCodes);
978 uhash_close(currencyPluralIsoCodes);
979
980 // quick sort the struct
981 qsort(*currencyNames, *total_currency_name_count,
982 sizeof(CurrencyNameStruct), currencyNameComparator);
983 qsort(*currencySymbols, *total_currency_symbol_count,
984 sizeof(CurrencyNameStruct), currencyNameComparator);
985
986 #ifdef UCURR_DEBUG
987 printf("currency name count: %d\n", *total_currency_name_count);
988 for (int32_t index = 0; index < *total_currency_name_count; ++index) {
989 printf("index: %d\n", index);
990 printf("iso: %s\n", (*currencyNames)[index].IsoCode);
991 printf("currencyName:");
992 for (int32_t i = 0; i < (*currencyNames)[index].currencyNameLen; ++i) {
993 printf("%c", (unsigned char)(*currencyNames)[index].currencyName[i]);
994 }
995 printf("\n");
996 printf("len: %d\n", (*currencyNames)[index].currencyNameLen);
997 }
998 printf("currency symbol count: %d\n", *total_currency_symbol_count);
999 for (int32_t index = 0; index < *total_currency_symbol_count; ++index) {
1000 printf("index: %d\n", index);
1001 printf("iso: %s\n", (*currencySymbols)[index].IsoCode);
1002 printf("currencySymbol:");
1003 for (int32_t i = 0; i < (*currencySymbols)[index].currencyNameLen; ++i) {
1004 printf("%c", (unsigned char)(*currencySymbols)[index].currencyName[i]);
1005 }
1006 printf("\n");
1007 printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
1008 }
1009 #endif
1010 }
1011
1012 // @param currencyNames: currency names array
1013 // @param indexInCurrencyNames: the index of the character in currency names
1014 // array against which the comparison is done
1015 // @param key: input text char to compare against
1016 // @param begin(IN/OUT): the begin index of matching range in currency names array
1017 // @param end(IN/OUT): the end index of matching range in currency names array.
1018 static int32_t
binarySearch(const CurrencyNameStruct * currencyNames,int32_t indexInCurrencyNames,const UChar key,int32_t * begin,int32_t * end)1019 binarySearch(const CurrencyNameStruct* currencyNames,
1020 int32_t indexInCurrencyNames,
1021 const UChar key,
1022 int32_t* begin, int32_t* end) {
1023 #ifdef UCURR_DEBUG
1024 printf("key = %x\n", key);
1025 #endif
1026 int32_t first = *begin;
1027 int32_t last = *end;
1028 while (first <= last) {
1029 int32_t mid = (first + last) / 2; // compute mid point.
1030 if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) {
1031 first = mid + 1;
1032 } else {
1033 if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) {
1034 first = mid + 1;
1035 }
1036 else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) {
1037 last = mid - 1;
1038 }
1039 else {
1040 // Find a match, and looking for ranges
1041 // Now do two more binary searches. First, on the left side for
1042 // the greatest L such that CurrencyNameStruct[L] < key.
1043 int32_t L = *begin;
1044 int32_t R = mid;
1045
1046 #ifdef UCURR_DEBUG
1047 printf("mid = %d\n", mid);
1048 #endif
1049 while (L < R) {
1050 int32_t M = (L + R) / 2;
1051 #ifdef UCURR_DEBUG
1052 printf("L = %d, R = %d, M = %d\n", L, R, M);
1053 #endif
1054 if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) {
1055 L = M + 1;
1056 } else {
1057 if (currencyNames[M].currencyName[indexInCurrencyNames] < key) {
1058 L = M + 1;
1059 } else {
1060 #ifdef UCURR_DEBUG
1061 U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
1062 #endif
1063 R = M;
1064 }
1065 }
1066 }
1067 #ifdef UCURR_DEBUG
1068 U_ASSERT(L == R);
1069 #endif
1070 *begin = L;
1071 #ifdef UCURR_DEBUG
1072 printf("begin = %d\n", *begin);
1073 U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key);
1074 #endif
1075
1076 // Now for the second search, finding the least R such that
1077 // key < CurrencyNameStruct[R].
1078 L = mid;
1079 R = *end;
1080 while (L < R) {
1081 int32_t M = (L + R) / 2;
1082 #ifdef UCURR_DEBUG
1083 printf("L = %d, R = %d, M = %d\n", L, R, M);
1084 #endif
1085 if (currencyNames[M].currencyNameLen < indexInCurrencyNames) {
1086 L = M + 1;
1087 } else {
1088 if (currencyNames[M].currencyName[indexInCurrencyNames] > key) {
1089 R = M;
1090 } else {
1091 #ifdef UCURR_DEBUG
1092 U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
1093 #endif
1094 L = M + 1;
1095 }
1096 }
1097 }
1098 #ifdef UCURR_DEBUG
1099 U_ASSERT(L == R);
1100 #endif
1101 if (currencyNames[R].currencyName[indexInCurrencyNames] > key) {
1102 *end = R - 1;
1103 } else {
1104 *end = R;
1105 }
1106 #ifdef UCURR_DEBUG
1107 printf("end = %d\n", *end);
1108 #endif
1109
1110 // now, found the range. check whether there is exact match
1111 if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) {
1112 return *begin; // find range and exact match.
1113 }
1114 return -1; // find range, but no exact match.
1115 }
1116 }
1117 }
1118 *begin = -1;
1119 *end = -1;
1120 return -1; // failed to find range.
1121 }
1122
1123
1124 // Linear search "text" in "currencyNames".
1125 // @param begin, end: the begin and end index in currencyNames, within which
1126 // range should the search be performed.
1127 // @param textLen: the length of the text to be compared
1128 // @param maxMatchLen(IN/OUT): passing in the computed max matching length
1129 // pass out the new max matching length
1130 // @param maxMatchIndex: the index in currencyName which has the longest
1131 // match with input text.
1132 static void
linearSearch(const CurrencyNameStruct * currencyNames,int32_t begin,int32_t end,const UChar * text,int32_t textLen,int32_t * maxMatchLen,int32_t * maxMatchIndex)1133 linearSearch(const CurrencyNameStruct* currencyNames,
1134 int32_t begin, int32_t end,
1135 const UChar* text, int32_t textLen,
1136 int32_t *maxMatchLen, int32_t* maxMatchIndex) {
1137 for (int32_t index = begin; index <= end; ++index) {
1138 int32_t len = currencyNames[index].currencyNameLen;
1139 if (len > *maxMatchLen && len <= textLen &&
1140 uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) {
1141 *maxMatchIndex = index;
1142 *maxMatchLen = len;
1143 #ifdef UCURR_DEBUG
1144 printf("maxMatchIndex = %d, maxMatchLen = %d\n",
1145 *maxMatchIndex, *maxMatchLen);
1146 #endif
1147 }
1148 }
1149 }
1150
1151 #define LINEAR_SEARCH_THRESHOLD 10
1152
1153 // Find longest match between "text" and currency names in "currencyNames".
1154 // @param total_currency_count: total number of currency names in CurrencyNames.
1155 // @param textLen: the length of the text to be compared
1156 // @param maxMatchLen: passing in the computed max matching length
1157 // pass out the new max matching length
1158 // @param maxMatchIndex: the index in currencyName which has the longest
1159 // match with input text.
1160 static void
searchCurrencyName(const CurrencyNameStruct * currencyNames,int32_t total_currency_count,const UChar * text,int32_t textLen,int32_t * maxMatchLen,int32_t * maxMatchIndex)1161 searchCurrencyName(const CurrencyNameStruct* currencyNames,
1162 int32_t total_currency_count,
1163 const UChar* text, int32_t textLen,
1164 int32_t* maxMatchLen, int32_t* maxMatchIndex) {
1165 *maxMatchIndex = -1;
1166 *maxMatchLen = 0;
1167 int32_t matchIndex = -1;
1168 int32_t binarySearchBegin = 0;
1169 int32_t binarySearchEnd = total_currency_count - 1;
1170 // It is a variant of binary search.
1171 // For example, given the currency names in currencyNames array are:
1172 // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
1173 // and the input text is BBEXST
1174 // The first round binary search search "B" in the text against
1175 // the first char in currency names, and find the first char matching range
1176 // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
1177 // The 2nd round binary search search the second "B" in the text against
1178 // the 2nd char in currency names, and narrow the matching range to
1179 // "BB BBEX BBEXYZ" (and the maximum matching "BB").
1180 // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
1181 // maximum matching).
1182 // The 4th round returns the same range (the maximum matching is "BBEX").
1183 // The 5th round returns no matching range.
1184 for (int32_t index = 0; index < textLen; ++index) {
1185 // matchIndex saves the one with exact match till the current point.
1186 // [binarySearchBegin, binarySearchEnd] saves the matching range.
1187 matchIndex = binarySearch(currencyNames, index,
1188 text[index],
1189 &binarySearchBegin, &binarySearchEnd);
1190 if (binarySearchBegin == -1) { // did not find the range
1191 break;
1192 }
1193 if (matchIndex != -1) {
1194 // find an exact match for text from text[0] to text[index]
1195 // in currencyNames array.
1196 *maxMatchLen = index + 1;
1197 *maxMatchIndex = matchIndex;
1198 }
1199 if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) {
1200 // linear search if within threshold.
1201 linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
1202 text, textLen,
1203 maxMatchLen, maxMatchIndex);
1204 break;
1205 }
1206 }
1207 return;
1208 }
1209
1210 //========================= currency name cache =====================
1211 typedef struct {
1212 char locale[ULOC_FULLNAME_CAPACITY]; //key
1213 // currency names, case insensitive
1214 CurrencyNameStruct* currencyNames; // value
1215 int32_t totalCurrencyNameCount; // currency name count
1216 // currency symbols and ISO code, case sensitive
1217 CurrencyNameStruct* currencySymbols; // value
1218 int32_t totalCurrencySymbolCount; // count
1219 // reference count.
1220 // reference count is set to 1 when an entry is put to cache.
1221 // it increases by 1 before accessing, and decreased by 1 after accessing.
1222 // The entry is deleted when ref count is zero, which means
1223 // the entry is replaced out of cache and no process is accessing it.
1224 int32_t refCount;
1225 } CurrencyNameCacheEntry;
1226
1227
1228 #define CURRENCY_NAME_CACHE_NUM 10
1229
1230 // Reserve 10 cache entries.
1231 static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {NULL};
1232 // Using an index to indicate which entry to be replaced when cache is full.
1233 // It is a simple round-robin replacement strategy.
1234 static int8_t currentCacheEntryIndex = 0;
1235
1236 // Cache deletion
1237 static void
deleteCurrencyNames(CurrencyNameStruct * currencyNames,int32_t count)1238 deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) {
1239 for (int32_t index = 0; index < count; ++index) {
1240 if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) {
1241 uprv_free(currencyNames[index].currencyName);
1242 }
1243 }
1244 uprv_free(currencyNames);
1245 }
1246
1247
1248 static void
deleteCacheEntry(CurrencyNameCacheEntry * entry)1249 deleteCacheEntry(CurrencyNameCacheEntry* entry) {
1250 deleteCurrencyNames(entry->currencyNames, entry->totalCurrencyNameCount);
1251 deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount);
1252 uprv_free(entry);
1253 }
1254
1255
1256 // Cache clean up
1257 static UBool U_CALLCONV
currency_cache_cleanup(void)1258 currency_cache_cleanup(void) {
1259 for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
1260 if (currCache[i]) {
1261 deleteCacheEntry(currCache[i]);
1262 currCache[i] = 0;
1263 }
1264 }
1265 return TRUE;
1266 }
1267
1268
1269 U_CFUNC void
uprv_parseCurrency(const char * locale,const U_NAMESPACE_QUALIFIER UnicodeString & text,U_NAMESPACE_QUALIFIER ParsePosition & pos,int8_t type,UChar * result,UErrorCode & ec)1270 uprv_parseCurrency(const char* locale,
1271 const U_NAMESPACE_QUALIFIER UnicodeString& text,
1272 U_NAMESPACE_QUALIFIER ParsePosition& pos,
1273 int8_t type,
1274 UChar* result,
1275 UErrorCode& ec)
1276 {
1277 U_NAMESPACE_USE
1278
1279 if (U_FAILURE(ec)) {
1280 return;
1281 }
1282
1283 int32_t total_currency_name_count = 0;
1284 CurrencyNameStruct* currencyNames = NULL;
1285 int32_t total_currency_symbol_count = 0;
1286 CurrencyNameStruct* currencySymbols = NULL;
1287 CurrencyNameCacheEntry* cacheEntry = NULL;
1288
1289 umtx_lock(NULL);
1290 // in order to handle racing correctly,
1291 // not putting 'search' in a separate function and using UMTX.
1292 int8_t found = -1;
1293 for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
1294 if (currCache[i]!= NULL &&
1295 uprv_strcmp(locale, currCache[i]->locale) == 0) {
1296 found = i;
1297 break;
1298 }
1299 }
1300 if (found != -1) {
1301 cacheEntry = currCache[found];
1302 currencyNames = cacheEntry->currencyNames;
1303 total_currency_name_count = cacheEntry->totalCurrencyNameCount;
1304 currencySymbols = cacheEntry->currencySymbols;
1305 total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
1306 ++(cacheEntry->refCount);
1307 }
1308 umtx_unlock(NULL);
1309 if (found == -1) {
1310 collectCurrencyNames(locale, ¤cyNames, &total_currency_name_count, ¤cySymbols, &total_currency_symbol_count, ec);
1311 if (U_FAILURE(ec)) {
1312 return;
1313 }
1314 umtx_lock(NULL);
1315 // check again.
1316 int8_t found = -1;
1317 for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
1318 if (currCache[i]!= NULL &&
1319 uprv_strcmp(locale, currCache[i]->locale) == 0) {
1320 found = i;
1321 break;
1322 }
1323 }
1324 if (found == -1) {
1325 // insert new entry to
1326 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1327 // and remove the existing entry
1328 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1329 // from cache.
1330 cacheEntry = currCache[currentCacheEntryIndex];
1331 if (cacheEntry) {
1332 --(cacheEntry->refCount);
1333 // delete if the ref count is zero
1334 if (cacheEntry->refCount == 0) {
1335 deleteCacheEntry(cacheEntry);
1336 }
1337 }
1338 cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry));
1339 currCache[currentCacheEntryIndex] = cacheEntry;
1340 uprv_strcpy(cacheEntry->locale, locale);
1341 cacheEntry->currencyNames = currencyNames;
1342 cacheEntry->totalCurrencyNameCount = total_currency_name_count;
1343 cacheEntry->currencySymbols = currencySymbols;
1344 cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count;
1345 cacheEntry->refCount = 2; // one for cache, one for reference
1346 currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM;
1347 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cache_cleanup);
1348
1349 } else {
1350 deleteCurrencyNames(currencyNames, total_currency_name_count);
1351 deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
1352 cacheEntry = currCache[found];
1353 currencyNames = cacheEntry->currencyNames;
1354 total_currency_name_count = cacheEntry->totalCurrencyNameCount;
1355 currencySymbols = cacheEntry->currencySymbols;
1356 total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
1357 ++(cacheEntry->refCount);
1358 }
1359 umtx_unlock(NULL);
1360 }
1361
1362 int32_t start = pos.getIndex();
1363
1364 UChar inputText[MAX_CURRENCY_NAME_LEN];
1365 UChar upperText[MAX_CURRENCY_NAME_LEN];
1366 int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start);
1367 text.extract(start, textLen, inputText);
1368 UErrorCode ec1 = U_ZERO_ERROR;
1369 textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, NULL, &ec1);
1370
1371 int32_t max = 0;
1372 int32_t matchIndex = -1;
1373 // case in-sensitive comparision against currency names
1374 searchCurrencyName(currencyNames, total_currency_name_count,
1375 upperText, textLen, &max, &matchIndex);
1376
1377 #ifdef UCURR_DEBUG
1378 printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
1379 #endif
1380
1381 int32_t maxInSymbol = 0;
1382 int32_t matchIndexInSymbol = -1;
1383 if (type != UCURR_LONG_NAME) { // not name only
1384 // case sensitive comparison against currency symbols and ISO code.
1385 searchCurrencyName(currencySymbols, total_currency_symbol_count,
1386 inputText, textLen,
1387 &maxInSymbol, &matchIndexInSymbol);
1388 }
1389
1390 #ifdef UCURR_DEBUG
1391 printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol);
1392 #endif
1393
1394 if (max >= maxInSymbol && matchIndex != -1) {
1395 u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4);
1396 pos.setIndex(start + max);
1397 } else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
1398 u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
1399 pos.setIndex(start + maxInSymbol);
1400 }
1401
1402 // decrease reference count
1403 umtx_lock(NULL);
1404 --(cacheEntry->refCount);
1405 if (cacheEntry->refCount == 0) { // remove
1406 deleteCacheEntry(cacheEntry);
1407 }
1408 umtx_unlock(NULL);
1409 }
1410
1411
1412 /**
1413 * Internal method. Given a currency ISO code and a locale, return
1414 * the "static" currency name. This is usually the same as the
1415 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
1416 * format is applied to the number 2.0 (to yield the more common
1417 * plural) to return a static name.
1418 *
1419 * This is used for backward compatibility with old currency logic in
1420 * DecimalFormat and DecimalFormatSymbols.
1421 */
1422 U_CFUNC void
uprv_getStaticCurrencyName(const UChar * iso,const char * loc,U_NAMESPACE_QUALIFIER UnicodeString & result,UErrorCode & ec)1423 uprv_getStaticCurrencyName(const UChar* iso, const char* loc,
1424 U_NAMESPACE_QUALIFIER UnicodeString& result, UErrorCode& ec)
1425 {
1426 U_NAMESPACE_USE
1427
1428 UBool isChoiceFormat;
1429 int32_t len;
1430 const UChar* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME,
1431 &isChoiceFormat, &len, &ec);
1432 if (U_SUCCESS(ec)) {
1433 // If this is a ChoiceFormat currency, then format an
1434 // arbitrary value; pick something != 1; more common.
1435 result.truncate(0);
1436 if (isChoiceFormat) {
1437 ChoiceFormat f(currname, ec);
1438 if (U_SUCCESS(ec)) {
1439 f.format(2.0, result);
1440 } else {
1441 result = iso;
1442 }
1443 } else {
1444 result = currname;
1445 }
1446 }
1447 }
1448
1449 U_CAPI int32_t U_EXPORT2
ucurr_getDefaultFractionDigits(const UChar * currency,UErrorCode * ec)1450 ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) {
1451 return (_findMetaData(currency, *ec))[0];
1452 }
1453
1454 U_CAPI double U_EXPORT2
ucurr_getRoundingIncrement(const UChar * currency,UErrorCode * ec)1455 ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) {
1456 const int32_t *data = _findMetaData(currency, *ec);
1457
1458 // If the meta data is invalid, return 0.0.
1459 if (data[0] < 0 || data[0] > MAX_POW10) {
1460 if (U_SUCCESS(*ec)) {
1461 *ec = U_INVALID_FORMAT_ERROR;
1462 }
1463 return 0.0;
1464 }
1465
1466 // If there is no rounding, return 0.0 to indicate no rounding. A
1467 // rounding value (data[1]) of 0 or 1 indicates no rounding.
1468 if (data[1] < 2) {
1469 return 0.0;
1470 }
1471
1472 // Return data[1] / 10^(data[0]). The only actual rounding data,
1473 // as of this writing, is CHF { 2, 5 }.
1474 return double(data[1]) / POW10[data[0]];
1475 }
1476
1477 U_CDECL_BEGIN
1478
1479 typedef struct UCurrencyContext {
1480 uint32_t currType; /* UCurrCurrencyType */
1481 uint32_t listIdx;
1482 } UCurrencyContext;
1483
1484 /*
1485 Please keep this list in alphabetical order.
1486 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
1487 of these items.
1488 ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
1489 */
1490 static const struct CurrencyList {
1491 const char *currency;
1492 uint32_t currType;
1493 } gCurrencyList[] = {
1494 {"ADP", UCURR_COMMON|UCURR_DEPRECATED},
1495 {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED},
1496 {"AFA", UCURR_COMMON|UCURR_DEPRECATED},
1497 {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1498 {"ALK", UCURR_COMMON|UCURR_DEPRECATED},
1499 {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1500 {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1501 {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1502 {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED},
1503 {"AOK", UCURR_COMMON|UCURR_DEPRECATED},
1504 {"AON", UCURR_COMMON|UCURR_DEPRECATED},
1505 {"AOR", UCURR_COMMON|UCURR_DEPRECATED},
1506 {"ARA", UCURR_COMMON|UCURR_DEPRECATED},
1507 {"ARL", UCURR_COMMON|UCURR_DEPRECATED},
1508 {"ARM", UCURR_COMMON|UCURR_DEPRECATED},
1509 {"ARP", UCURR_COMMON|UCURR_DEPRECATED},
1510 {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1511 {"ATS", UCURR_COMMON|UCURR_DEPRECATED},
1512 {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1513 {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1514 {"AZM", UCURR_COMMON|UCURR_DEPRECATED},
1515 {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1516 {"BAD", UCURR_COMMON|UCURR_DEPRECATED},
1517 {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED},
1518 {"BAN", UCURR_COMMON|UCURR_DEPRECATED},
1519 {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1520 {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1521 {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED},
1522 {"BEF", UCURR_COMMON|UCURR_DEPRECATED},
1523 {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED},
1524 {"BGL", UCURR_COMMON|UCURR_DEPRECATED},
1525 {"BGM", UCURR_COMMON|UCURR_DEPRECATED},
1526 {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1527 {"BGO", UCURR_COMMON|UCURR_DEPRECATED},
1528 {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1529 {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1530 {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1531 {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED},
1532 {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1533 {"BOL", UCURR_COMMON|UCURR_DEPRECATED},
1534 {"BOP", UCURR_COMMON|UCURR_DEPRECATED},
1535 {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1536 {"BRB", UCURR_COMMON|UCURR_DEPRECATED},
1537 {"BRC", UCURR_COMMON|UCURR_DEPRECATED},
1538 {"BRE", UCURR_COMMON|UCURR_DEPRECATED},
1539 {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1540 {"BRN", UCURR_COMMON|UCURR_DEPRECATED},
1541 {"BRR", UCURR_COMMON|UCURR_DEPRECATED},
1542 {"BRZ", UCURR_COMMON|UCURR_DEPRECATED},
1543 {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1544 {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1545 {"BUK", UCURR_COMMON|UCURR_DEPRECATED},
1546 {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1547 {"BYB", UCURR_COMMON|UCURR_DEPRECATED},
1548 {"BYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1549 {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1550 {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1551 {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1552 {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1553 {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1554 {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1555 {"CLE", UCURR_COMMON|UCURR_DEPRECATED},
1556 {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1557 {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1558 {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED},
1559 {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED},
1560 {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1561 {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1562 {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED},
1563 {"CSD", UCURR_COMMON|UCURR_DEPRECATED},
1564 {"CSK", UCURR_COMMON|UCURR_DEPRECATED},
1565 {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED},
1566 {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1567 {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED},
1568 {"CYP", UCURR_COMMON|UCURR_DEPRECATED},
1569 {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1570 {"DDM", UCURR_COMMON|UCURR_DEPRECATED},
1571 {"DEM", UCURR_COMMON|UCURR_DEPRECATED},
1572 {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1573 {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1574 {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1575 {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1576 {"ECS", UCURR_COMMON|UCURR_DEPRECATED},
1577 {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED},
1578 {"EEK", UCURR_COMMON|UCURR_DEPRECATED},
1579 {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1580 {"EQE", UCURR_COMMON|UCURR_DEPRECATED},
1581 {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1582 {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED},
1583 {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED},
1584 {"ESP", UCURR_COMMON|UCURR_DEPRECATED},
1585 {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1586 {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1587 {"FIM", UCURR_COMMON|UCURR_DEPRECATED},
1588 {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1589 {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1590 {"FRF", UCURR_COMMON|UCURR_DEPRECATED},
1591 {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1592 {"GEK", UCURR_COMMON|UCURR_DEPRECATED},
1593 {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1594 {"GHC", UCURR_COMMON|UCURR_DEPRECATED},
1595 {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1596 {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1597 {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1598 {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1599 {"GNS", UCURR_COMMON|UCURR_DEPRECATED},
1600 {"GQE", UCURR_COMMON|UCURR_DEPRECATED},
1601 {"GRD", UCURR_COMMON|UCURR_DEPRECATED},
1602 {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED},
1603 {"GWE", UCURR_COMMON|UCURR_DEPRECATED},
1604 {"GWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1605 {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1606 {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1607 {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1608 {"HRD", UCURR_COMMON|UCURR_DEPRECATED},
1609 {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1610 {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1611 {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1612 {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1613 {"IEP", UCURR_COMMON|UCURR_DEPRECATED},
1614 {"ILP", UCURR_COMMON|UCURR_DEPRECATED},
1615 {"ILR", UCURR_COMMON|UCURR_DEPRECATED},
1616 {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1617 {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1618 {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1619 {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1620 {"ISJ", UCURR_COMMON|UCURR_DEPRECATED},
1621 {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1622 {"ITL", UCURR_COMMON|UCURR_DEPRECATED},
1623 {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1624 {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1625 {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED},
1626 {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED},
1627 {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1628 {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1629 {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1630 {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED},
1631 {"KRH", UCURR_COMMON|UCURR_DEPRECATED},
1632 {"KRO", UCURR_COMMON|UCURR_DEPRECATED},
1633 {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED},
1634 {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1635 {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1636 {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1637 {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1638 {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1639 {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1640 {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1641 {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1642 {"LSM", UCURR_COMMON|UCURR_DEPRECATED},
1643 {"LTL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1644 {"LTT", UCURR_COMMON|UCURR_DEPRECATED},
1645 {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED},
1646 {"LUF", UCURR_COMMON|UCURR_DEPRECATED},
1647 {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED},
1648 {"LVL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1649 {"LVR", UCURR_COMMON|UCURR_DEPRECATED},
1650 {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1651 {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1652 {"MAF", UCURR_COMMON|UCURR_DEPRECATED},
1653 {"MCF", UCURR_COMMON|UCURR_DEPRECATED},
1654 {"MDC", UCURR_COMMON|UCURR_DEPRECATED},
1655 {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1656 {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED},
1657 {"MGF", UCURR_COMMON|UCURR_DEPRECATED},
1658 {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1659 {"MKN", UCURR_COMMON|UCURR_DEPRECATED},
1660 {"MLF", UCURR_COMMON|UCURR_DEPRECATED},
1661 {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1662 {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1663 {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1664 {"MRO", UCURR_COMMON|UCURR_NON_DEPRECATED},
1665 {"MTL", UCURR_COMMON|UCURR_DEPRECATED},
1666 {"MTP", UCURR_COMMON|UCURR_DEPRECATED},
1667 {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1668 {"MVP", UCURR_COMMON|UCURR_DEPRECATED},
1669 {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1670 {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1671 {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1672 {"MXP", UCURR_COMMON|UCURR_DEPRECATED},
1673 {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1674 {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1675 {"MZE", UCURR_COMMON|UCURR_NON_DEPRECATED},
1676 {"MZM", UCURR_COMMON|UCURR_DEPRECATED},
1677 {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1678 {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1679 {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1680 {"NIC", UCURR_COMMON|UCURR_DEPRECATED},
1681 {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED},
1682 {"NLG", UCURR_COMMON|UCURR_DEPRECATED},
1683 {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1684 {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1685 {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1686 {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1687 {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1688 {"PEI", UCURR_COMMON|UCURR_DEPRECATED},
1689 {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1690 {"PES", UCURR_COMMON|UCURR_DEPRECATED},
1691 {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1692 {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1693 {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1694 {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1695 {"PLZ", UCURR_COMMON|UCURR_DEPRECATED},
1696 {"PTE", UCURR_COMMON|UCURR_DEPRECATED},
1697 {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1698 {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1699 {"RHD", UCURR_COMMON|UCURR_DEPRECATED},
1700 {"ROL", UCURR_COMMON|UCURR_DEPRECATED},
1701 {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED},
1702 {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1703 {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1704 {"RUR", UCURR_COMMON|UCURR_DEPRECATED},
1705 {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1706 {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1707 {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1708 {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1709 {"SDD", UCURR_COMMON|UCURR_DEPRECATED},
1710 {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1711 {"SDP", UCURR_COMMON|UCURR_DEPRECATED},
1712 {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1713 {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1714 {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1715 {"SIT", UCURR_COMMON|UCURR_DEPRECATED},
1716 {"SKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1717 {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1718 {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1719 {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1720 {"SRG", UCURR_COMMON|UCURR_DEPRECATED},
1721 {"STD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1722 {"SUR", UCURR_COMMON|UCURR_DEPRECATED},
1723 {"SVC", UCURR_COMMON|UCURR_NON_DEPRECATED},
1724 {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1725 {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1726 {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1727 {"TJR", UCURR_COMMON|UCURR_DEPRECATED},
1728 {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1729 {"TMM", UCURR_COMMON|UCURR_DEPRECATED},
1730 {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1731 {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED},
1732 {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1733 {"TPE", UCURR_COMMON|UCURR_DEPRECATED},
1734 {"TRL", UCURR_COMMON|UCURR_DEPRECATED},
1735 {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED},
1736 {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1737 {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1738 {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1739 {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED},
1740 {"UAK", UCURR_COMMON|UCURR_DEPRECATED},
1741 {"UGS", UCURR_COMMON|UCURR_DEPRECATED},
1742 {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED},
1743 {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1744 {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1745 {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1746 {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1747 {"UYP", UCURR_COMMON|UCURR_DEPRECATED},
1748 {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED},
1749 {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1750 {"VEB", UCURR_COMMON|UCURR_DEPRECATED},
1751 {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1752 {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED},
1753 {"VNN", UCURR_COMMON|UCURR_DEPRECATED},
1754 {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED},
1755 {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED},
1756 {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1757 {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1758 {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1759 {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1760 {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1761 {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1762 {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1763 {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1764 {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1765 {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED},
1766 {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1767 {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1768 {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1769 {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1770 {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1771 {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1772 {"XRE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1773 {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1774 {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1775 {"YDD", UCURR_COMMON|UCURR_DEPRECATED},
1776 {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED},
1777 {"YUD", UCURR_COMMON|UCURR_DEPRECATED},
1778 {"YUM", UCURR_COMMON|UCURR_DEPRECATED},
1779 {"YUN", UCURR_COMMON|UCURR_DEPRECATED},
1780 {"YUR", UCURR_COMMON|UCURR_DEPRECATED},
1781 {"ZAL", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1782 {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1783 {"ZMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1784 {"ZRN", UCURR_COMMON|UCURR_DEPRECATED},
1785 {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED},
1786 {"ZWL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1787 {"ZWR", UCURR_COMMON|UCURR_DEPRECATED},
1788 {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
1789 { NULL, 0 } // Leave here to denote the end of the list.
1790 };
1791
1792 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
1793 ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
1794
1795 static int32_t U_CALLCONV
ucurr_countCurrencyList(UEnumeration * enumerator,UErrorCode *)1796 ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
1797 UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
1798 uint32_t currType = myContext->currType;
1799 int32_t count = 0;
1800
1801 /* Count the number of items matching the type we are looking for. */
1802 for (int32_t idx = 0; gCurrencyList[idx].currency != NULL; idx++) {
1803 if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) {
1804 count++;
1805 }
1806 }
1807 return count;
1808 }
1809
1810 static const char* U_CALLCONV
ucurr_nextCurrencyList(UEnumeration * enumerator,int32_t * resultLength,UErrorCode *)1811 ucurr_nextCurrencyList(UEnumeration *enumerator,
1812 int32_t* resultLength,
1813 UErrorCode * /*pErrorCode*/)
1814 {
1815 UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
1816
1817 /* Find the next in the list that matches the type we are looking for. */
1818 while (myContext->listIdx < (sizeof(gCurrencyList)/sizeof(gCurrencyList[0]))-1) {
1819 const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++];
1820 if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType))
1821 {
1822 if (resultLength) {
1823 *resultLength = 3; /* Currency codes are only 3 chars long */
1824 }
1825 return currItem->currency;
1826 }
1827 }
1828 /* We enumerated too far. */
1829 if (resultLength) {
1830 *resultLength = 0;
1831 }
1832 return NULL;
1833 }
1834
1835 static void U_CALLCONV
ucurr_resetCurrencyList(UEnumeration * enumerator,UErrorCode *)1836 ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
1837 ((UCurrencyContext *)(enumerator->context))->listIdx = 0;
1838 }
1839
1840 static void U_CALLCONV
ucurr_closeCurrencyList(UEnumeration * enumerator)1841 ucurr_closeCurrencyList(UEnumeration *enumerator) {
1842 uprv_free(enumerator->context);
1843 uprv_free(enumerator);
1844 }
1845
1846 static void U_CALLCONV
ucurr_createCurrencyList(UErrorCode * status)1847 ucurr_createCurrencyList(UErrorCode* status){
1848 UErrorCode localStatus = U_ZERO_ERROR;
1849
1850 // Look up the CurrencyMap element in the root bundle.
1851 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
1852 UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
1853
1854 if (U_SUCCESS(localStatus)) {
1855 // process each entry in currency map
1856 for (int32_t i=0; i<ures_getSize(currencyMapArray); i++) {
1857 // get the currency resource
1858 UResourceBundle *currencyArray = ures_getByIndex(currencyMapArray, i, NULL, &localStatus);
1859 // process each currency
1860 if (U_SUCCESS(localStatus)) {
1861 for (int32_t j=0; j<ures_getSize(currencyArray); j++) {
1862 // get the currency resource
1863 UResourceBundle *currencyRes = ures_getByIndex(currencyArray, j, NULL, &localStatus);
1864 IsoCodeEntry *entry = (IsoCodeEntry*)uprv_malloc(sizeof(IsoCodeEntry));
1865 if (entry == NULL) {
1866 *status = U_MEMORY_ALLOCATION_ERROR;
1867 return;
1868 }
1869
1870 // get the ISO code
1871 int32_t isoLength = 0;
1872 UResourceBundle *idRes = ures_getByKey(currencyRes, "id", NULL, &localStatus);
1873 if (idRes == NULL) {
1874 continue;
1875 }
1876 const UChar *isoCode = ures_getString(idRes, &isoLength, &localStatus);
1877
1878 // get the from date
1879 int32_t fromLength = 0;
1880 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
1881 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
1882 int64_t currDate64 = (int64_t)fromArray[0] << 32;
1883 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
1884 UDate fromDate = (UDate)currDate64;
1885 UDate toDate = U_DATE_MAX;
1886
1887 if (ures_getSize(currencyRes)> 2) {
1888 int32_t toLength = 0;
1889 UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
1890 const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
1891
1892 currDate64 = (int64_t)toArray[0] << 32;
1893 currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
1894 toDate = (UDate)currDate64;
1895
1896 ures_close(toRes);
1897 }
1898
1899 ures_close(fromRes);
1900 ures_close(idRes);
1901 ures_close(currencyRes);
1902
1903 entry->isoCode = isoCode;
1904 entry->from = fromDate;
1905 entry->to = toDate;
1906
1907 uhash_put(gIsoCodes, (UChar *)isoCode, entry, &localStatus);
1908 }
1909 } else {
1910 *status = localStatus;
1911 }
1912 ures_close(currencyArray);
1913 }
1914 } else {
1915 *status = localStatus;
1916 }
1917
1918 ures_close(currencyMapArray);
1919 }
1920
1921 static const UEnumeration gEnumCurrencyList = {
1922 NULL,
1923 NULL,
1924 ucurr_closeCurrencyList,
1925 ucurr_countCurrencyList,
1926 uenum_unextDefault,
1927 ucurr_nextCurrencyList,
1928 ucurr_resetCurrencyList
1929 };
1930 U_CDECL_END
1931
1932 U_CAPI UBool U_EXPORT2
ucurr_isAvailable(const UChar * isoCode,UDate from,UDate to,UErrorCode * eErrorCode)1933 ucurr_isAvailable(const UChar* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) {
1934 UErrorCode status = U_ZERO_ERROR;
1935 UBool initialized;
1936 UMTX_CHECK(&gIsoCodesLock, gIsoCodesInitialized, initialized);
1937
1938 if (!initialized) {
1939 umtx_lock(&gIsoCodesLock);
1940 gIsoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
1941 if (U_FAILURE(status)) {
1942 umtx_unlock(&gIsoCodesLock);
1943 return FALSE;
1944 }
1945 uhash_setValueDeleter(gIsoCodes, deleteIsoCodeEntry);
1946
1947 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
1948
1949 ucurr_createCurrencyList(&status);
1950 if (U_FAILURE(status)) {
1951 umtx_unlock(&gIsoCodesLock);
1952 return FALSE;
1953 }
1954
1955 gIsoCodesInitialized = TRUE;
1956 umtx_unlock(&gIsoCodesLock);
1957 }
1958
1959 umtx_lock(&gIsoCodesLock);
1960 IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode);
1961 umtx_unlock(&gIsoCodesLock);
1962
1963 if (result == NULL) {
1964 return FALSE;
1965 } else if (from > to) {
1966 *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
1967 return FALSE;
1968 } else if ((from > result->to) || (to < result->from)) {
1969 return FALSE;
1970 }
1971
1972 return TRUE;
1973 }
1974
1975 U_CAPI UEnumeration * U_EXPORT2
ucurr_openISOCurrencies(uint32_t currType,UErrorCode * pErrorCode)1976 ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) {
1977 UEnumeration *myEnum = NULL;
1978 UCurrencyContext *myContext;
1979
1980 myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration));
1981 if (myEnum == NULL) {
1982 *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
1983 return NULL;
1984 }
1985 uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration));
1986 myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext));
1987 if (myContext == NULL) {
1988 *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
1989 uprv_free(myEnum);
1990 return NULL;
1991 }
1992 myContext->currType = currType;
1993 myContext->listIdx = 0;
1994 myEnum->context = myContext;
1995 return myEnum;
1996 }
1997
1998 U_CAPI int32_t U_EXPORT2
ucurr_countCurrencies(const char * locale,UDate date,UErrorCode * ec)1999 ucurr_countCurrencies(const char* locale,
2000 UDate date,
2001 UErrorCode* ec)
2002 {
2003 int32_t currCount = 0;
2004 int32_t resLen = 0;
2005
2006 if (ec != NULL && U_SUCCESS(*ec))
2007 {
2008 // local variables
2009 UErrorCode localStatus = U_ZERO_ERROR;
2010 char id[ULOC_FULLNAME_CAPACITY];
2011 resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
2012 // get country or country_variant in `id'
2013 /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
2014
2015 if (U_FAILURE(*ec))
2016 {
2017 return 0;
2018 }
2019
2020 // Remove variants, which is only needed for registration.
2021 char *idDelim = strchr(id, VAR_DELIM);
2022 if (idDelim)
2023 {
2024 idDelim[0] = 0;
2025 }
2026
2027 // Look up the CurrencyMap element in the root bundle.
2028 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
2029 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
2030
2031 // Using the id derived from the local, get the currency data
2032 UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
2033
2034 // process each currency to see which one is valid for the given date
2035 if (U_SUCCESS(localStatus))
2036 {
2037 for (int32_t i=0; i<ures_getSize(countryArray); i++)
2038 {
2039 // get the currency resource
2040 UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus);
2041
2042 // get the from date
2043 int32_t fromLength = 0;
2044 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
2045 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
2046
2047 int64_t currDate64 = (int64_t)fromArray[0] << 32;
2048 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2049 UDate fromDate = (UDate)currDate64;
2050
2051 if (ures_getSize(currencyRes)> 2)
2052 {
2053 int32_t toLength = 0;
2054 UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
2055 const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
2056
2057 currDate64 = (int64_t)toArray[0] << 32;
2058 currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2059 UDate toDate = (UDate)currDate64;
2060
2061 if ((fromDate <= date) && (date < toDate))
2062 {
2063 currCount++;
2064 }
2065
2066 ures_close(toRes);
2067 }
2068 else
2069 {
2070 if (fromDate <= date)
2071 {
2072 currCount++;
2073 }
2074 }
2075
2076 // close open resources
2077 ures_close(currencyRes);
2078 ures_close(fromRes);
2079
2080 } // end For loop
2081 } // end if (U_SUCCESS(localStatus))
2082
2083 ures_close(countryArray);
2084
2085 // Check for errors
2086 if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
2087 {
2088 // There is nothing to fallback to.
2089 // Report the failure/warning if possible.
2090 *ec = localStatus;
2091 }
2092
2093 if (U_SUCCESS(*ec))
2094 {
2095 // no errors
2096 return currCount;
2097 }
2098
2099 }
2100
2101 // If we got here, either error code is invalid or
2102 // some argument passed is no good.
2103 return 0;
2104 }
2105
2106 U_CAPI int32_t U_EXPORT2
ucurr_forLocaleAndDate(const char * locale,UDate date,int32_t index,UChar * buff,int32_t buffCapacity,UErrorCode * ec)2107 ucurr_forLocaleAndDate(const char* locale,
2108 UDate date,
2109 int32_t index,
2110 UChar* buff,
2111 int32_t buffCapacity,
2112 UErrorCode* ec)
2113 {
2114 int32_t resLen = 0;
2115 int32_t currIndex = 0;
2116 const UChar* s = NULL;
2117
2118 if (ec != NULL && U_SUCCESS(*ec))
2119 {
2120 // check the arguments passed
2121 if ((buff && buffCapacity) || !buffCapacity )
2122 {
2123 // local variables
2124 UErrorCode localStatus = U_ZERO_ERROR;
2125 char id[ULOC_FULLNAME_CAPACITY];
2126 resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
2127
2128 // get country or country_variant in `id'
2129 /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
2130 if (U_FAILURE(*ec))
2131 {
2132 return 0;
2133 }
2134
2135 // Remove variants, which is only needed for registration.
2136 char *idDelim = strchr(id, VAR_DELIM);
2137 if (idDelim)
2138 {
2139 idDelim[0] = 0;
2140 }
2141
2142 // Look up the CurrencyMap element in the root bundle.
2143 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
2144 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
2145
2146 // Using the id derived from the local, get the currency data
2147 UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
2148
2149 // process each currency to see which one is valid for the given date
2150 bool matchFound = false;
2151 if (U_SUCCESS(localStatus))
2152 {
2153 if ((index <= 0) || (index> ures_getSize(countryArray)))
2154 {
2155 // requested index is out of bounds
2156 ures_close(countryArray);
2157 return 0;
2158 }
2159
2160 for (int32_t i=0; i<ures_getSize(countryArray); i++)
2161 {
2162 // get the currency resource
2163 UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus);
2164 s = ures_getStringByKey(currencyRes, "id", &resLen, &localStatus);
2165
2166 // get the from date
2167 int32_t fromLength = 0;
2168 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
2169 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
2170
2171 int64_t currDate64 = (int64_t)fromArray[0] << 32;
2172 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2173 UDate fromDate = (UDate)currDate64;
2174
2175 if (ures_getSize(currencyRes)> 2)
2176 {
2177 int32_t toLength = 0;
2178 UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
2179 const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
2180
2181 currDate64 = (int64_t)toArray[0] << 32;
2182 currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2183 UDate toDate = (UDate)currDate64;
2184
2185 if ((fromDate <= date) && (date < toDate))
2186 {
2187 currIndex++;
2188 if (currIndex == index)
2189 {
2190 matchFound = true;
2191 }
2192 }
2193
2194 ures_close(toRes);
2195 }
2196 else
2197 {
2198 if (fromDate <= date)
2199 {
2200 currIndex++;
2201 if (currIndex == index)
2202 {
2203 matchFound = true;
2204 }
2205 }
2206 }
2207
2208 // close open resources
2209 ures_close(currencyRes);
2210 ures_close(fromRes);
2211
2212 // check for loop exit
2213 if (matchFound)
2214 {
2215 break;
2216 }
2217
2218 } // end For loop
2219 }
2220
2221 ures_close(countryArray);
2222
2223 // Check for errors
2224 if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
2225 {
2226 // There is nothing to fallback to.
2227 // Report the failure/warning if possible.
2228 *ec = localStatus;
2229 }
2230
2231 if (U_SUCCESS(*ec))
2232 {
2233 // no errors
2234 if((buffCapacity> resLen) && matchFound)
2235 {
2236 // write out the currency value
2237 u_strcpy(buff, s);
2238 }
2239 else
2240 {
2241 return 0;
2242 }
2243 }
2244
2245 // return null terminated currency string
2246 return u_terminateUChars(buff, buffCapacity, resLen, ec);
2247 }
2248 else
2249 {
2250 // illegal argument encountered
2251 *ec = U_ILLEGAL_ARGUMENT_ERROR;
2252 }
2253
2254 }
2255
2256 // If we got here, either error code is invalid or
2257 // some argument passed is no good.
2258 return resLen;
2259 }
2260
2261 static const UEnumeration defaultKeywordValues = {
2262 NULL,
2263 NULL,
2264 ulist_close_keyword_values_iterator,
2265 ulist_count_keyword_values,
2266 uenum_unextDefault,
2267 ulist_next_keyword_value,
2268 ulist_reset_keyword_values_iterator
2269 };
2270
ucurr_getKeywordValuesForLocale(const char * key,const char * locale,UBool commonlyUsed,UErrorCode * status)2271 U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) {
2272 // Resolve region
2273 char prefRegion[ULOC_FULLNAME_CAPACITY] = "";
2274 int32_t prefRegionLength = 0;
2275 prefRegionLength = uloc_getCountry(locale, prefRegion, sizeof(prefRegion), status);
2276 if (prefRegionLength == 0) {
2277 char loc[ULOC_FULLNAME_CAPACITY] = "";
2278 int32_t locLength = 0;
2279 locLength = uloc_addLikelySubtags(locale, loc, sizeof(loc), status);
2280
2281 prefRegionLength = uloc_getCountry(loc, prefRegion, sizeof(prefRegion), status);
2282 }
2283
2284 // Read value from supplementalData
2285 UList *values = ulist_createEmptyList(status);
2286 UList *otherValues = ulist_createEmptyList(status);
2287 UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2288 if (U_FAILURE(*status) || en == NULL) {
2289 if (en == NULL) {
2290 *status = U_MEMORY_ALLOCATION_ERROR;
2291 } else {
2292 uprv_free(en);
2293 }
2294 ulist_deleteList(values);
2295 ulist_deleteList(otherValues);
2296 return NULL;
2297 }
2298 memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
2299 en->context = values;
2300
2301 UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status);
2302 ures_getByKey(bundle, "CurrencyMap", bundle, status);
2303 UResourceBundle bundlekey, regbndl, curbndl, to;
2304 ures_initStackObject(&bundlekey);
2305 ures_initStackObject(®bndl);
2306 ures_initStackObject(&curbndl);
2307 ures_initStackObject(&to);
2308
2309 while (U_SUCCESS(*status) && ures_hasNext(bundle)) {
2310 ures_getNextResource(bundle, &bundlekey, status);
2311 if (U_FAILURE(*status)) {
2312 break;
2313 }
2314 const char *region = ures_getKey(&bundlekey);
2315 UBool isPrefRegion = uprv_strcmp(region, prefRegion) == 0 ? TRUE : FALSE;
2316 if (!isPrefRegion && commonlyUsed) {
2317 // With commonlyUsed=true, we do not put
2318 // currencies for other regions in the
2319 // result list.
2320 continue;
2321 }
2322 ures_getByKey(bundle, region, ®bndl, status);
2323 if (U_FAILURE(*status)) {
2324 break;
2325 }
2326 while (U_SUCCESS(*status) && ures_hasNext(®bndl)) {
2327 ures_getNextResource(®bndl, &curbndl, status);
2328 if (ures_getType(&curbndl) != URES_TABLE) {
2329 // Currently, an empty ARRAY is mixed in.
2330 continue;
2331 }
2332 char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
2333 int32_t curIDLength = ULOC_KEYWORDS_CAPACITY;
2334 if (curID == NULL) {
2335 *status = U_MEMORY_ALLOCATION_ERROR;
2336 break;
2337 }
2338
2339 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
2340 ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, TRUE, status);
2341 /* optimize - use the utf-8 string */
2342 #else
2343 {
2344 const UChar* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status);
2345 if(U_SUCCESS(*status)) {
2346 if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) {
2347 *status = U_BUFFER_OVERFLOW_ERROR;
2348 } else {
2349 u_UCharsToChars(defString, curID, curIDLength+1);
2350 }
2351 }
2352 }
2353 #endif
2354
2355 if (U_FAILURE(*status)) {
2356 break;
2357 }
2358 UBool hasTo = FALSE;
2359 ures_getByKey(&curbndl, "to", &to, status);
2360 if (U_FAILURE(*status)) {
2361 // Do nothing here...
2362 *status = U_ZERO_ERROR;
2363 } else {
2364 hasTo = TRUE;
2365 }
2366 if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) {
2367 // Currently active currency for the target country
2368 ulist_addItemEndList(values, curID, TRUE, status);
2369 } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) {
2370 ulist_addItemEndList(otherValues, curID, TRUE, status);
2371 } else {
2372 uprv_free(curID);
2373 }
2374 }
2375
2376 }
2377 if (U_SUCCESS(*status)) {
2378 if (commonlyUsed) {
2379 if (ulist_getListSize(values) == 0) {
2380 // This could happen if no valid region is supplied in the input
2381 // locale. In this case, we use the CLDR's default.
2382 uenum_close(en);
2383 en = ucurr_getKeywordValuesForLocale(key, "und", TRUE, status);
2384 }
2385 } else {
2386 // Consolidate the list
2387 char *value = NULL;
2388 ulist_resetList(otherValues);
2389 while ((value = (char *)ulist_getNext(otherValues)) != NULL) {
2390 if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) {
2391 char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
2392 uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1);
2393 ulist_addItemEndList(values, tmpValue, TRUE, status);
2394 if (U_FAILURE(*status)) {
2395 break;
2396 }
2397 }
2398 }
2399 }
2400
2401 ulist_resetList((UList *)(en->context));
2402 } else {
2403 ulist_deleteList(values);
2404 uprv_free(en);
2405 values = NULL;
2406 en = NULL;
2407 }
2408 ures_close(&to);
2409 ures_close(&curbndl);
2410 ures_close(®bndl);
2411 ures_close(&bundlekey);
2412 ures_close(bundle);
2413
2414 ulist_deleteList(otherValues);
2415
2416 return en;
2417 }
2418
2419 #endif /* #if !UCONFIG_NO_FORMATTING */
2420
2421 //eof
2422