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";
89 static const char VAR_DELIM
= '_';
91 // Tag for localized display names (symbols) of currencies
92 static const char CURRENCIES
[] = "Currencies";
93 static const char CURRENCIES_NARROW
[] = "Currencies%narrow";
94 static const char CURRENCYPLURALS
[] = "CurrencyPlurals";
96 // ISO codes mapping table
97 static const UHashtable
* gIsoCodes
= NULL
;
98 static icu::UInitOnce gIsoCodesInitOnce
= U_INITONCE_INITIALIZER
;
100 // Currency symbol equivalances
101 static const icu::Hashtable
* gCurrSymbolsEquiv
= NULL
;
102 static icu::UInitOnce gCurrSymbolsEquivInitOnce
= U_INITONCE_INITIALIZER
;
106 // EquivIterator iterates over all strings that are equivalent to a given
107 // string, s. Note that EquivIterator will never yield s itself.
108 class EquivIterator
: public icu::UMemory
{
110 // Constructor. hash stores the equivalence relationships; s is the string
111 // for which we find equivalent strings.
112 inline EquivIterator(const icu::Hashtable
& hash
, const icu::UnicodeString
& s
)
114 _start
= _current
= &s
;
116 inline ~EquivIterator() { }
118 // next returns the next equivalent string or NULL if there are no more.
119 // If s has no equivalent strings, next returns NULL on the first call.
120 const icu::UnicodeString
*next();
122 const icu::Hashtable
& _hash
;
123 const icu::UnicodeString
* _start
;
124 const icu::UnicodeString
* _current
;
127 const icu::UnicodeString
*
128 EquivIterator::next() {
129 const icu::UnicodeString
* _next
= (const icu::UnicodeString
*) _hash
.get(*_current
);
131 U_ASSERT(_current
== _start
);
134 if (*_next
== *_start
) {
143 // makeEquivalent makes lhs and rhs equivalent by updating the equivalence
144 // relations in hash accordingly.
145 static void makeEquivalent(
146 const icu::UnicodeString
&lhs
,
147 const icu::UnicodeString
&rhs
,
148 icu::Hashtable
* hash
, UErrorCode
&status
) {
149 if (U_FAILURE(status
)) {
153 // already equivalent
156 icu::EquivIterator
leftIter(*hash
, lhs
);
157 icu::EquivIterator
rightIter(*hash
, rhs
);
158 const icu::UnicodeString
*firstLeft
= leftIter
.next();
159 const icu::UnicodeString
*firstRight
= rightIter
.next();
160 const icu::UnicodeString
*nextLeft
= firstLeft
;
161 const icu::UnicodeString
*nextRight
= firstRight
;
162 while (nextLeft
!= NULL
&& nextRight
!= NULL
) {
163 if (*nextLeft
== rhs
|| *nextRight
== lhs
) {
164 // Already equivalent
167 nextLeft
= leftIter
.next();
168 nextRight
= rightIter
.next();
170 // Not equivalent. Must join.
171 icu::UnicodeString
*newFirstLeft
;
172 icu::UnicodeString
*newFirstRight
;
173 if (firstRight
== NULL
&& firstLeft
== NULL
) {
174 // Neither lhs or rhs belong to an equivalence circle, so we form
175 // a new equivalnce circle of just lhs and rhs.
176 newFirstLeft
= new icu::UnicodeString(rhs
);
177 newFirstRight
= new icu::UnicodeString(lhs
);
178 } else if (firstRight
== NULL
) {
179 // lhs belongs to an equivalence circle, but rhs does not, so we link
180 // rhs into lhs' circle.
181 newFirstLeft
= new icu::UnicodeString(rhs
);
182 newFirstRight
= new icu::UnicodeString(*firstLeft
);
183 } else if (firstLeft
== NULL
) {
184 // rhs belongs to an equivlance circle, but lhs does not, so we link
185 // lhs into rhs' circle.
186 newFirstLeft
= new icu::UnicodeString(*firstRight
);
187 newFirstRight
= new icu::UnicodeString(lhs
);
189 // Both lhs and rhs belong to different equivalnce circles. We link
190 // them together to form one single, larger equivalnce circle.
191 newFirstLeft
= new icu::UnicodeString(*firstRight
);
192 newFirstRight
= new icu::UnicodeString(*firstLeft
);
194 if (newFirstLeft
== NULL
|| newFirstRight
== NULL
) {
196 delete newFirstRight
;
197 status
= U_MEMORY_ALLOCATION_ERROR
;
200 hash
->put(lhs
, (void *) newFirstLeft
, status
);
201 hash
->put(rhs
, (void *) newFirstRight
, status
);
204 // countEquivalent counts how many strings are equivalent to s.
205 // hash stores all the equivalnce relations.
206 // countEquivalent does not include s itself in the count.
207 static int32_t countEquivalent(const icu::Hashtable
&hash
, const icu::UnicodeString
&s
) {
209 icu::EquivIterator
iter(hash
, s
);
210 while (iter
.next() != NULL
) {
213 #ifdef UCURR_DEBUG_EQUIV
216 s
.extract(0,s
.length(),tmp
, "UTF-8");
217 printf("CountEquivalent('%s') = %d\n", tmp
, result
);
223 static const icu::Hashtable
* getCurrSymbolsEquiv();
225 //------------------------------------------------------------
229 * Cleanup callback func
231 static UBool U_CALLCONV
232 isoCodes_cleanup(void)
234 if (gIsoCodes
!= NULL
) {
235 uhash_close(const_cast<UHashtable
*>(gIsoCodes
));
238 gIsoCodesInitOnce
.reset();
243 * Cleanup callback func
245 static UBool U_CALLCONV
246 currSymbolsEquiv_cleanup(void)
248 delete const_cast<icu::Hashtable
*>(gCurrSymbolsEquiv
);
249 gCurrSymbolsEquiv
= NULL
;
250 gCurrSymbolsEquivInitOnce
.reset();
255 * Deleter for OlsonToMetaMappingEntry
257 static void U_CALLCONV
258 deleteIsoCodeEntry(void *obj
) {
259 IsoCodeEntry
*entry
= (IsoCodeEntry
*)obj
;
264 * Deleter for gCurrSymbolsEquiv.
266 static void U_CALLCONV
267 deleteUnicode(void *obj
) {
268 icu::UnicodeString
*entry
= (icu::UnicodeString
*)obj
;
273 * Unfortunately, we have to convert the UChar* currency code to char*
274 * to use it as a resource key.
277 myUCharsToChars(char* resultOfLen4
, const UChar
* currency
) {
278 u_UCharsToChars(currency
, resultOfLen4
, ISO_CURRENCY_CODE_LENGTH
);
279 resultOfLen4
[ISO_CURRENCY_CODE_LENGTH
] = 0;
284 * Internal function to look up currency data. Result is an array of
285 * four integers. The first is the fraction digits. The second is the
286 * rounding increment, or 0 if none. The rounding increment is in
287 * units of 10^(-fraction_digits). The third and fourth are the same
288 * except that they are those used in cash transations ( cashDigits
289 * and cashRounding ).
291 static const int32_t*
292 _findMetaData(const UChar
* currency
, UErrorCode
& ec
) {
294 if (currency
== 0 || *currency
== 0) {
296 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
298 return LAST_RESORT_DATA
;
301 // Get CurrencyMeta resource out of root locale file. [This may
302 // move out of the root locale file later; if it does, update this
304 UResourceBundle
* currencyData
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &ec
);
305 UResourceBundle
* currencyMeta
= ures_getByKey(currencyData
, CURRENCY_META
, currencyData
, &ec
);
308 ures_close(currencyMeta
);
309 // Config/build error; return hard-coded defaults
310 return LAST_RESORT_DATA
;
313 // Look up our currency, or if that's not available, then DEFAULT
314 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
315 UErrorCode ec2
= U_ZERO_ERROR
; // local error code: soft failure
316 UResourceBundle
* rb
= ures_getByKey(currencyMeta
, myUCharsToChars(buf
, currency
), NULL
, &ec2
);
317 if (U_FAILURE(ec2
)) {
319 rb
= ures_getByKey(currencyMeta
,DEFAULT_META
, NULL
, &ec
);
321 ures_close(currencyMeta
);
323 // Config/build error; return hard-coded defaults
324 return LAST_RESORT_DATA
;
329 const int32_t *data
= ures_getIntVector(rb
, &len
, &ec
);
330 if (U_FAILURE(ec
) || len
!= 4) {
331 // Config/build error; return hard-coded defaults
333 ec
= U_INVALID_FORMAT_ERROR
;
335 ures_close(currencyMeta
);
337 return LAST_RESORT_DATA
;
340 ures_close(currencyMeta
);
345 // -------------------------------------
348 idForLocale(const char* locale
, char* countryAndVariant
, int capacity
, UErrorCode
* ec
)
350 ulocimp_getRegionForSupplementalData(locale
, FALSE
, countryAndVariant
, capacity
, ec
);
353 // ------------------------------------------
357 //-------------------------------------------
359 // don't use ICUService since we don't need fallback
362 static UBool U_CALLCONV
currency_cleanup(void);
365 #if !UCONFIG_NO_SERVICE
368 static UMutex gCRegLock
;
369 static CReg
* gCRegHead
= 0;
371 struct CReg
: public icu::UMemory
{
373 UChar iso
[ISO_CURRENCY_CODE_LENGTH
+1];
374 char id
[ULOC_FULLNAME_CAPACITY
];
376 CReg(const UChar
* _iso
, const char* _id
)
379 int32_t len
= (int32_t)uprv_strlen(_id
);
380 if (len
> (int32_t)(sizeof(id
)-1)) {
381 len
= (sizeof(id
)-1);
383 uprv_strncpy(id
, _id
, len
);
385 u_memcpy(iso
, _iso
, ISO_CURRENCY_CODE_LENGTH
);
386 iso
[ISO_CURRENCY_CODE_LENGTH
] = 0;
389 static UCurrRegistryKey
reg(const UChar
* _iso
, const char* _id
, UErrorCode
* status
)
391 if (status
&& U_SUCCESS(*status
) && _iso
&& _id
) {
392 CReg
* n
= new CReg(_iso
, _id
);
394 umtx_lock(&gCRegLock
);
396 /* register for the first time */
397 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
401 umtx_unlock(&gCRegLock
);
404 *status
= U_MEMORY_ALLOCATION_ERROR
;
409 static UBool
unreg(UCurrRegistryKey key
) {
411 umtx_lock(&gCRegLock
);
413 CReg
** p
= &gCRegHead
;
416 *p
= ((CReg
*)key
)->next
;
424 umtx_unlock(&gCRegLock
);
428 static const UChar
* get(const char* id
) {
429 const UChar
* result
= NULL
;
430 umtx_lock(&gCRegLock
);
433 /* register cleanup of the mutex */
434 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
436 if (uprv_strcmp(id
, p
->id
) == 0) {
442 umtx_unlock(&gCRegLock
);
446 /* This doesn't need to be thread safe. It's for u_cleanup only. */
447 static void cleanup(void) {
450 gCRegHead
= gCRegHead
->next
;
456 // -------------------------------------
458 U_CAPI UCurrRegistryKey U_EXPORT2
459 ucurr_register(const UChar
* isoCode
, const char* locale
, UErrorCode
*status
)
461 if (status
&& U_SUCCESS(*status
)) {
462 char id
[ULOC_FULLNAME_CAPACITY
];
463 idForLocale(locale
, id
, sizeof(id
), status
);
464 return CReg::reg(isoCode
, id
, status
);
469 // -------------------------------------
471 U_CAPI UBool U_EXPORT2
472 ucurr_unregister(UCurrRegistryKey key
, UErrorCode
* status
)
474 if (status
&& U_SUCCESS(*status
)) {
475 return CReg::unreg(key
);
479 #endif /* UCONFIG_NO_SERVICE */
481 // -------------------------------------
484 * Release all static memory held by currency.
486 /*The declaration here is needed so currency_cleanup(void)
487 * can call this function.
489 static UBool U_CALLCONV
490 currency_cache_cleanup(void);
493 static UBool U_CALLCONV
currency_cleanup(void) {
494 #if !UCONFIG_NO_SERVICE
498 * There might be some cached currency data or isoCodes data.
500 currency_cache_cleanup();
502 currSymbolsEquiv_cleanup();
508 // -------------------------------------
510 U_CAPI
int32_t U_EXPORT2
511 ucurr_forLocale(const char* locale
,
513 int32_t buffCapacity
,
515 if (U_FAILURE(*ec
)) { return 0; }
516 if (buffCapacity
< 0 || (buff
== nullptr && buffCapacity
> 0)) {
517 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
521 char currency
[4]; // ISO currency codes are alpha3 codes.
522 UErrorCode localStatus
= U_ZERO_ERROR
;
523 int32_t resLen
= uloc_getKeywordValue(locale
, "currency",
524 currency
, UPRV_LENGTHOF(currency
), &localStatus
);
525 if (U_SUCCESS(localStatus
) && resLen
== 3 && uprv_isInvariantString(currency
, resLen
)) {
526 if (resLen
< buffCapacity
) {
527 T_CString_toUpperCase(currency
);
528 u_charsToUChars(currency
, buff
, resLen
);
530 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
533 // get country or country_variant in `id'
534 char id
[ULOC_FULLNAME_CAPACITY
];
535 idForLocale(locale
, id
, UPRV_LENGTHOF(id
), ec
);
536 if (U_FAILURE(*ec
)) {
540 #if !UCONFIG_NO_SERVICE
541 const UChar
* result
= CReg::get(id
);
543 if(buffCapacity
> u_strlen(result
)) {
544 u_strcpy(buff
, result
);
546 resLen
= u_strlen(result
);
547 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
550 // Remove variants, which is only needed for registration.
551 char *idDelim
= uprv_strchr(id
, VAR_DELIM
);
556 const UChar
* s
= NULL
; // Currency code from data file.
558 // No point looking in the data for an empty string.
559 // This is what we would get.
560 localStatus
= U_MISSING_RESOURCE_ERROR
;
562 // Look up the CurrencyMap element in the root bundle.
563 localStatus
= U_ZERO_ERROR
;
564 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
565 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
566 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
567 UResourceBundle
*currencyReq
= ures_getByIndex(countryArray
, 0, NULL
, &localStatus
);
568 s
= ures_getStringByKey(currencyReq
, "id", &resLen
, &localStatus
);
569 ures_close(currencyReq
);
570 ures_close(countryArray
);
573 if ((U_FAILURE(localStatus
)) && strchr(id
, '_') != 0) {
574 // We don't know about it. Check to see if we support the variant.
575 uloc_getParent(locale
, id
, UPRV_LENGTHOF(id
), ec
);
576 *ec
= U_USING_FALLBACK_WARNING
;
577 // TODO: Loop over the shortened id rather than recursing and
578 // looking again for a currency keyword.
579 return ucurr_forLocale(id
, buff
, buffCapacity
, ec
);
581 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
) {
582 // There is nothing to fallback to. Report the failure/warning if possible.
585 if (U_SUCCESS(*ec
)) {
586 if(buffCapacity
> resLen
) {
590 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
596 * Modify the given locale name by removing the rightmost _-delimited
597 * element. If there is none, empty the string ("" == root).
598 * NOTE: The string "root" is not recognized; do not use it.
599 * @return TRUE if the fallback happened; FALSE if locale is already
602 static UBool
fallback(char *loc
) {
606 UErrorCode status
= U_ZERO_ERROR
;
607 if (uprv_strcmp(loc
, "en_GB") == 0) {
608 // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en"
609 // in order to consume the correct data strings. This hack will be removed
610 // when proper data sink loading is implemented here.
611 // NOTE: "001" adds 1 char over "GB". However, both call sites allocate
612 // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001).
613 uprv_strcpy(loc
+ 3, "001");
615 uloc_getParent(loc
, loc
, (int32_t)uprv_strlen(loc
), &status
);
618 char *i = uprv_strrchr(loc, '_');
628 U_CAPI
const UChar
* U_EXPORT2
629 ucurr_getName(const UChar
* currency
,
631 UCurrNameStyle nameStyle
,
632 UBool
* isChoiceFormat
, // fillin
633 int32_t* len
, // fillin
636 // Look up the Currencies resource for the given locale. The
637 // Currencies locale data looks like this:
640 //| USD { "US$", "US Dollar" }
641 //| CHF { "Sw F", "Swiss Franc" }
642 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
647 if (U_FAILURE(*ec
)) {
651 int32_t choice
= (int32_t) nameStyle
;
652 if (choice
< 0 || choice
> 2) {
653 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
657 // In the future, resource bundles may implement multi-level
658 // fallback. That is, if a currency is not found in the en_US
659 // Currencies data, then the en Currencies data will be searched.
660 // Currently, if a Currencies datum exists in en_US and en, the
661 // en_US entry hides that in en.
663 // We want multi-level fallback for this resource, so we implement
666 // Use a separate UErrorCode here that does not propagate out of
668 UErrorCode ec2
= U_ZERO_ERROR
;
670 char loc
[ULOC_FULLNAME_CAPACITY
];
671 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
672 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
673 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
677 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
678 myUCharsToChars(buf
, currency
);
680 /* Normalize the keyword value to uppercase */
681 T_CString_toUpperCase(buf
);
683 const UChar
* s
= NULL
;
685 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_CURR
, loc
, &ec2
));
687 if (nameStyle
== UCURR_NARROW_SYMBOL_NAME
) {
689 key
.append(CURRENCIES_NARROW
, ec2
);
690 key
.append("/", ec2
);
691 key
.append(buf
, ec2
);
692 s
= ures_getStringByKeyWithFallback(rb
.getAlias(), key
.data(), len
, &ec2
);
693 if (ec2
== U_MISSING_RESOURCE_ERROR
) {
694 *ec
= U_USING_FALLBACK_WARNING
;
696 choice
= UCURR_SYMBOL_NAME
;
700 ures_getByKey(rb
.getAlias(), CURRENCIES
, rb
.getAlias(), &ec2
);
701 ures_getByKeyWithFallback(rb
.getAlias(), buf
, rb
.getAlias(), &ec2
);
702 s
= ures_getStringByIndex(rb
.getAlias(), choice
, len
, &ec2
);
705 // If we've succeeded we're done. Otherwise, try to fallback.
706 // If that fails (because we are already at root) then exit.
707 if (U_SUCCESS(ec2
)) {
708 if (ec2
== U_USING_DEFAULT_WARNING
709 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
714 // We no longer support choice format data in names. Data should not contain
716 if (isChoiceFormat
!= NULL
) {
717 *isChoiceFormat
= FALSE
;
719 if (U_SUCCESS(ec2
)) {
724 // If we fail to find a match, use the ISO 4217 code
725 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
726 *ec
= U_USING_DEFAULT_WARNING
;
730 U_CAPI
const UChar
* U_EXPORT2
731 ucurr_getPluralName(const UChar
* currency
,
733 UBool
* isChoiceFormat
,
734 const char* pluralCount
,
735 int32_t* len
, // fillin
737 // Look up the Currencies resource for the given locale. The
738 // Currencies locale data looks like this:
740 //| CurrencyPlurals {
743 //| other{"US dollars"}
748 if (U_FAILURE(*ec
)) {
752 // Use a separate UErrorCode here that does not propagate out of
754 UErrorCode ec2
= U_ZERO_ERROR
;
756 char loc
[ULOC_FULLNAME_CAPACITY
];
757 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
758 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
759 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
763 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
764 myUCharsToChars(buf
, currency
);
766 const UChar
* s
= NULL
;
768 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
770 rb
= ures_getByKey(rb
, CURRENCYPLURALS
, rb
, &ec2
);
772 // Fetch resource with multi-level resource inheritance fallback
773 rb
= ures_getByKeyWithFallback(rb
, buf
, rb
, &ec2
);
775 s
= ures_getStringByKeyWithFallback(rb
, pluralCount
, len
, &ec2
);
776 if (U_FAILURE(ec2
)) {
777 // fall back to "other"
779 s
= ures_getStringByKeyWithFallback(rb
, "other", len
, &ec2
);
780 if (U_FAILURE(ec2
)) {
782 // fall back to long name in Currencies
783 return ucurr_getName(currency
, locale
, UCURR_LONG_NAME
,
784 isChoiceFormat
, len
, ec
);
789 // If we've succeeded we're done. Otherwise, try to fallback.
790 // If that fails (because we are already at root) then exit.
791 if (U_SUCCESS(ec2
)) {
792 if (ec2
== U_USING_DEFAULT_WARNING
793 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
800 // If we fail to find a match, use the ISO 4217 code
801 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
802 *ec
= U_USING_DEFAULT_WARNING
;
807 //========================================================================
808 // Following are structure and function for parsing currency names
810 #define NEED_TO_BE_DELETED 0x1
812 // TODO: a better way to define this?
813 #define MAX_CURRENCY_NAME_LEN 100
816 const char* IsoCode
; // key
817 UChar
* currencyName
; // value
818 int32_t currencyNameLen
; // value length
819 int32_t flag
; // flags
820 } CurrencyNameStruct
;
824 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
828 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
832 // Comparason function used in quick sort.
833 static int U_CALLCONV
currencyNameComparator(const void* a
, const void* b
) {
834 const CurrencyNameStruct
* currName_1
= (const CurrencyNameStruct
*)a
;
835 const CurrencyNameStruct
* currName_2
= (const CurrencyNameStruct
*)b
;
837 i
< MIN(currName_1
->currencyNameLen
, currName_2
->currencyNameLen
);
839 if (currName_1
->currencyName
[i
] < currName_2
->currencyName
[i
]) {
842 if (currName_1
->currencyName
[i
] > currName_2
->currencyName
[i
]) {
846 if (currName_1
->currencyNameLen
< currName_2
->currencyNameLen
) {
848 } else if (currName_1
->currencyNameLen
> currName_2
->currencyNameLen
) {
855 // Give a locale, return the maximum number of currency names associated with
857 // It gets currency names from resource bundles using fallback.
858 // It is the maximum number because in the fallback chain, some of the
859 // currency names are duplicated.
860 // For example, given locale as "en_US", the currency names get from resource
861 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
862 // all currency names in "en_US" and "en".
864 getCurrencyNameCount(const char* loc
, int32_t* total_currency_name_count
, int32_t* total_currency_symbol_count
) {
866 *total_currency_name_count
= 0;
867 *total_currency_symbol_count
= 0;
868 const UChar
* s
= NULL
;
869 char locale
[ULOC_FULLNAME_CAPACITY
];
870 uprv_strcpy(locale
, loc
);
871 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
873 UErrorCode ec2
= U_ZERO_ERROR
;
874 // TODO: ures_openDirect?
875 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, locale
, &ec2
);
876 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
877 int32_t n
= ures_getSize(curr
);
878 for (int32_t i
=0; i
<n
; ++i
) {
879 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
881 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
882 ++(*total_currency_symbol_count
); // currency symbol
883 if (currencySymbolsEquiv
!= NULL
) {
884 *total_currency_symbol_count
+= countEquivalent(*currencySymbolsEquiv
, UnicodeString(TRUE
, s
, len
));
886 ++(*total_currency_symbol_count
); // iso code
887 ++(*total_currency_name_count
); // long name
892 UErrorCode ec3
= U_ZERO_ERROR
;
893 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec3
);
894 n
= ures_getSize(curr_p
);
895 for (int32_t i
=0; i
<n
; ++i
) {
896 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec3
);
897 *total_currency_name_count
+= ures_getSize(names
);
904 if (!fallback(locale
)) {
911 toUpperCase(const UChar
* source
, int32_t len
, const char* locale
) {
913 UErrorCode ec
= U_ZERO_ERROR
;
914 int32_t destLen
= u_strToUpper(dest
, 0, source
, len
, locale
, &ec
);
917 dest
= (UChar
*)uprv_malloc(sizeof(UChar
) * MAX(destLen
, len
));
918 u_strToUpper(dest
, destLen
, source
, len
, locale
, &ec
);
920 u_memcpy(dest
, source
, len
);
926 // Collect all available currency names associated with the given locale
927 // (enable fallback chain).
928 // Read currenc names defined in resource bundle "Currencies" and
929 // "CurrencyPlural", enable fallback chain.
930 // return the malloc-ed currency name arrays and the total number of currency
931 // names in the array.
933 collectCurrencyNames(const char* locale
,
934 CurrencyNameStruct
** currencyNames
,
935 int32_t* total_currency_name_count
,
936 CurrencyNameStruct
** currencySymbols
,
937 int32_t* total_currency_symbol_count
,
940 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
941 // Look up the Currencies resource for the given locale.
942 UErrorCode ec2
= U_ZERO_ERROR
;
944 char loc
[ULOC_FULLNAME_CAPACITY
];
945 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
946 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
947 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
950 // Get maximum currency name count first.
951 getCurrencyNameCount(loc
, total_currency_name_count
, total_currency_symbol_count
);
953 *currencyNames
= (CurrencyNameStruct
*)uprv_malloc
954 (sizeof(CurrencyNameStruct
) * (*total_currency_name_count
));
955 *currencySymbols
= (CurrencyNameStruct
*)uprv_malloc
956 (sizeof(CurrencyNameStruct
) * (*total_currency_symbol_count
));
958 if(currencyNames
== NULL
|| currencySymbols
== NULL
) {
959 ec
= U_MEMORY_ALLOCATION_ERROR
;
962 if (U_FAILURE(ec
)) return;
964 const UChar
* s
= NULL
; // currency name
965 char* iso
= NULL
; // currency ISO code
967 *total_currency_name_count
= 0;
968 *total_currency_symbol_count
= 0;
970 UErrorCode ec3
= U_ZERO_ERROR
;
971 UErrorCode ec4
= U_ZERO_ERROR
;
973 // Using hash to remove duplicates caused by locale fallback
974 UHashtable
* currencyIsoCodes
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &ec3
);
975 UHashtable
* currencyPluralIsoCodes
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &ec4
);
976 for (int32_t localeLevel
= 0; ; ++localeLevel
) {
978 // TODO: ures_openDirect
979 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
980 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
981 int32_t n
= ures_getSize(curr
);
982 for (int32_t i
=0; i
<n
; ++i
) {
983 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
985 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
986 // TODO: uhash_put wont change key/value?
987 iso
= (char*)ures_getKey(names
);
988 if (localeLevel
== 0) {
989 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
991 if (uhash_get(currencyIsoCodes
, iso
) != NULL
) {
995 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
998 // Add currency symbol.
999 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1000 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)s
;
1001 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1002 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= len
;
1003 // Add equivalent symbols
1004 if (currencySymbolsEquiv
!= NULL
) {
1005 UnicodeString
str(TRUE
, s
, len
);
1006 icu::EquivIterator
iter(*currencySymbolsEquiv
, str
);
1007 const UnicodeString
*symbol
;
1008 while ((symbol
= iter
.next()) != NULL
) {
1009 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1010 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
=
1011 const_cast<UChar
*>(symbol
->getBuffer());
1012 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1013 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= symbol
->length();
1017 // Add currency long name.
1018 s
= ures_getStringByIndex(names
, UCURR_LONG_NAME
, &len
, &ec2
);
1019 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1020 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1021 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1022 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1023 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1025 // put (iso, 3, and iso) in to array
1026 // Add currency ISO code.
1027 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1028 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)uprv_malloc(sizeof(UChar
)*3);
1029 // Must convert iso[] into Unicode
1030 u_charsToUChars(iso
, (*currencySymbols
)[*total_currency_symbol_count
].currencyName
, 3);
1031 (*currencySymbols
)[*total_currency_symbol_count
].flag
= NEED_TO_BE_DELETED
;
1032 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= 3;
1038 UErrorCode ec5
= U_ZERO_ERROR
;
1039 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec5
);
1040 n
= ures_getSize(curr_p
);
1041 for (int32_t i
=0; i
<n
; ++i
) {
1042 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec5
);
1043 iso
= (char*)ures_getKey(names
);
1044 // Using hash to remove duplicated ISO codes in fallback chain.
1045 if (localeLevel
== 0) {
1046 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1048 if (uhash_get(currencyPluralIsoCodes
, iso
) != NULL
) {
1052 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1055 int32_t num
= ures_getSize(names
);
1057 for (int32_t j
= 0; j
< num
; ++j
) {
1058 // TODO: remove duplicates between singular name and
1059 // currency long name?
1060 s
= ures_getStringByIndex(names
, j
, &len
, &ec5
);
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
;
1073 if (!fallback(loc
)) {
1078 uhash_close(currencyIsoCodes
);
1079 uhash_close(currencyPluralIsoCodes
);
1081 // quick sort the struct
1082 qsort(*currencyNames
, *total_currency_name_count
,
1083 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1084 qsort(*currencySymbols
, *total_currency_symbol_count
,
1085 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1088 printf("currency name count: %d\n", *total_currency_name_count
);
1089 for (int32_t index
= 0; index
< *total_currency_name_count
; ++index
) {
1090 printf("index: %d\n", index
);
1091 printf("iso: %s\n", (*currencyNames
)[index
].IsoCode
);
1092 char curNameBuf
[1024];
1093 memset(curNameBuf
, 0, 1024);
1094 u_austrncpy(curNameBuf
, (*currencyNames
)[index
].currencyName
, (*currencyNames
)[index
].currencyNameLen
);
1095 printf("currencyName: %s\n", curNameBuf
);
1096 printf("len: %d\n", (*currencyNames
)[index
].currencyNameLen
);
1098 printf("currency symbol count: %d\n", *total_currency_symbol_count
);
1099 for (int32_t index
= 0; index
< *total_currency_symbol_count
; ++index
) {
1100 printf("index: %d\n", index
);
1101 printf("iso: %s\n", (*currencySymbols
)[index
].IsoCode
);
1102 char curNameBuf
[1024];
1103 memset(curNameBuf
, 0, 1024);
1104 u_austrncpy(curNameBuf
, (*currencySymbols
)[index
].currencyName
, (*currencySymbols
)[index
].currencyNameLen
);
1105 printf("currencySymbol: %s\n", curNameBuf
);
1106 printf("len: %d\n", (*currencySymbols
)[index
].currencyNameLen
);
1109 // fail on hashtable errors
1110 if (U_FAILURE(ec3
)) {
1114 if (U_FAILURE(ec4
)) {
1120 // @param currencyNames: currency names array
1121 // @param indexInCurrencyNames: the index of the character in currency names
1122 // array against which the comparison is done
1123 // @param key: input text char to compare against
1124 // @param begin(IN/OUT): the begin index of matching range in currency names array
1125 // @param end(IN/OUT): the end index of matching range in currency names array.
1127 binarySearch(const CurrencyNameStruct
* currencyNames
,
1128 int32_t indexInCurrencyNames
,
1130 int32_t* begin
, int32_t* end
) {
1132 printf("key = %x\n", key
);
1134 int32_t first
= *begin
;
1135 int32_t last
= *end
;
1136 while (first
<= last
) {
1137 int32_t mid
= (first
+ last
) / 2; // compute mid point.
1138 if (indexInCurrencyNames
>= currencyNames
[mid
].currencyNameLen
) {
1141 if (key
> currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1144 else if (key
< currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1148 // Find a match, and looking for ranges
1149 // Now do two more binary searches. First, on the left side for
1150 // the greatest L such that CurrencyNameStruct[L] < key.
1155 printf("mid = %d\n", mid
);
1158 int32_t M
= (L
+ R
) / 2;
1160 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1162 if (indexInCurrencyNames
>= currencyNames
[M
].currencyNameLen
) {
1165 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] < key
) {
1169 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1180 printf("begin = %d\n", *begin
);
1181 U_ASSERT(currencyNames
[*begin
].currencyName
[indexInCurrencyNames
] == key
);
1184 // Now for the second search, finding the least R such that
1185 // key < CurrencyNameStruct[R].
1189 int32_t M
= (L
+ R
) / 2;
1191 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1193 if (currencyNames
[M
].currencyNameLen
< indexInCurrencyNames
) {
1196 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] > key
) {
1200 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1209 if (currencyNames
[R
].currencyName
[indexInCurrencyNames
] > key
) {
1215 printf("end = %d\n", *end
);
1218 // now, found the range. check whether there is exact match
1219 if (currencyNames
[*begin
].currencyNameLen
== indexInCurrencyNames
+ 1) {
1220 return *begin
; // find range and exact match.
1222 return -1; // find range, but no exact match.
1228 return -1; // failed to find range.
1232 // Linear search "text" in "currencyNames".
1233 // @param begin, end: the begin and end index in currencyNames, within which
1234 // range should the search be performed.
1235 // @param textLen: the length of the text to be compared
1236 // @param maxMatchLen(IN/OUT): passing in the computed max matching length
1237 // pass out the new max matching length
1238 // @param maxMatchIndex: the index in currencyName which has the longest
1239 // match with input text.
1241 linearSearch(const CurrencyNameStruct
* currencyNames
,
1242 int32_t begin
, int32_t end
,
1243 const UChar
* text
, int32_t textLen
,
1244 int32_t *partialMatchLen
,
1245 int32_t *maxMatchLen
, int32_t* maxMatchIndex
) {
1246 int32_t initialPartialMatchLen
= *partialMatchLen
;
1247 for (int32_t index
= begin
; index
<= end
; ++index
) {
1248 int32_t len
= currencyNames
[index
].currencyNameLen
;
1249 if (len
> *maxMatchLen
&& len
<= textLen
&&
1250 uprv_memcmp(currencyNames
[index
].currencyName
, text
, len
* sizeof(UChar
)) == 0) {
1251 *partialMatchLen
= MAX(*partialMatchLen
, len
);
1252 *maxMatchIndex
= index
;
1255 printf("maxMatchIndex = %d, maxMatchLen = %d\n",
1256 *maxMatchIndex
, *maxMatchLen
);
1259 // Check for partial matches.
1260 for (int32_t i
=initialPartialMatchLen
; i
<MIN(len
, textLen
); i
++) {
1261 if (currencyNames
[index
].currencyName
[i
] != text
[i
]) {
1264 *partialMatchLen
= MAX(*partialMatchLen
, i
+ 1);
1270 #define LINEAR_SEARCH_THRESHOLD 10
1272 // Find longest match between "text" and currency names in "currencyNames".
1273 // @param total_currency_count: total number of currency names in CurrencyNames.
1274 // @param textLen: the length of the text to be compared
1275 // @param maxMatchLen: passing in the computed max matching length
1276 // pass out the new max matching length
1277 // @param maxMatchIndex: the index in currencyName which has the longest
1278 // match with input text.
1280 searchCurrencyName(const CurrencyNameStruct
* currencyNames
,
1281 int32_t total_currency_count
,
1282 const UChar
* text
, int32_t textLen
,
1283 int32_t *partialMatchLen
,
1284 int32_t* maxMatchLen
, int32_t* maxMatchIndex
) {
1285 *maxMatchIndex
= -1;
1287 int32_t matchIndex
= -1;
1288 int32_t binarySearchBegin
= 0;
1289 int32_t binarySearchEnd
= total_currency_count
- 1;
1290 // It is a variant of binary search.
1291 // For example, given the currency names in currencyNames array are:
1292 // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
1293 // and the input text is BBEXST
1294 // The first round binary search search "B" in the text against
1295 // the first char in currency names, and find the first char matching range
1296 // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
1297 // The 2nd round binary search search the second "B" in the text against
1298 // the 2nd char in currency names, and narrow the matching range to
1299 // "BB BBEX BBEXYZ" (and the maximum matching "BB").
1300 // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
1301 // maximum matching).
1302 // The 4th round returns the same range (the maximum matching is "BBEX").
1303 // The 5th round returns no matching range.
1304 for (int32_t index
= 0; index
< textLen
; ++index
) {
1305 // matchIndex saves the one with exact match till the current point.
1306 // [binarySearchBegin, binarySearchEnd] saves the matching range.
1307 matchIndex
= binarySearch(currencyNames
, index
,
1309 &binarySearchBegin
, &binarySearchEnd
);
1310 if (binarySearchBegin
== -1) { // did not find the range
1313 *partialMatchLen
= MAX(*partialMatchLen
, index
+ 1);
1314 if (matchIndex
!= -1) {
1315 // find an exact match for text from text[0] to text[index]
1316 // in currencyNames array.
1317 *maxMatchLen
= index
+ 1;
1318 *maxMatchIndex
= matchIndex
;
1320 if (binarySearchEnd
- binarySearchBegin
< LINEAR_SEARCH_THRESHOLD
) {
1321 // linear search if within threshold.
1322 linearSearch(currencyNames
, binarySearchBegin
, binarySearchEnd
,
1325 maxMatchLen
, maxMatchIndex
);
1332 //========================= currency name cache =====================
1334 char locale
[ULOC_FULLNAME_CAPACITY
]; //key
1335 // currency names, case insensitive
1336 CurrencyNameStruct
* currencyNames
; // value
1337 int32_t totalCurrencyNameCount
; // currency name count
1338 // currency symbols and ISO code, case sensitive
1339 CurrencyNameStruct
* currencySymbols
; // value
1340 int32_t totalCurrencySymbolCount
; // count
1342 // reference count is set to 1 when an entry is put to cache.
1343 // it increases by 1 before accessing, and decreased by 1 after accessing.
1344 // The entry is deleted when ref count is zero, which means
1345 // the entry is replaced out of cache and no process is accessing it.
1347 } CurrencyNameCacheEntry
;
1350 #define CURRENCY_NAME_CACHE_NUM 10
1352 // Reserve 10 cache entries.
1353 static CurrencyNameCacheEntry
* currCache
[CURRENCY_NAME_CACHE_NUM
] = {NULL
};
1354 // Using an index to indicate which entry to be replaced when cache is full.
1355 // It is a simple round-robin replacement strategy.
1356 static int8_t currentCacheEntryIndex
= 0;
1358 static UMutex gCurrencyCacheMutex
;
1362 deleteCurrencyNames(CurrencyNameStruct
* currencyNames
, int32_t count
) {
1363 for (int32_t index
= 0; index
< count
; ++index
) {
1364 if ( (currencyNames
[index
].flag
& NEED_TO_BE_DELETED
) ) {
1365 uprv_free(currencyNames
[index
].currencyName
);
1368 uprv_free(currencyNames
);
1373 deleteCacheEntry(CurrencyNameCacheEntry
* entry
) {
1374 deleteCurrencyNames(entry
->currencyNames
, entry
->totalCurrencyNameCount
);
1375 deleteCurrencyNames(entry
->currencySymbols
, entry
->totalCurrencySymbolCount
);
1381 static UBool U_CALLCONV
1382 currency_cache_cleanup(void) {
1383 for (int32_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1385 deleteCacheEntry(currCache
[i
]);
1394 * Loads the currency name data from the cache, or from resource bundles if necessary.
1395 * The refCount is automatically incremented. It is the caller's responsibility
1396 * to decrement it when done!
1398 static CurrencyNameCacheEntry
*
1399 getCacheEntry(const char* locale
, UErrorCode
& ec
) {
1401 int32_t total_currency_name_count
= 0;
1402 CurrencyNameStruct
* currencyNames
= NULL
;
1403 int32_t total_currency_symbol_count
= 0;
1404 CurrencyNameStruct
* currencySymbols
= NULL
;
1405 CurrencyNameCacheEntry
* cacheEntry
= NULL
;
1407 umtx_lock(&gCurrencyCacheMutex
);
1408 // in order to handle racing correctly,
1409 // not putting 'search' in a separate function.
1411 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1412 if (currCache
[i
]!= NULL
&&
1413 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1419 cacheEntry
= currCache
[found
];
1420 ++(cacheEntry
->refCount
);
1422 umtx_unlock(&gCurrencyCacheMutex
);
1424 collectCurrencyNames(locale
, ¤cyNames
, &total_currency_name_count
, ¤cySymbols
, &total_currency_symbol_count
, ec
);
1425 if (U_FAILURE(ec
)) {
1428 umtx_lock(&gCurrencyCacheMutex
);
1430 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1431 if (currCache
[i
]!= NULL
&&
1432 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1438 // insert new entry to
1439 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1440 // and remove the existing entry
1441 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1443 cacheEntry
= currCache
[currentCacheEntryIndex
];
1445 --(cacheEntry
->refCount
);
1446 // delete if the ref count is zero
1447 if (cacheEntry
->refCount
== 0) {
1448 deleteCacheEntry(cacheEntry
);
1451 cacheEntry
= (CurrencyNameCacheEntry
*)uprv_malloc(sizeof(CurrencyNameCacheEntry
));
1452 currCache
[currentCacheEntryIndex
] = cacheEntry
;
1453 uprv_strcpy(cacheEntry
->locale
, locale
);
1454 cacheEntry
->currencyNames
= currencyNames
;
1455 cacheEntry
->totalCurrencyNameCount
= total_currency_name_count
;
1456 cacheEntry
->currencySymbols
= currencySymbols
;
1457 cacheEntry
->totalCurrencySymbolCount
= total_currency_symbol_count
;
1458 cacheEntry
->refCount
= 2; // one for cache, one for reference
1459 currentCacheEntryIndex
= (currentCacheEntryIndex
+ 1) % CURRENCY_NAME_CACHE_NUM
;
1460 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
1462 deleteCurrencyNames(currencyNames
, total_currency_name_count
);
1463 deleteCurrencyNames(currencySymbols
, total_currency_symbol_count
);
1464 cacheEntry
= currCache
[found
];
1465 ++(cacheEntry
->refCount
);
1467 umtx_unlock(&gCurrencyCacheMutex
);
1473 static void releaseCacheEntry(CurrencyNameCacheEntry
* cacheEntry
) {
1474 umtx_lock(&gCurrencyCacheMutex
);
1475 --(cacheEntry
->refCount
);
1476 if (cacheEntry
->refCount
== 0) { // remove
1477 deleteCacheEntry(cacheEntry
);
1479 umtx_unlock(&gCurrencyCacheMutex
);
1483 uprv_parseCurrency(const char* locale
,
1484 const icu::UnicodeString
& text
,
1485 icu::ParsePosition
& pos
,
1487 int32_t* partialMatchLen
,
1491 if (U_FAILURE(ec
)) {
1494 CurrencyNameCacheEntry
* cacheEntry
= getCacheEntry(locale
, ec
);
1495 if (U_FAILURE(ec
)) {
1499 int32_t total_currency_name_count
= cacheEntry
->totalCurrencyNameCount
;
1500 CurrencyNameStruct
* currencyNames
= cacheEntry
->currencyNames
;
1501 int32_t total_currency_symbol_count
= cacheEntry
->totalCurrencySymbolCount
;
1502 CurrencyNameStruct
* currencySymbols
= cacheEntry
->currencySymbols
;
1504 int32_t start
= pos
.getIndex();
1506 UChar inputText
[MAX_CURRENCY_NAME_LEN
];
1507 UChar upperText
[MAX_CURRENCY_NAME_LEN
];
1508 int32_t textLen
= MIN(MAX_CURRENCY_NAME_LEN
, text
.length() - start
);
1509 text
.extract(start
, textLen
, inputText
);
1510 UErrorCode ec1
= U_ZERO_ERROR
;
1511 textLen
= u_strToUpper(upperText
, MAX_CURRENCY_NAME_LEN
, inputText
, textLen
, locale
, &ec1
);
1513 // Make sure partialMatchLen is initialized
1514 *partialMatchLen
= 0;
1517 int32_t matchIndex
= -1;
1518 // case in-sensitive comparision against currency names
1519 searchCurrencyName(currencyNames
, total_currency_name_count
,
1520 upperText
, textLen
, partialMatchLen
, &max
, &matchIndex
);
1523 printf("search in names, max = %d, matchIndex = %d\n", max
, matchIndex
);
1526 int32_t maxInSymbol
= 0;
1527 int32_t matchIndexInSymbol
= -1;
1528 if (type
!= UCURR_LONG_NAME
) { // not name only
1529 // case sensitive comparison against currency symbols and ISO code.
1530 searchCurrencyName(currencySymbols
, total_currency_symbol_count
,
1533 &maxInSymbol
, &matchIndexInSymbol
);
1537 printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol
, matchIndexInSymbol
);
1538 if(matchIndexInSymbol
!= -1) {
1539 printf("== ISO=%s\n", currencySymbols
[matchIndexInSymbol
].IsoCode
);
1543 if (max
>= maxInSymbol
&& matchIndex
!= -1) {
1544 u_charsToUChars(currencyNames
[matchIndex
].IsoCode
, result
, 4);
1545 pos
.setIndex(start
+ max
);
1546 } else if (maxInSymbol
>= max
&& matchIndexInSymbol
!= -1) {
1547 u_charsToUChars(currencySymbols
[matchIndexInSymbol
].IsoCode
, result
, 4);
1548 pos
.setIndex(start
+ maxInSymbol
);
1551 // decrease reference count
1552 releaseCacheEntry(cacheEntry
);
1555 void uprv_currencyLeads(const char* locale
, icu::UnicodeSet
& result
, UErrorCode
& ec
) {
1557 if (U_FAILURE(ec
)) {
1560 CurrencyNameCacheEntry
* cacheEntry
= getCacheEntry(locale
, ec
);
1561 if (U_FAILURE(ec
)) {
1565 for (int32_t i
=0; i
<cacheEntry
->totalCurrencySymbolCount
; i
++) {
1566 const CurrencyNameStruct
& info
= cacheEntry
->currencySymbols
[i
];
1568 U16_GET(info
.currencyName
, 0, 0, info
.currencyNameLen
, cp
);
1572 for (int32_t i
=0; i
<cacheEntry
->totalCurrencyNameCount
; i
++) {
1573 const CurrencyNameStruct
& info
= cacheEntry
->currencyNames
[i
];
1575 U16_GET(info
.currencyName
, 0, 0, info
.currencyNameLen
, cp
);
1579 // decrease reference count
1580 releaseCacheEntry(cacheEntry
);
1585 * Internal method. Given a currency ISO code and a locale, return
1586 * the "static" currency name. This is usually the same as the
1587 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
1588 * format is applied to the number 2.0 (to yield the more common
1589 * plural) to return a static name.
1591 * This is used for backward compatibility with old currency logic in
1592 * DecimalFormat and DecimalFormatSymbols.
1595 uprv_getStaticCurrencyName(const UChar
* iso
, const char* loc
,
1596 icu::UnicodeString
& result
, UErrorCode
& ec
)
1601 const UChar
* currname
= ucurr_getName(iso
, loc
, UCURR_SYMBOL_NAME
,
1602 nullptr /* isChoiceFormat */, &len
, &ec
);
1603 if (U_SUCCESS(ec
)) {
1604 result
.setTo(currname
, len
);
1608 U_CAPI
int32_t U_EXPORT2
1609 ucurr_getDefaultFractionDigits(const UChar
* currency
, UErrorCode
* ec
) {
1610 return ucurr_getDefaultFractionDigitsForUsage(currency
,UCURR_USAGE_STANDARD
,ec
);
1613 U_DRAFT
int32_t U_EXPORT2
1614 ucurr_getDefaultFractionDigitsForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1615 int32_t fracDigits
= 0;
1616 if (U_SUCCESS(*ec
)) {
1618 case UCURR_USAGE_STANDARD
:
1619 fracDigits
= (_findMetaData(currency
, *ec
))[0];
1621 case UCURR_USAGE_CASH
:
1622 fracDigits
= (_findMetaData(currency
, *ec
))[2];
1625 *ec
= U_UNSUPPORTED_ERROR
;
1631 U_CAPI
double U_EXPORT2
1632 ucurr_getRoundingIncrement(const UChar
* currency
, UErrorCode
* ec
) {
1633 return ucurr_getRoundingIncrementForUsage(currency
, UCURR_USAGE_STANDARD
, ec
);
1636 U_DRAFT
double U_EXPORT2
1637 ucurr_getRoundingIncrementForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1638 double result
= 0.0;
1640 const int32_t *data
= _findMetaData(currency
, *ec
);
1641 if (U_SUCCESS(*ec
)) {
1645 case UCURR_USAGE_STANDARD
:
1646 fracDigits
= data
[0];
1647 increment
= data
[1];
1649 case UCURR_USAGE_CASH
:
1650 fracDigits
= data
[2];
1651 increment
= data
[3];
1654 *ec
= U_UNSUPPORTED_ERROR
;
1658 // If the meta data is invalid, return 0.0
1659 if (fracDigits
< 0 || fracDigits
> MAX_POW10
) {
1660 *ec
= U_INVALID_FORMAT_ERROR
;
1662 // A rounding value of 0 or 1 indicates no rounding.
1663 if (increment
>= 2) {
1664 // Return (increment) / 10^(fracDigits). The only actual rounding data,
1665 // as of this writing, is CHF { 2, 5 }.
1666 result
= double(increment
) / POW10
[fracDigits
];
1676 typedef struct UCurrencyContext
{
1677 uint32_t currType
; /* UCurrCurrencyType */
1682 Please keep this list in alphabetical order.
1683 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
1685 ISO-4217: https://www.iso.org/iso-4217-currency-codes.html
1687 static const struct CurrencyList
{
1688 const char *currency
;
1690 } gCurrencyList
[] = {
1691 {"ADP", UCURR_COMMON
|UCURR_DEPRECATED
},
1692 {"AED", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1693 {"AFA", UCURR_COMMON
|UCURR_DEPRECATED
},
1694 {"AFN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1695 {"ALK", UCURR_COMMON
|UCURR_DEPRECATED
},
1696 {"ALL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1697 {"AMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1698 {"ANG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1699 {"AOA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1700 {"AOK", UCURR_COMMON
|UCURR_DEPRECATED
},
1701 {"AON", UCURR_COMMON
|UCURR_DEPRECATED
},
1702 {"AOR", UCURR_COMMON
|UCURR_DEPRECATED
},
1703 {"ARA", UCURR_COMMON
|UCURR_DEPRECATED
},
1704 {"ARL", UCURR_COMMON
|UCURR_DEPRECATED
},
1705 {"ARM", UCURR_COMMON
|UCURR_DEPRECATED
},
1706 {"ARP", UCURR_COMMON
|UCURR_DEPRECATED
},
1707 {"ARS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1708 {"ATS", UCURR_COMMON
|UCURR_DEPRECATED
},
1709 {"AUD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1710 {"AWG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1711 {"AZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1712 {"AZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1713 {"BAD", UCURR_COMMON
|UCURR_DEPRECATED
},
1714 {"BAM", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1715 {"BAN", UCURR_COMMON
|UCURR_DEPRECATED
},
1716 {"BBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1717 {"BDT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1718 {"BEC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1719 {"BEF", UCURR_COMMON
|UCURR_DEPRECATED
},
1720 {"BEL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1721 {"BGL", UCURR_COMMON
|UCURR_DEPRECATED
},
1722 {"BGM", UCURR_COMMON
|UCURR_DEPRECATED
},
1723 {"BGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1724 {"BGO", UCURR_COMMON
|UCURR_DEPRECATED
},
1725 {"BHD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1726 {"BIF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1727 {"BMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1728 {"BND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1729 {"BOB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1730 {"BOL", UCURR_COMMON
|UCURR_DEPRECATED
},
1731 {"BOP", UCURR_COMMON
|UCURR_DEPRECATED
},
1732 {"BOV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1733 {"BRB", UCURR_COMMON
|UCURR_DEPRECATED
},
1734 {"BRC", UCURR_COMMON
|UCURR_DEPRECATED
},
1735 {"BRE", UCURR_COMMON
|UCURR_DEPRECATED
},
1736 {"BRL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1737 {"BRN", UCURR_COMMON
|UCURR_DEPRECATED
},
1738 {"BRR", UCURR_COMMON
|UCURR_DEPRECATED
},
1739 {"BRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1740 {"BSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1741 {"BTN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1742 {"BUK", UCURR_COMMON
|UCURR_DEPRECATED
},
1743 {"BWP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1744 {"BYB", UCURR_COMMON
|UCURR_DEPRECATED
},
1745 {"BYN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1746 {"BYR", UCURR_COMMON
|UCURR_DEPRECATED
},
1747 {"BZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1748 {"CAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1749 {"CDF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1750 {"CHE", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1751 {"CHF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1752 {"CHW", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1753 {"CLE", UCURR_COMMON
|UCURR_DEPRECATED
},
1754 {"CLF", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1755 {"CLP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1756 {"CNH", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1757 {"CNX", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1758 {"CNY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1759 {"COP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1760 {"COU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1761 {"CRC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1762 {"CSD", UCURR_COMMON
|UCURR_DEPRECATED
},
1763 {"CSK", UCURR_COMMON
|UCURR_DEPRECATED
},
1764 {"CUC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1765 {"CUP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1766 {"CVE", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1767 {"CYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1768 {"CZK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1769 {"DDM", UCURR_COMMON
|UCURR_DEPRECATED
},
1770 {"DEM", UCURR_COMMON
|UCURR_DEPRECATED
},
1771 {"DJF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1772 {"DKK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1773 {"DOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1774 {"DZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1775 {"ECS", UCURR_COMMON
|UCURR_DEPRECATED
},
1776 {"ECV", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1777 {"EEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1778 {"EGP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1779 {"EQE", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1780 {"ERN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1781 {"ESA", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1782 {"ESB", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1783 {"ESP", UCURR_COMMON
|UCURR_DEPRECATED
},
1784 {"ETB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1785 {"EUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1786 {"FIM", UCURR_COMMON
|UCURR_DEPRECATED
},
1787 {"FJD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1788 {"FKP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1789 {"FRF", UCURR_COMMON
|UCURR_DEPRECATED
},
1790 {"GBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1791 {"GEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1792 {"GEL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1793 {"GHC", UCURR_COMMON
|UCURR_DEPRECATED
},
1794 {"GHS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1795 {"GIP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1796 {"GMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1797 {"GNF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1798 {"GNS", UCURR_COMMON
|UCURR_DEPRECATED
},
1799 {"GQE", UCURR_COMMON
|UCURR_DEPRECATED
},
1800 {"GRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1801 {"GTQ", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1802 {"GWE", UCURR_COMMON
|UCURR_DEPRECATED
},
1803 {"GWP", UCURR_COMMON
|UCURR_DEPRECATED
},
1804 {"GYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1805 {"HKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1806 {"HNL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1807 {"HRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1808 {"HRK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1809 {"HTG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1810 {"HUF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1811 {"IDR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1812 {"IEP", UCURR_COMMON
|UCURR_DEPRECATED
},
1813 {"ILP", UCURR_COMMON
|UCURR_DEPRECATED
},
1814 {"ILR", UCURR_COMMON
|UCURR_DEPRECATED
},
1815 {"ILS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1816 {"INR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1817 {"IQD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1818 {"IRR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1819 {"ISJ", UCURR_COMMON
|UCURR_DEPRECATED
},
1820 {"ISK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1821 {"ITL", UCURR_COMMON
|UCURR_DEPRECATED
},
1822 {"JMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1823 {"JOD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1824 {"JPY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1825 {"KES", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1826 {"KGS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1827 {"KHR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1828 {"KMF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1829 {"KPW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1830 {"KRH", UCURR_COMMON
|UCURR_DEPRECATED
},
1831 {"KRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1832 {"KRW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1833 {"KWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1834 {"KYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1835 {"KZT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1836 {"LAK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1837 {"LBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1838 {"LKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1839 {"LRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1840 {"LSL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1841 {"LSM", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1842 {"LTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1843 {"LTT", UCURR_COMMON
|UCURR_DEPRECATED
},
1844 {"LUC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1845 {"LUF", UCURR_COMMON
|UCURR_DEPRECATED
},
1846 {"LUL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1847 {"LVL", UCURR_COMMON
|UCURR_DEPRECATED
},
1848 {"LVR", UCURR_COMMON
|UCURR_DEPRECATED
},
1849 {"LYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1850 {"MAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1851 {"MAF", UCURR_COMMON
|UCURR_DEPRECATED
},
1852 {"MCF", UCURR_COMMON
|UCURR_DEPRECATED
},
1853 {"MDC", UCURR_COMMON
|UCURR_DEPRECATED
},
1854 {"MDL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1855 {"MGA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1856 {"MGF", UCURR_COMMON
|UCURR_DEPRECATED
},
1857 {"MKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1858 {"MKN", UCURR_COMMON
|UCURR_DEPRECATED
},
1859 {"MLF", UCURR_COMMON
|UCURR_DEPRECATED
},
1860 {"MMK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1861 {"MNT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1862 {"MOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1863 {"MRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1864 {"MRU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1865 {"MTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1866 {"MTP", UCURR_COMMON
|UCURR_DEPRECATED
},
1867 {"MUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1868 {"MVP", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1869 {"MVR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1870 {"MWK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1871 {"MXN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1872 {"MXP", UCURR_COMMON
|UCURR_DEPRECATED
},
1873 {"MXV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1874 {"MYR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1875 {"MZE", UCURR_COMMON
|UCURR_DEPRECATED
},
1876 {"MZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1877 {"MZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1878 {"NAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1879 {"NGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1880 {"NIC", UCURR_COMMON
|UCURR_DEPRECATED
},
1881 {"NIO", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1882 {"NLG", UCURR_COMMON
|UCURR_DEPRECATED
},
1883 {"NOK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1884 {"NPR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1885 {"NZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1886 {"OMR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1887 {"PAB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1888 {"PEI", UCURR_COMMON
|UCURR_DEPRECATED
},
1889 {"PEN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1890 {"PES", UCURR_COMMON
|UCURR_DEPRECATED
},
1891 {"PGK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1892 {"PHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1893 {"PKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1894 {"PLN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1895 {"PLZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1896 {"PTE", UCURR_COMMON
|UCURR_DEPRECATED
},
1897 {"PYG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1898 {"QAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1899 {"RHD", UCURR_COMMON
|UCURR_DEPRECATED
},
1900 {"ROL", UCURR_COMMON
|UCURR_DEPRECATED
},
1901 {"RON", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1902 {"RSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1903 {"RUB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1904 {"RUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1905 {"RWF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1906 {"SAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1907 {"SBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1908 {"SCR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1909 {"SDD", UCURR_COMMON
|UCURR_DEPRECATED
},
1910 {"SDG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1911 {"SDP", UCURR_COMMON
|UCURR_DEPRECATED
},
1912 {"SEK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1913 {"SGD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1914 {"SHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1915 {"SIT", UCURR_COMMON
|UCURR_DEPRECATED
},
1916 {"SKK", UCURR_COMMON
|UCURR_DEPRECATED
},
1917 {"SLL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1918 {"SOS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1919 {"SRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1920 {"SRG", UCURR_COMMON
|UCURR_DEPRECATED
},
1921 {"SSP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1922 {"STD", UCURR_COMMON
|UCURR_DEPRECATED
},
1923 {"STN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1924 {"SUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1925 {"SVC", UCURR_COMMON
|UCURR_DEPRECATED
},
1926 {"SYP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1927 {"SZL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1928 {"THB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1929 {"TJR", UCURR_COMMON
|UCURR_DEPRECATED
},
1930 {"TJS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1931 {"TMM", UCURR_COMMON
|UCURR_DEPRECATED
},
1932 {"TMT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1933 {"TND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1934 {"TOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1935 {"TPE", UCURR_COMMON
|UCURR_DEPRECATED
},
1936 {"TRL", UCURR_COMMON
|UCURR_DEPRECATED
},
1937 {"TRY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1938 {"TTD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1939 {"TWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1940 {"TZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1941 {"UAH", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1942 {"UAK", UCURR_COMMON
|UCURR_DEPRECATED
},
1943 {"UGS", UCURR_COMMON
|UCURR_DEPRECATED
},
1944 {"UGX", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1945 {"USD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1946 {"USN", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1947 {"USS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1948 {"UYI", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1949 {"UYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1950 {"UYU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1951 {"UZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1952 {"VEB", UCURR_COMMON
|UCURR_DEPRECATED
},
1953 {"VEF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1954 {"VND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1955 {"VNN", UCURR_COMMON
|UCURR_DEPRECATED
},
1956 {"VUV", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1957 {"WST", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1958 {"XAF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1959 {"XAG", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1960 {"XAU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1961 {"XBA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1962 {"XBB", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1963 {"XBC", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1964 {"XBD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1965 {"XCD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1966 {"XDR", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1967 {"XEU", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1968 {"XFO", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1969 {"XFU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1970 {"XOF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1971 {"XPD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1972 {"XPF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1973 {"XPT", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1974 {"XRE", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1975 {"XSU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1976 {"XTS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1977 {"XUA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1978 {"XXX", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1979 {"YDD", UCURR_COMMON
|UCURR_DEPRECATED
},
1980 {"YER", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1981 {"YUD", UCURR_COMMON
|UCURR_DEPRECATED
},
1982 {"YUM", UCURR_COMMON
|UCURR_DEPRECATED
},
1983 {"YUN", UCURR_COMMON
|UCURR_DEPRECATED
},
1984 {"YUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1985 {"ZAL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1986 {"ZAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1987 {"ZMK", UCURR_COMMON
|UCURR_DEPRECATED
},
1988 {"ZMW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1989 {"ZRN", UCURR_COMMON
|UCURR_DEPRECATED
},
1990 {"ZRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1991 {"ZWD", UCURR_COMMON
|UCURR_DEPRECATED
},
1992 {"ZWL", UCURR_COMMON
|UCURR_DEPRECATED
},
1993 {"ZWR", UCURR_COMMON
|UCURR_DEPRECATED
},
1994 { NULL
, 0 } // Leave here to denote the end of the list.
1997 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
1998 ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
2000 static int32_t U_CALLCONV
2001 ucurr_countCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2002 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2003 uint32_t currType
= myContext
->currType
;
2006 /* Count the number of items matching the type we are looking for. */
2007 for (int32_t idx
= 0; gCurrencyList
[idx
].currency
!= NULL
; idx
++) {
2008 if (UCURR_MATCHES_BITMASK(gCurrencyList
[idx
].currType
, currType
)) {
2015 static const char* U_CALLCONV
2016 ucurr_nextCurrencyList(UEnumeration
*enumerator
,
2017 int32_t* resultLength
,
2018 UErrorCode
* /*pErrorCode*/)
2020 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2022 /* Find the next in the list that matches the type we are looking for. */
2023 while (myContext
->listIdx
< UPRV_LENGTHOF(gCurrencyList
)-1) {
2024 const struct CurrencyList
*currItem
= &gCurrencyList
[myContext
->listIdx
++];
2025 if (UCURR_MATCHES_BITMASK(currItem
->currType
, myContext
->currType
))
2028 *resultLength
= 3; /* Currency codes are only 3 chars long */
2030 return currItem
->currency
;
2033 /* We enumerated too far. */
2040 static void U_CALLCONV
2041 ucurr_resetCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2042 ((UCurrencyContext
*)(enumerator
->context
))->listIdx
= 0;
2045 static void U_CALLCONV
2046 ucurr_closeCurrencyList(UEnumeration
*enumerator
) {
2047 uprv_free(enumerator
->context
);
2048 uprv_free(enumerator
);
2051 static void U_CALLCONV
2052 ucurr_createCurrencyList(UHashtable
*isoCodes
, UErrorCode
* status
){
2053 UErrorCode localStatus
= U_ZERO_ERROR
;
2055 // Look up the CurrencyMap element in the root bundle.
2056 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2057 UResourceBundle
*currencyMapArray
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2059 if (U_SUCCESS(localStatus
)) {
2060 // process each entry in currency map
2061 for (int32_t i
=0; i
<ures_getSize(currencyMapArray
); i
++) {
2062 // get the currency resource
2063 UResourceBundle
*currencyArray
= ures_getByIndex(currencyMapArray
, i
, NULL
, &localStatus
);
2064 // process each currency
2065 if (U_SUCCESS(localStatus
)) {
2066 for (int32_t j
=0; j
<ures_getSize(currencyArray
); j
++) {
2067 // get the currency resource
2068 UResourceBundle
*currencyRes
= ures_getByIndex(currencyArray
, j
, NULL
, &localStatus
);
2069 IsoCodeEntry
*entry
= (IsoCodeEntry
*)uprv_malloc(sizeof(IsoCodeEntry
));
2070 if (entry
== NULL
) {
2071 *status
= U_MEMORY_ALLOCATION_ERROR
;
2076 int32_t isoLength
= 0;
2077 UResourceBundle
*idRes
= ures_getByKey(currencyRes
, "id", NULL
, &localStatus
);
2078 if (idRes
== NULL
) {
2081 const UChar
*isoCode
= ures_getString(idRes
, &isoLength
, &localStatus
);
2084 UDate fromDate
= U_DATE_MIN
;
2085 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2087 if (U_SUCCESS(localStatus
)) {
2088 int32_t fromLength
= 0;
2089 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2090 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2091 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2092 fromDate
= (UDate
)currDate64
;
2094 ures_close(fromRes
);
2097 UDate toDate
= U_DATE_MAX
;
2098 localStatus
= U_ZERO_ERROR
;
2099 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2101 if (U_SUCCESS(localStatus
)) {
2102 int32_t toLength
= 0;
2103 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2104 int64_t currDate64
= (int64_t)toArray
[0] << 32;
2105 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2106 toDate
= (UDate
)currDate64
;
2111 ures_close(currencyRes
);
2113 entry
->isoCode
= isoCode
;
2114 entry
->from
= fromDate
;
2117 localStatus
= U_ZERO_ERROR
;
2118 uhash_put(isoCodes
, (UChar
*)isoCode
, entry
, &localStatus
);
2121 *status
= localStatus
;
2123 ures_close(currencyArray
);
2126 *status
= localStatus
;
2129 ures_close(currencyMapArray
);
2132 static const UEnumeration gEnumCurrencyList
= {
2135 ucurr_closeCurrencyList
,
2136 ucurr_countCurrencyList
,
2138 ucurr_nextCurrencyList
,
2139 ucurr_resetCurrencyList
2144 static void U_CALLCONV
initIsoCodes(UErrorCode
&status
) {
2145 U_ASSERT(gIsoCodes
== NULL
);
2146 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
2148 UHashtable
*isoCodes
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2149 if (U_FAILURE(status
)) {
2152 uhash_setValueDeleter(isoCodes
, deleteIsoCodeEntry
);
2154 ucurr_createCurrencyList(isoCodes
, &status
);
2155 if (U_FAILURE(status
)) {
2156 uhash_close(isoCodes
);
2159 gIsoCodes
= isoCodes
; // Note: gIsoCodes is const. Once set up here it is never altered,
2160 // and read only access is safe without synchronization.
2163 static void populateCurrSymbolsEquiv(icu::Hashtable
*hash
, UErrorCode
&status
) {
2164 if (U_FAILURE(status
)) { return; }
2165 for (auto& entry
: unisets::kCurrencyEntries
) {
2166 UnicodeString
exemplar(entry
.exemplar
);
2167 const UnicodeSet
* set
= unisets::get(entry
.key
);
2168 if (set
== nullptr) { return; }
2169 UnicodeSetIterator
it(*set
);
2171 UnicodeString value
= it
.getString();
2172 if (value
== exemplar
) {
2173 // No need to mark the exemplar character as an equivalent
2176 makeEquivalent(exemplar
, value
, hash
, status
);
2177 if (U_FAILURE(status
)) { return; }
2182 static void U_CALLCONV
initCurrSymbolsEquiv() {
2183 U_ASSERT(gCurrSymbolsEquiv
== NULL
);
2184 UErrorCode status
= U_ZERO_ERROR
;
2185 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
2186 icu::Hashtable
*temp
= new icu::Hashtable(status
);
2190 if (U_FAILURE(status
)) {
2194 temp
->setValueDeleter(deleteUnicode
);
2195 populateCurrSymbolsEquiv(temp
, status
);
2196 if (U_FAILURE(status
)) {
2200 gCurrSymbolsEquiv
= temp
;
2203 U_CAPI UBool U_EXPORT2
2204 ucurr_isAvailable(const UChar
* isoCode
, UDate from
, UDate to
, UErrorCode
* eErrorCode
) {
2205 umtx_initOnce(gIsoCodesInitOnce
, &initIsoCodes
, *eErrorCode
);
2206 if (U_FAILURE(*eErrorCode
)) {
2210 IsoCodeEntry
* result
= (IsoCodeEntry
*) uhash_get(gIsoCodes
, isoCode
);
2211 if (result
== NULL
) {
2213 } else if (from
> to
) {
2214 *eErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2216 } else if ((from
> result
->to
) || (to
< result
->from
)) {
2222 static const icu::Hashtable
* getCurrSymbolsEquiv() {
2223 umtx_initOnce(gCurrSymbolsEquivInitOnce
, &initCurrSymbolsEquiv
);
2224 return gCurrSymbolsEquiv
;
2227 U_CAPI UEnumeration
* U_EXPORT2
2228 ucurr_openISOCurrencies(uint32_t currType
, UErrorCode
*pErrorCode
) {
2229 UEnumeration
*myEnum
= NULL
;
2230 UCurrencyContext
*myContext
;
2232 myEnum
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2233 if (myEnum
== NULL
) {
2234 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2237 uprv_memcpy(myEnum
, &gEnumCurrencyList
, sizeof(UEnumeration
));
2238 myContext
= (UCurrencyContext
*)uprv_malloc(sizeof(UCurrencyContext
));
2239 if (myContext
== NULL
) {
2240 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2244 myContext
->currType
= currType
;
2245 myContext
->listIdx
= 0;
2246 myEnum
->context
= myContext
;
2250 U_CAPI
int32_t U_EXPORT2
2251 ucurr_countCurrencies(const char* locale
,
2255 int32_t currCount
= 0;
2257 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2260 UErrorCode localStatus
= U_ZERO_ERROR
;
2261 char id
[ULOC_FULLNAME_CAPACITY
];
2262 uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2264 // get country or country_variant in `id'
2265 idForLocale(locale
, id
, sizeof(id
), ec
);
2272 // Remove variants, which is only needed for registration.
2273 char *idDelim
= strchr(id
, VAR_DELIM
);
2279 // Look up the CurrencyMap element in the root bundle.
2280 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2281 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2283 // Using the id derived from the local, get the currency data
2284 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2286 // process each currency to see which one is valid for the given date
2287 if (U_SUCCESS(localStatus
))
2289 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2291 // get the currency resource
2292 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2294 // get the from date
2295 int32_t fromLength
= 0;
2296 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2297 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2299 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2300 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2301 UDate fromDate
= (UDate
)currDate64
;
2303 if (ures_getSize(currencyRes
)> 2)
2305 int32_t toLength
= 0;
2306 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2307 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2309 currDate64
= (int64_t)toArray
[0] << 32;
2310 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2311 UDate toDate
= (UDate
)currDate64
;
2313 if ((fromDate
<= date
) && (date
< toDate
))
2322 if (fromDate
<= date
)
2328 // close open resources
2329 ures_close(currencyRes
);
2330 ures_close(fromRes
);
2333 } // end if (U_SUCCESS(localStatus))
2335 ures_close(countryArray
);
2338 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2340 // There is nothing to fallback to.
2341 // Report the failure/warning if possible.
2353 // If we got here, either error code is invalid or
2354 // some argument passed is no good.
2358 U_CAPI
int32_t U_EXPORT2
2359 ucurr_forLocaleAndDate(const char* locale
,
2363 int32_t buffCapacity
,
2367 int32_t currIndex
= 0;
2368 const UChar
* s
= NULL
;
2370 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2372 // check the arguments passed
2373 if ((buff
&& buffCapacity
) || !buffCapacity
)
2376 UErrorCode localStatus
= U_ZERO_ERROR
;
2377 char id
[ULOC_FULLNAME_CAPACITY
];
2378 resLen
= uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2380 // get country or country_variant in `id'
2381 idForLocale(locale
, id
, sizeof(id
), ec
);
2387 // Remove variants, which is only needed for registration.
2388 char *idDelim
= strchr(id
, VAR_DELIM
);
2394 // Look up the CurrencyMap element in the root bundle.
2395 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2396 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2398 // Using the id derived from the local, get the currency data
2399 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2401 // process each currency to see which one is valid for the given date
2402 bool matchFound
= false;
2403 if (U_SUCCESS(localStatus
))
2405 if ((index
<= 0) || (index
> ures_getSize(countryArray
)))
2407 // requested index is out of bounds
2408 ures_close(countryArray
);
2412 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2414 // get the currency resource
2415 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2416 s
= ures_getStringByKey(currencyRes
, "id", &resLen
, &localStatus
);
2418 // get the from date
2419 int32_t fromLength
= 0;
2420 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2421 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2423 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2424 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2425 UDate fromDate
= (UDate
)currDate64
;
2427 if (ures_getSize(currencyRes
)> 2)
2429 int32_t toLength
= 0;
2430 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2431 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2433 currDate64
= (int64_t)toArray
[0] << 32;
2434 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2435 UDate toDate
= (UDate
)currDate64
;
2437 if ((fromDate
<= date
) && (date
< toDate
))
2440 if (currIndex
== index
)
2450 if (fromDate
<= date
)
2453 if (currIndex
== index
)
2460 // close open resources
2461 ures_close(currencyRes
);
2462 ures_close(fromRes
);
2464 // check for loop exit
2473 ures_close(countryArray
);
2476 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2478 // There is nothing to fallback to.
2479 // Report the failure/warning if possible.
2486 if((buffCapacity
> resLen
) && matchFound
)
2488 // write out the currency value
2497 // return null terminated currency string
2498 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
2502 // illegal argument encountered
2503 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2508 // If we got here, either error code is invalid or
2509 // some argument passed is no good.
2513 static const UEnumeration defaultKeywordValues
= {
2516 ulist_close_keyword_values_iterator
,
2517 ulist_count_keyword_values
,
2519 ulist_next_keyword_value
,
2520 ulist_reset_keyword_values_iterator
2523 U_CAPI UEnumeration
*U_EXPORT2
ucurr_getKeywordValuesForLocale(const char *key
, const char *locale
, UBool commonlyUsed
, UErrorCode
* status
) {
2525 char prefRegion
[ULOC_COUNTRY_CAPACITY
];
2526 ulocimp_getRegionForSupplementalData(locale
, TRUE
, prefRegion
, sizeof(prefRegion
), status
);
2528 // Read value from supplementalData
2529 UList
*values
= ulist_createEmptyList(status
);
2530 UList
*otherValues
= ulist_createEmptyList(status
);
2531 UEnumeration
*en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2532 if (U_FAILURE(*status
) || en
== NULL
) {
2534 *status
= U_MEMORY_ALLOCATION_ERROR
;
2538 ulist_deleteList(values
);
2539 ulist_deleteList(otherValues
);
2542 memcpy(en
, &defaultKeywordValues
, sizeof(UEnumeration
));
2543 en
->context
= values
;
2545 UResourceBundle
*bundle
= ures_openDirect(U_ICUDATA_CURR
, "supplementalData", status
);
2546 ures_getByKey(bundle
, "CurrencyMap", bundle
, status
);
2547 UResourceBundle bundlekey
, regbndl
, curbndl
, to
;
2548 ures_initStackObject(&bundlekey
);
2549 ures_initStackObject(®bndl
);
2550 ures_initStackObject(&curbndl
);
2551 ures_initStackObject(&to
);
2553 while (U_SUCCESS(*status
) && ures_hasNext(bundle
)) {
2554 ures_getNextResource(bundle
, &bundlekey
, status
);
2555 if (U_FAILURE(*status
)) {
2558 const char *region
= ures_getKey(&bundlekey
);
2559 UBool isPrefRegion
= uprv_strcmp(region
, prefRegion
) == 0 ? TRUE
: FALSE
;
2560 if (!isPrefRegion
&& commonlyUsed
) {
2561 // With commonlyUsed=true, we do not put
2562 // currencies for other regions in the
2566 ures_getByKey(bundle
, region
, ®bndl
, status
);
2567 if (U_FAILURE(*status
)) {
2570 while (U_SUCCESS(*status
) && ures_hasNext(®bndl
)) {
2571 ures_getNextResource(®bndl
, &curbndl
, status
);
2572 if (ures_getType(&curbndl
) != URES_TABLE
) {
2573 // Currently, an empty ARRAY is mixed in.
2576 char *curID
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2577 int32_t curIDLength
= ULOC_KEYWORDS_CAPACITY
;
2578 if (curID
== NULL
) {
2579 *status
= U_MEMORY_ALLOCATION_ERROR
;
2583 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
2584 ures_getUTF8StringByKey(&curbndl
, "id", curID
, &curIDLength
, TRUE
, status
);
2585 /* optimize - use the utf-8 string */
2588 const UChar
* defString
= ures_getStringByKey(&curbndl
, "id", &curIDLength
, status
);
2589 if(U_SUCCESS(*status
)) {
2590 if(curIDLength
+1 > ULOC_KEYWORDS_CAPACITY
) {
2591 *status
= U_BUFFER_OVERFLOW_ERROR
;
2593 u_UCharsToChars(defString
, curID
, curIDLength
+1);
2599 if (U_FAILURE(*status
)) {
2602 UBool hasTo
= FALSE
;
2603 ures_getByKey(&curbndl
, "to", &to
, status
);
2604 if (U_FAILURE(*status
)) {
2605 // Do nothing here...
2606 *status
= U_ZERO_ERROR
;
2610 if (isPrefRegion
&& !hasTo
&& !ulist_containsString(values
, curID
, (int32_t)uprv_strlen(curID
))) {
2611 // Currently active currency for the target country
2612 ulist_addItemEndList(values
, curID
, TRUE
, status
);
2613 } else if (!ulist_containsString(otherValues
, curID
, (int32_t)uprv_strlen(curID
)) && !commonlyUsed
) {
2614 ulist_addItemEndList(otherValues
, curID
, TRUE
, status
);
2621 if (U_SUCCESS(*status
)) {
2623 if (ulist_getListSize(values
) == 0) {
2624 // This could happen if no valid region is supplied in the input
2625 // locale. In this case, we use the CLDR's default.
2627 en
= ucurr_getKeywordValuesForLocale(key
, "und", TRUE
, status
);
2630 // Consolidate the list
2632 ulist_resetList(otherValues
);
2633 while ((value
= (char *)ulist_getNext(otherValues
)) != NULL
) {
2634 if (!ulist_containsString(values
, value
, (int32_t)uprv_strlen(value
))) {
2635 char *tmpValue
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2636 uprv_memcpy(tmpValue
, value
, uprv_strlen(value
) + 1);
2637 ulist_addItemEndList(values
, tmpValue
, TRUE
, status
);
2638 if (U_FAILURE(*status
)) {
2645 ulist_resetList((UList
*)(en
->context
));
2647 ulist_deleteList(values
);
2653 ures_close(&curbndl
);
2654 ures_close(®bndl
);
2655 ures_close(&bundlekey
);
2658 ulist_deleteList(otherValues
);
2664 U_CAPI
int32_t U_EXPORT2
2665 ucurr_getNumericCode(const UChar
* currency
) {
2667 if (currency
&& u_strlen(currency
) == ISO_CURRENCY_CODE_LENGTH
) {
2668 UErrorCode status
= U_ZERO_ERROR
;
2670 UResourceBundle
*bundle
= ures_openDirect(0, "currencyNumericCodes", &status
);
2671 ures_getByKey(bundle
, "codeMap", bundle
, &status
);
2672 if (U_SUCCESS(status
)) {
2673 char alphaCode
[ISO_CURRENCY_CODE_LENGTH
+1];
2674 myUCharsToChars(alphaCode
, currency
);
2675 T_CString_toUpperCase(alphaCode
);
2676 ures_getByKey(bundle
, alphaCode
, bundle
, &status
);
2677 int tmpCode
= ures_getInt(bundle
, &status
);
2678 if (U_SUCCESS(status
)) {
2686 #endif /* #if !UCONFIG_NO_FORMATTING */