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