Thu, 22 Jan 2015 13:21:57 +0100
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, ¤cyNames, &total_currency_name_count, ¤cySymbols, &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(®bndl); |
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, ®bndl, status); |
michael@0 | 2536 | if (U_FAILURE(*status)) { |
michael@0 | 2537 | break; |
michael@0 | 2538 | } |
michael@0 | 2539 | while (U_SUCCESS(*status) && ures_hasNext(®bndl)) { |
michael@0 | 2540 | ures_getNextResource(®bndl, &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(®bndl); |
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 |