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