1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 **********************************************************************
5 * Copyright (c) 2002-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/ucurr.h"
15 #include "unicode/locid.h"
16 #include "unicode/ures.h"
17 #include "unicode/ustring.h"
18 #include "unicode/parsepos.h"
19 #include "unicode/uniset.h"
20 #include "unicode/usetiter.h"
21 #include "unicode/utf16.h"
26 #include "static_unicode_sets.h"
42 //#define UCURR_DEBUG_EQUIV 1
43 #ifdef UCURR_DEBUG_EQUIV
46 //#define UCURR_DEBUG 1
51 typedef struct IsoCodeEntry
{
52 const UChar
*isoCode
; /* const because it's a reference to a resource bundle string. */
57 //------------------------------------------------------------
60 // Default currency meta data of last resort. We try to use the
61 // defaults encoded in the meta data resource bundle. If there is a
62 // configuration/build error and these are not available, we use these
63 // hard-coded defaults (which should be identical).
64 static const int32_t LAST_RESORT_DATA
[] = { 2, 0, 2, 0 };
66 // POW10[i] = 10^i, i=0..MAX_POW10
67 static const int32_t POW10
[] = { 1, 10, 100, 1000, 10000, 100000,
68 1000000, 10000000, 100000000, 1000000000 };
70 static const int32_t MAX_POW10
= UPRV_LENGTHOF(POW10
) - 1;
72 #define ISO_CURRENCY_CODE_LENGTH 3
74 //------------------------------------------------------------
78 static const char CURRENCY_DATA
[] = "supplementalData";
79 // Tag for meta-data, in root.
80 static const char CURRENCY_META
[] = "CurrencyMeta";
82 // Tag for map from countries to currencies, in root.
83 static const char CURRENCY_MAP
[] = "CurrencyMap";
85 // Tag for default meta-data, in CURRENCY_META
86 static const char DEFAULT_META
[] = "DEFAULT";
88 // Variant for legacy pre-euro mapping in CurrencyMap
89 static const char VAR_PRE_EURO
[] = "PREEURO";
91 // Variant for legacy euro mapping in CurrencyMap
92 static const char VAR_EURO
[] = "EURO";
95 static const char VAR_DELIM
= '_';
96 static const char VAR_DELIM_STR
[] = "_";
98 // Variant for legacy euro mapping in CurrencyMap
99 //static const char VAR_DELIM_EURO[] = "_EURO";
101 #define VARIANT_IS_EMPTY 0
102 #define VARIANT_IS_EURO 0x1
103 #define VARIANT_IS_PREEURO 0x2
105 // Tag for localized display names (symbols) of currencies
106 static const char CURRENCIES
[] = "Currencies";
107 static const char CURRENCIES_NARROW
[] = "Currencies%narrow";
108 static const char CURRENCYPLURALS
[] = "CurrencyPlurals";
110 static const UChar EUR_STR
[] = {0x0045,0x0055,0x0052,0};
112 // ISO codes mapping table
113 static const UHashtable
* gIsoCodes
= NULL
;
114 static icu::UInitOnce gIsoCodesInitOnce
= U_INITONCE_INITIALIZER
;
116 // Currency symbol equivalances
117 static const icu::Hashtable
* gCurrSymbolsEquiv
= NULL
;
118 static icu::UInitOnce gCurrSymbolsEquivInitOnce
= U_INITONCE_INITIALIZER
;
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
: public icu::UMemory
{
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
)
130 _start
= _current
= &s
;
132 inline ~EquivIterator() { }
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();
138 const icu::Hashtable
& _hash
;
139 const icu::UnicodeString
* _start
;
140 const icu::UnicodeString
* _current
;
143 const icu::UnicodeString
*
144 EquivIterator::next() {
145 const icu::UnicodeString
* _next
= (const icu::UnicodeString
*) _hash
.get(*_current
);
147 U_ASSERT(_current
== _start
);
150 if (*_next
== *_start
) {
159 // makeEquivalent makes lhs and rhs equivalent by updating the equivalence
160 // relations in hash accordingly.
161 static void makeEquivalent(
162 const icu::UnicodeString
&lhs
,
163 const icu::UnicodeString
&rhs
,
164 icu::Hashtable
* hash
, UErrorCode
&status
) {
165 if (U_FAILURE(status
)) {
169 // already equivalent
172 icu::EquivIterator
leftIter(*hash
, lhs
);
173 icu::EquivIterator
rightIter(*hash
, rhs
);
174 const icu::UnicodeString
*firstLeft
= leftIter
.next();
175 const icu::UnicodeString
*firstRight
= rightIter
.next();
176 const icu::UnicodeString
*nextLeft
= firstLeft
;
177 const icu::UnicodeString
*nextRight
= firstRight
;
178 while (nextLeft
!= NULL
&& nextRight
!= NULL
) {
179 if (*nextLeft
== rhs
|| *nextRight
== lhs
) {
180 // Already equivalent
183 nextLeft
= leftIter
.next();
184 nextRight
= rightIter
.next();
186 // Not equivalent. Must join.
187 icu::UnicodeString
*newFirstLeft
;
188 icu::UnicodeString
*newFirstRight
;
189 if (firstRight
== NULL
&& firstLeft
== NULL
) {
190 // Neither lhs or rhs belong to an equivalence circle, so we form
191 // a new equivalnce circle of just lhs and rhs.
192 newFirstLeft
= new icu::UnicodeString(rhs
);
193 newFirstRight
= new icu::UnicodeString(lhs
);
194 } else if (firstRight
== NULL
) {
195 // lhs belongs to an equivalence circle, but rhs does not, so we link
196 // rhs into lhs' circle.
197 newFirstLeft
= new icu::UnicodeString(rhs
);
198 newFirstRight
= new icu::UnicodeString(*firstLeft
);
199 } else if (firstLeft
== NULL
) {
200 // rhs belongs to an equivlance circle, but lhs does not, so we link
201 // lhs into rhs' circle.
202 newFirstLeft
= new icu::UnicodeString(*firstRight
);
203 newFirstRight
= new icu::UnicodeString(lhs
);
205 // Both lhs and rhs belong to different equivalnce circles. We link
206 // them together to form one single, larger equivalnce circle.
207 newFirstLeft
= new icu::UnicodeString(*firstRight
);
208 newFirstRight
= new icu::UnicodeString(*firstLeft
);
210 if (newFirstLeft
== NULL
|| newFirstRight
== NULL
) {
212 delete newFirstRight
;
213 status
= U_MEMORY_ALLOCATION_ERROR
;
216 hash
->put(lhs
, (void *) newFirstLeft
, status
);
217 hash
->put(rhs
, (void *) newFirstRight
, status
);
220 // countEquivalent counts how many strings are equivalent to s.
221 // hash stores all the equivalnce relations.
222 // countEquivalent does not include s itself in the count.
223 static int32_t countEquivalent(const icu::Hashtable
&hash
, const icu::UnicodeString
&s
) {
225 icu::EquivIterator
iter(hash
, s
);
226 while (iter
.next() != NULL
) {
229 #ifdef UCURR_DEBUG_EQUIV
232 s
.extract(0,s
.length(),tmp
, "UTF-8");
233 printf("CountEquivalent('%s') = %d\n", tmp
, result
);
239 static const icu::Hashtable
* getCurrSymbolsEquiv();
241 //------------------------------------------------------------
245 * Cleanup callback func
247 static UBool U_CALLCONV
248 isoCodes_cleanup(void)
250 if (gIsoCodes
!= NULL
) {
251 uhash_close(const_cast<UHashtable
*>(gIsoCodes
));
254 gIsoCodesInitOnce
.reset();
259 * Cleanup callback func
261 static UBool U_CALLCONV
262 currSymbolsEquiv_cleanup(void)
264 delete const_cast<icu::Hashtable
*>(gCurrSymbolsEquiv
);
265 gCurrSymbolsEquiv
= NULL
;
266 gCurrSymbolsEquivInitOnce
.reset();
271 * Deleter for OlsonToMetaMappingEntry
273 static void U_CALLCONV
274 deleteIsoCodeEntry(void *obj
) {
275 IsoCodeEntry
*entry
= (IsoCodeEntry
*)obj
;
280 * Deleter for gCurrSymbolsEquiv.
282 static void U_CALLCONV
283 deleteUnicode(void *obj
) {
284 icu::UnicodeString
*entry
= (icu::UnicodeString
*)obj
;
289 * Unfortunately, we have to convert the UChar* currency code to char*
290 * to use it as a resource key.
293 myUCharsToChars(char* resultOfLen4
, const UChar
* currency
) {
294 u_UCharsToChars(currency
, resultOfLen4
, ISO_CURRENCY_CODE_LENGTH
);
295 resultOfLen4
[ISO_CURRENCY_CODE_LENGTH
] = 0;
300 * Internal function to look up currency data. Result is an array of
301 * four integers. The first is the fraction digits. The second is the
302 * rounding increment, or 0 if none. The rounding increment is in
303 * units of 10^(-fraction_digits). The third and fourth are the same
304 * except that they are those used in cash transations ( cashDigits
305 * and cashRounding ).
307 static const int32_t*
308 _findMetaData(const UChar
* currency
, UErrorCode
& ec
) {
310 if (currency
== 0 || *currency
== 0) {
312 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
314 return LAST_RESORT_DATA
;
317 // Get CurrencyMeta resource out of root locale file. [This may
318 // move out of the root locale file later; if it does, update this
320 UResourceBundle
* currencyData
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &ec
);
321 UResourceBundle
* currencyMeta
= ures_getByKey(currencyData
, CURRENCY_META
, currencyData
, &ec
);
324 ures_close(currencyMeta
);
325 // Config/build error; return hard-coded defaults
326 return LAST_RESORT_DATA
;
329 // Look up our currency, or if that's not available, then DEFAULT
330 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
331 UErrorCode ec2
= U_ZERO_ERROR
; // local error code: soft failure
332 UResourceBundle
* rb
= ures_getByKey(currencyMeta
, myUCharsToChars(buf
, currency
), NULL
, &ec2
);
333 if (U_FAILURE(ec2
)) {
335 rb
= ures_getByKey(currencyMeta
,DEFAULT_META
, NULL
, &ec
);
337 ures_close(currencyMeta
);
339 // Config/build error; return hard-coded defaults
340 return LAST_RESORT_DATA
;
345 const int32_t *data
= ures_getIntVector(rb
, &len
, &ec
);
346 if (U_FAILURE(ec
) || len
!= 4) {
347 // Config/build error; return hard-coded defaults
349 ec
= U_INVALID_FORMAT_ERROR
;
351 ures_close(currencyMeta
);
353 return LAST_RESORT_DATA
;
356 ures_close(currencyMeta
);
361 // -------------------------------------
364 * @see VARIANT_IS_EURO
365 * @see VARIANT_IS_PREEURO
368 idForLocale(const char* locale
, char* countryAndVariant
, int capacity
, UErrorCode
* ec
)
370 uint32_t variantType
= 0;
371 // !!! this is internal only, assumes buffer is not null and capacity is sufficient
372 // Extract the country name and variant name. We only
373 // recognize two variant names, EURO and PREEURO.
374 char variant
[ULOC_FULLNAME_CAPACITY
];
375 ulocimp_getRegionForSupplementalData(locale
, FALSE
, countryAndVariant
, capacity
, ec
);
376 uloc_getVariant(locale
, variant
, sizeof(variant
), ec
);
377 if (variant
[0] != 0) {
378 variantType
= (uint32_t)(0 == uprv_strcmp(variant
, VAR_EURO
))
379 | ((uint32_t)(0 == uprv_strcmp(variant
, VAR_PRE_EURO
)) << 1);
382 uprv_strcat(countryAndVariant
, VAR_DELIM_STR
);
383 uprv_strcat(countryAndVariant
, variant
);
389 // ------------------------------------------
393 //-------------------------------------------
395 // don't use ICUService since we don't need fallback
398 static UBool U_CALLCONV
currency_cleanup(void);
401 #if !UCONFIG_NO_SERVICE
404 static UMutex gCRegLock
= U_MUTEX_INITIALIZER
;
405 static CReg
* gCRegHead
= 0;
407 struct CReg
: public icu::UMemory
{
409 UChar iso
[ISO_CURRENCY_CODE_LENGTH
+1];
410 char id
[ULOC_FULLNAME_CAPACITY
];
412 CReg(const UChar
* _iso
, const char* _id
)
415 int32_t len
= (int32_t)uprv_strlen(_id
);
416 if (len
> (int32_t)(sizeof(id
)-1)) {
417 len
= (sizeof(id
)-1);
419 uprv_strncpy(id
, _id
, len
);
421 u_memcpy(iso
, _iso
, ISO_CURRENCY_CODE_LENGTH
);
422 iso
[ISO_CURRENCY_CODE_LENGTH
] = 0;
425 static UCurrRegistryKey
reg(const UChar
* _iso
, const char* _id
, UErrorCode
* status
)
427 if (status
&& U_SUCCESS(*status
) && _iso
&& _id
) {
428 CReg
* n
= new CReg(_iso
, _id
);
430 umtx_lock(&gCRegLock
);
432 /* register for the first time */
433 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
437 umtx_unlock(&gCRegLock
);
440 *status
= U_MEMORY_ALLOCATION_ERROR
;
445 static UBool
unreg(UCurrRegistryKey key
) {
447 umtx_lock(&gCRegLock
);
449 CReg
** p
= &gCRegHead
;
452 *p
= ((CReg
*)key
)->next
;
460 umtx_unlock(&gCRegLock
);
464 static const UChar
* get(const char* id
) {
465 const UChar
* result
= NULL
;
466 umtx_lock(&gCRegLock
);
469 /* register cleanup of the mutex */
470 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
472 if (uprv_strcmp(id
, p
->id
) == 0) {
478 umtx_unlock(&gCRegLock
);
482 /* This doesn't need to be thread safe. It's for u_cleanup only. */
483 static void cleanup(void) {
486 gCRegHead
= gCRegHead
->next
;
492 // -------------------------------------
494 U_CAPI UCurrRegistryKey U_EXPORT2
495 ucurr_register(const UChar
* isoCode
, const char* locale
, UErrorCode
*status
)
497 if (status
&& U_SUCCESS(*status
)) {
498 char id
[ULOC_FULLNAME_CAPACITY
];
499 idForLocale(locale
, id
, sizeof(id
), status
);
500 return CReg::reg(isoCode
, id
, status
);
505 // -------------------------------------
507 U_CAPI UBool U_EXPORT2
508 ucurr_unregister(UCurrRegistryKey key
, UErrorCode
* status
)
510 if (status
&& U_SUCCESS(*status
)) {
511 return CReg::unreg(key
);
515 #endif /* UCONFIG_NO_SERVICE */
517 // -------------------------------------
520 * Release all static memory held by currency.
522 /*The declaration here is needed so currency_cleanup(void)
523 * can call this function.
525 static UBool U_CALLCONV
526 currency_cache_cleanup(void);
529 static UBool U_CALLCONV
currency_cleanup(void) {
530 #if !UCONFIG_NO_SERVICE
534 * There might be some cached currency data or isoCodes data.
536 currency_cache_cleanup();
538 currSymbolsEquiv_cleanup();
544 // -------------------------------------
546 U_CAPI
int32_t U_EXPORT2
547 ucurr_forLocale(const char* locale
,
549 int32_t buffCapacity
,
551 if (U_FAILURE(*ec
)) { return 0; }
552 if (buffCapacity
< 0 || (buff
== nullptr && buffCapacity
> 0)) {
553 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
557 char currency
[4]; // ISO currency codes are alpha3 codes.
558 UErrorCode localStatus
= U_ZERO_ERROR
;
559 int32_t resLen
= uloc_getKeywordValue(locale
, "currency",
560 currency
, UPRV_LENGTHOF(currency
), &localStatus
);
561 if (U_SUCCESS(localStatus
) && resLen
== 3 && uprv_isInvariantString(currency
, resLen
)) {
562 if (resLen
< buffCapacity
) {
563 T_CString_toUpperCase(currency
);
564 u_charsToUChars(currency
, buff
, resLen
);
566 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
569 // get country or country_variant in `id'
570 char id
[ULOC_FULLNAME_CAPACITY
];
571 uint32_t variantType
= idForLocale(locale
, id
, UPRV_LENGTHOF(id
), ec
);
572 if (U_FAILURE(*ec
)) {
576 #if !UCONFIG_NO_SERVICE
577 const UChar
* result
= CReg::get(id
);
579 if(buffCapacity
> u_strlen(result
)) {
580 u_strcpy(buff
, result
);
582 resLen
= u_strlen(result
);
583 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
586 // Remove variants, which is only needed for registration.
587 char *idDelim
= uprv_strchr(id
, VAR_DELIM
);
592 const UChar
* s
= NULL
; // Currency code from data file.
594 // No point looking in the data for an empty string.
595 // This is what we would get.
596 localStatus
= U_MISSING_RESOURCE_ERROR
;
598 // Look up the CurrencyMap element in the root bundle.
599 localStatus
= U_ZERO_ERROR
;
600 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
601 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
602 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
603 UResourceBundle
*currencyReq
= ures_getByIndex(countryArray
, 0, NULL
, &localStatus
);
604 s
= ures_getStringByKey(currencyReq
, "id", &resLen
, &localStatus
);
606 // Get the second item when PREEURO is requested, and this is a known Euro country.
607 // If the requested variant is PREEURO, and this isn't a Euro country,
608 // assume that the country changed over to the Euro in the future.
609 // This is probably an old version of ICU that hasn't been updated yet.
610 // The latest currency is probably correct.
611 if (U_SUCCESS(localStatus
)) {
612 if ((variantType
& VARIANT_IS_PREEURO
) && u_strcmp(s
, EUR_STR
) == 0) {
613 currencyReq
= ures_getByIndex(countryArray
, 1, currencyReq
, &localStatus
);
614 s
= ures_getStringByKey(currencyReq
, "id", &resLen
, &localStatus
);
615 } else if ((variantType
& VARIANT_IS_EURO
)) {
619 ures_close(currencyReq
);
620 ures_close(countryArray
);
623 if ((U_FAILURE(localStatus
)) && strchr(id
, '_') != 0) {
624 // We don't know about it. Check to see if we support the variant.
625 uloc_getParent(locale
, id
, UPRV_LENGTHOF(id
), ec
);
626 *ec
= U_USING_FALLBACK_WARNING
;
627 // TODO: Loop over the shortened id rather than recursing and
628 // looking again for a currency keyword.
629 return ucurr_forLocale(id
, buff
, buffCapacity
, ec
);
631 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
) {
632 // There is nothing to fallback to. Report the failure/warning if possible.
635 if (U_SUCCESS(*ec
)) {
636 if(buffCapacity
> resLen
) {
640 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
646 * Modify the given locale name by removing the rightmost _-delimited
647 * element. If there is none, empty the string ("" == root).
648 * NOTE: The string "root" is not recognized; do not use it.
649 * @return TRUE if the fallback happened; FALSE if locale is already
652 static UBool
fallback(char *loc
) {
656 UErrorCode status
= U_ZERO_ERROR
;
657 if (uprv_strcmp(loc
, "en_GB") == 0) {
658 // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en"
659 // in order to consume the correct data strings. This hack will be removed
660 // when proper data sink loading is implemented here.
661 // NOTE: "001" adds 1 char over "GB". However, both call sites allocate
662 // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001).
663 uprv_strcpy(loc
+ 3, "001");
665 uloc_getParent(loc
, loc
, (int32_t)uprv_strlen(loc
), &status
);
668 char *i = uprv_strrchr(loc, '_');
678 U_CAPI
const UChar
* U_EXPORT2
679 ucurr_getName(const UChar
* currency
,
681 UCurrNameStyle nameStyle
,
682 UBool
* isChoiceFormat
, // fillin
683 int32_t* len
, // fillin
686 // Look up the Currencies resource for the given locale. The
687 // Currencies locale data looks like this:
690 //| USD { "US$", "US Dollar" }
691 //| CHF { "Sw F", "Swiss Franc" }
692 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
697 if (U_FAILURE(*ec
)) {
701 int32_t choice
= (int32_t) nameStyle
;
702 if (choice
< 0 || choice
> 2) {
703 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
707 // In the future, resource bundles may implement multi-level
708 // fallback. That is, if a currency is not found in the en_US
709 // Currencies data, then the en Currencies data will be searched.
710 // Currently, if a Currencies datum exists in en_US and en, the
711 // en_US entry hides that in en.
713 // We want multi-level fallback for this resource, so we implement
716 // Use a separate UErrorCode here that does not propagate out of
718 UErrorCode ec2
= U_ZERO_ERROR
;
720 char loc
[ULOC_FULLNAME_CAPACITY
];
721 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
722 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
723 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
727 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
728 myUCharsToChars(buf
, currency
);
730 /* Normalize the keyword value to uppercase */
731 T_CString_toUpperCase(buf
);
733 const UChar
* s
= NULL
;
735 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_CURR
, loc
, &ec2
));
737 if (nameStyle
== UCURR_NARROW_SYMBOL_NAME
) {
739 key
.append(CURRENCIES_NARROW
, ec2
);
740 key
.append("/", ec2
);
741 key
.append(buf
, ec2
);
742 s
= ures_getStringByKeyWithFallback(rb
.getAlias(), key
.data(), len
, &ec2
);
744 ures_getByKey(rb
.getAlias(), CURRENCIES
, rb
.getAlias(), &ec2
);
745 ures_getByKeyWithFallback(rb
.getAlias(), buf
, rb
.getAlias(), &ec2
);
746 s
= ures_getStringByIndex(rb
.getAlias(), choice
, len
, &ec2
);
749 // If we've succeeded we're done. Otherwise, try to fallback.
750 // If that fails (because we are already at root) then exit.
751 if (U_SUCCESS(ec2
)) {
752 if (ec2
== U_USING_DEFAULT_WARNING
753 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
758 // We no longer support choice format data in names. Data should not contain
760 *isChoiceFormat
= FALSE
;
761 if (U_SUCCESS(ec2
)) {
766 // If we fail to find a match, use the ISO 4217 code
767 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
768 *ec
= U_USING_DEFAULT_WARNING
;
772 U_CAPI
const UChar
* U_EXPORT2
773 ucurr_getPluralName(const UChar
* currency
,
775 UBool
* isChoiceFormat
,
776 const char* pluralCount
,
777 int32_t* len
, // fillin
779 // Look up the Currencies resource for the given locale. The
780 // Currencies locale data looks like this:
782 //| CurrencyPlurals {
785 //| other{"US dollars"}
790 if (U_FAILURE(*ec
)) {
794 // Use a separate UErrorCode here that does not propagate out of
796 UErrorCode ec2
= U_ZERO_ERROR
;
798 char loc
[ULOC_FULLNAME_CAPACITY
];
799 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
800 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
801 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
805 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
806 myUCharsToChars(buf
, currency
);
808 const UChar
* s
= NULL
;
810 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
812 rb
= ures_getByKey(rb
, CURRENCYPLURALS
, rb
, &ec2
);
814 // Fetch resource with multi-level resource inheritance fallback
815 rb
= ures_getByKeyWithFallback(rb
, buf
, rb
, &ec2
);
817 s
= ures_getStringByKeyWithFallback(rb
, pluralCount
, len
, &ec2
);
818 if (U_FAILURE(ec2
)) {
819 // fall back to "other"
821 s
= ures_getStringByKeyWithFallback(rb
, "other", len
, &ec2
);
822 if (U_FAILURE(ec2
)) {
824 // fall back to long name in Currencies
825 return ucurr_getName(currency
, locale
, UCURR_LONG_NAME
,
826 isChoiceFormat
, len
, ec
);
831 // If we've succeeded we're done. Otherwise, try to fallback.
832 // If that fails (because we are already at root) then exit.
833 if (U_SUCCESS(ec2
)) {
834 if (ec2
== U_USING_DEFAULT_WARNING
835 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
842 // If we fail to find a match, use the ISO 4217 code
843 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
844 *ec
= U_USING_DEFAULT_WARNING
;
849 //========================================================================
850 // Following are structure and function for parsing currency names
852 #define NEED_TO_BE_DELETED 0x1
854 // TODO: a better way to define this?
855 #define MAX_CURRENCY_NAME_LEN 100
858 const char* IsoCode
; // key
859 UChar
* currencyName
; // value
860 int32_t currencyNameLen
; // value length
861 int32_t flag
; // flags
862 } CurrencyNameStruct
;
866 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
870 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
874 // Comparason function used in quick sort.
875 static int U_CALLCONV
currencyNameComparator(const void* a
, const void* b
) {
876 const CurrencyNameStruct
* currName_1
= (const CurrencyNameStruct
*)a
;
877 const CurrencyNameStruct
* currName_2
= (const CurrencyNameStruct
*)b
;
879 i
< MIN(currName_1
->currencyNameLen
, currName_2
->currencyNameLen
);
881 if (currName_1
->currencyName
[i
] < currName_2
->currencyName
[i
]) {
884 if (currName_1
->currencyName
[i
] > currName_2
->currencyName
[i
]) {
888 if (currName_1
->currencyNameLen
< currName_2
->currencyNameLen
) {
890 } else if (currName_1
->currencyNameLen
> currName_2
->currencyNameLen
) {
897 // Give a locale, return the maximum number of currency names associated with
899 // It gets currency names from resource bundles using fallback.
900 // It is the maximum number because in the fallback chain, some of the
901 // currency names are duplicated.
902 // For example, given locale as "en_US", the currency names get from resource
903 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
904 // all currency names in "en_US" and "en".
906 getCurrencyNameCount(const char* loc
, int32_t* total_currency_name_count
, int32_t* total_currency_symbol_count
) {
908 *total_currency_name_count
= 0;
909 *total_currency_symbol_count
= 0;
910 const UChar
* s
= NULL
;
911 char locale
[ULOC_FULLNAME_CAPACITY
];
912 uprv_strcpy(locale
, loc
);
913 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
915 UErrorCode ec2
= U_ZERO_ERROR
;
916 // TODO: ures_openDirect?
917 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, locale
, &ec2
);
918 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
919 int32_t n
= ures_getSize(curr
);
920 for (int32_t i
=0; i
<n
; ++i
) {
921 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
923 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
924 ++(*total_currency_symbol_count
); // currency symbol
925 if (currencySymbolsEquiv
!= NULL
) {
926 *total_currency_symbol_count
+= countEquivalent(*currencySymbolsEquiv
, UnicodeString(TRUE
, s
, len
));
928 ++(*total_currency_symbol_count
); // iso code
929 ++(*total_currency_name_count
); // long name
934 UErrorCode ec3
= U_ZERO_ERROR
;
935 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec3
);
936 n
= ures_getSize(curr_p
);
937 for (int32_t i
=0; i
<n
; ++i
) {
938 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec3
);
939 *total_currency_name_count
+= ures_getSize(names
);
946 if (!fallback(locale
)) {
953 toUpperCase(const UChar
* source
, int32_t len
, const char* locale
) {
955 UErrorCode ec
= U_ZERO_ERROR
;
956 int32_t destLen
= u_strToUpper(dest
, 0, source
, len
, locale
, &ec
);
959 dest
= (UChar
*)uprv_malloc(sizeof(UChar
) * MAX(destLen
, len
));
960 u_strToUpper(dest
, destLen
, source
, len
, locale
, &ec
);
962 u_memcpy(dest
, source
, len
);
968 // Collect all available currency names associated with the given locale
969 // (enable fallback chain).
970 // Read currenc names defined in resource bundle "Currencies" and
971 // "CurrencyPlural", enable fallback chain.
972 // return the malloc-ed currency name arrays and the total number of currency
973 // names in the array.
975 collectCurrencyNames(const char* locale
,
976 CurrencyNameStruct
** currencyNames
,
977 int32_t* total_currency_name_count
,
978 CurrencyNameStruct
** currencySymbols
,
979 int32_t* total_currency_symbol_count
,
982 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
983 // Look up the Currencies resource for the given locale.
984 UErrorCode ec2
= U_ZERO_ERROR
;
986 char loc
[ULOC_FULLNAME_CAPACITY
];
987 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
988 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
989 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
992 // Get maximum currency name count first.
993 getCurrencyNameCount(loc
, total_currency_name_count
, total_currency_symbol_count
);
995 *currencyNames
= (CurrencyNameStruct
*)uprv_malloc
996 (sizeof(CurrencyNameStruct
) * (*total_currency_name_count
));
997 *currencySymbols
= (CurrencyNameStruct
*)uprv_malloc
998 (sizeof(CurrencyNameStruct
) * (*total_currency_symbol_count
));
1000 if(currencyNames
== NULL
|| currencySymbols
== NULL
) {
1001 ec
= U_MEMORY_ALLOCATION_ERROR
;
1004 if (U_FAILURE(ec
)) return;
1006 const UChar
* s
= NULL
; // currency name
1007 char* iso
= NULL
; // currency ISO code
1009 *total_currency_name_count
= 0;
1010 *total_currency_symbol_count
= 0;
1012 UErrorCode ec3
= U_ZERO_ERROR
;
1013 UErrorCode ec4
= U_ZERO_ERROR
;
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
) {
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
);
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
);
1033 if (uhash_get(currencyIsoCodes
, iso
) != NULL
) {
1037 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
1040 // Add currency symbol.
1041 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1042 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)s
;
1043 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1044 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= len
;
1045 // Add equivalent symbols
1046 if (currencySymbolsEquiv
!= NULL
) {
1047 UnicodeString
str(TRUE
, s
, len
);
1048 icu::EquivIterator
iter(*currencySymbolsEquiv
, str
);
1049 const UnicodeString
*symbol
;
1050 while ((symbol
= iter
.next()) != NULL
) {
1051 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1052 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
=
1053 const_cast<UChar
*>(symbol
->getBuffer());
1054 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1055 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= symbol
->length();
1059 // Add currency long name.
1060 s
= ures_getStringByIndex(names
, UCURR_LONG_NAME
, &len
, &ec2
);
1061 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1062 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1063 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1064 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1065 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1067 // put (iso, 3, and iso) in to array
1068 // Add currency ISO code.
1069 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1070 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)uprv_malloc(sizeof(UChar
)*3);
1071 // Must convert iso[] into Unicode
1072 u_charsToUChars(iso
, (*currencySymbols
)[*total_currency_symbol_count
].currencyName
, 3);
1073 (*currencySymbols
)[*total_currency_symbol_count
].flag
= NEED_TO_BE_DELETED
;
1074 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= 3;
1080 UErrorCode ec3
= U_ZERO_ERROR
;
1081 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec3
);
1082 n
= ures_getSize(curr_p
);
1083 for (int32_t i
=0; i
<n
; ++i
) {
1084 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec3
);
1085 iso
= (char*)ures_getKey(names
);
1086 // Using hash to remove duplicated ISO codes in fallback chain.
1087 if (localeLevel
== 0) {
1088 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1090 if (uhash_get(currencyPluralIsoCodes
, iso
) != NULL
) {
1094 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1097 int32_t num
= ures_getSize(names
);
1099 for (int32_t j
= 0; j
< num
; ++j
) {
1100 // TODO: remove duplicates between singular name and
1101 // currency long name?
1102 s
= ures_getStringByIndex(names
, j
, &len
, &ec3
);
1103 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1104 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1105 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1106 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1107 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1115 if (!fallback(loc
)) {
1120 uhash_close(currencyIsoCodes
);
1121 uhash_close(currencyPluralIsoCodes
);
1123 // quick sort the struct
1124 qsort(*currencyNames
, *total_currency_name_count
,
1125 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1126 qsort(*currencySymbols
, *total_currency_symbol_count
,
1127 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1130 printf("currency name count: %d\n", *total_currency_name_count
);
1131 for (int32_t index
= 0; index
< *total_currency_name_count
; ++index
) {
1132 printf("index: %d\n", index
);
1133 printf("iso: %s\n", (*currencyNames
)[index
].IsoCode
);
1134 char curNameBuf
[1024];
1135 memset(curNameBuf
, 0, 1024);
1136 u_austrncpy(curNameBuf
, (*currencyNames
)[index
].currencyName
, (*currencyNames
)[index
].currencyNameLen
);
1137 printf("currencyName: %s\n", curNameBuf
);
1138 printf("len: %d\n", (*currencyNames
)[index
].currencyNameLen
);
1140 printf("currency symbol count: %d\n", *total_currency_symbol_count
);
1141 for (int32_t index
= 0; index
< *total_currency_symbol_count
; ++index
) {
1142 printf("index: %d\n", index
);
1143 printf("iso: %s\n", (*currencySymbols
)[index
].IsoCode
);
1144 char curNameBuf
[1024];
1145 memset(curNameBuf
, 0, 1024);
1146 u_austrncpy(curNameBuf
, (*currencySymbols
)[index
].currencyName
, (*currencySymbols
)[index
].currencyNameLen
);
1147 printf("currencySymbol: %s\n", curNameBuf
);
1148 printf("len: %d\n", (*currencySymbols
)[index
].currencyNameLen
);
1151 // fail on hashtable errors
1152 if (U_FAILURE(ec3
)) {
1156 if (U_FAILURE(ec4
)) {
1162 // @param currencyNames: currency names array
1163 // @param indexInCurrencyNames: the index of the character in currency names
1164 // array against which the comparison is done
1165 // @param key: input text char to compare against
1166 // @param begin(IN/OUT): the begin index of matching range in currency names array
1167 // @param end(IN/OUT): the end index of matching range in currency names array.
1169 binarySearch(const CurrencyNameStruct
* currencyNames
,
1170 int32_t indexInCurrencyNames
,
1172 int32_t* begin
, int32_t* end
) {
1174 printf("key = %x\n", key
);
1176 int32_t first
= *begin
;
1177 int32_t last
= *end
;
1178 while (first
<= last
) {
1179 int32_t mid
= (first
+ last
) / 2; // compute mid point.
1180 if (indexInCurrencyNames
>= currencyNames
[mid
].currencyNameLen
) {
1183 if (key
> currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1186 else if (key
< currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1190 // Find a match, and looking for ranges
1191 // Now do two more binary searches. First, on the left side for
1192 // the greatest L such that CurrencyNameStruct[L] < key.
1197 printf("mid = %d\n", mid
);
1200 int32_t M
= (L
+ R
) / 2;
1202 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1204 if (indexInCurrencyNames
>= currencyNames
[M
].currencyNameLen
) {
1207 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] < key
) {
1211 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1222 printf("begin = %d\n", *begin
);
1223 U_ASSERT(currencyNames
[*begin
].currencyName
[indexInCurrencyNames
] == key
);
1226 // Now for the second search, finding the least R such that
1227 // key < CurrencyNameStruct[R].
1231 int32_t M
= (L
+ R
) / 2;
1233 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1235 if (currencyNames
[M
].currencyNameLen
< indexInCurrencyNames
) {
1238 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] > key
) {
1242 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1251 if (currencyNames
[R
].currencyName
[indexInCurrencyNames
] > key
) {
1257 printf("end = %d\n", *end
);
1260 // now, found the range. check whether there is exact match
1261 if (currencyNames
[*begin
].currencyNameLen
== indexInCurrencyNames
+ 1) {
1262 return *begin
; // find range and exact match.
1264 return -1; // find range, but no exact match.
1270 return -1; // failed to find range.
1274 // Linear search "text" in "currencyNames".
1275 // @param begin, end: the begin and end index in currencyNames, within which
1276 // range should the search be performed.
1277 // @param textLen: the length of the text to be compared
1278 // @param maxMatchLen(IN/OUT): passing in the computed max matching length
1279 // pass out the new max matching length
1280 // @param maxMatchIndex: the index in currencyName which has the longest
1281 // match with input text.
1283 linearSearch(const CurrencyNameStruct
* currencyNames
,
1284 int32_t begin
, int32_t end
,
1285 const UChar
* text
, int32_t textLen
,
1286 int32_t *partialMatchLen
,
1287 int32_t *maxMatchLen
, int32_t* maxMatchIndex
) {
1288 int32_t initialPartialMatchLen
= *partialMatchLen
;
1289 for (int32_t index
= begin
; index
<= end
; ++index
) {
1290 int32_t len
= currencyNames
[index
].currencyNameLen
;
1291 if (len
> *maxMatchLen
&& len
<= textLen
&&
1292 uprv_memcmp(currencyNames
[index
].currencyName
, text
, len
* sizeof(UChar
)) == 0) {
1293 *partialMatchLen
= MAX(*partialMatchLen
, len
);
1294 *maxMatchIndex
= index
;
1297 printf("maxMatchIndex = %d, maxMatchLen = %d\n",
1298 *maxMatchIndex
, *maxMatchLen
);
1301 // Check for partial matches.
1302 for (int32_t i
=initialPartialMatchLen
; i
<MIN(len
, textLen
); i
++) {
1303 if (currencyNames
[index
].currencyName
[i
] != text
[i
]) {
1306 *partialMatchLen
= MAX(*partialMatchLen
, i
+ 1);
1312 #define LINEAR_SEARCH_THRESHOLD 10
1314 // Find longest match between "text" and currency names in "currencyNames".
1315 // @param total_currency_count: total number of currency names in CurrencyNames.
1316 // @param textLen: the length of the text to be compared
1317 // @param maxMatchLen: passing in the computed max matching length
1318 // pass out the new max matching length
1319 // @param maxMatchIndex: the index in currencyName which has the longest
1320 // match with input text.
1322 searchCurrencyName(const CurrencyNameStruct
* currencyNames
,
1323 int32_t total_currency_count
,
1324 const UChar
* text
, int32_t textLen
,
1325 int32_t *partialMatchLen
,
1326 int32_t* maxMatchLen
, int32_t* maxMatchIndex
) {
1327 *maxMatchIndex
= -1;
1329 int32_t matchIndex
= -1;
1330 int32_t binarySearchBegin
= 0;
1331 int32_t binarySearchEnd
= total_currency_count
- 1;
1332 // It is a variant of binary search.
1333 // For example, given the currency names in currencyNames array are:
1334 // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
1335 // and the input text is BBEXST
1336 // The first round binary search search "B" in the text against
1337 // the first char in currency names, and find the first char matching range
1338 // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
1339 // The 2nd round binary search search the second "B" in the text against
1340 // the 2nd char in currency names, and narrow the matching range to
1341 // "BB BBEX BBEXYZ" (and the maximum matching "BB").
1342 // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
1343 // maximum matching).
1344 // The 4th round returns the same range (the maximum matching is "BBEX").
1345 // The 5th round returns no matching range.
1346 for (int32_t index
= 0; index
< textLen
; ++index
) {
1347 // matchIndex saves the one with exact match till the current point.
1348 // [binarySearchBegin, binarySearchEnd] saves the matching range.
1349 matchIndex
= binarySearch(currencyNames
, index
,
1351 &binarySearchBegin
, &binarySearchEnd
);
1352 if (binarySearchBegin
== -1) { // did not find the range
1355 *partialMatchLen
= MAX(*partialMatchLen
, index
+ 1);
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
;
1362 if (binarySearchEnd
- binarySearchBegin
< LINEAR_SEARCH_THRESHOLD
) {
1363 // linear search if within threshold.
1364 linearSearch(currencyNames
, binarySearchBegin
, binarySearchEnd
,
1367 maxMatchLen
, maxMatchIndex
);
1374 //========================= currency name cache =====================
1376 char locale
[ULOC_FULLNAME_CAPACITY
]; //key
1377 // currency names, case insensitive
1378 CurrencyNameStruct
* currencyNames
; // value
1379 int32_t totalCurrencyNameCount
; // currency name count
1380 // currency symbols and ISO code, case sensitive
1381 CurrencyNameStruct
* currencySymbols
; // value
1382 int32_t totalCurrencySymbolCount
; // count
1384 // reference count is set to 1 when an entry is put to cache.
1385 // it increases by 1 before accessing, and decreased by 1 after accessing.
1386 // The entry is deleted when ref count is zero, which means
1387 // the entry is replaced out of cache and no process is accessing it.
1389 } CurrencyNameCacheEntry
;
1392 #define CURRENCY_NAME_CACHE_NUM 10
1394 // Reserve 10 cache entries.
1395 static CurrencyNameCacheEntry
* currCache
[CURRENCY_NAME_CACHE_NUM
] = {NULL
};
1396 // Using an index to indicate which entry to be replaced when cache is full.
1397 // It is a simple round-robin replacement strategy.
1398 static int8_t currentCacheEntryIndex
= 0;
1400 static UMutex gCurrencyCacheMutex
= U_MUTEX_INITIALIZER
;
1404 deleteCurrencyNames(CurrencyNameStruct
* currencyNames
, int32_t count
) {
1405 for (int32_t index
= 0; index
< count
; ++index
) {
1406 if ( (currencyNames
[index
].flag
& NEED_TO_BE_DELETED
) ) {
1407 uprv_free(currencyNames
[index
].currencyName
);
1410 uprv_free(currencyNames
);
1415 deleteCacheEntry(CurrencyNameCacheEntry
* entry
) {
1416 deleteCurrencyNames(entry
->currencyNames
, entry
->totalCurrencyNameCount
);
1417 deleteCurrencyNames(entry
->currencySymbols
, entry
->totalCurrencySymbolCount
);
1423 static UBool U_CALLCONV
1424 currency_cache_cleanup(void) {
1425 for (int32_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1427 deleteCacheEntry(currCache
[i
]);
1436 * Loads the currency name data from the cache, or from resource bundles if necessary.
1437 * The refCount is automatically incremented. It is the caller's responsibility
1438 * to decrement it when done!
1440 static CurrencyNameCacheEntry
*
1441 getCacheEntry(const char* locale
, UErrorCode
& ec
) {
1443 int32_t total_currency_name_count
= 0;
1444 CurrencyNameStruct
* currencyNames
= NULL
;
1445 int32_t total_currency_symbol_count
= 0;
1446 CurrencyNameStruct
* currencySymbols
= NULL
;
1447 CurrencyNameCacheEntry
* cacheEntry
= NULL
;
1449 umtx_lock(&gCurrencyCacheMutex
);
1450 // in order to handle racing correctly,
1451 // not putting 'search' in a separate function.
1453 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1454 if (currCache
[i
]!= NULL
&&
1455 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1461 cacheEntry
= currCache
[found
];
1462 ++(cacheEntry
->refCount
);
1464 umtx_unlock(&gCurrencyCacheMutex
);
1466 collectCurrencyNames(locale
, ¤cyNames
, &total_currency_name_count
, ¤cySymbols
, &total_currency_symbol_count
, ec
);
1467 if (U_FAILURE(ec
)) {
1470 umtx_lock(&gCurrencyCacheMutex
);
1473 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1474 if (currCache
[i
]!= NULL
&&
1475 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1481 // insert new entry to
1482 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1483 // and remove the existing entry
1484 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1486 cacheEntry
= currCache
[currentCacheEntryIndex
];
1488 --(cacheEntry
->refCount
);
1489 // delete if the ref count is zero
1490 if (cacheEntry
->refCount
== 0) {
1491 deleteCacheEntry(cacheEntry
);
1494 cacheEntry
= (CurrencyNameCacheEntry
*)uprv_malloc(sizeof(CurrencyNameCacheEntry
));
1495 currCache
[currentCacheEntryIndex
] = cacheEntry
;
1496 uprv_strcpy(cacheEntry
->locale
, locale
);
1497 cacheEntry
->currencyNames
= currencyNames
;
1498 cacheEntry
->totalCurrencyNameCount
= total_currency_name_count
;
1499 cacheEntry
->currencySymbols
= currencySymbols
;
1500 cacheEntry
->totalCurrencySymbolCount
= total_currency_symbol_count
;
1501 cacheEntry
->refCount
= 2; // one for cache, one for reference
1502 currentCacheEntryIndex
= (currentCacheEntryIndex
+ 1) % CURRENCY_NAME_CACHE_NUM
;
1503 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
1505 deleteCurrencyNames(currencyNames
, total_currency_name_count
);
1506 deleteCurrencyNames(currencySymbols
, total_currency_symbol_count
);
1507 cacheEntry
= currCache
[found
];
1508 ++(cacheEntry
->refCount
);
1510 umtx_unlock(&gCurrencyCacheMutex
);
1516 static void releaseCacheEntry(CurrencyNameCacheEntry
* cacheEntry
) {
1517 umtx_lock(&gCurrencyCacheMutex
);
1518 --(cacheEntry
->refCount
);
1519 if (cacheEntry
->refCount
== 0) { // remove
1520 deleteCacheEntry(cacheEntry
);
1522 umtx_unlock(&gCurrencyCacheMutex
);
1526 uprv_parseCurrency(const char* locale
,
1527 const icu::UnicodeString
& text
,
1528 icu::ParsePosition
& pos
,
1530 int32_t* partialMatchLen
,
1534 if (U_FAILURE(ec
)) {
1537 CurrencyNameCacheEntry
* cacheEntry
= getCacheEntry(locale
, ec
);
1538 if (U_FAILURE(ec
)) {
1542 int32_t total_currency_name_count
= cacheEntry
->totalCurrencyNameCount
;
1543 CurrencyNameStruct
* currencyNames
= cacheEntry
->currencyNames
;
1544 int32_t total_currency_symbol_count
= cacheEntry
->totalCurrencySymbolCount
;
1545 CurrencyNameStruct
* currencySymbols
= cacheEntry
->currencySymbols
;
1547 int32_t start
= pos
.getIndex();
1549 UChar inputText
[MAX_CURRENCY_NAME_LEN
];
1550 UChar upperText
[MAX_CURRENCY_NAME_LEN
];
1551 int32_t textLen
= MIN(MAX_CURRENCY_NAME_LEN
, text
.length() - start
);
1552 text
.extract(start
, textLen
, inputText
);
1553 UErrorCode ec1
= U_ZERO_ERROR
;
1554 textLen
= u_strToUpper(upperText
, MAX_CURRENCY_NAME_LEN
, inputText
, textLen
, locale
, &ec1
);
1556 // Make sure partialMatchLen is initialized
1557 *partialMatchLen
= 0;
1560 int32_t matchIndex
= -1;
1561 // case in-sensitive comparision against currency names
1562 searchCurrencyName(currencyNames
, total_currency_name_count
,
1563 upperText
, textLen
, partialMatchLen
, &max
, &matchIndex
);
1566 printf("search in names, max = %d, matchIndex = %d\n", max
, matchIndex
);
1569 int32_t maxInSymbol
= 0;
1570 int32_t matchIndexInSymbol
= -1;
1571 if (type
!= UCURR_LONG_NAME
) { // not name only
1572 // case sensitive comparison against currency symbols and ISO code.
1573 searchCurrencyName(currencySymbols
, total_currency_symbol_count
,
1576 &maxInSymbol
, &matchIndexInSymbol
);
1580 printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol
, matchIndexInSymbol
);
1581 if(matchIndexInSymbol
!= -1) {
1582 printf("== ISO=%s\n", currencySymbols
[matchIndexInSymbol
].IsoCode
);
1586 if (max
>= maxInSymbol
&& matchIndex
!= -1) {
1587 u_charsToUChars(currencyNames
[matchIndex
].IsoCode
, result
, 4);
1588 pos
.setIndex(start
+ max
);
1589 } else if (maxInSymbol
>= max
&& matchIndexInSymbol
!= -1) {
1590 u_charsToUChars(currencySymbols
[matchIndexInSymbol
].IsoCode
, result
, 4);
1591 pos
.setIndex(start
+ maxInSymbol
);
1594 // decrease reference count
1595 releaseCacheEntry(cacheEntry
);
1598 void uprv_currencyLeads(const char* locale
, icu::UnicodeSet
& result
, UErrorCode
& ec
) {
1600 if (U_FAILURE(ec
)) {
1603 CurrencyNameCacheEntry
* cacheEntry
= getCacheEntry(locale
, ec
);
1604 if (U_FAILURE(ec
)) {
1608 for (int32_t i
=0; i
<cacheEntry
->totalCurrencySymbolCount
; i
++) {
1609 const CurrencyNameStruct
& info
= cacheEntry
->currencySymbols
[i
];
1611 U16_GET(info
.currencyName
, 0, 0, info
.currencyNameLen
, cp
);
1615 for (int32_t i
=0; i
<cacheEntry
->totalCurrencyNameCount
; i
++) {
1616 const CurrencyNameStruct
& info
= cacheEntry
->currencyNames
[i
];
1618 U16_GET(info
.currencyName
, 0, 0, info
.currencyNameLen
, cp
);
1622 // decrease reference count
1623 releaseCacheEntry(cacheEntry
);
1628 * Internal method. Given a currency ISO code and a locale, return
1629 * the "static" currency name. This is usually the same as the
1630 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
1631 * format is applied to the number 2.0 (to yield the more common
1632 * plural) to return a static name.
1634 * This is used for backward compatibility with old currency logic in
1635 * DecimalFormat and DecimalFormatSymbols.
1638 uprv_getStaticCurrencyName(const UChar
* iso
, const char* loc
,
1639 icu::UnicodeString
& result
, UErrorCode
& ec
)
1643 UBool isChoiceFormat
;
1645 const UChar
* currname
= ucurr_getName(iso
, loc
, UCURR_SYMBOL_NAME
,
1646 &isChoiceFormat
, &len
, &ec
);
1647 if (U_SUCCESS(ec
)) {
1648 result
.setTo(currname
, len
);
1652 U_CAPI
int32_t U_EXPORT2
1653 ucurr_getDefaultFractionDigits(const UChar
* currency
, UErrorCode
* ec
) {
1654 return ucurr_getDefaultFractionDigitsForUsage(currency
,UCURR_USAGE_STANDARD
,ec
);
1657 U_DRAFT
int32_t U_EXPORT2
1658 ucurr_getDefaultFractionDigitsForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1659 int32_t fracDigits
= 0;
1660 if (U_SUCCESS(*ec
)) {
1662 case UCURR_USAGE_STANDARD
:
1663 fracDigits
= (_findMetaData(currency
, *ec
))[0];
1665 case UCURR_USAGE_CASH
:
1666 fracDigits
= (_findMetaData(currency
, *ec
))[2];
1669 *ec
= U_UNSUPPORTED_ERROR
;
1675 U_CAPI
double U_EXPORT2
1676 ucurr_getRoundingIncrement(const UChar
* currency
, UErrorCode
* ec
) {
1677 return ucurr_getRoundingIncrementForUsage(currency
, UCURR_USAGE_STANDARD
, ec
);
1680 U_DRAFT
double U_EXPORT2
1681 ucurr_getRoundingIncrementForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1682 double result
= 0.0;
1684 const int32_t *data
= _findMetaData(currency
, *ec
);
1685 if (U_SUCCESS(*ec
)) {
1689 case UCURR_USAGE_STANDARD
:
1690 fracDigits
= data
[0];
1691 increment
= data
[1];
1693 case UCURR_USAGE_CASH
:
1694 fracDigits
= data
[2];
1695 increment
= data
[3];
1698 *ec
= U_UNSUPPORTED_ERROR
;
1702 // If the meta data is invalid, return 0.0
1703 if (fracDigits
< 0 || fracDigits
> MAX_POW10
) {
1704 *ec
= U_INVALID_FORMAT_ERROR
;
1706 // A rounding value of 0 or 1 indicates no rounding.
1707 if (increment
>= 2) {
1708 // Return (increment) / 10^(fracDigits). The only actual rounding data,
1709 // as of this writing, is CHF { 2, 5 }.
1710 result
= double(increment
) / POW10
[fracDigits
];
1720 typedef struct UCurrencyContext
{
1721 uint32_t currType
; /* UCurrCurrencyType */
1726 Please keep this list in alphabetical order.
1727 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
1729 ISO-4217: https://www.iso.org/iso-4217-currency-codes.html
1731 static const struct CurrencyList
{
1732 const char *currency
;
1734 } gCurrencyList
[] = {
1735 {"ADP", UCURR_COMMON
|UCURR_DEPRECATED
},
1736 {"AED", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1737 {"AFA", UCURR_COMMON
|UCURR_DEPRECATED
},
1738 {"AFN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1739 {"ALK", UCURR_COMMON
|UCURR_DEPRECATED
},
1740 {"ALL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1741 {"AMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1742 {"ANG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1743 {"AOA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1744 {"AOK", UCURR_COMMON
|UCURR_DEPRECATED
},
1745 {"AON", UCURR_COMMON
|UCURR_DEPRECATED
},
1746 {"AOR", UCURR_COMMON
|UCURR_DEPRECATED
},
1747 {"ARA", UCURR_COMMON
|UCURR_DEPRECATED
},
1748 {"ARL", UCURR_COMMON
|UCURR_DEPRECATED
},
1749 {"ARM", UCURR_COMMON
|UCURR_DEPRECATED
},
1750 {"ARP", UCURR_COMMON
|UCURR_DEPRECATED
},
1751 {"ARS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1752 {"ATS", UCURR_COMMON
|UCURR_DEPRECATED
},
1753 {"AUD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1754 {"AWG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1755 {"AZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1756 {"AZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1757 {"BAD", UCURR_COMMON
|UCURR_DEPRECATED
},
1758 {"BAM", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1759 {"BAN", UCURR_COMMON
|UCURR_DEPRECATED
},
1760 {"BBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1761 {"BDT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1762 {"BEC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1763 {"BEF", UCURR_COMMON
|UCURR_DEPRECATED
},
1764 {"BEL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1765 {"BGL", UCURR_COMMON
|UCURR_DEPRECATED
},
1766 {"BGM", UCURR_COMMON
|UCURR_DEPRECATED
},
1767 {"BGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1768 {"BGO", UCURR_COMMON
|UCURR_DEPRECATED
},
1769 {"BHD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1770 {"BIF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1771 {"BMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1772 {"BND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1773 {"BOB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1774 {"BOL", UCURR_COMMON
|UCURR_DEPRECATED
},
1775 {"BOP", UCURR_COMMON
|UCURR_DEPRECATED
},
1776 {"BOV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1777 {"BRB", UCURR_COMMON
|UCURR_DEPRECATED
},
1778 {"BRC", UCURR_COMMON
|UCURR_DEPRECATED
},
1779 {"BRE", UCURR_COMMON
|UCURR_DEPRECATED
},
1780 {"BRL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1781 {"BRN", UCURR_COMMON
|UCURR_DEPRECATED
},
1782 {"BRR", UCURR_COMMON
|UCURR_DEPRECATED
},
1783 {"BRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1784 {"BSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1785 {"BTN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1786 {"BUK", UCURR_COMMON
|UCURR_DEPRECATED
},
1787 {"BWP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1788 {"BYB", UCURR_COMMON
|UCURR_DEPRECATED
},
1789 {"BYN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1790 {"BYR", UCURR_COMMON
|UCURR_DEPRECATED
},
1791 {"BZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1792 {"CAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1793 {"CDF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1794 {"CHE", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1795 {"CHF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1796 {"CHW", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1797 {"CLE", UCURR_COMMON
|UCURR_DEPRECATED
},
1798 {"CLF", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1799 {"CLP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1800 {"CNH", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1801 {"CNX", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1802 {"CNY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1803 {"COP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1804 {"COU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1805 {"CRC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1806 {"CSD", UCURR_COMMON
|UCURR_DEPRECATED
},
1807 {"CSK", UCURR_COMMON
|UCURR_DEPRECATED
},
1808 {"CUC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1809 {"CUP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1810 {"CVE", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1811 {"CYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1812 {"CZK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1813 {"DDM", UCURR_COMMON
|UCURR_DEPRECATED
},
1814 {"DEM", UCURR_COMMON
|UCURR_DEPRECATED
},
1815 {"DJF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1816 {"DKK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1817 {"DOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1818 {"DZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1819 {"ECS", UCURR_COMMON
|UCURR_DEPRECATED
},
1820 {"ECV", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1821 {"EEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1822 {"EGP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1823 {"EQE", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1824 {"ERN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1825 {"ESA", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1826 {"ESB", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1827 {"ESP", UCURR_COMMON
|UCURR_DEPRECATED
},
1828 {"ETB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1829 {"EUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1830 {"FIM", UCURR_COMMON
|UCURR_DEPRECATED
},
1831 {"FJD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1832 {"FKP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1833 {"FRF", UCURR_COMMON
|UCURR_DEPRECATED
},
1834 {"GBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1835 {"GEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1836 {"GEL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1837 {"GHC", UCURR_COMMON
|UCURR_DEPRECATED
},
1838 {"GHS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1839 {"GIP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1840 {"GMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1841 {"GNF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1842 {"GNS", UCURR_COMMON
|UCURR_DEPRECATED
},
1843 {"GQE", UCURR_COMMON
|UCURR_DEPRECATED
},
1844 {"GRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1845 {"GTQ", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1846 {"GWE", UCURR_COMMON
|UCURR_DEPRECATED
},
1847 {"GWP", UCURR_COMMON
|UCURR_DEPRECATED
},
1848 {"GYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1849 {"HKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1850 {"HNL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1851 {"HRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1852 {"HRK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1853 {"HTG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1854 {"HUF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1855 {"IDR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1856 {"IEP", UCURR_COMMON
|UCURR_DEPRECATED
},
1857 {"ILP", UCURR_COMMON
|UCURR_DEPRECATED
},
1858 {"ILR", UCURR_COMMON
|UCURR_DEPRECATED
},
1859 {"ILS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1860 {"INR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1861 {"IQD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1862 {"IRR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1863 {"ISJ", UCURR_COMMON
|UCURR_DEPRECATED
},
1864 {"ISK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1865 {"ITL", UCURR_COMMON
|UCURR_DEPRECATED
},
1866 {"JMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1867 {"JOD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1868 {"JPY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1869 {"KES", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1870 {"KGS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1871 {"KHR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1872 {"KMF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1873 {"KPW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1874 {"KRH", UCURR_COMMON
|UCURR_DEPRECATED
},
1875 {"KRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1876 {"KRW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1877 {"KWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1878 {"KYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1879 {"KZT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1880 {"LAK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1881 {"LBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1882 {"LKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1883 {"LRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1884 {"LSL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1885 {"LSM", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1886 {"LTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1887 {"LTT", UCURR_COMMON
|UCURR_DEPRECATED
},
1888 {"LUC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1889 {"LUF", UCURR_COMMON
|UCURR_DEPRECATED
},
1890 {"LUL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1891 {"LVL", UCURR_COMMON
|UCURR_DEPRECATED
},
1892 {"LVR", UCURR_COMMON
|UCURR_DEPRECATED
},
1893 {"LYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1894 {"MAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1895 {"MAF", UCURR_COMMON
|UCURR_DEPRECATED
},
1896 {"MCF", UCURR_COMMON
|UCURR_DEPRECATED
},
1897 {"MDC", UCURR_COMMON
|UCURR_DEPRECATED
},
1898 {"MDL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1899 {"MGA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1900 {"MGF", UCURR_COMMON
|UCURR_DEPRECATED
},
1901 {"MKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1902 {"MKN", UCURR_COMMON
|UCURR_DEPRECATED
},
1903 {"MLF", UCURR_COMMON
|UCURR_DEPRECATED
},
1904 {"MMK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1905 {"MNT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1906 {"MOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1907 {"MRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1908 {"MRU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1909 {"MTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1910 {"MTP", UCURR_COMMON
|UCURR_DEPRECATED
},
1911 {"MUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1912 {"MVP", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1913 {"MVR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1914 {"MWK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1915 {"MXN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1916 {"MXP", UCURR_COMMON
|UCURR_DEPRECATED
},
1917 {"MXV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1918 {"MYR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1919 {"MZE", UCURR_COMMON
|UCURR_DEPRECATED
},
1920 {"MZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1921 {"MZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1922 {"NAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1923 {"NGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1924 {"NIC", UCURR_COMMON
|UCURR_DEPRECATED
},
1925 {"NIO", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1926 {"NLG", UCURR_COMMON
|UCURR_DEPRECATED
},
1927 {"NOK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1928 {"NPR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1929 {"NZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1930 {"OMR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1931 {"PAB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1932 {"PEI", UCURR_COMMON
|UCURR_DEPRECATED
},
1933 {"PEN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1934 {"PES", UCURR_COMMON
|UCURR_DEPRECATED
},
1935 {"PGK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1936 {"PHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1937 {"PKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1938 {"PLN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1939 {"PLZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1940 {"PTE", UCURR_COMMON
|UCURR_DEPRECATED
},
1941 {"PYG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1942 {"QAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1943 {"RHD", UCURR_COMMON
|UCURR_DEPRECATED
},
1944 {"ROL", UCURR_COMMON
|UCURR_DEPRECATED
},
1945 {"RON", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1946 {"RSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1947 {"RUB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1948 {"RUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1949 {"RWF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1950 {"SAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1951 {"SBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1952 {"SCR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1953 {"SDD", UCURR_COMMON
|UCURR_DEPRECATED
},
1954 {"SDG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1955 {"SDP", UCURR_COMMON
|UCURR_DEPRECATED
},
1956 {"SEK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1957 {"SGD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1958 {"SHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1959 {"SIT", UCURR_COMMON
|UCURR_DEPRECATED
},
1960 {"SKK", UCURR_COMMON
|UCURR_DEPRECATED
},
1961 {"SLL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1962 {"SOS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1963 {"SRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1964 {"SRG", UCURR_COMMON
|UCURR_DEPRECATED
},
1965 {"SSP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1966 {"STD", UCURR_COMMON
|UCURR_DEPRECATED
},
1967 {"STN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1968 {"SUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1969 {"SVC", UCURR_COMMON
|UCURR_DEPRECATED
},
1970 {"SYP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1971 {"SZL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1972 {"THB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1973 {"TJR", UCURR_COMMON
|UCURR_DEPRECATED
},
1974 {"TJS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1975 {"TMM", UCURR_COMMON
|UCURR_DEPRECATED
},
1976 {"TMT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1977 {"TND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1978 {"TOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1979 {"TPE", UCURR_COMMON
|UCURR_DEPRECATED
},
1980 {"TRL", UCURR_COMMON
|UCURR_DEPRECATED
},
1981 {"TRY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1982 {"TTD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1983 {"TWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1984 {"TZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1985 {"UAH", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1986 {"UAK", UCURR_COMMON
|UCURR_DEPRECATED
},
1987 {"UGS", UCURR_COMMON
|UCURR_DEPRECATED
},
1988 {"UGX", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1989 {"USD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1990 {"USN", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1991 {"USS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1992 {"UYI", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1993 {"UYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1994 {"UYU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1995 {"UZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1996 {"VEB", UCURR_COMMON
|UCURR_DEPRECATED
},
1997 {"VEF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1998 {"VND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1999 {"VNN", UCURR_COMMON
|UCURR_DEPRECATED
},
2000 {"VUV", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2001 {"WST", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2002 {"XAF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2003 {"XAG", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2004 {"XAU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2005 {"XBA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2006 {"XBB", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2007 {"XBC", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2008 {"XBD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2009 {"XCD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2010 {"XDR", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2011 {"XEU", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
2012 {"XFO", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2013 {"XFU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2014 {"XOF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2015 {"XPD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2016 {"XPF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2017 {"XPT", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2018 {"XRE", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
2019 {"XSU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2020 {"XTS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2021 {"XUA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2022 {"XXX", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
2023 {"YDD", UCURR_COMMON
|UCURR_DEPRECATED
},
2024 {"YER", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2025 {"YUD", UCURR_COMMON
|UCURR_DEPRECATED
},
2026 {"YUM", UCURR_COMMON
|UCURR_DEPRECATED
},
2027 {"YUN", UCURR_COMMON
|UCURR_DEPRECATED
},
2028 {"YUR", UCURR_COMMON
|UCURR_DEPRECATED
},
2029 {"ZAL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
2030 {"ZAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2031 {"ZMK", UCURR_COMMON
|UCURR_DEPRECATED
},
2032 {"ZMW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
2033 {"ZRN", UCURR_COMMON
|UCURR_DEPRECATED
},
2034 {"ZRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
2035 {"ZWD", UCURR_COMMON
|UCURR_DEPRECATED
},
2036 {"ZWL", UCURR_COMMON
|UCURR_DEPRECATED
},
2037 {"ZWR", UCURR_COMMON
|UCURR_DEPRECATED
},
2038 { NULL
, 0 } // Leave here to denote the end of the list.
2041 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
2042 ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
2044 static int32_t U_CALLCONV
2045 ucurr_countCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2046 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2047 uint32_t currType
= myContext
->currType
;
2050 /* Count the number of items matching the type we are looking for. */
2051 for (int32_t idx
= 0; gCurrencyList
[idx
].currency
!= NULL
; idx
++) {
2052 if (UCURR_MATCHES_BITMASK(gCurrencyList
[idx
].currType
, currType
)) {
2059 static const char* U_CALLCONV
2060 ucurr_nextCurrencyList(UEnumeration
*enumerator
,
2061 int32_t* resultLength
,
2062 UErrorCode
* /*pErrorCode*/)
2064 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2066 /* Find the next in the list that matches the type we are looking for. */
2067 while (myContext
->listIdx
< UPRV_LENGTHOF(gCurrencyList
)-1) {
2068 const struct CurrencyList
*currItem
= &gCurrencyList
[myContext
->listIdx
++];
2069 if (UCURR_MATCHES_BITMASK(currItem
->currType
, myContext
->currType
))
2072 *resultLength
= 3; /* Currency codes are only 3 chars long */
2074 return currItem
->currency
;
2077 /* We enumerated too far. */
2084 static void U_CALLCONV
2085 ucurr_resetCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2086 ((UCurrencyContext
*)(enumerator
->context
))->listIdx
= 0;
2089 static void U_CALLCONV
2090 ucurr_closeCurrencyList(UEnumeration
*enumerator
) {
2091 uprv_free(enumerator
->context
);
2092 uprv_free(enumerator
);
2095 static void U_CALLCONV
2096 ucurr_createCurrencyList(UHashtable
*isoCodes
, UErrorCode
* status
){
2097 UErrorCode localStatus
= U_ZERO_ERROR
;
2099 // Look up the CurrencyMap element in the root bundle.
2100 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2101 UResourceBundle
*currencyMapArray
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2103 if (U_SUCCESS(localStatus
)) {
2104 // process each entry in currency map
2105 for (int32_t i
=0; i
<ures_getSize(currencyMapArray
); i
++) {
2106 // get the currency resource
2107 UResourceBundle
*currencyArray
= ures_getByIndex(currencyMapArray
, i
, NULL
, &localStatus
);
2108 // process each currency
2109 if (U_SUCCESS(localStatus
)) {
2110 for (int32_t j
=0; j
<ures_getSize(currencyArray
); j
++) {
2111 // get the currency resource
2112 UResourceBundle
*currencyRes
= ures_getByIndex(currencyArray
, j
, NULL
, &localStatus
);
2113 IsoCodeEntry
*entry
= (IsoCodeEntry
*)uprv_malloc(sizeof(IsoCodeEntry
));
2114 if (entry
== NULL
) {
2115 *status
= U_MEMORY_ALLOCATION_ERROR
;
2120 int32_t isoLength
= 0;
2121 UResourceBundle
*idRes
= ures_getByKey(currencyRes
, "id", NULL
, &localStatus
);
2122 if (idRes
== NULL
) {
2125 const UChar
*isoCode
= ures_getString(idRes
, &isoLength
, &localStatus
);
2128 UDate fromDate
= U_DATE_MIN
;
2129 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2131 if (U_SUCCESS(localStatus
)) {
2132 int32_t fromLength
= 0;
2133 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2134 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2135 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2136 fromDate
= (UDate
)currDate64
;
2138 ures_close(fromRes
);
2141 UDate toDate
= U_DATE_MAX
;
2142 localStatus
= U_ZERO_ERROR
;
2143 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2145 if (U_SUCCESS(localStatus
)) {
2146 int32_t toLength
= 0;
2147 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2148 int64_t currDate64
= (int64_t)toArray
[0] << 32;
2149 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2150 toDate
= (UDate
)currDate64
;
2155 ures_close(currencyRes
);
2157 entry
->isoCode
= isoCode
;
2158 entry
->from
= fromDate
;
2161 localStatus
= U_ZERO_ERROR
;
2162 uhash_put(isoCodes
, (UChar
*)isoCode
, entry
, &localStatus
);
2165 *status
= localStatus
;
2167 ures_close(currencyArray
);
2170 *status
= localStatus
;
2173 ures_close(currencyMapArray
);
2176 static const UEnumeration gEnumCurrencyList
= {
2179 ucurr_closeCurrencyList
,
2180 ucurr_countCurrencyList
,
2182 ucurr_nextCurrencyList
,
2183 ucurr_resetCurrencyList
2188 static void U_CALLCONV
initIsoCodes(UErrorCode
&status
) {
2189 U_ASSERT(gIsoCodes
== NULL
);
2190 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
2192 UHashtable
*isoCodes
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2193 if (U_FAILURE(status
)) {
2196 uhash_setValueDeleter(isoCodes
, deleteIsoCodeEntry
);
2198 ucurr_createCurrencyList(isoCodes
, &status
);
2199 if (U_FAILURE(status
)) {
2200 uhash_close(isoCodes
);
2203 gIsoCodes
= isoCodes
; // Note: gIsoCodes is const. Once set up here it is never altered,
2204 // and read only access is safe without synchronization.
2207 static void populateCurrSymbolsEquiv(icu::Hashtable
*hash
, UErrorCode
&status
) {
2208 if (U_FAILURE(status
)) { return; }
2209 for (auto& entry
: unisets::kCurrencyEntries
) {
2210 UnicodeString
exemplar(entry
.exemplar
);
2211 const UnicodeSet
* set
= unisets::get(entry
.key
);
2212 if (set
== nullptr) { return; }
2213 UnicodeSetIterator
it(*set
);
2215 UnicodeString value
= it
.getString();
2216 if (value
== exemplar
) {
2217 // No need to mark the exemplar character as an equivalent
2220 makeEquivalent(exemplar
, value
, hash
, status
);
2221 if (U_FAILURE(status
)) { return; }
2226 static void U_CALLCONV
initCurrSymbolsEquiv() {
2227 U_ASSERT(gCurrSymbolsEquiv
== NULL
);
2228 UErrorCode status
= U_ZERO_ERROR
;
2229 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
2230 icu::Hashtable
*temp
= new icu::Hashtable(status
);
2234 if (U_FAILURE(status
)) {
2238 temp
->setValueDeleter(deleteUnicode
);
2239 populateCurrSymbolsEquiv(temp
, status
);
2240 if (U_FAILURE(status
)) {
2244 gCurrSymbolsEquiv
= temp
;
2247 U_CAPI UBool U_EXPORT2
2248 ucurr_isAvailable(const UChar
* isoCode
, UDate from
, UDate to
, UErrorCode
* eErrorCode
) {
2249 umtx_initOnce(gIsoCodesInitOnce
, &initIsoCodes
, *eErrorCode
);
2250 if (U_FAILURE(*eErrorCode
)) {
2254 IsoCodeEntry
* result
= (IsoCodeEntry
*) uhash_get(gIsoCodes
, isoCode
);
2255 if (result
== NULL
) {
2257 } else if (from
> to
) {
2258 *eErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2260 } else if ((from
> result
->to
) || (to
< result
->from
)) {
2266 static const icu::Hashtable
* getCurrSymbolsEquiv() {
2267 umtx_initOnce(gCurrSymbolsEquivInitOnce
, &initCurrSymbolsEquiv
);
2268 return gCurrSymbolsEquiv
;
2271 U_CAPI UEnumeration
* U_EXPORT2
2272 ucurr_openISOCurrencies(uint32_t currType
, UErrorCode
*pErrorCode
) {
2273 UEnumeration
*myEnum
= NULL
;
2274 UCurrencyContext
*myContext
;
2276 myEnum
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2277 if (myEnum
== NULL
) {
2278 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2281 uprv_memcpy(myEnum
, &gEnumCurrencyList
, sizeof(UEnumeration
));
2282 myContext
= (UCurrencyContext
*)uprv_malloc(sizeof(UCurrencyContext
));
2283 if (myContext
== NULL
) {
2284 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2288 myContext
->currType
= currType
;
2289 myContext
->listIdx
= 0;
2290 myEnum
->context
= myContext
;
2294 U_CAPI
int32_t U_EXPORT2
2295 ucurr_countCurrencies(const char* locale
,
2299 int32_t currCount
= 0;
2301 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2304 UErrorCode localStatus
= U_ZERO_ERROR
;
2305 char id
[ULOC_FULLNAME_CAPACITY
];
2306 uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2308 // get country or country_variant in `id'
2309 /*uint32_t variantType =*/ idForLocale(locale
, id
, sizeof(id
), ec
);
2316 // Remove variants, which is only needed for registration.
2317 char *idDelim
= strchr(id
, VAR_DELIM
);
2323 // Look up the CurrencyMap element in the root bundle.
2324 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2325 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2327 // Using the id derived from the local, get the currency data
2328 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2330 // process each currency to see which one is valid for the given date
2331 if (U_SUCCESS(localStatus
))
2333 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2335 // get the currency resource
2336 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2338 // get the from date
2339 int32_t fromLength
= 0;
2340 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2341 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2343 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2344 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2345 UDate fromDate
= (UDate
)currDate64
;
2347 if (ures_getSize(currencyRes
)> 2)
2349 int32_t toLength
= 0;
2350 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2351 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2353 currDate64
= (int64_t)toArray
[0] << 32;
2354 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2355 UDate toDate
= (UDate
)currDate64
;
2357 if ((fromDate
<= date
) && (date
< toDate
))
2366 if (fromDate
<= date
)
2372 // close open resources
2373 ures_close(currencyRes
);
2374 ures_close(fromRes
);
2377 } // end if (U_SUCCESS(localStatus))
2379 ures_close(countryArray
);
2382 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2384 // There is nothing to fallback to.
2385 // Report the failure/warning if possible.
2397 // If we got here, either error code is invalid or
2398 // some argument passed is no good.
2402 U_CAPI
int32_t U_EXPORT2
2403 ucurr_forLocaleAndDate(const char* locale
,
2407 int32_t buffCapacity
,
2411 int32_t currIndex
= 0;
2412 const UChar
* s
= NULL
;
2414 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2416 // check the arguments passed
2417 if ((buff
&& buffCapacity
) || !buffCapacity
)
2420 UErrorCode localStatus
= U_ZERO_ERROR
;
2421 char id
[ULOC_FULLNAME_CAPACITY
];
2422 resLen
= uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2424 // get country or country_variant in `id'
2425 /*uint32_t variantType =*/ idForLocale(locale
, id
, sizeof(id
), ec
);
2431 // Remove variants, which is only needed for registration.
2432 char *idDelim
= strchr(id
, VAR_DELIM
);
2438 // Look up the CurrencyMap element in the root bundle.
2439 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2440 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2442 // Using the id derived from the local, get the currency data
2443 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2445 // process each currency to see which one is valid for the given date
2446 bool matchFound
= false;
2447 if (U_SUCCESS(localStatus
))
2449 if ((index
<= 0) || (index
> ures_getSize(countryArray
)))
2451 // requested index is out of bounds
2452 ures_close(countryArray
);
2456 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2458 // get the currency resource
2459 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2460 s
= ures_getStringByKey(currencyRes
, "id", &resLen
, &localStatus
);
2462 // get the from date
2463 int32_t fromLength
= 0;
2464 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2465 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2467 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2468 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2469 UDate fromDate
= (UDate
)currDate64
;
2471 if (ures_getSize(currencyRes
)> 2)
2473 int32_t toLength
= 0;
2474 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2475 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2477 currDate64
= (int64_t)toArray
[0] << 32;
2478 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2479 UDate toDate
= (UDate
)currDate64
;
2481 if ((fromDate
<= date
) && (date
< toDate
))
2484 if (currIndex
== index
)
2494 if (fromDate
<= date
)
2497 if (currIndex
== index
)
2504 // close open resources
2505 ures_close(currencyRes
);
2506 ures_close(fromRes
);
2508 // check for loop exit
2517 ures_close(countryArray
);
2520 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2522 // There is nothing to fallback to.
2523 // Report the failure/warning if possible.
2530 if((buffCapacity
> resLen
) && matchFound
)
2532 // write out the currency value
2541 // return null terminated currency string
2542 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
2546 // illegal argument encountered
2547 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2552 // If we got here, either error code is invalid or
2553 // some argument passed is no good.
2557 static const UEnumeration defaultKeywordValues
= {
2560 ulist_close_keyword_values_iterator
,
2561 ulist_count_keyword_values
,
2563 ulist_next_keyword_value
,
2564 ulist_reset_keyword_values_iterator
2567 U_CAPI UEnumeration
*U_EXPORT2
ucurr_getKeywordValuesForLocale(const char *key
, const char *locale
, UBool commonlyUsed
, UErrorCode
* status
) {
2569 char prefRegion
[ULOC_COUNTRY_CAPACITY
];
2570 ulocimp_getRegionForSupplementalData(locale
, TRUE
, prefRegion
, sizeof(prefRegion
), status
);
2572 // Read value from supplementalData
2573 UList
*values
= ulist_createEmptyList(status
);
2574 UList
*otherValues
= ulist_createEmptyList(status
);
2575 UEnumeration
*en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2576 if (U_FAILURE(*status
) || en
== NULL
) {
2578 *status
= U_MEMORY_ALLOCATION_ERROR
;
2582 ulist_deleteList(values
);
2583 ulist_deleteList(otherValues
);
2586 memcpy(en
, &defaultKeywordValues
, sizeof(UEnumeration
));
2587 en
->context
= values
;
2589 UResourceBundle
*bundle
= ures_openDirect(U_ICUDATA_CURR
, "supplementalData", status
);
2590 ures_getByKey(bundle
, "CurrencyMap", bundle
, status
);
2591 UResourceBundle bundlekey
, regbndl
, curbndl
, to
;
2592 ures_initStackObject(&bundlekey
);
2593 ures_initStackObject(®bndl
);
2594 ures_initStackObject(&curbndl
);
2595 ures_initStackObject(&to
);
2597 while (U_SUCCESS(*status
) && ures_hasNext(bundle
)) {
2598 ures_getNextResource(bundle
, &bundlekey
, status
);
2599 if (U_FAILURE(*status
)) {
2602 const char *region
= ures_getKey(&bundlekey
);
2603 UBool isPrefRegion
= uprv_strcmp(region
, prefRegion
) == 0 ? TRUE
: FALSE
;
2604 if (!isPrefRegion
&& commonlyUsed
) {
2605 // With commonlyUsed=true, we do not put
2606 // currencies for other regions in the
2610 ures_getByKey(bundle
, region
, ®bndl
, status
);
2611 if (U_FAILURE(*status
)) {
2614 while (U_SUCCESS(*status
) && ures_hasNext(®bndl
)) {
2615 ures_getNextResource(®bndl
, &curbndl
, status
);
2616 if (ures_getType(&curbndl
) != URES_TABLE
) {
2617 // Currently, an empty ARRAY is mixed in.
2620 char *curID
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2621 int32_t curIDLength
= ULOC_KEYWORDS_CAPACITY
;
2622 if (curID
== NULL
) {
2623 *status
= U_MEMORY_ALLOCATION_ERROR
;
2627 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
2628 ures_getUTF8StringByKey(&curbndl
, "id", curID
, &curIDLength
, TRUE
, status
);
2629 /* optimize - use the utf-8 string */
2632 const UChar
* defString
= ures_getStringByKey(&curbndl
, "id", &curIDLength
, status
);
2633 if(U_SUCCESS(*status
)) {
2634 if(curIDLength
+1 > ULOC_KEYWORDS_CAPACITY
) {
2635 *status
= U_BUFFER_OVERFLOW_ERROR
;
2637 u_UCharsToChars(defString
, curID
, curIDLength
+1);
2643 if (U_FAILURE(*status
)) {
2646 UBool hasTo
= FALSE
;
2647 ures_getByKey(&curbndl
, "to", &to
, status
);
2648 if (U_FAILURE(*status
)) {
2649 // Do nothing here...
2650 *status
= U_ZERO_ERROR
;
2654 if (isPrefRegion
&& !hasTo
&& !ulist_containsString(values
, curID
, (int32_t)uprv_strlen(curID
))) {
2655 // Currently active currency for the target country
2656 ulist_addItemEndList(values
, curID
, TRUE
, status
);
2657 } else if (!ulist_containsString(otherValues
, curID
, (int32_t)uprv_strlen(curID
)) && !commonlyUsed
) {
2658 ulist_addItemEndList(otherValues
, curID
, TRUE
, status
);
2665 if (U_SUCCESS(*status
)) {
2667 if (ulist_getListSize(values
) == 0) {
2668 // This could happen if no valid region is supplied in the input
2669 // locale. In this case, we use the CLDR's default.
2671 en
= ucurr_getKeywordValuesForLocale(key
, "und", TRUE
, status
);
2674 // Consolidate the list
2676 ulist_resetList(otherValues
);
2677 while ((value
= (char *)ulist_getNext(otherValues
)) != NULL
) {
2678 if (!ulist_containsString(values
, value
, (int32_t)uprv_strlen(value
))) {
2679 char *tmpValue
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2680 uprv_memcpy(tmpValue
, value
, uprv_strlen(value
) + 1);
2681 ulist_addItemEndList(values
, tmpValue
, TRUE
, status
);
2682 if (U_FAILURE(*status
)) {
2689 ulist_resetList((UList
*)(en
->context
));
2691 ulist_deleteList(values
);
2697 ures_close(&curbndl
);
2698 ures_close(®bndl
);
2699 ures_close(&bundlekey
);
2702 ulist_deleteList(otherValues
);
2708 U_CAPI
int32_t U_EXPORT2
2709 ucurr_getNumericCode(const UChar
* currency
) {
2711 if (currency
&& u_strlen(currency
) == ISO_CURRENCY_CODE_LENGTH
) {
2712 UErrorCode status
= U_ZERO_ERROR
;
2714 UResourceBundle
*bundle
= ures_openDirect(0, "currencyNumericCodes", &status
);
2715 ures_getByKey(bundle
, "codeMap", bundle
, &status
);
2716 if (U_SUCCESS(status
)) {
2717 char alphaCode
[ISO_CURRENCY_CODE_LENGTH
+1];
2718 myUCharsToChars(alphaCode
, currency
);
2719 T_CString_toUpperCase(alphaCode
);
2720 ures_getByKey(bundle
, alphaCode
, bundle
, &status
);
2721 int tmpCode
= ures_getInt(bundle
, &status
);
2722 if (U_SUCCESS(status
)) {
2730 #endif /* #if !UCONFIG_NO_FORMATTING */