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