2 **********************************************************************
3 * Copyright (c) 2002-2014, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
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"
31 //#define UCURR_DEBUG_EQUIV 1
32 #ifdef UCURR_DEBUG_EQUIV
35 //#define UCURR_DEBUG 1
40 typedef struct IsoCodeEntry
{
41 const UChar
*isoCode
; /* const because it's a reference to a resource bundle string. */
46 //------------------------------------------------------------
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 };
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 };
59 static const int32_t MAX_POW10
= (sizeof(POW10
)/sizeof(POW10
[0])) - 1;
61 // Defines equivalent currency symbols.
62 static const char *EQUIV_CURRENCY_SYMBOLS
[][2] = {
63 {"\\u00a5", "\\uffe5"},
66 {"\\u20a8", "\\u20b9"},
67 {"\\u00a3", "\\u20a4"}};
69 #define ISO_CURRENCY_CODE_LENGTH 3
71 //------------------------------------------------------------
75 static const char CURRENCY_DATA
[] = "supplementalData";
76 // Tag for meta-data, in root.
77 static const char CURRENCY_META
[] = "CurrencyMeta";
79 // Tag for map from countries to currencies, in root.
80 static const char CURRENCY_MAP
[] = "CurrencyMap";
82 // Tag for default meta-data, in CURRENCY_META
83 static const char DEFAULT_META
[] = "DEFAULT";
85 // Variant for legacy pre-euro mapping in CurrencyMap
86 static const char VAR_PRE_EURO
[] = "PREEURO";
88 // Variant for legacy euro mapping in CurrencyMap
89 static const char VAR_EURO
[] = "EURO";
92 static const char VAR_DELIM
= '_';
93 static const char VAR_DELIM_STR
[] = "_";
95 // Variant for legacy euro mapping in CurrencyMap
96 //static const char VAR_DELIM_EURO[] = "_EURO";
98 #define VARIANT_IS_EMPTY 0
99 #define VARIANT_IS_EURO 0x1
100 #define VARIANT_IS_PREEURO 0x2
102 // Tag for localized display names (symbols) of currencies
103 static const char CURRENCIES
[] = "Currencies";
104 static const char CURRENCYPLURALS
[] = "CurrencyPlurals";
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
112 static const UChar EUR_STR
[] = {0x0045,0x0055,0x0052,0};
114 // ISO codes mapping table
115 static const UHashtable
* gIsoCodes
= NULL
;
116 static icu::UInitOnce gIsoCodesInitOnce
= U_INITONCE_INITIALIZER
;
118 // Currency symbol equivalances
119 static const icu::Hashtable
* gCurrSymbolsEquiv
= NULL
;
120 static icu::UInitOnce gCurrSymbolsEquivInitOnce
= U_INITONCE_INITIALIZER
;
124 // EquivIterator iterates over all strings that are equivalent to a given
125 // string, s. Note that EquivIterator will never yield s itself.
126 class EquivIterator
: icu::UMemory
{
128 // Constructor. hash stores the equivalence relationships; s is the string
129 // for which we find equivalent strings.
130 inline EquivIterator(const icu::Hashtable
& hash
, const icu::UnicodeString
& s
)
132 _start
= _current
= &s
;
134 inline ~EquivIterator() { }
136 // next returns the next equivalent string or NULL if there are no more.
137 // If s has no equivalent strings, next returns NULL on the first call.
138 const icu::UnicodeString
*next();
140 const icu::Hashtable
& _hash
;
141 const icu::UnicodeString
* _start
;
142 const icu::UnicodeString
* _current
;
145 const icu::UnicodeString
*
146 EquivIterator::next() {
147 const icu::UnicodeString
* _next
= (const icu::UnicodeString
*) _hash
.get(*_current
);
149 U_ASSERT(_current
== _start
);
152 if (*_next
== *_start
) {
161 // makeEquivalent makes lhs and rhs equivalent by updating the equivalence
162 // relations in hash accordingly.
163 static void makeEquivalent(
164 const icu::UnicodeString
&lhs
,
165 const icu::UnicodeString
&rhs
,
166 icu::Hashtable
* hash
, UErrorCode
&status
) {
167 if (U_FAILURE(status
)) {
171 // already equivalent
174 icu::EquivIterator
leftIter(*hash
, lhs
);
175 icu::EquivIterator
rightIter(*hash
, rhs
);
176 const icu::UnicodeString
*firstLeft
= leftIter
.next();
177 const icu::UnicodeString
*firstRight
= rightIter
.next();
178 const icu::UnicodeString
*nextLeft
= firstLeft
;
179 const icu::UnicodeString
*nextRight
= firstRight
;
180 while (nextLeft
!= NULL
&& nextRight
!= NULL
) {
181 if (*nextLeft
== rhs
|| *nextRight
== lhs
) {
182 // Already equivalent
185 nextLeft
= leftIter
.next();
186 nextRight
= rightIter
.next();
188 // Not equivalent. Must join.
189 icu::UnicodeString
*newFirstLeft
;
190 icu::UnicodeString
*newFirstRight
;
191 if (firstRight
== NULL
&& firstLeft
== NULL
) {
192 // Neither lhs or rhs belong to an equivalence circle, so we form
193 // a new equivalnce circle of just lhs and rhs.
194 newFirstLeft
= new icu::UnicodeString(rhs
);
195 newFirstRight
= new icu::UnicodeString(lhs
);
196 } else if (firstRight
== NULL
) {
197 // lhs belongs to an equivalence circle, but rhs does not, so we link
198 // rhs into lhs' circle.
199 newFirstLeft
= new icu::UnicodeString(rhs
);
200 newFirstRight
= new icu::UnicodeString(*firstLeft
);
201 } else if (firstLeft
== NULL
) {
202 // rhs belongs to an equivlance circle, but lhs does not, so we link
203 // lhs into rhs' circle.
204 newFirstLeft
= new icu::UnicodeString(*firstRight
);
205 newFirstRight
= new icu::UnicodeString(lhs
);
207 // Both lhs and rhs belong to different equivalnce circles. We link
208 // them together to form one single, larger equivalnce circle.
209 newFirstLeft
= new icu::UnicodeString(*firstRight
);
210 newFirstRight
= new icu::UnicodeString(*firstLeft
);
212 if (newFirstLeft
== NULL
|| newFirstRight
== NULL
) {
214 delete newFirstRight
;
215 status
= U_MEMORY_ALLOCATION_ERROR
;
218 hash
->put(lhs
, (void *) newFirstLeft
, status
);
219 hash
->put(rhs
, (void *) newFirstRight
, status
);
222 // countEquivalent counts how many strings are equivalent to s.
223 // hash stores all the equivalnce relations.
224 // countEquivalent does not include s itself in the count.
225 static int32_t countEquivalent(const icu::Hashtable
&hash
, const icu::UnicodeString
&s
) {
227 icu::EquivIterator
iter(hash
, s
);
228 while (iter
.next() != NULL
) {
231 #ifdef UCURR_DEBUG_EQUIV
234 s
.extract(0,s
.length(),tmp
, "UTF-8");
235 printf("CountEquivalent('%s') = %d\n", tmp
, result
);
241 static const icu::Hashtable
* getCurrSymbolsEquiv();
243 //------------------------------------------------------------
247 * Cleanup callback func
249 static UBool U_CALLCONV
250 isoCodes_cleanup(void)
252 if (gIsoCodes
!= NULL
) {
253 uhash_close(const_cast<UHashtable
*>(gIsoCodes
));
256 gIsoCodesInitOnce
.reset();
261 * Cleanup callback func
263 static UBool U_CALLCONV
264 currSymbolsEquiv_cleanup(void)
266 delete const_cast<icu::Hashtable
*>(gCurrSymbolsEquiv
);
267 gCurrSymbolsEquiv
= NULL
;
268 gCurrSymbolsEquivInitOnce
.reset();
273 * Deleter for OlsonToMetaMappingEntry
275 static void U_CALLCONV
276 deleteIsoCodeEntry(void *obj
) {
277 IsoCodeEntry
*entry
= (IsoCodeEntry
*)obj
;
282 * Deleter for gCurrSymbolsEquiv.
284 static void U_CALLCONV
285 deleteUnicode(void *obj
) {
286 icu::UnicodeString
*entry
= (icu::UnicodeString
*)obj
;
291 * Unfortunately, we have to convert the UChar* currency code to char*
292 * to use it as a resource key.
295 myUCharsToChars(char* resultOfLen4
, const UChar
* currency
) {
296 u_UCharsToChars(currency
, resultOfLen4
, ISO_CURRENCY_CODE_LENGTH
);
297 resultOfLen4
[ISO_CURRENCY_CODE_LENGTH
] = 0;
302 * Internal function to look up currency data. Result is an array of
303 * four integers. The first is the fraction digits. The second is the
304 * rounding increment, or 0 if none. The rounding increment is in
305 * units of 10^(-fraction_digits). The third and fourth are the same
306 * except that they are those used in cash transations ( cashDigits
307 * and cashRounding ).
309 static const int32_t*
310 _findMetaData(const UChar
* currency
, UErrorCode
& ec
) {
312 if (currency
== 0 || *currency
== 0) {
314 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
316 return LAST_RESORT_DATA
;
319 // Get CurrencyMeta resource out of root locale file. [This may
320 // move out of the root locale file later; if it does, update this
322 UResourceBundle
* currencyData
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &ec
);
323 UResourceBundle
* currencyMeta
= ures_getByKey(currencyData
, CURRENCY_META
, currencyData
, &ec
);
326 ures_close(currencyMeta
);
327 // Config/build error; return hard-coded defaults
328 return LAST_RESORT_DATA
;
331 // Look up our currency, or if that's not available, then DEFAULT
332 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
333 UErrorCode ec2
= U_ZERO_ERROR
; // local error code: soft failure
334 UResourceBundle
* rb
= ures_getByKey(currencyMeta
, myUCharsToChars(buf
, currency
), NULL
, &ec2
);
335 if (U_FAILURE(ec2
)) {
337 rb
= ures_getByKey(currencyMeta
,DEFAULT_META
, NULL
, &ec
);
339 ures_close(currencyMeta
);
341 // Config/build error; return hard-coded defaults
342 return LAST_RESORT_DATA
;
347 const int32_t *data
= ures_getIntVector(rb
, &len
, &ec
);
348 if (U_FAILURE(ec
) || len
!= 4) {
349 // Config/build error; return hard-coded defaults
351 ec
= U_INVALID_FORMAT_ERROR
;
353 ures_close(currencyMeta
);
355 return LAST_RESORT_DATA
;
358 ures_close(currencyMeta
);
363 // -------------------------------------
366 * @see VARIANT_IS_EURO
367 * @see VARIANT_IS_PREEURO
370 idForLocale(const char* locale
, char* countryAndVariant
, int capacity
, UErrorCode
* ec
)
372 uint32_t variantType
= 0;
373 // !!! this is internal only, assumes buffer is not null and capacity is sufficient
374 // Extract the country name and variant name. We only
375 // recognize two variant names, EURO and PREEURO.
376 char variant
[ULOC_FULLNAME_CAPACITY
];
377 uloc_getCountry(locale
, countryAndVariant
, capacity
, ec
);
378 uloc_getVariant(locale
, variant
, sizeof(variant
), ec
);
379 if (variant
[0] != 0) {
380 variantType
= (uint32_t)(0 == uprv_strcmp(variant
, VAR_EURO
))
381 | ((uint32_t)(0 == uprv_strcmp(variant
, VAR_PRE_EURO
)) << 1);
384 uprv_strcat(countryAndVariant
, VAR_DELIM_STR
);
385 uprv_strcat(countryAndVariant
, variant
);
391 // ------------------------------------------
395 //-------------------------------------------
397 // don't use ICUService since we don't need fallback
400 static UBool U_CALLCONV
currency_cleanup(void);
403 #if !UCONFIG_NO_SERVICE
406 static UMutex gCRegLock
= U_MUTEX_INITIALIZER
;
407 static CReg
* gCRegHead
= 0;
409 struct CReg
: public icu::UMemory
{
411 UChar iso
[ISO_CURRENCY_CODE_LENGTH
+1];
412 char id
[ULOC_FULLNAME_CAPACITY
];
414 CReg(const UChar
* _iso
, const char* _id
)
417 int32_t len
= (int32_t)uprv_strlen(_id
);
418 if (len
> (int32_t)(sizeof(id
)-1)) {
419 len
= (sizeof(id
)-1);
421 uprv_strncpy(id
, _id
, len
);
423 uprv_memcpy(iso
, _iso
, ISO_CURRENCY_CODE_LENGTH
* sizeof(const UChar
));
424 iso
[ISO_CURRENCY_CODE_LENGTH
] = 0;
427 static UCurrRegistryKey
reg(const UChar
* _iso
, const char* _id
, UErrorCode
* status
)
429 if (status
&& U_SUCCESS(*status
) && _iso
&& _id
) {
430 CReg
* n
= new CReg(_iso
, _id
);
432 umtx_lock(&gCRegLock
);
434 /* register for the first time */
435 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY
, currency_cleanup
);
439 umtx_unlock(&gCRegLock
);
442 *status
= U_MEMORY_ALLOCATION_ERROR
;
447 static UBool
unreg(UCurrRegistryKey key
) {
449 umtx_lock(&gCRegLock
);
451 CReg
** p
= &gCRegHead
;
454 *p
= ((CReg
*)key
)->next
;
462 umtx_unlock(&gCRegLock
);
466 static const UChar
* get(const char* id
) {
467 const UChar
* result
= NULL
;
468 umtx_lock(&gCRegLock
);
471 /* register cleanup of the mutex */
472 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY
, currency_cleanup
);
474 if (uprv_strcmp(id
, p
->id
) == 0) {
480 umtx_unlock(&gCRegLock
);
484 /* This doesn't need to be thread safe. It's for u_cleanup only. */
485 static void cleanup(void) {
488 gCRegHead
= gCRegHead
->next
;
494 // -------------------------------------
496 U_CAPI UCurrRegistryKey U_EXPORT2
497 ucurr_register(const UChar
* isoCode
, const char* locale
, UErrorCode
*status
)
499 if (status
&& U_SUCCESS(*status
)) {
500 char id
[ULOC_FULLNAME_CAPACITY
];
501 idForLocale(locale
, id
, sizeof(id
), status
);
502 return CReg::reg(isoCode
, id
, status
);
507 // -------------------------------------
509 U_CAPI UBool U_EXPORT2
510 ucurr_unregister(UCurrRegistryKey key
, UErrorCode
* status
)
512 if (status
&& U_SUCCESS(*status
)) {
513 return CReg::unreg(key
);
517 #endif /* UCONFIG_NO_SERVICE */
519 // -------------------------------------
522 * Release all static memory held by currency.
524 /*The declaration here is needed so currency_cleanup(void)
525 * can call this function.
527 static UBool U_CALLCONV
528 currency_cache_cleanup(void);
531 static UBool U_CALLCONV
currency_cleanup(void) {
532 #if !UCONFIG_NO_SERVICE
536 * There might be some cached currency data or isoCodes data.
538 currency_cache_cleanup();
540 currSymbolsEquiv_cleanup();
546 // -------------------------------------
548 U_CAPI
int32_t U_EXPORT2
549 ucurr_forLocale(const char* locale
,
551 int32_t buffCapacity
,
555 const UChar
* s
= NULL
;
556 if (ec
!= NULL
&& U_SUCCESS(*ec
)) {
557 if ((buff
&& buffCapacity
) || !buffCapacity
) {
558 UErrorCode localStatus
= U_ZERO_ERROR
;
559 char id
[ULOC_FULLNAME_CAPACITY
];
560 if ((resLen
= uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
))) {
561 // there is a currency keyword. Try to see if it's valid
562 if(buffCapacity
> resLen
) {
563 /* Normalize the currency keyword value to upper case. */
564 T_CString_toUpperCase(id
);
565 u_charsToUChars(id
, buff
, resLen
);
568 // get country or country_variant in `id'
569 uint32_t variantType
= idForLocale(locale
, id
, sizeof(id
), ec
);
571 if (U_FAILURE(*ec
)) {
575 #if !UCONFIG_NO_SERVICE
576 const UChar
* result
= CReg::get(id
);
578 if(buffCapacity
> u_strlen(result
)) {
579 u_strcpy(buff
, result
);
581 return u_strlen(result
);
584 // Remove variants, which is only needed for registration.
585 char *idDelim
= strchr(id
, VAR_DELIM
);
590 // Look up the CurrencyMap element in the root bundle.
591 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
592 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
593 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
594 UResourceBundle
*currencyReq
= ures_getByIndex(countryArray
, 0, NULL
, &localStatus
);
595 s
= ures_getStringByKey(currencyReq
, "id", &resLen
, &localStatus
);
598 Get the second item when PREEURO is requested, and this is a known Euro country.
599 If the requested variant is PREEURO, and this isn't a Euro country, assume
600 that the country changed over to the Euro in the future. This is probably
601 an old version of ICU that hasn't been updated yet. The latest currency is
604 if (U_SUCCESS(localStatus
)) {
605 if ((variantType
& VARIANT_IS_PREEURO
) && u_strcmp(s
, EUR_STR
) == 0) {
606 currencyReq
= ures_getByIndex(countryArray
, 1, currencyReq
, &localStatus
);
607 s
= ures_getStringByKey(currencyReq
, "id", &resLen
, &localStatus
);
609 else if ((variantType
& VARIANT_IS_EURO
)) {
613 ures_close(countryArray
);
614 ures_close(currencyReq
);
616 if ((U_FAILURE(localStatus
)) && strchr(id
, '_') != 0)
618 // We don't know about it. Check to see if we support the variant.
619 uloc_getParent(locale
, id
, sizeof(id
), ec
);
620 *ec
= U_USING_FALLBACK_WARNING
;
621 return ucurr_forLocale(id
, buff
, buffCapacity
, ec
);
623 else if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
) {
624 // There is nothing to fallback to. Report the failure/warning if possible.
627 if (U_SUCCESS(*ec
)) {
628 if(buffCapacity
> resLen
) {
633 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
635 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
644 * Modify the given locale name by removing the rightmost _-delimited
645 * element. If there is none, empty the string ("" == root).
646 * NOTE: The string "root" is not recognized; do not use it.
647 * @return TRUE if the fallback happened; FALSE if locale is already
650 static UBool
fallback(char *loc
) {
654 UErrorCode status
= U_ZERO_ERROR
;
655 uloc_getParent(loc
, loc
, (int32_t)uprv_strlen(loc
), &status
);
657 char *i = uprv_strrchr(loc, '_');
667 U_CAPI
const UChar
* U_EXPORT2
668 ucurr_getName(const UChar
* currency
,
670 UCurrNameStyle nameStyle
,
671 UBool
* isChoiceFormat
, // fillin
672 int32_t* len
, // fillin
675 // Look up the Currencies resource for the given locale. The
676 // Currencies locale data looks like this:
679 //| USD { "US$", "US Dollar" }
680 //| CHF { "Sw F", "Swiss Franc" }
681 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
686 if (U_FAILURE(*ec
)) {
690 int32_t choice
= (int32_t) nameStyle
;
691 if (choice
< 0 || choice
> 1) {
692 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
696 // In the future, resource bundles may implement multi-level
697 // fallback. That is, if a currency is not found in the en_US
698 // Currencies data, then the en Currencies data will be searched.
699 // Currently, if a Currencies datum exists in en_US and en, the
700 // en_US entry hides that in en.
702 // We want multi-level fallback for this resource, so we implement
705 // Use a separate UErrorCode here that does not propagate out of
707 UErrorCode ec2
= U_ZERO_ERROR
;
709 char loc
[ULOC_FULLNAME_CAPACITY
];
710 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
711 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
712 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
716 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
717 myUCharsToChars(buf
, currency
);
719 /* Normalize the keyword value to uppercase */
720 T_CString_toUpperCase(buf
);
722 const UChar
* s
= NULL
;
724 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
726 rb
= ures_getByKey(rb
, CURRENCIES
, rb
, &ec2
);
728 // Fetch resource with multi-level resource inheritance fallback
729 rb
= ures_getByKeyWithFallback(rb
, buf
, rb
, &ec2
);
731 s
= ures_getStringByIndex(rb
, choice
, len
, &ec2
);
734 // If we've succeeded we're done. Otherwise, try to fallback.
735 // If that fails (because we are already at root) then exit.
736 if (U_SUCCESS(ec2
)) {
737 if (ec2
== U_USING_DEFAULT_WARNING
738 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
743 // Determine if this is a ChoiceFormat pattern. One leading mark
744 // indicates a ChoiceFormat. Two indicates a static string that
745 // starts with a mark. In either case, the first mark is ignored,
746 // if present. Marks in the rest of the string have no special
748 *isChoiceFormat
= FALSE
;
749 if (U_SUCCESS(ec2
)) {
752 while (i
< *len
&& s
[i
] == CHOICE_FORMAT_MARK
&& i
< 2) {
755 *isChoiceFormat
= (i
== 1);
756 if (i
!= 0) ++s
; // Skip over first mark
760 // If we fail to find a match, use the ISO 4217 code
761 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
762 *ec
= U_USING_DEFAULT_WARNING
;
766 U_CAPI
const UChar
* U_EXPORT2
767 ucurr_getPluralName(const UChar
* currency
,
769 UBool
* isChoiceFormat
,
770 const char* pluralCount
,
771 int32_t* len
, // fillin
773 // Look up the Currencies resource for the given locale. The
774 // Currencies locale data looks like this:
776 //| CurrencyPlurals {
779 //| other{"US dollars"}
784 if (U_FAILURE(*ec
)) {
788 // Use a separate UErrorCode here that does not propagate out of
790 UErrorCode ec2
= U_ZERO_ERROR
;
792 char loc
[ULOC_FULLNAME_CAPACITY
];
793 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
794 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
795 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
799 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
800 myUCharsToChars(buf
, currency
);
802 const UChar
* s
= NULL
;
804 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
806 rb
= ures_getByKey(rb
, CURRENCYPLURALS
, rb
, &ec2
);
808 // Fetch resource with multi-level resource inheritance fallback
809 rb
= ures_getByKeyWithFallback(rb
, buf
, rb
, &ec2
);
811 s
= ures_getStringByKeyWithFallback(rb
, pluralCount
, len
, &ec2
);
812 if (U_FAILURE(ec2
)) {
813 // fall back to "other"
815 s
= ures_getStringByKeyWithFallback(rb
, "other", len
, &ec2
);
816 if (U_FAILURE(ec2
)) {
818 // fall back to long name in Currencies
819 return ucurr_getName(currency
, locale
, UCURR_LONG_NAME
,
820 isChoiceFormat
, len
, ec
);
825 // If we've succeeded we're done. Otherwise, try to fallback.
826 // If that fails (because we are already at root) then exit.
827 if (U_SUCCESS(ec2
)) {
828 if (ec2
== U_USING_DEFAULT_WARNING
829 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
836 // If we fail to find a match, use the ISO 4217 code
837 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
838 *ec
= U_USING_DEFAULT_WARNING
;
843 //========================================================================
844 // Following are structure and function for parsing currency names
846 #define NEED_TO_BE_DELETED 0x1
848 // TODO: a better way to define this?
849 #define MAX_CURRENCY_NAME_LEN 100
852 const char* IsoCode
; // key
853 UChar
* currencyName
; // value
854 int32_t currencyNameLen
; // value length
855 int32_t flag
; // flags
856 } CurrencyNameStruct
;
860 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
864 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
868 // Comparason function used in quick sort.
869 static int U_CALLCONV
currencyNameComparator(const void* a
, const void* b
) {
870 const CurrencyNameStruct
* currName_1
= (const CurrencyNameStruct
*)a
;
871 const CurrencyNameStruct
* currName_2
= (const CurrencyNameStruct
*)b
;
873 i
< MIN(currName_1
->currencyNameLen
, currName_2
->currencyNameLen
);
875 if (currName_1
->currencyName
[i
] < currName_2
->currencyName
[i
]) {
878 if (currName_1
->currencyName
[i
] > currName_2
->currencyName
[i
]) {
882 if (currName_1
->currencyNameLen
< currName_2
->currencyNameLen
) {
884 } else if (currName_1
->currencyNameLen
> currName_2
->currencyNameLen
) {
891 // Give a locale, return the maximum number of currency names associated with
893 // It gets currency names from resource bundles using fallback.
894 // It is the maximum number because in the fallback chain, some of the
895 // currency names are duplicated.
896 // For example, given locale as "en_US", the currency names get from resource
897 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
898 // all currency names in "en_US" and "en".
900 getCurrencyNameCount(const char* loc
, int32_t* total_currency_name_count
, int32_t* total_currency_symbol_count
) {
902 *total_currency_name_count
= 0;
903 *total_currency_symbol_count
= 0;
904 const UChar
* s
= NULL
;
905 char locale
[ULOC_FULLNAME_CAPACITY
];
906 uprv_strcpy(locale
, loc
);
907 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
909 UErrorCode ec2
= U_ZERO_ERROR
;
910 // TODO: ures_openDirect?
911 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, locale
, &ec2
);
912 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
913 int32_t n
= ures_getSize(curr
);
914 for (int32_t i
=0; i
<n
; ++i
) {
915 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
917 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
918 UBool isChoice
= FALSE
;
919 if (len
> 0 && s
[0] == CHOICE_FORMAT_MARK
) {
922 if (len
> 0 && s
[0] != CHOICE_FORMAT_MARK
) {
927 ChoiceFormat
fmt(UnicodeString(TRUE
, s
, len
), ec2
);
929 fmt
.getFormats(fmt_count
);
930 *total_currency_symbol_count
+= fmt_count
;
932 ++(*total_currency_symbol_count
); // currency symbol
933 if (currencySymbolsEquiv
!= NULL
) {
934 *total_currency_symbol_count
+= countEquivalent(*currencySymbolsEquiv
, UnicodeString(TRUE
, s
, len
));
938 ++(*total_currency_symbol_count
); // iso code
939 ++(*total_currency_name_count
); // long name
944 UErrorCode ec3
= U_ZERO_ERROR
;
945 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec3
);
946 n
= ures_getSize(curr_p
);
947 for (int32_t i
=0; i
<n
; ++i
) {
948 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec3
);
949 *total_currency_name_count
+= ures_getSize(names
);
956 if (!fallback(locale
)) {
963 toUpperCase(const UChar
* source
, int32_t len
, const char* locale
) {
965 UErrorCode ec
= U_ZERO_ERROR
;
966 int32_t destLen
= u_strToUpper(dest
, 0, source
, len
, locale
, &ec
);
969 dest
= (UChar
*)uprv_malloc(sizeof(UChar
) * MAX(destLen
, len
));
970 u_strToUpper(dest
, destLen
, source
, len
, locale
, &ec
);
972 uprv_memcpy(dest
, source
, sizeof(UChar
) * len
);
978 // Collect all available currency names associated with the given locale
979 // (enable fallback chain).
980 // Read currenc names defined in resource bundle "Currencies" and
981 // "CurrencyPlural", enable fallback chain.
982 // return the malloc-ed currency name arrays and the total number of currency
983 // names in the array.
985 collectCurrencyNames(const char* locale
,
986 CurrencyNameStruct
** currencyNames
,
987 int32_t* total_currency_name_count
,
988 CurrencyNameStruct
** currencySymbols
,
989 int32_t* total_currency_symbol_count
,
992 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
993 // Look up the Currencies resource for the given locale.
994 UErrorCode ec2
= U_ZERO_ERROR
;
996 char loc
[ULOC_FULLNAME_CAPACITY
];
997 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
998 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
999 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
1002 // Get maximum currency name count first.
1003 getCurrencyNameCount(loc
, total_currency_name_count
, total_currency_symbol_count
);
1005 *currencyNames
= (CurrencyNameStruct
*)uprv_malloc
1006 (sizeof(CurrencyNameStruct
) * (*total_currency_name_count
));
1007 *currencySymbols
= (CurrencyNameStruct
*)uprv_malloc
1008 (sizeof(CurrencyNameStruct
) * (*total_currency_symbol_count
));
1010 if(currencyNames
== NULL
|| currencySymbols
== NULL
) {
1011 ec
= U_MEMORY_ALLOCATION_ERROR
;
1014 if (U_FAILURE(ec
)) return;
1016 const UChar
* s
= NULL
; // currency name
1017 char* iso
= NULL
; // currency ISO code
1019 *total_currency_name_count
= 0;
1020 *total_currency_symbol_count
= 0;
1022 UErrorCode ec3
= U_ZERO_ERROR
;
1023 UErrorCode ec4
= U_ZERO_ERROR
;
1025 // Using hash to remove duplicates caused by locale fallback
1026 UHashtable
* currencyIsoCodes
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &ec3
);
1027 UHashtable
* currencyPluralIsoCodes
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &ec4
);
1028 for (int32_t localeLevel
= 0; ; ++localeLevel
) {
1030 // TODO: ures_openDirect
1031 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
1032 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
1033 int32_t n
= ures_getSize(curr
);
1034 for (int32_t i
=0; i
<n
; ++i
) {
1035 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
1037 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
1038 // TODO: uhash_put wont change key/value?
1039 iso
= (char*)ures_getKey(names
);
1040 if (localeLevel
== 0) {
1041 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
1043 if (uhash_get(currencyIsoCodes
, iso
) != NULL
) {
1047 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
1050 UBool isChoice
= FALSE
;
1051 if (len
> 0 && s
[0] == CHOICE_FORMAT_MARK
) {
1054 if (len
> 0 && s
[0] != CHOICE_FORMAT_MARK
) {
1059 ChoiceFormat
fmt(UnicodeString(TRUE
, s
, len
), ec2
);
1061 const UnicodeString
* formats
= fmt
.getFormats(fmt_count
);
1062 for (int i
= 0; i
< fmt_count
; ++i
) {
1063 // put iso, formats[i]; into array
1064 int32_t length
= formats
[i
].length();
1065 UChar
* name
= (UChar
*)uprv_malloc(sizeof(UChar
)*length
);
1066 formats
[i
].extract(0, length
, name
);
1067 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1068 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= name
;
1069 (*currencySymbols
)[*total_currency_symbol_count
].flag
= NEED_TO_BE_DELETED
;
1070 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= length
;
1073 // Add currency symbol.
1074 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1075 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)s
;
1076 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1077 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= len
;
1078 // Add equivalent symbols
1079 if (currencySymbolsEquiv
!= NULL
) {
1080 icu::EquivIterator
iter(*currencySymbolsEquiv
, UnicodeString(TRUE
, s
, len
));
1081 const UnicodeString
*symbol
;
1082 while ((symbol
= iter
.next()) != NULL
) {
1083 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1084 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*) symbol
->getBuffer();
1085 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1086 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= symbol
->length();
1091 // Add currency long name.
1092 s
= ures_getStringByIndex(names
, UCURR_LONG_NAME
, &len
, &ec2
);
1093 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1094 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1095 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1096 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1097 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1099 // put (iso, 3, and iso) in to array
1100 // Add currency ISO code.
1101 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1102 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)uprv_malloc(sizeof(UChar
)*3);
1103 // Must convert iso[] into Unicode
1104 u_charsToUChars(iso
, (*currencySymbols
)[*total_currency_symbol_count
].currencyName
, 3);
1105 (*currencySymbols
)[*total_currency_symbol_count
].flag
= NEED_TO_BE_DELETED
;
1106 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= 3;
1112 UErrorCode ec3
= U_ZERO_ERROR
;
1113 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec3
);
1114 n
= ures_getSize(curr_p
);
1115 for (int32_t i
=0; i
<n
; ++i
) {
1116 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec3
);
1117 iso
= (char*)ures_getKey(names
);
1118 // Using hash to remove duplicated ISO codes in fallback chain.
1119 if (localeLevel
== 0) {
1120 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1122 if (uhash_get(currencyPluralIsoCodes
, iso
) != NULL
) {
1126 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1129 int32_t num
= ures_getSize(names
);
1131 for (int32_t j
= 0; j
< num
; ++j
) {
1132 // TODO: remove duplicates between singular name and
1133 // currency long name?
1134 s
= ures_getStringByIndex(names
, j
, &len
, &ec3
);
1135 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1136 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1137 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1138 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1139 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1147 if (!fallback(loc
)) {
1152 uhash_close(currencyIsoCodes
);
1153 uhash_close(currencyPluralIsoCodes
);
1155 // quick sort the struct
1156 qsort(*currencyNames
, *total_currency_name_count
,
1157 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1158 qsort(*currencySymbols
, *total_currency_symbol_count
,
1159 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1162 printf("currency name count: %d\n", *total_currency_name_count
);
1163 for (int32_t index
= 0; index
< *total_currency_name_count
; ++index
) {
1164 printf("index: %d\n", index
);
1165 printf("iso: %s\n", (*currencyNames
)[index
].IsoCode
);
1166 char curNameBuf
[1024];
1167 memset(curNameBuf
, 0, 1024);
1168 u_austrncpy(curNameBuf
, (*currencyNames
)[index
].currencyName
, (*currencyNames
)[index
].currencyNameLen
);
1169 printf("currencyName: %s\n", curNameBuf
);
1170 printf("len: %d\n", (*currencyNames
)[index
].currencyNameLen
);
1172 printf("currency symbol count: %d\n", *total_currency_symbol_count
);
1173 for (int32_t index
= 0; index
< *total_currency_symbol_count
; ++index
) {
1174 printf("index: %d\n", index
);
1175 printf("iso: %s\n", (*currencySymbols
)[index
].IsoCode
);
1176 char curNameBuf
[1024];
1177 memset(curNameBuf
, 0, 1024);
1178 u_austrncpy(curNameBuf
, (*currencySymbols
)[index
].currencyName
, (*currencySymbols
)[index
].currencyNameLen
);
1179 printf("currencySymbol: %s\n", curNameBuf
);
1180 printf("len: %d\n", (*currencySymbols
)[index
].currencyNameLen
);
1183 // fail on hashtable errors
1184 if (U_FAILURE(ec3
)) {
1188 if (U_FAILURE(ec4
)) {
1194 // @param currencyNames: currency names array
1195 // @param indexInCurrencyNames: the index of the character in currency names
1196 // array against which the comparison is done
1197 // @param key: input text char to compare against
1198 // @param begin(IN/OUT): the begin index of matching range in currency names array
1199 // @param end(IN/OUT): the end index of matching range in currency names array.
1201 binarySearch(const CurrencyNameStruct
* currencyNames
,
1202 int32_t indexInCurrencyNames
,
1204 int32_t* begin
, int32_t* end
) {
1206 printf("key = %x\n", key
);
1208 int32_t first
= *begin
;
1209 int32_t last
= *end
;
1210 while (first
<= last
) {
1211 int32_t mid
= (first
+ last
) / 2; // compute mid point.
1212 if (indexInCurrencyNames
>= currencyNames
[mid
].currencyNameLen
) {
1215 if (key
> currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1218 else if (key
< currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1222 // Find a match, and looking for ranges
1223 // Now do two more binary searches. First, on the left side for
1224 // the greatest L such that CurrencyNameStruct[L] < key.
1229 printf("mid = %d\n", mid
);
1232 int32_t M
= (L
+ R
) / 2;
1234 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1236 if (indexInCurrencyNames
>= currencyNames
[M
].currencyNameLen
) {
1239 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] < key
) {
1243 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1254 printf("begin = %d\n", *begin
);
1255 U_ASSERT(currencyNames
[*begin
].currencyName
[indexInCurrencyNames
] == key
);
1258 // Now for the second search, finding the least R such that
1259 // key < CurrencyNameStruct[R].
1263 int32_t M
= (L
+ R
) / 2;
1265 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1267 if (currencyNames
[M
].currencyNameLen
< indexInCurrencyNames
) {
1270 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] > key
) {
1274 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1283 if (currencyNames
[R
].currencyName
[indexInCurrencyNames
] > key
) {
1289 printf("end = %d\n", *end
);
1292 // now, found the range. check whether there is exact match
1293 if (currencyNames
[*begin
].currencyNameLen
== indexInCurrencyNames
+ 1) {
1294 return *begin
; // find range and exact match.
1296 return -1; // find range, but no exact match.
1302 return -1; // failed to find range.
1306 // Linear search "text" in "currencyNames".
1307 // @param begin, end: the begin and end index in currencyNames, within which
1308 // range should the search be performed.
1309 // @param textLen: the length of the text to be compared
1310 // @param maxMatchLen(IN/OUT): passing in the computed max matching length
1311 // pass out the new max matching length
1312 // @param maxMatchIndex: the index in currencyName which has the longest
1313 // match with input text.
1315 linearSearch(const CurrencyNameStruct
* currencyNames
,
1316 int32_t begin
, int32_t end
,
1317 const UChar
* text
, int32_t textLen
,
1318 int32_t *maxMatchLen
, int32_t* maxMatchIndex
) {
1319 for (int32_t index
= begin
; index
<= end
; ++index
) {
1320 int32_t len
= currencyNames
[index
].currencyNameLen
;
1321 if (len
> *maxMatchLen
&& len
<= textLen
&&
1322 uprv_memcmp(currencyNames
[index
].currencyName
, text
, len
* sizeof(UChar
)) == 0) {
1323 *maxMatchIndex
= index
;
1326 printf("maxMatchIndex = %d, maxMatchLen = %d\n",
1327 *maxMatchIndex
, *maxMatchLen
);
1333 #define LINEAR_SEARCH_THRESHOLD 10
1335 // Find longest match between "text" and currency names in "currencyNames".
1336 // @param total_currency_count: total number of currency names in CurrencyNames.
1337 // @param textLen: the length of the text to be compared
1338 // @param maxMatchLen: passing in the computed max matching length
1339 // pass out the new max matching length
1340 // @param maxMatchIndex: the index in currencyName which has the longest
1341 // match with input text.
1343 searchCurrencyName(const CurrencyNameStruct
* currencyNames
,
1344 int32_t total_currency_count
,
1345 const UChar
* text
, int32_t textLen
,
1346 int32_t* maxMatchLen
, int32_t* maxMatchIndex
) {
1347 *maxMatchIndex
= -1;
1349 int32_t matchIndex
= -1;
1350 int32_t binarySearchBegin
= 0;
1351 int32_t binarySearchEnd
= total_currency_count
- 1;
1352 // It is a variant of binary search.
1353 // For example, given the currency names in currencyNames array are:
1354 // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
1355 // and the input text is BBEXST
1356 // The first round binary search search "B" in the text against
1357 // the first char in currency names, and find the first char matching range
1358 // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
1359 // The 2nd round binary search search the second "B" in the text against
1360 // the 2nd char in currency names, and narrow the matching range to
1361 // "BB BBEX BBEXYZ" (and the maximum matching "BB").
1362 // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
1363 // maximum matching).
1364 // The 4th round returns the same range (the maximum matching is "BBEX").
1365 // The 5th round returns no matching range.
1366 for (int32_t index
= 0; index
< textLen
; ++index
) {
1367 // matchIndex saves the one with exact match till the current point.
1368 // [binarySearchBegin, binarySearchEnd] saves the matching range.
1369 matchIndex
= binarySearch(currencyNames
, index
,
1371 &binarySearchBegin
, &binarySearchEnd
);
1372 if (binarySearchBegin
== -1) { // did not find the range
1375 if (matchIndex
!= -1) {
1376 // find an exact match for text from text[0] to text[index]
1377 // in currencyNames array.
1378 *maxMatchLen
= index
+ 1;
1379 *maxMatchIndex
= matchIndex
;
1381 if (binarySearchEnd
- binarySearchBegin
< LINEAR_SEARCH_THRESHOLD
) {
1382 // linear search if within threshold.
1383 linearSearch(currencyNames
, binarySearchBegin
, binarySearchEnd
,
1385 maxMatchLen
, maxMatchIndex
);
1392 //========================= currency name cache =====================
1394 char locale
[ULOC_FULLNAME_CAPACITY
]; //key
1395 // currency names, case insensitive
1396 CurrencyNameStruct
* currencyNames
; // value
1397 int32_t totalCurrencyNameCount
; // currency name count
1398 // currency symbols and ISO code, case sensitive
1399 CurrencyNameStruct
* currencySymbols
; // value
1400 int32_t totalCurrencySymbolCount
; // count
1402 // reference count is set to 1 when an entry is put to cache.
1403 // it increases by 1 before accessing, and decreased by 1 after accessing.
1404 // The entry is deleted when ref count is zero, which means
1405 // the entry is replaced out of cache and no process is accessing it.
1407 } CurrencyNameCacheEntry
;
1410 #define CURRENCY_NAME_CACHE_NUM 10
1412 // Reserve 10 cache entries.
1413 static CurrencyNameCacheEntry
* currCache
[CURRENCY_NAME_CACHE_NUM
] = {NULL
};
1414 // Using an index to indicate which entry to be replaced when cache is full.
1415 // It is a simple round-robin replacement strategy.
1416 static int8_t currentCacheEntryIndex
= 0;
1418 static UMutex gCurrencyCacheMutex
= U_MUTEX_INITIALIZER
;
1422 deleteCurrencyNames(CurrencyNameStruct
* currencyNames
, int32_t count
) {
1423 for (int32_t index
= 0; index
< count
; ++index
) {
1424 if ( (currencyNames
[index
].flag
& NEED_TO_BE_DELETED
) ) {
1425 uprv_free(currencyNames
[index
].currencyName
);
1428 uprv_free(currencyNames
);
1433 deleteCacheEntry(CurrencyNameCacheEntry
* entry
) {
1434 deleteCurrencyNames(entry
->currencyNames
, entry
->totalCurrencyNameCount
);
1435 deleteCurrencyNames(entry
->currencySymbols
, entry
->totalCurrencySymbolCount
);
1441 static UBool U_CALLCONV
1442 currency_cache_cleanup(void) {
1443 for (int32_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1445 deleteCacheEntry(currCache
[i
]);
1454 uprv_parseCurrency(const char* locale
,
1455 const icu::UnicodeString
& text
,
1456 icu::ParsePosition
& pos
,
1463 if (U_FAILURE(ec
)) {
1467 int32_t total_currency_name_count
= 0;
1468 CurrencyNameStruct
* currencyNames
= NULL
;
1469 int32_t total_currency_symbol_count
= 0;
1470 CurrencyNameStruct
* currencySymbols
= NULL
;
1471 CurrencyNameCacheEntry
* cacheEntry
= NULL
;
1473 umtx_lock(&gCurrencyCacheMutex
);
1474 // in order to handle racing correctly,
1475 // not putting 'search' in a separate function.
1477 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1478 if (currCache
[i
]!= NULL
&&
1479 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1485 cacheEntry
= currCache
[found
];
1486 currencyNames
= cacheEntry
->currencyNames
;
1487 total_currency_name_count
= cacheEntry
->totalCurrencyNameCount
;
1488 currencySymbols
= cacheEntry
->currencySymbols
;
1489 total_currency_symbol_count
= cacheEntry
->totalCurrencySymbolCount
;
1490 ++(cacheEntry
->refCount
);
1492 umtx_unlock(&gCurrencyCacheMutex
);
1494 collectCurrencyNames(locale
, ¤cyNames
, &total_currency_name_count
, ¤cySymbols
, &total_currency_symbol_count
, ec
);
1495 if (U_FAILURE(ec
)) {
1498 umtx_lock(&gCurrencyCacheMutex
);
1501 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1502 if (currCache
[i
]!= NULL
&&
1503 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1509 // insert new entry to
1510 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1511 // and remove the existing entry
1512 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1514 cacheEntry
= currCache
[currentCacheEntryIndex
];
1516 --(cacheEntry
->refCount
);
1517 // delete if the ref count is zero
1518 if (cacheEntry
->refCount
== 0) {
1519 deleteCacheEntry(cacheEntry
);
1522 cacheEntry
= (CurrencyNameCacheEntry
*)uprv_malloc(sizeof(CurrencyNameCacheEntry
));
1523 currCache
[currentCacheEntryIndex
] = cacheEntry
;
1524 uprv_strcpy(cacheEntry
->locale
, locale
);
1525 cacheEntry
->currencyNames
= currencyNames
;
1526 cacheEntry
->totalCurrencyNameCount
= total_currency_name_count
;
1527 cacheEntry
->currencySymbols
= currencySymbols
;
1528 cacheEntry
->totalCurrencySymbolCount
= total_currency_symbol_count
;
1529 cacheEntry
->refCount
= 2; // one for cache, one for reference
1530 currentCacheEntryIndex
= (currentCacheEntryIndex
+ 1) % CURRENCY_NAME_CACHE_NUM
;
1531 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY
, currency_cache_cleanup
);
1534 deleteCurrencyNames(currencyNames
, total_currency_name_count
);
1535 deleteCurrencyNames(currencySymbols
, total_currency_symbol_count
);
1536 cacheEntry
= currCache
[found
];
1537 currencyNames
= cacheEntry
->currencyNames
;
1538 total_currency_name_count
= cacheEntry
->totalCurrencyNameCount
;
1539 currencySymbols
= cacheEntry
->currencySymbols
;
1540 total_currency_symbol_count
= cacheEntry
->totalCurrencySymbolCount
;
1541 ++(cacheEntry
->refCount
);
1543 umtx_unlock(&gCurrencyCacheMutex
);
1546 int32_t start
= pos
.getIndex();
1548 UChar inputText
[MAX_CURRENCY_NAME_LEN
];
1549 UChar upperText
[MAX_CURRENCY_NAME_LEN
];
1550 int32_t textLen
= MIN(MAX_CURRENCY_NAME_LEN
, text
.length() - start
);
1551 text
.extract(start
, textLen
, inputText
);
1552 UErrorCode ec1
= U_ZERO_ERROR
;
1553 textLen
= u_strToUpper(upperText
, MAX_CURRENCY_NAME_LEN
, inputText
, textLen
, locale
, &ec1
);
1556 int32_t matchIndex
= -1;
1557 // case in-sensitive comparision against currency names
1558 searchCurrencyName(currencyNames
, total_currency_name_count
,
1559 upperText
, textLen
, &max
, &matchIndex
);
1562 printf("search in names, max = %d, matchIndex = %d\n", max
, matchIndex
);
1565 int32_t maxInSymbol
= 0;
1566 int32_t matchIndexInSymbol
= -1;
1567 if (type
!= UCURR_LONG_NAME
) { // not name only
1568 // case sensitive comparison against currency symbols and ISO code.
1569 searchCurrencyName(currencySymbols
, total_currency_symbol_count
,
1571 &maxInSymbol
, &matchIndexInSymbol
);
1575 printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol
, matchIndexInSymbol
);
1576 if(matchIndexInSymbol
!= -1) {
1577 printf("== ISO=%s\n", currencySymbols
[matchIndexInSymbol
].IsoCode
);
1581 if (max
>= maxInSymbol
&& matchIndex
!= -1) {
1582 u_charsToUChars(currencyNames
[matchIndex
].IsoCode
, result
, 4);
1583 pos
.setIndex(start
+ max
);
1584 } else if (maxInSymbol
>= max
&& matchIndexInSymbol
!= -1) {
1585 u_charsToUChars(currencySymbols
[matchIndexInSymbol
].IsoCode
, result
, 4);
1586 pos
.setIndex(start
+ maxInSymbol
);
1589 // decrease reference count
1590 umtx_lock(&gCurrencyCacheMutex
);
1591 --(cacheEntry
->refCount
);
1592 if (cacheEntry
->refCount
== 0) { // remove
1593 deleteCacheEntry(cacheEntry
);
1595 umtx_unlock(&gCurrencyCacheMutex
);
1600 * Internal method. Given a currency ISO code and a locale, return
1601 * the "static" currency name. This is usually the same as the
1602 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
1603 * format is applied to the number 2.0 (to yield the more common
1604 * plural) to return a static name.
1606 * This is used for backward compatibility with old currency logic in
1607 * DecimalFormat and DecimalFormatSymbols.
1610 uprv_getStaticCurrencyName(const UChar
* iso
, const char* loc
,
1611 icu::UnicodeString
& result
, UErrorCode
& ec
)
1615 UBool isChoiceFormat
;
1617 const UChar
* currname
= ucurr_getName(iso
, loc
, UCURR_SYMBOL_NAME
,
1618 &isChoiceFormat
, &len
, &ec
);
1619 if (U_SUCCESS(ec
)) {
1620 // If this is a ChoiceFormat currency, then format an
1621 // arbitrary value; pick something != 1; more common.
1623 if (isChoiceFormat
) {
1624 ChoiceFormat
f(UnicodeString(TRUE
, currname
, len
), ec
);
1625 if (U_SUCCESS(ec
)) {
1626 f
.format(2.0, result
);
1628 result
.setTo(iso
, -1);
1631 result
.setTo(currname
, -1);
1636 U_CAPI
int32_t U_EXPORT2
1637 ucurr_getDefaultFractionDigits(const UChar
* currency
, UErrorCode
* ec
) {
1638 return ucurr_getDefaultFractionDigitsForUsage(currency
,UCURR_USAGE_STANDARD
,ec
);
1641 U_DRAFT
int32_t U_EXPORT2
1642 ucurr_getDefaultFractionDigitsForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1643 int32_t fracDigits
= 0;
1644 if (U_SUCCESS(*ec
)) {
1646 case UCURR_USAGE_STANDARD
:
1647 fracDigits
= (_findMetaData(currency
, *ec
))[0];
1649 case UCURR_USAGE_CASH
:
1650 fracDigits
= (_findMetaData(currency
, *ec
))[2];
1653 *ec
= U_UNSUPPORTED_ERROR
;
1659 U_CAPI
double U_EXPORT2
1660 ucurr_getRoundingIncrement(const UChar
* currency
, UErrorCode
* ec
) {
1661 return ucurr_getRoundingIncrementForUsage(currency
, UCURR_USAGE_STANDARD
, ec
);
1664 U_DRAFT
double U_EXPORT2
1665 ucurr_getRoundingIncrementForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1666 double result
= 0.0;
1668 const int32_t *data
= _findMetaData(currency
, *ec
);
1669 if (U_SUCCESS(*ec
)) {
1673 case UCURR_USAGE_STANDARD
:
1674 fracDigits
= data
[0];
1675 increment
= data
[1];
1677 case UCURR_USAGE_CASH
:
1678 fracDigits
= data
[2];
1679 increment
= data
[3];
1682 *ec
= U_UNSUPPORTED_ERROR
;
1686 // If the meta data is invalid, return 0.0
1687 if (fracDigits
< 0 || fracDigits
> MAX_POW10
) {
1688 *ec
= U_INVALID_FORMAT_ERROR
;
1690 // A rounding value of 0 or 1 indicates no rounding.
1691 if (increment
>= 2) {
1692 // Return (increment) / 10^(fracDigits). The only actual rounding data,
1693 // as of this writing, is CHF { 2, 5 }.
1694 result
= double(increment
) / POW10
[fracDigits
];
1704 typedef struct UCurrencyContext
{
1705 uint32_t currType
; /* UCurrCurrencyType */
1710 Please keep this list in alphabetical order.
1711 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
1713 ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
1715 static const struct CurrencyList
{
1716 const char *currency
;
1718 } gCurrencyList
[] = {
1719 {"ADP", UCURR_COMMON
|UCURR_DEPRECATED
},
1720 {"AED", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1721 {"AFA", UCURR_COMMON
|UCURR_DEPRECATED
},
1722 {"AFN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1723 {"ALK", UCURR_COMMON
|UCURR_DEPRECATED
},
1724 {"ALL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1725 {"AMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1726 {"ANG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1727 {"AOA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1728 {"AOK", UCURR_COMMON
|UCURR_DEPRECATED
},
1729 {"AON", UCURR_COMMON
|UCURR_DEPRECATED
},
1730 {"AOR", UCURR_COMMON
|UCURR_DEPRECATED
},
1731 {"ARA", UCURR_COMMON
|UCURR_DEPRECATED
},
1732 {"ARL", UCURR_COMMON
|UCURR_DEPRECATED
},
1733 {"ARM", UCURR_COMMON
|UCURR_DEPRECATED
},
1734 {"ARP", UCURR_COMMON
|UCURR_DEPRECATED
},
1735 {"ARS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1736 {"ATS", UCURR_COMMON
|UCURR_DEPRECATED
},
1737 {"AUD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1738 {"AWG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1739 {"AZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1740 {"AZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1741 {"BAD", UCURR_COMMON
|UCURR_DEPRECATED
},
1742 {"BAM", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1743 {"BAN", UCURR_COMMON
|UCURR_DEPRECATED
},
1744 {"BBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1745 {"BDT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1746 {"BEC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1747 {"BEF", UCURR_COMMON
|UCURR_DEPRECATED
},
1748 {"BEL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1749 {"BGL", UCURR_COMMON
|UCURR_DEPRECATED
},
1750 {"BGM", UCURR_COMMON
|UCURR_DEPRECATED
},
1751 {"BGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1752 {"BGO", UCURR_COMMON
|UCURR_DEPRECATED
},
1753 {"BHD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1754 {"BIF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1755 {"BMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1756 {"BND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1757 {"BOB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1758 {"BOL", UCURR_COMMON
|UCURR_DEPRECATED
},
1759 {"BOP", UCURR_COMMON
|UCURR_DEPRECATED
},
1760 {"BOV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1761 {"BRB", UCURR_COMMON
|UCURR_DEPRECATED
},
1762 {"BRC", UCURR_COMMON
|UCURR_DEPRECATED
},
1763 {"BRE", UCURR_COMMON
|UCURR_DEPRECATED
},
1764 {"BRL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1765 {"BRN", UCURR_COMMON
|UCURR_DEPRECATED
},
1766 {"BRR", UCURR_COMMON
|UCURR_DEPRECATED
},
1767 {"BRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1768 {"BSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1769 {"BTN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1770 {"BUK", UCURR_COMMON
|UCURR_DEPRECATED
},
1771 {"BWP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1772 {"BYB", UCURR_COMMON
|UCURR_DEPRECATED
},
1773 {"BYR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1774 {"BZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1775 {"CAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1776 {"CDF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1777 {"CHE", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1778 {"CHF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1779 {"CHW", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1780 {"CLE", UCURR_COMMON
|UCURR_DEPRECATED
},
1781 {"CLF", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1782 {"CLP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1783 {"CNX", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1784 {"CNY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1785 {"COP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1786 {"COU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1787 {"CRC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1788 {"CSD", UCURR_COMMON
|UCURR_DEPRECATED
},
1789 {"CSK", UCURR_COMMON
|UCURR_DEPRECATED
},
1790 {"CUC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1791 {"CUP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1792 {"CVE", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1793 {"CYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1794 {"CZK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1795 {"DDM", UCURR_COMMON
|UCURR_DEPRECATED
},
1796 {"DEM", UCURR_COMMON
|UCURR_DEPRECATED
},
1797 {"DJF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1798 {"DKK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1799 {"DOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1800 {"DZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1801 {"ECS", UCURR_COMMON
|UCURR_DEPRECATED
},
1802 {"ECV", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1803 {"EEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1804 {"EGP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1805 {"EQE", UCURR_COMMON
|UCURR_DEPRECATED
},
1806 {"ERN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1807 {"ESA", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1808 {"ESB", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1809 {"ESP", UCURR_COMMON
|UCURR_DEPRECATED
},
1810 {"ETB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1811 {"EUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1812 {"FIM", UCURR_COMMON
|UCURR_DEPRECATED
},
1813 {"FJD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1814 {"FKP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1815 {"FRF", UCURR_COMMON
|UCURR_DEPRECATED
},
1816 {"GBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1817 {"GEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1818 {"GEL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1819 {"GHC", UCURR_COMMON
|UCURR_DEPRECATED
},
1820 {"GHS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1821 {"GIP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1822 {"GMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1823 {"GNF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1824 {"GNS", UCURR_COMMON
|UCURR_DEPRECATED
},
1825 {"GQE", UCURR_COMMON
|UCURR_DEPRECATED
},
1826 {"GRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1827 {"GTQ", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1828 {"GWE", UCURR_COMMON
|UCURR_DEPRECATED
},
1829 {"GWP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1830 {"GYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1831 {"HKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1832 {"HNL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1833 {"HRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1834 {"HRK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1835 {"HTG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1836 {"HUF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1837 {"IDR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1838 {"IEP", UCURR_COMMON
|UCURR_DEPRECATED
},
1839 {"ILP", UCURR_COMMON
|UCURR_DEPRECATED
},
1840 {"ILR", UCURR_COMMON
|UCURR_DEPRECATED
},
1841 {"ILS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1842 {"INR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1843 {"IQD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1844 {"IRR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1845 {"ISJ", UCURR_COMMON
|UCURR_DEPRECATED
},
1846 {"ISK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1847 {"ITL", UCURR_COMMON
|UCURR_DEPRECATED
},
1848 {"JMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1849 {"JOD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1850 {"JPY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1851 {"KES", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1852 {"KGS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1853 {"KHR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1854 {"KMF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1855 {"KPW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1856 {"KRH", UCURR_COMMON
|UCURR_DEPRECATED
},
1857 {"KRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1858 {"KRW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1859 {"KWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1860 {"KYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1861 {"KZT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1862 {"LAK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1863 {"LBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1864 {"LKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1865 {"LRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1866 {"LSL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1867 {"LSM", UCURR_COMMON
|UCURR_DEPRECATED
},
1868 {"LTL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1869 {"LTT", UCURR_COMMON
|UCURR_DEPRECATED
},
1870 {"LUC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1871 {"LUF", UCURR_COMMON
|UCURR_DEPRECATED
},
1872 {"LUL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1873 {"LVL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1874 {"LVR", UCURR_COMMON
|UCURR_DEPRECATED
},
1875 {"LYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1876 {"MAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1877 {"MAF", UCURR_COMMON
|UCURR_DEPRECATED
},
1878 {"MCF", UCURR_COMMON
|UCURR_DEPRECATED
},
1879 {"MDC", UCURR_COMMON
|UCURR_DEPRECATED
},
1880 {"MDL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1881 {"MGA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1882 {"MGF", UCURR_COMMON
|UCURR_DEPRECATED
},
1883 {"MKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1884 {"MKN", UCURR_COMMON
|UCURR_DEPRECATED
},
1885 {"MLF", UCURR_COMMON
|UCURR_DEPRECATED
},
1886 {"MMK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1887 {"MNT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1888 {"MOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1889 {"MRO", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1890 {"MTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1891 {"MTP", UCURR_COMMON
|UCURR_DEPRECATED
},
1892 {"MUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1893 {"MVP", UCURR_COMMON
|UCURR_DEPRECATED
},
1894 {"MVR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1895 {"MWK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1896 {"MXN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1897 {"MXP", UCURR_COMMON
|UCURR_DEPRECATED
},
1898 {"MXV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1899 {"MYR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1900 {"MZE", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1901 {"MZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1902 {"MZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1903 {"NAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1904 {"NGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1905 {"NIC", UCURR_COMMON
|UCURR_DEPRECATED
},
1906 {"NIO", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1907 {"NLG", UCURR_COMMON
|UCURR_DEPRECATED
},
1908 {"NOK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1909 {"NPR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1910 {"NZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1911 {"OMR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1912 {"PAB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1913 {"PEI", UCURR_COMMON
|UCURR_DEPRECATED
},
1914 {"PEN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1915 {"PES", UCURR_COMMON
|UCURR_DEPRECATED
},
1916 {"PGK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1917 {"PHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1918 {"PKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1919 {"PLN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1920 {"PLZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1921 {"PTE", UCURR_COMMON
|UCURR_DEPRECATED
},
1922 {"PYG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1923 {"QAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1924 {"RHD", UCURR_COMMON
|UCURR_DEPRECATED
},
1925 {"ROL", UCURR_COMMON
|UCURR_DEPRECATED
},
1926 {"RON", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1927 {"RSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1928 {"RUB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1929 {"RUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1930 {"RWF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1931 {"SAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1932 {"SBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1933 {"SCR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1934 {"SDD", UCURR_COMMON
|UCURR_DEPRECATED
},
1935 {"SDG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1936 {"SDP", UCURR_COMMON
|UCURR_DEPRECATED
},
1937 {"SEK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1938 {"SGD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1939 {"SHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1940 {"SIT", UCURR_COMMON
|UCURR_DEPRECATED
},
1941 {"SKK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1942 {"SLL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1943 {"SOS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1944 {"SRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1945 {"SRG", UCURR_COMMON
|UCURR_DEPRECATED
},
1946 {"SSP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1947 {"STD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1948 {"SUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1949 {"SVC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1950 {"SYP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1951 {"SZL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1952 {"THB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1953 {"TJR", UCURR_COMMON
|UCURR_DEPRECATED
},
1954 {"TJS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1955 {"TMM", UCURR_COMMON
|UCURR_DEPRECATED
},
1956 {"TMT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1957 {"TND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1958 {"TOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1959 {"TPE", UCURR_COMMON
|UCURR_DEPRECATED
},
1960 {"TRL", UCURR_COMMON
|UCURR_DEPRECATED
},
1961 {"TRY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1962 {"TTD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1963 {"TWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1964 {"TZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1965 {"UAH", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1966 {"UAK", UCURR_COMMON
|UCURR_DEPRECATED
},
1967 {"UGS", UCURR_COMMON
|UCURR_DEPRECATED
},
1968 {"UGX", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1969 {"USD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1970 {"USN", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1971 {"USS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1972 {"UYI", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1973 {"UYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1974 {"UYU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1975 {"UZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1976 {"VEB", UCURR_COMMON
|UCURR_DEPRECATED
},
1977 {"VEF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1978 {"VND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1979 {"VNN", UCURR_COMMON
|UCURR_DEPRECATED
},
1980 {"VUV", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1981 {"WST", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1982 {"XAF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1983 {"XAG", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1984 {"XAU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1985 {"XBA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1986 {"XBB", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1987 {"XBC", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1988 {"XBD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1989 {"XCD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1990 {"XDR", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1991 {"XEU", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1992 {"XFO", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1993 {"XFU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1994 {"XOF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1995 {"XPD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1996 {"XPF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1997 {"XPT", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1998 {"XRE", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1999 {"XSU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2000 {"XTS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2001 {"XUA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2002 {"XXX", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2003 {"YDD", UCURR_COMMON
|UCURR_DEPRECATED
},
2004 {"YER", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2005 {"YUD", UCURR_COMMON
|UCURR_DEPRECATED
},
2006 {"YUM", UCURR_COMMON
|UCURR_DEPRECATED
},
2007 {"YUN", UCURR_COMMON
|UCURR_DEPRECATED
},
2008 {"YUR", UCURR_COMMON
|UCURR_DEPRECATED
},
2009 {"ZAL", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2010 {"ZAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2011 {"ZMK", UCURR_COMMON
|UCURR_DEPRECATED
},
2012 {"ZMW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2013 {"ZRN", UCURR_COMMON
|UCURR_DEPRECATED
},
2014 {"ZRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
2015 {"ZWL", UCURR_COMMON
|UCURR_DEPRECATED
},
2016 {"ZWR", UCURR_COMMON
|UCURR_DEPRECATED
},
2017 {"ZWD", UCURR_COMMON
|UCURR_DEPRECATED
},
2018 { NULL
, 0 } // Leave here to denote the end of the list.
2021 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
2022 ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
2024 static int32_t U_CALLCONV
2025 ucurr_countCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2026 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2027 uint32_t currType
= myContext
->currType
;
2030 /* Count the number of items matching the type we are looking for. */
2031 for (int32_t idx
= 0; gCurrencyList
[idx
].currency
!= NULL
; idx
++) {
2032 if (UCURR_MATCHES_BITMASK(gCurrencyList
[idx
].currType
, currType
)) {
2039 static const char* U_CALLCONV
2040 ucurr_nextCurrencyList(UEnumeration
*enumerator
,
2041 int32_t* resultLength
,
2042 UErrorCode
* /*pErrorCode*/)
2044 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2046 /* Find the next in the list that matches the type we are looking for. */
2047 while (myContext
->listIdx
< (sizeof(gCurrencyList
)/sizeof(gCurrencyList
[0]))-1) {
2048 const struct CurrencyList
*currItem
= &gCurrencyList
[myContext
->listIdx
++];
2049 if (UCURR_MATCHES_BITMASK(currItem
->currType
, myContext
->currType
))
2052 *resultLength
= 3; /* Currency codes are only 3 chars long */
2054 return currItem
->currency
;
2057 /* We enumerated too far. */
2064 static void U_CALLCONV
2065 ucurr_resetCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2066 ((UCurrencyContext
*)(enumerator
->context
))->listIdx
= 0;
2069 static void U_CALLCONV
2070 ucurr_closeCurrencyList(UEnumeration
*enumerator
) {
2071 uprv_free(enumerator
->context
);
2072 uprv_free(enumerator
);
2075 static void U_CALLCONV
2076 ucurr_createCurrencyList(UHashtable
*isoCodes
, UErrorCode
* status
){
2077 UErrorCode localStatus
= U_ZERO_ERROR
;
2079 // Look up the CurrencyMap element in the root bundle.
2080 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2081 UResourceBundle
*currencyMapArray
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2083 if (U_SUCCESS(localStatus
)) {
2084 // process each entry in currency map
2085 for (int32_t i
=0; i
<ures_getSize(currencyMapArray
); i
++) {
2086 // get the currency resource
2087 UResourceBundle
*currencyArray
= ures_getByIndex(currencyMapArray
, i
, NULL
, &localStatus
);
2088 // process each currency
2089 if (U_SUCCESS(localStatus
)) {
2090 for (int32_t j
=0; j
<ures_getSize(currencyArray
); j
++) {
2091 // get the currency resource
2092 UResourceBundle
*currencyRes
= ures_getByIndex(currencyArray
, j
, NULL
, &localStatus
);
2093 IsoCodeEntry
*entry
= (IsoCodeEntry
*)uprv_malloc(sizeof(IsoCodeEntry
));
2094 if (entry
== NULL
) {
2095 *status
= U_MEMORY_ALLOCATION_ERROR
;
2100 int32_t isoLength
= 0;
2101 UResourceBundle
*idRes
= ures_getByKey(currencyRes
, "id", NULL
, &localStatus
);
2102 if (idRes
== NULL
) {
2105 const UChar
*isoCode
= ures_getString(idRes
, &isoLength
, &localStatus
);
2108 UDate fromDate
= U_DATE_MIN
;
2109 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2111 if (U_SUCCESS(localStatus
)) {
2112 int32_t fromLength
= 0;
2113 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2114 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2115 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2116 fromDate
= (UDate
)currDate64
;
2118 ures_close(fromRes
);
2121 UDate toDate
= U_DATE_MAX
;
2122 localStatus
= U_ZERO_ERROR
;
2123 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2125 if (U_SUCCESS(localStatus
)) {
2126 int32_t toLength
= 0;
2127 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2128 int64_t currDate64
= (int64_t)toArray
[0] << 32;
2129 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2130 toDate
= (UDate
)currDate64
;
2135 ures_close(currencyRes
);
2137 entry
->isoCode
= isoCode
;
2138 entry
->from
= fromDate
;
2141 localStatus
= U_ZERO_ERROR
;
2142 uhash_put(isoCodes
, (UChar
*)isoCode
, entry
, &localStatus
);
2145 *status
= localStatus
;
2147 ures_close(currencyArray
);
2150 *status
= localStatus
;
2153 ures_close(currencyMapArray
);
2156 static const UEnumeration gEnumCurrencyList
= {
2159 ucurr_closeCurrencyList
,
2160 ucurr_countCurrencyList
,
2162 ucurr_nextCurrencyList
,
2163 ucurr_resetCurrencyList
2168 static void U_CALLCONV
initIsoCodes(UErrorCode
&status
) {
2169 U_ASSERT(gIsoCodes
== NULL
);
2170 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY
, currency_cleanup
);
2172 UHashtable
*isoCodes
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2173 if (U_FAILURE(status
)) {
2176 uhash_setValueDeleter(isoCodes
, deleteIsoCodeEntry
);
2178 ucurr_createCurrencyList(isoCodes
, &status
);
2179 if (U_FAILURE(status
)) {
2180 uhash_close(isoCodes
);
2183 gIsoCodes
= isoCodes
; // Note: gIsoCodes is const. Once set up here it is never altered,
2184 // and read only access is safe without synchronization.
2187 static void populateCurrSymbolsEquiv(icu::Hashtable
*hash
, UErrorCode
&status
) {
2188 if (U_FAILURE(status
)) {
2191 int32_t length
= sizeof(EQUIV_CURRENCY_SYMBOLS
) / sizeof(EQUIV_CURRENCY_SYMBOLS
[0]);
2192 for (int32_t i
= 0; i
< length
; ++i
) {
2193 icu::UnicodeString
lhs(EQUIV_CURRENCY_SYMBOLS
[i
][0], -1, US_INV
);
2194 icu::UnicodeString
rhs(EQUIV_CURRENCY_SYMBOLS
[i
][1], -1, US_INV
);
2195 makeEquivalent(lhs
.unescape(), rhs
.unescape(), hash
, status
);
2196 if (U_FAILURE(status
)) {
2202 static void U_CALLCONV
initCurrSymbolsEquiv() {
2203 U_ASSERT(gCurrSymbolsEquiv
== NULL
);
2204 UErrorCode status
= U_ZERO_ERROR
;
2205 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY
, currency_cleanup
);
2206 icu::Hashtable
*temp
= new icu::Hashtable(status
);
2210 if (U_FAILURE(status
)) {
2214 temp
->setValueDeleter(deleteUnicode
);
2215 populateCurrSymbolsEquiv(temp
, status
);
2216 if (U_FAILURE(status
)) {
2220 gCurrSymbolsEquiv
= temp
;
2223 U_CAPI UBool U_EXPORT2
2224 ucurr_isAvailable(const UChar
* isoCode
, UDate from
, UDate to
, UErrorCode
* eErrorCode
) {
2225 umtx_initOnce(gIsoCodesInitOnce
, &initIsoCodes
, *eErrorCode
);
2226 if (U_FAILURE(*eErrorCode
)) {
2230 IsoCodeEntry
* result
= (IsoCodeEntry
*) uhash_get(gIsoCodes
, isoCode
);
2231 if (result
== NULL
) {
2233 } else if (from
> to
) {
2234 *eErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2236 } else if ((from
> result
->to
) || (to
< result
->from
)) {
2242 static const icu::Hashtable
* getCurrSymbolsEquiv() {
2243 umtx_initOnce(gCurrSymbolsEquivInitOnce
, &initCurrSymbolsEquiv
);
2244 return gCurrSymbolsEquiv
;
2247 U_CAPI UEnumeration
* U_EXPORT2
2248 ucurr_openISOCurrencies(uint32_t currType
, UErrorCode
*pErrorCode
) {
2249 UEnumeration
*myEnum
= NULL
;
2250 UCurrencyContext
*myContext
;
2252 myEnum
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2253 if (myEnum
== NULL
) {
2254 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2257 uprv_memcpy(myEnum
, &gEnumCurrencyList
, sizeof(UEnumeration
));
2258 myContext
= (UCurrencyContext
*)uprv_malloc(sizeof(UCurrencyContext
));
2259 if (myContext
== NULL
) {
2260 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2264 myContext
->currType
= currType
;
2265 myContext
->listIdx
= 0;
2266 myEnum
->context
= myContext
;
2270 U_CAPI
int32_t U_EXPORT2
2271 ucurr_countCurrencies(const char* locale
,
2275 int32_t currCount
= 0;
2277 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2280 UErrorCode localStatus
= U_ZERO_ERROR
;
2281 char id
[ULOC_FULLNAME_CAPACITY
];
2282 uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2283 // get country or country_variant in `id'
2284 /*uint32_t variantType =*/ idForLocale(locale
, id
, sizeof(id
), ec
);
2291 // Remove variants, which is only needed for registration.
2292 char *idDelim
= strchr(id
, VAR_DELIM
);
2298 // Look up the CurrencyMap element in the root bundle.
2299 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2300 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2302 // Using the id derived from the local, get the currency data
2303 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2305 // process each currency to see which one is valid for the given date
2306 if (U_SUCCESS(localStatus
))
2308 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2310 // get the currency resource
2311 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2313 // get the from date
2314 int32_t fromLength
= 0;
2315 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2316 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2318 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2319 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2320 UDate fromDate
= (UDate
)currDate64
;
2322 if (ures_getSize(currencyRes
)> 2)
2324 int32_t toLength
= 0;
2325 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2326 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2328 currDate64
= (int64_t)toArray
[0] << 32;
2329 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2330 UDate toDate
= (UDate
)currDate64
;
2332 if ((fromDate
<= date
) && (date
< toDate
))
2341 if (fromDate
<= date
)
2347 // close open resources
2348 ures_close(currencyRes
);
2349 ures_close(fromRes
);
2352 } // end if (U_SUCCESS(localStatus))
2354 ures_close(countryArray
);
2357 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2359 // There is nothing to fallback to.
2360 // Report the failure/warning if possible.
2372 // If we got here, either error code is invalid or
2373 // some argument passed is no good.
2377 U_CAPI
int32_t U_EXPORT2
2378 ucurr_forLocaleAndDate(const char* locale
,
2382 int32_t buffCapacity
,
2386 int32_t currIndex
= 0;
2387 const UChar
* s
= NULL
;
2389 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2391 // check the arguments passed
2392 if ((buff
&& buffCapacity
) || !buffCapacity
)
2395 UErrorCode localStatus
= U_ZERO_ERROR
;
2396 char id
[ULOC_FULLNAME_CAPACITY
];
2397 resLen
= uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2399 // get country or country_variant in `id'
2400 /*uint32_t variantType =*/ idForLocale(locale
, id
, sizeof(id
), ec
);
2406 // Remove variants, which is only needed for registration.
2407 char *idDelim
= strchr(id
, VAR_DELIM
);
2413 // Look up the CurrencyMap element in the root bundle.
2414 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2415 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2417 // Using the id derived from the local, get the currency data
2418 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2420 // process each currency to see which one is valid for the given date
2421 bool matchFound
= false;
2422 if (U_SUCCESS(localStatus
))
2424 if ((index
<= 0) || (index
> ures_getSize(countryArray
)))
2426 // requested index is out of bounds
2427 ures_close(countryArray
);
2431 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2433 // get the currency resource
2434 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2435 s
= ures_getStringByKey(currencyRes
, "id", &resLen
, &localStatus
);
2437 // get the from date
2438 int32_t fromLength
= 0;
2439 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2440 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2442 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2443 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2444 UDate fromDate
= (UDate
)currDate64
;
2446 if (ures_getSize(currencyRes
)> 2)
2448 int32_t toLength
= 0;
2449 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2450 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2452 currDate64
= (int64_t)toArray
[0] << 32;
2453 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2454 UDate toDate
= (UDate
)currDate64
;
2456 if ((fromDate
<= date
) && (date
< toDate
))
2459 if (currIndex
== index
)
2469 if (fromDate
<= date
)
2472 if (currIndex
== index
)
2479 // close open resources
2480 ures_close(currencyRes
);
2481 ures_close(fromRes
);
2483 // check for loop exit
2492 ures_close(countryArray
);
2495 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2497 // There is nothing to fallback to.
2498 // Report the failure/warning if possible.
2505 if((buffCapacity
> resLen
) && matchFound
)
2507 // write out the currency value
2516 // return null terminated currency string
2517 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
2521 // illegal argument encountered
2522 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2527 // If we got here, either error code is invalid or
2528 // some argument passed is no good.
2532 static const UEnumeration defaultKeywordValues
= {
2535 ulist_close_keyword_values_iterator
,
2536 ulist_count_keyword_values
,
2538 ulist_next_keyword_value
,
2539 ulist_reset_keyword_values_iterator
2542 U_CAPI UEnumeration
*U_EXPORT2
ucurr_getKeywordValuesForLocale(const char *key
, const char *locale
, UBool commonlyUsed
, UErrorCode
* status
) {
2544 char prefRegion
[ULOC_FULLNAME_CAPACITY
] = "";
2545 int32_t prefRegionLength
= 0;
2546 prefRegionLength
= uloc_getCountry(locale
, prefRegion
, sizeof(prefRegion
), status
);
2547 if (prefRegionLength
== 0) {
2548 char loc
[ULOC_FULLNAME_CAPACITY
] = "";
2549 uloc_addLikelySubtags(locale
, loc
, sizeof(loc
), status
);
2551 /*prefRegionLength = */ uloc_getCountry(loc
, prefRegion
, sizeof(prefRegion
), status
);
2554 // Read value from supplementalData
2555 UList
*values
= ulist_createEmptyList(status
);
2556 UList
*otherValues
= ulist_createEmptyList(status
);
2557 UEnumeration
*en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2558 if (U_FAILURE(*status
) || en
== NULL
) {
2560 *status
= U_MEMORY_ALLOCATION_ERROR
;
2564 ulist_deleteList(values
);
2565 ulist_deleteList(otherValues
);
2568 memcpy(en
, &defaultKeywordValues
, sizeof(UEnumeration
));
2569 en
->context
= values
;
2571 UResourceBundle
*bundle
= ures_openDirect(U_ICUDATA_CURR
, "supplementalData", status
);
2572 ures_getByKey(bundle
, "CurrencyMap", bundle
, status
);
2573 UResourceBundle bundlekey
, regbndl
, curbndl
, to
;
2574 ures_initStackObject(&bundlekey
);
2575 ures_initStackObject(®bndl
);
2576 ures_initStackObject(&curbndl
);
2577 ures_initStackObject(&to
);
2579 while (U_SUCCESS(*status
) && ures_hasNext(bundle
)) {
2580 ures_getNextResource(bundle
, &bundlekey
, status
);
2581 if (U_FAILURE(*status
)) {
2584 const char *region
= ures_getKey(&bundlekey
);
2585 UBool isPrefRegion
= uprv_strcmp(region
, prefRegion
) == 0 ? TRUE
: FALSE
;
2586 if (!isPrefRegion
&& commonlyUsed
) {
2587 // With commonlyUsed=true, we do not put
2588 // currencies for other regions in the
2592 ures_getByKey(bundle
, region
, ®bndl
, status
);
2593 if (U_FAILURE(*status
)) {
2596 while (U_SUCCESS(*status
) && ures_hasNext(®bndl
)) {
2597 ures_getNextResource(®bndl
, &curbndl
, status
);
2598 if (ures_getType(&curbndl
) != URES_TABLE
) {
2599 // Currently, an empty ARRAY is mixed in.
2602 char *curID
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2603 int32_t curIDLength
= ULOC_KEYWORDS_CAPACITY
;
2604 if (curID
== NULL
) {
2605 *status
= U_MEMORY_ALLOCATION_ERROR
;
2609 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
2610 ures_getUTF8StringByKey(&curbndl
, "id", curID
, &curIDLength
, TRUE
, status
);
2611 /* optimize - use the utf-8 string */
2614 const UChar
* defString
= ures_getStringByKey(&curbndl
, "id", &curIDLength
, status
);
2615 if(U_SUCCESS(*status
)) {
2616 if(curIDLength
+1 > ULOC_KEYWORDS_CAPACITY
) {
2617 *status
= U_BUFFER_OVERFLOW_ERROR
;
2619 u_UCharsToChars(defString
, curID
, curIDLength
+1);
2625 if (U_FAILURE(*status
)) {
2628 UBool hasTo
= FALSE
;
2629 ures_getByKey(&curbndl
, "to", &to
, status
);
2630 if (U_FAILURE(*status
)) {
2631 // Do nothing here...
2632 *status
= U_ZERO_ERROR
;
2636 if (isPrefRegion
&& !hasTo
&& !ulist_containsString(values
, curID
, (int32_t)uprv_strlen(curID
))) {
2637 // Currently active currency for the target country
2638 ulist_addItemEndList(values
, curID
, TRUE
, status
);
2639 } else if (!ulist_containsString(otherValues
, curID
, (int32_t)uprv_strlen(curID
)) && !commonlyUsed
) {
2640 ulist_addItemEndList(otherValues
, curID
, TRUE
, status
);
2647 if (U_SUCCESS(*status
)) {
2649 if (ulist_getListSize(values
) == 0) {
2650 // This could happen if no valid region is supplied in the input
2651 // locale. In this case, we use the CLDR's default.
2653 en
= ucurr_getKeywordValuesForLocale(key
, "und", TRUE
, status
);
2656 // Consolidate the list
2658 ulist_resetList(otherValues
);
2659 while ((value
= (char *)ulist_getNext(otherValues
)) != NULL
) {
2660 if (!ulist_containsString(values
, value
, (int32_t)uprv_strlen(value
))) {
2661 char *tmpValue
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2662 uprv_memcpy(tmpValue
, value
, uprv_strlen(value
) + 1);
2663 ulist_addItemEndList(values
, tmpValue
, TRUE
, status
);
2664 if (U_FAILURE(*status
)) {
2671 ulist_resetList((UList
*)(en
->context
));
2673 ulist_deleteList(values
);
2679 ures_close(&curbndl
);
2680 ures_close(®bndl
);
2681 ures_close(&bundlekey
);
2684 ulist_deleteList(otherValues
);
2690 U_CAPI
int32_t U_EXPORT2
2691 ucurr_getNumericCode(const UChar
* currency
) {
2693 if (currency
&& u_strlen(currency
) == ISO_CURRENCY_CODE_LENGTH
) {
2694 UErrorCode status
= U_ZERO_ERROR
;
2696 UResourceBundle
*bundle
= ures_openDirect(0, "currencyNumericCodes", &status
);
2697 ures_getByKey(bundle
, "codeMap", bundle
, &status
);
2698 if (U_SUCCESS(status
)) {
2699 char alphaCode
[ISO_CURRENCY_CODE_LENGTH
+1];
2700 myUCharsToChars(alphaCode
, currency
);
2701 T_CString_toUpperCase(alphaCode
);
2702 ures_getByKey(bundle
, alphaCode
, bundle
, &status
);
2703 int tmpCode
= ures_getInt(bundle
, &status
);
2704 if (U_SUCCESS(status
)) {
2712 #endif /* #if !UCONFIG_NO_FORMATTING */