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