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