intl/icu/source/i18n/ucurr.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial