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