1 /* 2 ********************************************************************** 3 * Copyright (c) 2002-2010, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ********************************************************************** 6 */ 7 8 #include "unicode/utypes.h" 9 10 #if !UCONFIG_NO_FORMATTING 11 12 #include "unicode/ucurr.h" 13 #include "unicode/locid.h" 14 #include "unicode/ures.h" 15 #include "unicode/ustring.h" 16 #include "unicode/choicfmt.h" 17 #include "unicode/parsepos.h" 18 #include "ustr_imp.h" 19 #include "cmemory.h" 20 #include "cstring.h" 21 #include "uassert.h" 22 #include "umutex.h" 23 #include "ucln_in.h" 24 #include "uenumimp.h" 25 #include "uhash.h" 26 #include "uresimp.h" 27 #include "ulist.h" 28 #include "ureslocs.h" 29 30 // #define UCURR_DEBUG 1 31 #ifdef UCURR_DEBUG 32 #include "stdio.h" 33 #endif 34 35 //------------------------------------------------------------ 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). 42 static const int32_t LAST_RESORT_DATA[] = { 2, 0 }; 43 44 // POW10[i] = 10^i, i=0..MAX_POW10 45 static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000, 46 1000000, 10000000, 100000000, 1000000000 }; 47 48 static const int32_t MAX_POW10 = (sizeof(POW10)/sizeof(POW10[0])) - 1; 49 50 #define ISO_COUNTRY_CODE_LENGTH 3 51 52 //------------------------------------------------------------ 53 // Resource tags 54 // 55 56 static const char CURRENCY_DATA[] = "supplementalData"; 57 // Tag for meta-data, in root. 58 static const char CURRENCY_META[] = "CurrencyMeta"; 59 60 // Tag for map from countries to currencies, in root. 61 static const char CURRENCY_MAP[] = "CurrencyMap"; 62 63 // Tag for default meta-data, in CURRENCY_META 64 static const char DEFAULT_META[] = "DEFAULT"; 65 66 // Variant for legacy pre-euro mapping in CurrencyMap 67 static const char VAR_PRE_EURO[] = "PREEURO"; 68 69 // Variant for legacy euro mapping in CurrencyMap 70 static const char VAR_EURO[] = "EURO"; 71 72 // Variant delimiter 73 static const char VAR_DELIM = '_'; 74 static const char VAR_DELIM_STR[] = "_"; 75 76 // Variant for legacy euro mapping in CurrencyMap 77 //static const char VAR_DELIM_EURO[] = "_EURO"; 78 79 #define VARIANT_IS_EMPTY 0 80 #define VARIANT_IS_EURO 0x1 81 #define VARIANT_IS_PREEURO 0x2 82 83 // Tag for localized display names (symbols) of currencies 84 static const char CURRENCIES[] = "Currencies"; 85 static const char CURRENCYPLURALS[] = "CurrencyPlurals"; 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. 91 static const UChar CHOICE_FORMAT_MARK = 0x003D; // Equals sign 92 93 static const UChar EUR_STR[] = {0x0045,0x0055,0x0052,0}; 94 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 */ 102 static inline char* 103 myUCharsToChars(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 */ 115 static const int32_t* 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 } 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.] 128 UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec); 129 UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec); 130 131 if (U_FAILURE(ec)) { 132 ures_close(currencyMeta); 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]; 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); 144 if (U_FAILURE(ec)) { 145 ures_close(currencyMeta); 146 ures_close(rb); 147 // Config/build error; return hard-coded defaults 148 return LAST_RESORT_DATA; 149 } 150 } 151 152 int32_t len; 153 const int32_t *data = ures_getIntVector(rb, &len, &ec); 154 if (U_FAILURE(ec) || len != 2) { 155 // Config/build error; return hard-coded defaults 156 if (U_SUCCESS(ec)) { 157 ec = U_INVALID_FORMAT_ERROR; 158 } 159 ures_close(currencyMeta); 160 ures_close(rb); 161 return LAST_RESORT_DATA; 162 } 163 164 ures_close(currencyMeta); 165 ures_close(rb); 166 return data; 167 } 168 169 // ------------------------------------- 170 171 /** 172 * @see VARIANT_IS_EURO 173 * @see VARIANT_IS_PREEURO 174 */ 175 static uint32_t 176 idForLocale(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 { 190 uprv_strcat(countryAndVariant, VAR_DELIM_STR); 191 uprv_strcat(countryAndVariant, variant); 192 } 193 } 194 return variantType; 195 } 196 197 // ------------------------------------------ 198 // 199 // Registration 200 // 201 //------------------------------------------- 202 203 // don't use ICUService since we don't need fallback 204 205 #if !UCONFIG_NO_SERVICE 206 U_CDECL_BEGIN 207 static UBool U_CALLCONV currency_cleanup(void); 208 U_CDECL_END 209 struct CReg; 210 211 static UMTX gCRegLock = 0; 212 static CReg* gCRegHead = 0; 213 214 struct CReg : public U_NAMESPACE_QUALIFIER UMemory { 215 CReg *next; 216 UChar iso[ISO_COUNTRY_CODE_LENGTH+1]; 217 char id[ULOC_FULLNAME_CAPACITY]; 218 219 CReg(const UChar* _iso, const char* _id) 220 : next(0) 221 { 222 int32_t len = (int32_t)uprv_strlen(_id); 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 } 231 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) { 237 umtx_lock(&gCRegLock); 238 if (!gCRegHead) { 239 /* register for the first time */ 240 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup); 241 } 242 n->next = gCRegHead; 243 gCRegHead = n; 244 umtx_unlock(&gCRegLock); 245 return n; 246 } 247 *status = U_MEMORY_ALLOCATION_ERROR; 248 } 249 return 0; 250 } 251 252 static UBool unreg(UCurrRegistryKey key) { 253 UBool found = FALSE; 254 umtx_lock(&gCRegLock); 255 256 CReg** p = &gCRegHead; 257 while (*p) { 258 if (*p == key) { 259 *p = ((CReg*)key)->next; 260 delete (CReg*)key; 261 found = TRUE; 262 break; 263 } 264 p = &((*p)->next); 265 } 266 267 umtx_unlock(&gCRegLock); 268 return found; 269 } 270 271 static const UChar* get(const char* id) { 272 const UChar* result = NULL; 273 umtx_lock(&gCRegLock); 274 CReg* p = gCRegHead; 275 276 /* register cleanup of the mutex */ 277 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup); 278 while (p) { 279 if (uprv_strcmp(id, p->id) == 0) { 280 result = p->iso; 281 break; 282 } 283 p = p->next; 284 } 285 umtx_unlock(&gCRegLock); 286 return result; 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 300 /** 301 * Release all static memory held by currency. 302 */ 303 /*The declaration here is needed so currency_cleanup(void) 304 * can call this function. 305 */ 306 static UBool U_CALLCONV 307 currency_cache_cleanup(void); 308 309 U_CDECL_BEGIN 310 static UBool U_CALLCONV currency_cleanup(void) { 311 #if !UCONFIG_NO_SERVICE 312 CReg::cleanup(); 313 #endif 314 /* 315 * There might be some cached currency data. 316 */ 317 currency_cache_cleanup(); 318 return TRUE; 319 } 320 U_CDECL_END 321 322 // ------------------------------------- 323 324 U_CAPI UCurrRegistryKey U_EXPORT2 325 ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status) 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 337 U_CAPI UBool U_EXPORT2 338 ucurr_unregister(UCurrRegistryKey key, UErrorCode* status) 339 { 340 if (status && U_SUCCESS(*status)) { 341 return CReg::unreg(key); 342 } 343 return FALSE; 344 } 345 #endif /* UCONFIG_NO_SERVICE */ 346 347 // ------------------------------------- 348 349 U_CAPI int32_t U_EXPORT2 350 ucurr_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; 357 if (ec != NULL && U_SUCCESS(*ec)) { 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) { 364 /* Normalize the currency keyword value to upper case. */ 365 T_CString_toUpperCase(id); 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 385 // Remove variants, which is only needed for registration. 386 char *idDelim = strchr(id, VAR_DELIM); 387 if (idDelim) { 388 idDelim[0] = 0; 389 } 390 391 // Look up the CurrencyMap element in the root bundle. 392 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); 393 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); 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); 416 417 if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0) 418 { 419 // We don't know about it. Check to see if we support the variant. 420 uloc_getParent(locale, id, sizeof(id), ec); 421 *ec = U_USING_FALLBACK_WARNING; 422 return ucurr_forLocale(id, buff, buffCapacity, ec); 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 } 433 } 434 return u_terminateUChars(buff, buffCapacity, resLen, ec); 435 } else { 436 *ec = U_ILLEGAL_ARGUMENT_ERROR; 437 } 438 } 439 return resLen; 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 */ 451 static UBool fallback(char *loc) { 452 if (!*loc) { 453 return FALSE; 454 } 455 UErrorCode status = U_ZERO_ERROR; 456 uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status); 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 468 U_CAPI const UChar* U_EXPORT2 469 ucurr_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 { 479 //| Currencies { 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 487 if (U_FAILURE(*ec)) { 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 } 496 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); 519 520 /* Normalize the keyword value to uppercase */ 521 T_CString_toUpperCase(buf); 522 523 const UChar* s = NULL; 524 ec2 = U_ZERO_ERROR; 525 UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2); 526 527 rb = ures_getByKey(rb, CURRENCIES, rb, &ec2); 528 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; 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...? 563 *ec = U_USING_DEFAULT_WARNING; 564 return currency; 565 } 566 567 U_CAPI const UChar* U_EXPORT2 568 ucurr_getPluralName(const UChar* currency, 569 const char* locale, 570 UBool* isChoiceFormat, 571 const char* pluralCount, 572 int32_t* len, // fillin 573 UErrorCode* ec) { 574 // Look up the Currencies resource for the given locale. The 575 // Currencies locale data looks like this: 576 //|en { 577 //| CurrencyPlurals { 578 //| USD{ 579 //| one{"US dollar"} 580 //| other{"US dollars"} 581 //| } 582 //| } 583 //|} 584 585 if (U_FAILURE(*ec)) { 586 return 0; 587 } 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) { 596 *ec = U_ILLEGAL_ARGUMENT_ERROR; 597 return 0; 598 } 599 600 char buf[ISO_COUNTRY_CODE_LENGTH+1]; 601 myUCharsToChars(buf, currency); 602 603 const UChar* s = NULL; 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 652 typedef 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. 670 static 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 } 690 691 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". 700 static void 701 getCurrencyNameCount(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); 708 for (;;) { 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 760 static UChar* 761 toUpperCase(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. 782 static void 783 collectCurrencyNames(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) { 820 ec2 = U_ZERO_ERROR; 821 // TODO: ures_openDirect 822 UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2); 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); 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 } 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) { 850 ChoiceFormat fmt(s, ec2); 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 } 908 } 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; 920 } 921 ures_close(names); 922 } 923 ures_close(curr_p); 924 ures_close(curr); 925 ures_close(rb); 926 927 if (!fallback(loc)) { 928 break; 929 } 930 } 931 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); 952 } 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. 973 static int32_t 974 binarySearch(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 1030 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. 1087 static void 1088 linearSearch(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. 1115 static void 1116 searchCurrencyName(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 ===================== 1166 typedef 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. 1186 static 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. 1189 static int8_t currentCacheEntryIndex = 0; 1190 1191 // Cache deletion 1192 static void 1193 deleteCurrencyNames(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 1203 static void 1204 deleteCacheEntry(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 1212 static UBool U_CALLCONV 1213 currency_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 1224 U_CFUNC void 1225 uprv_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, ¤cyNames, &total_currency_name_count, ¤cySymbols, &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; 1276 break; 1277 } 1278 } 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); 1313 } 1314 umtx_unlock(NULL); 1315 } 1316 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); 1364 } 1365 1366 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 */ 1377 U_CFUNC void 1378 uprv_getStaticCurrencyName(const UChar* iso, const char* loc, 1379 U_NAMESPACE_QUALIFIER UnicodeString& result, UErrorCode& ec) 1380 { 1381 U_NAMESPACE_USE 1382 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 } 1403 1404 U_CAPI int32_t U_EXPORT2 1405 ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) { 1406 return (_findMetaData(currency, *ec))[0]; 1407 } 1408 1409 U_CAPI double U_EXPORT2 1410 ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) { 1411 const int32_t *data = _findMetaData(currency, *ec); 1412 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) { 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 1432 U_CDECL_BEGIN 1433 1434 typedef struct UCurrencyContext { 1435 uint32_t currType; /* UCurrCurrencyType */ 1436 uint32_t listIdx; 1437 } UCurrencyContext; 1438 1439 /* 1440 Please keep this list in alphabetical order. 1441 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some 1442 of these items. 1443 ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html 1444 */ 1445 static 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}, 1453 {"ALK", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1462 {"ARL", UCURR_COMMON|UCURR_DEPRECATED}, 1463 {"ARM", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1473 {"BAN", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1480 {"BGM", UCURR_COMMON|UCURR_DEPRECATED}, 1481 {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1482 {"BGO", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1488 {"BOL", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1497 {"BRZ", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1510 {"CLE", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1518 {"CSD", UCURR_COMMON|UCURR_DEPRECATED}, 1519 {"CSK", UCURR_COMMON|UCURR_DEPRECATED}, 1520 {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1521 {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1522 {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1523 {"CYP", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1549 {"GHC", UCURR_COMMON|UCURR_DEPRECATED}, 1550 {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED}, 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}, 1570 {"ILR", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1575 {"ISJ", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1586 {"KRH", UCURR_COMMON|UCURR_DEPRECATED}, 1587 {"KRO", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1608 {"MCF", UCURR_COMMON|UCURR_DEPRECATED}, 1609 {"MDC", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1614 {"MKN", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1620 {"MTL", UCURR_COMMON|UCURR_DEPRECATED}, 1621 {"MTP", UCURR_COMMON|UCURR_DEPRECATED}, 1622 {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1623 {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1657 {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED}, 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}, 1664 {"SDD", UCURR_COMMON|UCURR_DEPRECATED}, 1665 {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED}, 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}, 1684 {"TMM", UCURR_COMMON|UCURR_DEPRECATED}, 1685 {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED}, 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}, 1701 {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, 1702 {"UYP", UCURR_COMMON|UCURR_DEPRECATED}, 1703 {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1704 {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1705 {"VEB", UCURR_COMMON|UCURR_DEPRECATED}, 1706 {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1707 {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1708 {"VNN", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1735 {"YUR", UCURR_COMMON|UCURR_DEPRECATED}, 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}, 1741 {"ZWL", UCURR_COMMON|UCURR_NON_DEPRECATED}, 1742 {"ZWR", UCURR_COMMON|UCURR_DEPRECATED}, 1743 {"ZWD", UCURR_COMMON|UCURR_DEPRECATED}, 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 1750 static int32_t U_CALLCONV 1751 ucurr_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 1765 static const char* U_CALLCONV 1766 ucurr_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 1790 static void U_CALLCONV 1791 ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) { 1792 ((UCurrencyContext *)(enumerator->context))->listIdx = 0; 1793 } 1794 1795 static void U_CALLCONV 1796 ucurr_closeCurrencyList(UEnumeration *enumerator) { 1797 uprv_free(enumerator->context); 1798 uprv_free(enumerator); 1799 } 1800 1801 static const UEnumeration gEnumCurrencyList = { 1802 NULL, 1803 NULL, 1804 ucurr_closeCurrencyList, 1805 ucurr_countCurrencyList, 1806 uenum_unextDefault, 1807 ucurr_nextCurrencyList, 1808 ucurr_resetCurrencyList 1809 }; 1810 U_CDECL_END 1811 1812 U_CAPI UEnumeration * U_EXPORT2 1813 ucurr_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 1835 U_CAPI int32_t U_EXPORT2 1836 ucurr_countCurrencies(const char* locale, 1837 UDate date, 1838 UErrorCode* ec) 1839 { 1840 int32_t currCount = 0; 1841 int32_t resLen = 0; 1842 1843 if (ec != NULL && U_SUCCESS(*ec)) 1844 { 1845 // local variables 1846 UErrorCode localStatus = U_ZERO_ERROR; 1847 char id[ULOC_FULLNAME_CAPACITY]; 1848 resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus); 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); 1878 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 } 1935 1936 } 1937 1938 // If we got here, either error code is invalid or 1939 // some argument passed is no good. 1940 return 0; 1941 } 1942 1943 U_CAPI int32_t U_EXPORT2 1944 ucurr_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 1955 if (ec != NULL && U_SUCCESS(*ec)) 1956 { 1957 // check the arguments passed 1958 if ((buff && buffCapacity) || !buffCapacity ) 1959 { 1960 // local variables 1961 UErrorCode localStatus = U_ZERO_ERROR; 1962 char id[ULOC_FULLNAME_CAPACITY]; 1963 resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus); 1964 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 { 1992 // requested index is out of bounds 1993 ures_close(countryArray); 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 2083 return u_terminateUChars(buff, buffCapacity, resLen, ec); 2084 } 2085 else 2086 { 2087 // illegal argument encountered 2088 *ec = U_ILLEGAL_ARGUMENT_ERROR; 2089 } 2090 2091 } 2092 2093 // If we got here, either error code is invalid or 2094 // some argument passed is no good. 2095 return resLen; 2096 } 2097 2098 static 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 2108 U_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(®bndl); 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, ®bndl, status); 2160 if (U_FAILURE(*status)) { 2161 break; 2162 } 2163 while (U_SUCCESS(*status) && ures_hasNext(®bndl)) { 2164 ures_getNextResource(®bndl, &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(®bndl); 2248 ures_close(&bundlekey); 2249 ures_close(bundle); 2250 2251 ulist_deleteList(otherValues); 2252 2253 return en; 2254 } 2255 2256 #endif /* #if !UCONFIG_NO_FORMATTING */ 2257 2258 //eof 2259