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