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