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 UMutex
*m
= STATIC_NEW(UMutex
);
372 static CReg
* gCRegHead
= 0;
374 struct CReg
: public icu::UMemory
{
376 UChar iso
[ISO_CURRENCY_CODE_LENGTH
+1];
377 char id
[ULOC_FULLNAME_CAPACITY
];
379 CReg(const UChar
* _iso
, const char* _id
)
382 int32_t len
= (int32_t)uprv_strlen(_id
);
383 if (len
> (int32_t)(sizeof(id
)-1)) {
384 len
= (sizeof(id
)-1);
386 uprv_strncpy(id
, _id
, len
);
388 u_memcpy(iso
, _iso
, ISO_CURRENCY_CODE_LENGTH
);
389 iso
[ISO_CURRENCY_CODE_LENGTH
] = 0;
392 static UCurrRegistryKey
reg(const UChar
* _iso
, const char* _id
, UErrorCode
* status
)
394 if (status
&& U_SUCCESS(*status
) && _iso
&& _id
) {
395 CReg
* n
= new CReg(_iso
, _id
);
397 umtx_lock(gCRegLock());
399 /* register for the first time */
400 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
404 umtx_unlock(gCRegLock());
407 *status
= U_MEMORY_ALLOCATION_ERROR
;
412 static UBool
unreg(UCurrRegistryKey key
) {
414 umtx_lock(gCRegLock());
416 CReg
** p
= &gCRegHead
;
419 *p
= ((CReg
*)key
)->next
;
427 umtx_unlock(gCRegLock());
431 static const UChar
* get(const char* id
) {
432 const UChar
* result
= NULL
;
433 umtx_lock(gCRegLock());
436 /* register cleanup of the mutex */
437 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
439 if (uprv_strcmp(id
, p
->id
) == 0) {
445 umtx_unlock(gCRegLock());
449 /* This doesn't need to be thread safe. It's for u_cleanup only. */
450 static void cleanup(void) {
453 gCRegHead
= gCRegHead
->next
;
459 // -------------------------------------
461 U_CAPI UCurrRegistryKey U_EXPORT2
462 ucurr_register(const UChar
* isoCode
, const char* locale
, UErrorCode
*status
)
464 if (status
&& U_SUCCESS(*status
)) {
465 char id
[ULOC_FULLNAME_CAPACITY
];
466 idForLocale(locale
, id
, sizeof(id
), status
);
467 return CReg::reg(isoCode
, id
, status
);
472 // -------------------------------------
474 U_CAPI UBool U_EXPORT2
475 ucurr_unregister(UCurrRegistryKey key
, UErrorCode
* status
)
477 if (status
&& U_SUCCESS(*status
)) {
478 return CReg::unreg(key
);
482 #endif /* UCONFIG_NO_SERVICE */
484 // -------------------------------------
487 * Release all static memory held by currency.
489 /*The declaration here is needed so currency_cleanup(void)
490 * can call this function.
492 static UBool U_CALLCONV
493 currency_cache_cleanup(void);
496 static UBool U_CALLCONV
currency_cleanup(void) {
497 #if !UCONFIG_NO_SERVICE
501 * There might be some cached currency data or isoCodes data.
503 currency_cache_cleanup();
505 currSymbolsEquiv_cleanup();
511 // -------------------------------------
513 U_CAPI
int32_t U_EXPORT2
514 ucurr_forLocale(const char* locale
,
516 int32_t buffCapacity
,
518 if (U_FAILURE(*ec
)) { return 0; }
519 if (buffCapacity
< 0 || (buff
== nullptr && buffCapacity
> 0)) {
520 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
524 char currency
[4]; // ISO currency codes are alpha3 codes.
525 UErrorCode localStatus
= U_ZERO_ERROR
;
526 int32_t resLen
= uloc_getKeywordValue(locale
, "currency",
527 currency
, UPRV_LENGTHOF(currency
), &localStatus
);
528 if (U_SUCCESS(localStatus
) && resLen
== 3 && uprv_isInvariantString(currency
, resLen
)) {
529 if (resLen
< buffCapacity
) {
530 T_CString_toUpperCase(currency
);
531 u_charsToUChars(currency
, buff
, resLen
);
533 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
536 // get country or country_variant in `id'
537 char id
[ULOC_FULLNAME_CAPACITY
];
538 idForLocale(locale
, id
, UPRV_LENGTHOF(id
), ec
);
539 if (U_FAILURE(*ec
)) {
543 #if !UCONFIG_NO_SERVICE
544 const UChar
* result
= CReg::get(id
);
546 if(buffCapacity
> u_strlen(result
)) {
547 u_strcpy(buff
, result
);
549 resLen
= u_strlen(result
);
550 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
553 // Remove variants, which is only needed for registration.
554 char *idDelim
= uprv_strchr(id
, VAR_DELIM
);
559 const UChar
* s
= NULL
; // Currency code from data file.
561 // No point looking in the data for an empty string.
562 // This is what we would get.
563 localStatus
= U_MISSING_RESOURCE_ERROR
;
565 // Look up the CurrencyMap element in the root bundle.
566 localStatus
= U_ZERO_ERROR
;
567 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
568 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
569 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
570 UResourceBundle
*currencyReq
= ures_getByIndex(countryArray
, 0, NULL
, &localStatus
);
571 s
= ures_getStringByKey(currencyReq
, "id", &resLen
, &localStatus
);
572 ures_close(currencyReq
);
573 ures_close(countryArray
);
576 if ((U_FAILURE(localStatus
)) && strchr(id
, '_') != 0) {
577 // We don't know about it. Check to see if we support the variant.
578 uloc_getParent(locale
, id
, UPRV_LENGTHOF(id
), ec
);
579 *ec
= U_USING_FALLBACK_WARNING
;
580 // TODO: Loop over the shortened id rather than recursing and
581 // looking again for a currency keyword.
582 return ucurr_forLocale(id
, buff
, buffCapacity
, ec
);
584 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
) {
585 // There is nothing to fallback to. Report the failure/warning if possible.
588 if (U_SUCCESS(*ec
)) {
589 if(buffCapacity
> resLen
) {
593 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
599 * Modify the given locale name by removing the rightmost _-delimited
600 * element. If there is none, empty the string ("" == root).
601 * NOTE: The string "root" is not recognized; do not use it.
602 * @return TRUE if the fallback happened; FALSE if locale is already
605 static UBool
fallback(char *loc
) {
609 UErrorCode status
= U_ZERO_ERROR
;
610 if (uprv_strcmp(loc
, "en_GB") == 0) {
611 // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en"
612 // in order to consume the correct data strings. This hack will be removed
613 // when proper data sink loading is implemented here.
614 // NOTE: "001" adds 1 char over "GB". However, both call sites allocate
615 // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001).
616 uprv_strcpy(loc
+ 3, "001");
618 uloc_getParent(loc
, loc
, (int32_t)uprv_strlen(loc
), &status
);
621 char *i = uprv_strrchr(loc, '_');
631 U_CAPI
const UChar
* U_EXPORT2
632 ucurr_getName(const UChar
* currency
,
634 UCurrNameStyle nameStyle
,
635 UBool
* isChoiceFormat
, // fillin
636 int32_t* len
, // fillin
639 // Look up the Currencies resource for the given locale. The
640 // Currencies locale data looks like this:
643 //| USD { "US$", "US Dollar" }
644 //| CHF { "Sw F", "Swiss Franc" }
645 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
650 if (U_FAILURE(*ec
)) {
654 int32_t choice
= (int32_t) nameStyle
;
655 if (choice
< 0 || choice
> 2) {
656 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
660 // In the future, resource bundles may implement multi-level
661 // fallback. That is, if a currency is not found in the en_US
662 // Currencies data, then the en Currencies data will be searched.
663 // Currently, if a Currencies datum exists in en_US and en, the
664 // en_US entry hides that in en.
666 // We want multi-level fallback for this resource, so we implement
669 // Use a separate UErrorCode here that does not propagate out of
671 UErrorCode ec2
= U_ZERO_ERROR
;
673 char loc
[ULOC_FULLNAME_CAPACITY
];
674 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
675 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
676 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
680 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
681 myUCharsToChars(buf
, currency
);
683 /* Normalize the keyword value to uppercase */
684 T_CString_toUpperCase(buf
);
686 const UChar
* s
= NULL
;
688 LocalUResourceBundlePointer
rb(ures_open(U_ICUDATA_CURR
, loc
, &ec2
));
690 if (nameStyle
== UCURR_NARROW_SYMBOL_NAME
) {
692 key
.append(CURRENCIES_NARROW
, ec2
);
693 key
.append("/", ec2
);
694 key
.append(buf
, ec2
);
695 s
= ures_getStringByKeyWithFallback(rb
.getAlias(), key
.data(), len
, &ec2
);
696 if (ec2
== U_MISSING_RESOURCE_ERROR
) {
697 *ec
= U_USING_FALLBACK_WARNING
;
699 choice
= UCURR_SYMBOL_NAME
;
703 ures_getByKey(rb
.getAlias(), CURRENCIES
, rb
.getAlias(), &ec2
);
704 ures_getByKeyWithFallback(rb
.getAlias(), buf
, rb
.getAlias(), &ec2
);
705 s
= ures_getStringByIndex(rb
.getAlias(), choice
, len
, &ec2
);
708 // If we've succeeded we're done. Otherwise, try to fallback.
709 // If that fails (because we are already at root) then exit.
710 if (U_SUCCESS(ec2
)) {
711 if (ec2
== U_USING_DEFAULT_WARNING
712 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
717 // We no longer support choice format data in names. Data should not contain
719 *isChoiceFormat
= FALSE
;
720 if (U_SUCCESS(ec2
)) {
725 // If we fail to find a match, use the ISO 4217 code
726 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
727 *ec
= U_USING_DEFAULT_WARNING
;
731 U_CAPI
const UChar
* U_EXPORT2
732 ucurr_getPluralName(const UChar
* currency
,
734 UBool
* isChoiceFormat
,
735 const char* pluralCount
,
736 int32_t* len
, // fillin
738 // Look up the Currencies resource for the given locale. The
739 // Currencies locale data looks like this:
741 //| CurrencyPlurals {
744 //| other{"US dollars"}
749 if (U_FAILURE(*ec
)) {
753 // Use a separate UErrorCode here that does not propagate out of
755 UErrorCode ec2
= U_ZERO_ERROR
;
757 char loc
[ULOC_FULLNAME_CAPACITY
];
758 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
759 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
760 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
764 char buf
[ISO_CURRENCY_CODE_LENGTH
+1];
765 myUCharsToChars(buf
, currency
);
767 const UChar
* s
= NULL
;
769 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
771 rb
= ures_getByKey(rb
, CURRENCYPLURALS
, rb
, &ec2
);
773 // Fetch resource with multi-level resource inheritance fallback
774 rb
= ures_getByKeyWithFallback(rb
, buf
, rb
, &ec2
);
776 s
= ures_getStringByKeyWithFallback(rb
, pluralCount
, len
, &ec2
);
777 if (U_FAILURE(ec2
)) {
778 // fall back to "other"
780 s
= ures_getStringByKeyWithFallback(rb
, "other", len
, &ec2
);
781 if (U_FAILURE(ec2
)) {
783 // fall back to long name in Currencies
784 return ucurr_getName(currency
, locale
, UCURR_LONG_NAME
,
785 isChoiceFormat
, len
, ec
);
790 // If we've succeeded we're done. Otherwise, try to fallback.
791 // If that fails (because we are already at root) then exit.
792 if (U_SUCCESS(ec2
)) {
793 if (ec2
== U_USING_DEFAULT_WARNING
794 || (ec2
== U_USING_FALLBACK_WARNING
&& *ec
!= U_USING_DEFAULT_WARNING
)) {
801 // If we fail to find a match, use the ISO 4217 code
802 *len
= u_strlen(currency
); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
803 *ec
= U_USING_DEFAULT_WARNING
;
808 //========================================================================
809 // Following are structure and function for parsing currency names
811 #define NEED_TO_BE_DELETED 0x1
813 // TODO: a better way to define this?
814 #define MAX_CURRENCY_NAME_LEN 100
817 const char* IsoCode
; // key
818 UChar
* currencyName
; // value
819 int32_t currencyNameLen
; // value length
820 int32_t flag
; // flags
821 } CurrencyNameStruct
;
825 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
829 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
833 // Comparason function used in quick sort.
834 static int U_CALLCONV
currencyNameComparator(const void* a
, const void* b
) {
835 const CurrencyNameStruct
* currName_1
= (const CurrencyNameStruct
*)a
;
836 const CurrencyNameStruct
* currName_2
= (const CurrencyNameStruct
*)b
;
838 i
< MIN(currName_1
->currencyNameLen
, currName_2
->currencyNameLen
);
840 if (currName_1
->currencyName
[i
] < currName_2
->currencyName
[i
]) {
843 if (currName_1
->currencyName
[i
] > currName_2
->currencyName
[i
]) {
847 if (currName_1
->currencyNameLen
< currName_2
->currencyNameLen
) {
849 } else if (currName_1
->currencyNameLen
> currName_2
->currencyNameLen
) {
856 // Give a locale, return the maximum number of currency names associated with
858 // It gets currency names from resource bundles using fallback.
859 // It is the maximum number because in the fallback chain, some of the
860 // currency names are duplicated.
861 // For example, given locale as "en_US", the currency names get from resource
862 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
863 // all currency names in "en_US" and "en".
865 getCurrencyNameCount(const char* loc
, int32_t* total_currency_name_count
, int32_t* total_currency_symbol_count
) {
867 *total_currency_name_count
= 0;
868 *total_currency_symbol_count
= 0;
869 const UChar
* s
= NULL
;
870 char locale
[ULOC_FULLNAME_CAPACITY
];
871 uprv_strcpy(locale
, loc
);
872 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
874 UErrorCode ec2
= U_ZERO_ERROR
;
875 // TODO: ures_openDirect?
876 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, locale
, &ec2
);
877 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
878 int32_t n
= ures_getSize(curr
);
879 for (int32_t i
=0; i
<n
; ++i
) {
880 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
882 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
883 ++(*total_currency_symbol_count
); // currency symbol
884 if (currencySymbolsEquiv
!= NULL
) {
885 *total_currency_symbol_count
+= countEquivalent(*currencySymbolsEquiv
, UnicodeString(TRUE
, s
, len
));
887 ++(*total_currency_symbol_count
); // iso code
888 ++(*total_currency_name_count
); // long name
893 UErrorCode ec3
= U_ZERO_ERROR
;
894 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec3
);
895 n
= ures_getSize(curr_p
);
896 for (int32_t i
=0; i
<n
; ++i
) {
897 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec3
);
898 *total_currency_name_count
+= ures_getSize(names
);
905 if (!fallback(locale
)) {
912 toUpperCase(const UChar
* source
, int32_t len
, const char* locale
) {
914 UErrorCode ec
= U_ZERO_ERROR
;
915 int32_t destLen
= u_strToUpper(dest
, 0, source
, len
, locale
, &ec
);
918 dest
= (UChar
*)uprv_malloc(sizeof(UChar
) * MAX(destLen
, len
));
919 u_strToUpper(dest
, destLen
, source
, len
, locale
, &ec
);
921 u_memcpy(dest
, source
, len
);
927 // Collect all available currency names associated with the given locale
928 // (enable fallback chain).
929 // Read currenc names defined in resource bundle "Currencies" and
930 // "CurrencyPlural", enable fallback chain.
931 // return the malloc-ed currency name arrays and the total number of currency
932 // names in the array.
934 collectCurrencyNames(const char* locale
,
935 CurrencyNameStruct
** currencyNames
,
936 int32_t* total_currency_name_count
,
937 CurrencyNameStruct
** currencySymbols
,
938 int32_t* total_currency_symbol_count
,
941 const icu::Hashtable
*currencySymbolsEquiv
= getCurrSymbolsEquiv();
942 // Look up the Currencies resource for the given locale.
943 UErrorCode ec2
= U_ZERO_ERROR
;
945 char loc
[ULOC_FULLNAME_CAPACITY
];
946 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
947 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
948 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
951 // Get maximum currency name count first.
952 getCurrencyNameCount(loc
, total_currency_name_count
, total_currency_symbol_count
);
954 *currencyNames
= (CurrencyNameStruct
*)uprv_malloc
955 (sizeof(CurrencyNameStruct
) * (*total_currency_name_count
));
956 *currencySymbols
= (CurrencyNameStruct
*)uprv_malloc
957 (sizeof(CurrencyNameStruct
) * (*total_currency_symbol_count
));
959 if(currencyNames
== NULL
|| currencySymbols
== NULL
) {
960 ec
= U_MEMORY_ALLOCATION_ERROR
;
963 if (U_FAILURE(ec
)) return;
965 const UChar
* s
= NULL
; // currency name
966 char* iso
= NULL
; // currency ISO code
968 *total_currency_name_count
= 0;
969 *total_currency_symbol_count
= 0;
971 UErrorCode ec3
= U_ZERO_ERROR
;
972 UErrorCode ec4
= U_ZERO_ERROR
;
974 // Using hash to remove duplicates caused by locale fallback
975 UHashtable
* currencyIsoCodes
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &ec3
);
976 UHashtable
* currencyPluralIsoCodes
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &ec4
);
977 for (int32_t localeLevel
= 0; ; ++localeLevel
) {
979 // TODO: ures_openDirect
980 UResourceBundle
* rb
= ures_open(U_ICUDATA_CURR
, loc
, &ec2
);
981 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
982 int32_t n
= ures_getSize(curr
);
983 for (int32_t i
=0; i
<n
; ++i
) {
984 UResourceBundle
* names
= ures_getByIndex(curr
, i
, NULL
, &ec2
);
986 s
= ures_getStringByIndex(names
, UCURR_SYMBOL_NAME
, &len
, &ec2
);
987 // TODO: uhash_put wont change key/value?
988 iso
= (char*)ures_getKey(names
);
989 if (localeLevel
== 0) {
990 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
992 if (uhash_get(currencyIsoCodes
, iso
) != NULL
) {
996 uhash_put(currencyIsoCodes
, iso
, iso
, &ec3
);
999 // Add currency symbol.
1000 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1001 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)s
;
1002 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1003 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= len
;
1004 // Add equivalent symbols
1005 if (currencySymbolsEquiv
!= NULL
) {
1006 UnicodeString
str(TRUE
, s
, len
);
1007 icu::EquivIterator
iter(*currencySymbolsEquiv
, str
);
1008 const UnicodeString
*symbol
;
1009 while ((symbol
= iter
.next()) != NULL
) {
1010 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1011 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
=
1012 const_cast<UChar
*>(symbol
->getBuffer());
1013 (*currencySymbols
)[*total_currency_symbol_count
].flag
= 0;
1014 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= symbol
->length();
1018 // Add currency long name.
1019 s
= ures_getStringByIndex(names
, UCURR_LONG_NAME
, &len
, &ec2
);
1020 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1021 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1022 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1023 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1024 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1026 // put (iso, 3, and iso) in to array
1027 // Add currency ISO code.
1028 (*currencySymbols
)[*total_currency_symbol_count
].IsoCode
= iso
;
1029 (*currencySymbols
)[*total_currency_symbol_count
].currencyName
= (UChar
*)uprv_malloc(sizeof(UChar
)*3);
1030 // Must convert iso[] into Unicode
1031 u_charsToUChars(iso
, (*currencySymbols
)[*total_currency_symbol_count
].currencyName
, 3);
1032 (*currencySymbols
)[*total_currency_symbol_count
].flag
= NEED_TO_BE_DELETED
;
1033 (*currencySymbols
)[(*total_currency_symbol_count
)++].currencyNameLen
= 3;
1039 UErrorCode ec5
= U_ZERO_ERROR
;
1040 UResourceBundle
* curr_p
= ures_getByKey(rb
, CURRENCYPLURALS
, NULL
, &ec5
);
1041 n
= ures_getSize(curr_p
);
1042 for (int32_t i
=0; i
<n
; ++i
) {
1043 UResourceBundle
* names
= ures_getByIndex(curr_p
, i
, NULL
, &ec5
);
1044 iso
= (char*)ures_getKey(names
);
1045 // Using hash to remove duplicated ISO codes in fallback chain.
1046 if (localeLevel
== 0) {
1047 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1049 if (uhash_get(currencyPluralIsoCodes
, iso
) != NULL
) {
1053 uhash_put(currencyPluralIsoCodes
, iso
, iso
, &ec4
);
1056 int32_t num
= ures_getSize(names
);
1058 for (int32_t j
= 0; j
< num
; ++j
) {
1059 // TODO: remove duplicates between singular name and
1060 // currency long name?
1061 s
= ures_getStringByIndex(names
, j
, &len
, &ec5
);
1062 (*currencyNames
)[*total_currency_name_count
].IsoCode
= iso
;
1063 UChar
* upperName
= toUpperCase(s
, len
, locale
);
1064 (*currencyNames
)[*total_currency_name_count
].currencyName
= upperName
;
1065 (*currencyNames
)[*total_currency_name_count
].flag
= NEED_TO_BE_DELETED
;
1066 (*currencyNames
)[(*total_currency_name_count
)++].currencyNameLen
= len
;
1074 if (!fallback(loc
)) {
1079 uhash_close(currencyIsoCodes
);
1080 uhash_close(currencyPluralIsoCodes
);
1082 // quick sort the struct
1083 qsort(*currencyNames
, *total_currency_name_count
,
1084 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1085 qsort(*currencySymbols
, *total_currency_symbol_count
,
1086 sizeof(CurrencyNameStruct
), currencyNameComparator
);
1089 printf("currency name count: %d\n", *total_currency_name_count
);
1090 for (int32_t index
= 0; index
< *total_currency_name_count
; ++index
) {
1091 printf("index: %d\n", index
);
1092 printf("iso: %s\n", (*currencyNames
)[index
].IsoCode
);
1093 char curNameBuf
[1024];
1094 memset(curNameBuf
, 0, 1024);
1095 u_austrncpy(curNameBuf
, (*currencyNames
)[index
].currencyName
, (*currencyNames
)[index
].currencyNameLen
);
1096 printf("currencyName: %s\n", curNameBuf
);
1097 printf("len: %d\n", (*currencyNames
)[index
].currencyNameLen
);
1099 printf("currency symbol count: %d\n", *total_currency_symbol_count
);
1100 for (int32_t index
= 0; index
< *total_currency_symbol_count
; ++index
) {
1101 printf("index: %d\n", index
);
1102 printf("iso: %s\n", (*currencySymbols
)[index
].IsoCode
);
1103 char curNameBuf
[1024];
1104 memset(curNameBuf
, 0, 1024);
1105 u_austrncpy(curNameBuf
, (*currencySymbols
)[index
].currencyName
, (*currencySymbols
)[index
].currencyNameLen
);
1106 printf("currencySymbol: %s\n", curNameBuf
);
1107 printf("len: %d\n", (*currencySymbols
)[index
].currencyNameLen
);
1110 // fail on hashtable errors
1111 if (U_FAILURE(ec3
)) {
1115 if (U_FAILURE(ec4
)) {
1121 // @param currencyNames: currency names array
1122 // @param indexInCurrencyNames: the index of the character in currency names
1123 // array against which the comparison is done
1124 // @param key: input text char to compare against
1125 // @param begin(IN/OUT): the begin index of matching range in currency names array
1126 // @param end(IN/OUT): the end index of matching range in currency names array.
1128 binarySearch(const CurrencyNameStruct
* currencyNames
,
1129 int32_t indexInCurrencyNames
,
1131 int32_t* begin
, int32_t* end
) {
1133 printf("key = %x\n", key
);
1135 int32_t first
= *begin
;
1136 int32_t last
= *end
;
1137 while (first
<= last
) {
1138 int32_t mid
= (first
+ last
) / 2; // compute mid point.
1139 if (indexInCurrencyNames
>= currencyNames
[mid
].currencyNameLen
) {
1142 if (key
> currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1145 else if (key
< currencyNames
[mid
].currencyName
[indexInCurrencyNames
]) {
1149 // Find a match, and looking for ranges
1150 // Now do two more binary searches. First, on the left side for
1151 // the greatest L such that CurrencyNameStruct[L] < key.
1156 printf("mid = %d\n", mid
);
1159 int32_t M
= (L
+ R
) / 2;
1161 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1163 if (indexInCurrencyNames
>= currencyNames
[M
].currencyNameLen
) {
1166 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] < key
) {
1170 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1181 printf("begin = %d\n", *begin
);
1182 U_ASSERT(currencyNames
[*begin
].currencyName
[indexInCurrencyNames
] == key
);
1185 // Now for the second search, finding the least R such that
1186 // key < CurrencyNameStruct[R].
1190 int32_t M
= (L
+ R
) / 2;
1192 printf("L = %d, R = %d, M = %d\n", L
, R
, M
);
1194 if (currencyNames
[M
].currencyNameLen
< indexInCurrencyNames
) {
1197 if (currencyNames
[M
].currencyName
[indexInCurrencyNames
] > key
) {
1201 U_ASSERT(currencyNames
[M
].currencyName
[indexInCurrencyNames
] == key
);
1210 if (currencyNames
[R
].currencyName
[indexInCurrencyNames
] > key
) {
1216 printf("end = %d\n", *end
);
1219 // now, found the range. check whether there is exact match
1220 if (currencyNames
[*begin
].currencyNameLen
== indexInCurrencyNames
+ 1) {
1221 return *begin
; // find range and exact match.
1223 return -1; // find range, but no exact match.
1229 return -1; // failed to find range.
1233 // Linear search "text" in "currencyNames".
1234 // @param begin, end: the begin and end index in currencyNames, within which
1235 // range should the search be performed.
1236 // @param textLen: the length of the text to be compared
1237 // @param maxMatchLen(IN/OUT): passing in the computed max matching length
1238 // pass out the new max matching length
1239 // @param maxMatchIndex: the index in currencyName which has the longest
1240 // match with input text.
1242 linearSearch(const CurrencyNameStruct
* currencyNames
,
1243 int32_t begin
, int32_t end
,
1244 const UChar
* text
, int32_t textLen
,
1245 int32_t *partialMatchLen
,
1246 int32_t *maxMatchLen
, int32_t* maxMatchIndex
) {
1247 int32_t initialPartialMatchLen
= *partialMatchLen
;
1248 for (int32_t index
= begin
; index
<= end
; ++index
) {
1249 int32_t len
= currencyNames
[index
].currencyNameLen
;
1250 if (len
> *maxMatchLen
&& len
<= textLen
&&
1251 uprv_memcmp(currencyNames
[index
].currencyName
, text
, len
* sizeof(UChar
)) == 0) {
1252 *partialMatchLen
= MAX(*partialMatchLen
, len
);
1253 *maxMatchIndex
= index
;
1256 printf("maxMatchIndex = %d, maxMatchLen = %d\n",
1257 *maxMatchIndex
, *maxMatchLen
);
1260 // Check for partial matches.
1261 for (int32_t i
=initialPartialMatchLen
; i
<MIN(len
, textLen
); i
++) {
1262 if (currencyNames
[index
].currencyName
[i
] != text
[i
]) {
1265 *partialMatchLen
= MAX(*partialMatchLen
, i
+ 1);
1271 #define LINEAR_SEARCH_THRESHOLD 10
1273 // Find longest match between "text" and currency names in "currencyNames".
1274 // @param total_currency_count: total number of currency names in CurrencyNames.
1275 // @param textLen: the length of the text to be compared
1276 // @param maxMatchLen: passing in the computed max matching length
1277 // pass out the new max matching length
1278 // @param maxMatchIndex: the index in currencyName which has the longest
1279 // match with input text.
1281 searchCurrencyName(const CurrencyNameStruct
* currencyNames
,
1282 int32_t total_currency_count
,
1283 const UChar
* text
, int32_t textLen
,
1284 int32_t *partialMatchLen
,
1285 int32_t* maxMatchLen
, int32_t* maxMatchIndex
) {
1286 *maxMatchIndex
= -1;
1288 int32_t matchIndex
= -1;
1289 int32_t binarySearchBegin
= 0;
1290 int32_t binarySearchEnd
= total_currency_count
- 1;
1291 // It is a variant of binary search.
1292 // For example, given the currency names in currencyNames array are:
1293 // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
1294 // and the input text is BBEXST
1295 // The first round binary search search "B" in the text against
1296 // the first char in currency names, and find the first char matching range
1297 // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
1298 // The 2nd round binary search search the second "B" in the text against
1299 // the 2nd char in currency names, and narrow the matching range to
1300 // "BB BBEX BBEXYZ" (and the maximum matching "BB").
1301 // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
1302 // maximum matching).
1303 // The 4th round returns the same range (the maximum matching is "BBEX").
1304 // The 5th round returns no matching range.
1305 for (int32_t index
= 0; index
< textLen
; ++index
) {
1306 // matchIndex saves the one with exact match till the current point.
1307 // [binarySearchBegin, binarySearchEnd] saves the matching range.
1308 matchIndex
= binarySearch(currencyNames
, index
,
1310 &binarySearchBegin
, &binarySearchEnd
);
1311 if (binarySearchBegin
== -1) { // did not find the range
1314 *partialMatchLen
= MAX(*partialMatchLen
, index
+ 1);
1315 if (matchIndex
!= -1) {
1316 // find an exact match for text from text[0] to text[index]
1317 // in currencyNames array.
1318 *maxMatchLen
= index
+ 1;
1319 *maxMatchIndex
= matchIndex
;
1321 if (binarySearchEnd
- binarySearchBegin
< LINEAR_SEARCH_THRESHOLD
) {
1322 // linear search if within threshold.
1323 linearSearch(currencyNames
, binarySearchBegin
, binarySearchEnd
,
1326 maxMatchLen
, maxMatchIndex
);
1333 //========================= currency name cache =====================
1335 char locale
[ULOC_FULLNAME_CAPACITY
]; //key
1336 // currency names, case insensitive
1337 CurrencyNameStruct
* currencyNames
; // value
1338 int32_t totalCurrencyNameCount
; // currency name count
1339 // currency symbols and ISO code, case sensitive
1340 CurrencyNameStruct
* currencySymbols
; // value
1341 int32_t totalCurrencySymbolCount
; // count
1343 // reference count is set to 1 when an entry is put to cache.
1344 // it increases by 1 before accessing, and decreased by 1 after accessing.
1345 // The entry is deleted when ref count is zero, which means
1346 // the entry is replaced out of cache and no process is accessing it.
1348 } CurrencyNameCacheEntry
;
1351 #define CURRENCY_NAME_CACHE_NUM 10
1353 // Reserve 10 cache entries.
1354 static CurrencyNameCacheEntry
* currCache
[CURRENCY_NAME_CACHE_NUM
] = {NULL
};
1355 // Using an index to indicate which entry to be replaced when cache is full.
1356 // It is a simple round-robin replacement strategy.
1357 static int8_t currentCacheEntryIndex
= 0;
1359 static UMutex
*gCurrencyCacheMutex() {
1360 static UMutex
*m
= STATIC_NEW(UMutex
);
1366 deleteCurrencyNames(CurrencyNameStruct
* currencyNames
, int32_t count
) {
1367 for (int32_t index
= 0; index
< count
; ++index
) {
1368 if ( (currencyNames
[index
].flag
& NEED_TO_BE_DELETED
) ) {
1369 uprv_free(currencyNames
[index
].currencyName
);
1372 uprv_free(currencyNames
);
1377 deleteCacheEntry(CurrencyNameCacheEntry
* entry
) {
1378 deleteCurrencyNames(entry
->currencyNames
, entry
->totalCurrencyNameCount
);
1379 deleteCurrencyNames(entry
->currencySymbols
, entry
->totalCurrencySymbolCount
);
1385 static UBool U_CALLCONV
1386 currency_cache_cleanup(void) {
1387 for (int32_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1389 deleteCacheEntry(currCache
[i
]);
1398 * Loads the currency name data from the cache, or from resource bundles if necessary.
1399 * The refCount is automatically incremented. It is the caller's responsibility
1400 * to decrement it when done!
1402 static CurrencyNameCacheEntry
*
1403 getCacheEntry(const char* locale
, UErrorCode
& ec
) {
1405 int32_t total_currency_name_count
= 0;
1406 CurrencyNameStruct
* currencyNames
= NULL
;
1407 int32_t total_currency_symbol_count
= 0;
1408 CurrencyNameStruct
* currencySymbols
= NULL
;
1409 CurrencyNameCacheEntry
* cacheEntry
= NULL
;
1411 umtx_lock(gCurrencyCacheMutex());
1412 // in order to handle racing correctly,
1413 // not putting 'search' in a separate function.
1415 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1416 if (currCache
[i
]!= NULL
&&
1417 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1423 cacheEntry
= currCache
[found
];
1424 ++(cacheEntry
->refCount
);
1426 umtx_unlock(gCurrencyCacheMutex());
1428 collectCurrencyNames(locale
, ¤cyNames
, &total_currency_name_count
, ¤cySymbols
, &total_currency_symbol_count
, ec
);
1429 if (U_FAILURE(ec
)) {
1432 umtx_lock(gCurrencyCacheMutex());
1434 for (int8_t i
= 0; i
< CURRENCY_NAME_CACHE_NUM
; ++i
) {
1435 if (currCache
[i
]!= NULL
&&
1436 uprv_strcmp(locale
, currCache
[i
]->locale
) == 0) {
1442 // insert new entry to
1443 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1444 // and remove the existing entry
1445 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1447 cacheEntry
= currCache
[currentCacheEntryIndex
];
1449 --(cacheEntry
->refCount
);
1450 // delete if the ref count is zero
1451 if (cacheEntry
->refCount
== 0) {
1452 deleteCacheEntry(cacheEntry
);
1455 cacheEntry
= (CurrencyNameCacheEntry
*)uprv_malloc(sizeof(CurrencyNameCacheEntry
));
1456 currCache
[currentCacheEntryIndex
] = cacheEntry
;
1457 uprv_strcpy(cacheEntry
->locale
, locale
);
1458 cacheEntry
->currencyNames
= currencyNames
;
1459 cacheEntry
->totalCurrencyNameCount
= total_currency_name_count
;
1460 cacheEntry
->currencySymbols
= currencySymbols
;
1461 cacheEntry
->totalCurrencySymbolCount
= total_currency_symbol_count
;
1462 cacheEntry
->refCount
= 2; // one for cache, one for reference
1463 currentCacheEntryIndex
= (currentCacheEntryIndex
+ 1) % CURRENCY_NAME_CACHE_NUM
;
1464 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
1466 deleteCurrencyNames(currencyNames
, total_currency_name_count
);
1467 deleteCurrencyNames(currencySymbols
, total_currency_symbol_count
);
1468 cacheEntry
= currCache
[found
];
1469 ++(cacheEntry
->refCount
);
1471 umtx_unlock(gCurrencyCacheMutex());
1477 static void releaseCacheEntry(CurrencyNameCacheEntry
* cacheEntry
) {
1478 umtx_lock(gCurrencyCacheMutex());
1479 --(cacheEntry
->refCount
);
1480 if (cacheEntry
->refCount
== 0) { // remove
1481 deleteCacheEntry(cacheEntry
);
1483 umtx_unlock(gCurrencyCacheMutex());
1487 uprv_parseCurrency(const char* locale
,
1488 const icu::UnicodeString
& text
,
1489 icu::ParsePosition
& pos
,
1491 int32_t* partialMatchLen
,
1495 if (U_FAILURE(ec
)) {
1498 CurrencyNameCacheEntry
* cacheEntry
= getCacheEntry(locale
, ec
);
1499 if (U_FAILURE(ec
)) {
1503 int32_t total_currency_name_count
= cacheEntry
->totalCurrencyNameCount
;
1504 CurrencyNameStruct
* currencyNames
= cacheEntry
->currencyNames
;
1505 int32_t total_currency_symbol_count
= cacheEntry
->totalCurrencySymbolCount
;
1506 CurrencyNameStruct
* currencySymbols
= cacheEntry
->currencySymbols
;
1508 int32_t start
= pos
.getIndex();
1510 UChar inputText
[MAX_CURRENCY_NAME_LEN
];
1511 UChar upperText
[MAX_CURRENCY_NAME_LEN
];
1512 int32_t textLen
= MIN(MAX_CURRENCY_NAME_LEN
, text
.length() - start
);
1513 text
.extract(start
, textLen
, inputText
);
1514 UErrorCode ec1
= U_ZERO_ERROR
;
1515 textLen
= u_strToUpper(upperText
, MAX_CURRENCY_NAME_LEN
, inputText
, textLen
, locale
, &ec1
);
1517 // Make sure partialMatchLen is initialized
1518 *partialMatchLen
= 0;
1521 int32_t matchIndex
= -1;
1522 // case in-sensitive comparision against currency names
1523 searchCurrencyName(currencyNames
, total_currency_name_count
,
1524 upperText
, textLen
, partialMatchLen
, &max
, &matchIndex
);
1527 printf("search in names, max = %d, matchIndex = %d\n", max
, matchIndex
);
1530 int32_t maxInSymbol
= 0;
1531 int32_t matchIndexInSymbol
= -1;
1532 if (type
!= UCURR_LONG_NAME
) { // not name only
1533 // case sensitive comparison against currency symbols and ISO code.
1534 searchCurrencyName(currencySymbols
, total_currency_symbol_count
,
1537 &maxInSymbol
, &matchIndexInSymbol
);
1541 printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol
, matchIndexInSymbol
);
1542 if(matchIndexInSymbol
!= -1) {
1543 printf("== ISO=%s\n", currencySymbols
[matchIndexInSymbol
].IsoCode
);
1547 if (max
>= maxInSymbol
&& matchIndex
!= -1) {
1548 u_charsToUChars(currencyNames
[matchIndex
].IsoCode
, result
, 4);
1549 pos
.setIndex(start
+ max
);
1550 } else if (maxInSymbol
>= max
&& matchIndexInSymbol
!= -1) {
1551 u_charsToUChars(currencySymbols
[matchIndexInSymbol
].IsoCode
, result
, 4);
1552 pos
.setIndex(start
+ maxInSymbol
);
1555 // decrease reference count
1556 releaseCacheEntry(cacheEntry
);
1559 void uprv_currencyLeads(const char* locale
, icu::UnicodeSet
& result
, UErrorCode
& ec
) {
1561 if (U_FAILURE(ec
)) {
1564 CurrencyNameCacheEntry
* cacheEntry
= getCacheEntry(locale
, ec
);
1565 if (U_FAILURE(ec
)) {
1569 for (int32_t i
=0; i
<cacheEntry
->totalCurrencySymbolCount
; i
++) {
1570 const CurrencyNameStruct
& info
= cacheEntry
->currencySymbols
[i
];
1572 U16_GET(info
.currencyName
, 0, 0, info
.currencyNameLen
, cp
);
1576 for (int32_t i
=0; i
<cacheEntry
->totalCurrencyNameCount
; i
++) {
1577 const CurrencyNameStruct
& info
= cacheEntry
->currencyNames
[i
];
1579 U16_GET(info
.currencyName
, 0, 0, info
.currencyNameLen
, cp
);
1583 // decrease reference count
1584 releaseCacheEntry(cacheEntry
);
1589 * Internal method. Given a currency ISO code and a locale, return
1590 * the "static" currency name. This is usually the same as the
1591 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
1592 * format is applied to the number 2.0 (to yield the more common
1593 * plural) to return a static name.
1595 * This is used for backward compatibility with old currency logic in
1596 * DecimalFormat and DecimalFormatSymbols.
1599 uprv_getStaticCurrencyName(const UChar
* iso
, const char* loc
,
1600 icu::UnicodeString
& result
, UErrorCode
& ec
)
1604 UBool isChoiceFormat
;
1606 const UChar
* currname
= ucurr_getName(iso
, loc
, UCURR_SYMBOL_NAME
,
1607 &isChoiceFormat
, &len
, &ec
);
1608 if (U_SUCCESS(ec
)) {
1609 result
.setTo(currname
, len
);
1613 U_CAPI
int32_t U_EXPORT2
1614 ucurr_getDefaultFractionDigits(const UChar
* currency
, UErrorCode
* ec
) {
1615 return ucurr_getDefaultFractionDigitsForUsage(currency
,UCURR_USAGE_STANDARD
,ec
);
1618 U_DRAFT
int32_t U_EXPORT2
1619 ucurr_getDefaultFractionDigitsForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1620 int32_t fracDigits
= 0;
1621 if (U_SUCCESS(*ec
)) {
1623 case UCURR_USAGE_STANDARD
:
1624 fracDigits
= (_findMetaData(currency
, *ec
))[0];
1626 case UCURR_USAGE_CASH
:
1627 fracDigits
= (_findMetaData(currency
, *ec
))[2];
1630 *ec
= U_UNSUPPORTED_ERROR
;
1636 U_CAPI
double U_EXPORT2
1637 ucurr_getRoundingIncrement(const UChar
* currency
, UErrorCode
* ec
) {
1638 return ucurr_getRoundingIncrementForUsage(currency
, UCURR_USAGE_STANDARD
, ec
);
1641 U_DRAFT
double U_EXPORT2
1642 ucurr_getRoundingIncrementForUsage(const UChar
* currency
, const UCurrencyUsage usage
, UErrorCode
* ec
) {
1643 double result
= 0.0;
1645 const int32_t *data
= _findMetaData(currency
, *ec
);
1646 if (U_SUCCESS(*ec
)) {
1650 case UCURR_USAGE_STANDARD
:
1651 fracDigits
= data
[0];
1652 increment
= data
[1];
1654 case UCURR_USAGE_CASH
:
1655 fracDigits
= data
[2];
1656 increment
= data
[3];
1659 *ec
= U_UNSUPPORTED_ERROR
;
1663 // If the meta data is invalid, return 0.0
1664 if (fracDigits
< 0 || fracDigits
> MAX_POW10
) {
1665 *ec
= U_INVALID_FORMAT_ERROR
;
1667 // A rounding value of 0 or 1 indicates no rounding.
1668 if (increment
>= 2) {
1669 // Return (increment) / 10^(fracDigits). The only actual rounding data,
1670 // as of this writing, is CHF { 2, 5 }.
1671 result
= double(increment
) / POW10
[fracDigits
];
1681 typedef struct UCurrencyContext
{
1682 uint32_t currType
; /* UCurrCurrencyType */
1687 Please keep this list in alphabetical order.
1688 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
1690 ISO-4217: https://www.iso.org/iso-4217-currency-codes.html
1692 static const struct CurrencyList
{
1693 const char *currency
;
1695 } gCurrencyList
[] = {
1696 {"ADP", UCURR_COMMON
|UCURR_DEPRECATED
},
1697 {"AED", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1698 {"AFA", UCURR_COMMON
|UCURR_DEPRECATED
},
1699 {"AFN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1700 {"ALK", UCURR_COMMON
|UCURR_DEPRECATED
},
1701 {"ALL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1702 {"AMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1703 {"ANG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1704 {"AOA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1705 {"AOK", UCURR_COMMON
|UCURR_DEPRECATED
},
1706 {"AON", UCURR_COMMON
|UCURR_DEPRECATED
},
1707 {"AOR", UCURR_COMMON
|UCURR_DEPRECATED
},
1708 {"ARA", UCURR_COMMON
|UCURR_DEPRECATED
},
1709 {"ARL", UCURR_COMMON
|UCURR_DEPRECATED
},
1710 {"ARM", UCURR_COMMON
|UCURR_DEPRECATED
},
1711 {"ARP", UCURR_COMMON
|UCURR_DEPRECATED
},
1712 {"ARS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1713 {"ATS", UCURR_COMMON
|UCURR_DEPRECATED
},
1714 {"AUD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1715 {"AWG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1716 {"AZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1717 {"AZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1718 {"BAD", UCURR_COMMON
|UCURR_DEPRECATED
},
1719 {"BAM", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1720 {"BAN", UCURR_COMMON
|UCURR_DEPRECATED
},
1721 {"BBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1722 {"BDT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1723 {"BEC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1724 {"BEF", UCURR_COMMON
|UCURR_DEPRECATED
},
1725 {"BEL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1726 {"BGL", UCURR_COMMON
|UCURR_DEPRECATED
},
1727 {"BGM", UCURR_COMMON
|UCURR_DEPRECATED
},
1728 {"BGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1729 {"BGO", UCURR_COMMON
|UCURR_DEPRECATED
},
1730 {"BHD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1731 {"BIF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1732 {"BMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1733 {"BND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1734 {"BOB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1735 {"BOL", UCURR_COMMON
|UCURR_DEPRECATED
},
1736 {"BOP", UCURR_COMMON
|UCURR_DEPRECATED
},
1737 {"BOV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1738 {"BRB", UCURR_COMMON
|UCURR_DEPRECATED
},
1739 {"BRC", UCURR_COMMON
|UCURR_DEPRECATED
},
1740 {"BRE", UCURR_COMMON
|UCURR_DEPRECATED
},
1741 {"BRL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1742 {"BRN", UCURR_COMMON
|UCURR_DEPRECATED
},
1743 {"BRR", UCURR_COMMON
|UCURR_DEPRECATED
},
1744 {"BRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1745 {"BSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1746 {"BTN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1747 {"BUK", UCURR_COMMON
|UCURR_DEPRECATED
},
1748 {"BWP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1749 {"BYB", UCURR_COMMON
|UCURR_DEPRECATED
},
1750 {"BYN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1751 {"BYR", UCURR_COMMON
|UCURR_DEPRECATED
},
1752 {"BZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1753 {"CAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1754 {"CDF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1755 {"CHE", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1756 {"CHF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1757 {"CHW", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1758 {"CLE", UCURR_COMMON
|UCURR_DEPRECATED
},
1759 {"CLF", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1760 {"CLP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1761 {"CNH", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1762 {"CNX", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1763 {"CNY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1764 {"COP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1765 {"COU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1766 {"CRC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1767 {"CSD", UCURR_COMMON
|UCURR_DEPRECATED
},
1768 {"CSK", UCURR_COMMON
|UCURR_DEPRECATED
},
1769 {"CUC", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1770 {"CUP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1771 {"CVE", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1772 {"CYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1773 {"CZK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1774 {"DDM", UCURR_COMMON
|UCURR_DEPRECATED
},
1775 {"DEM", UCURR_COMMON
|UCURR_DEPRECATED
},
1776 {"DJF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1777 {"DKK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1778 {"DOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1779 {"DZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1780 {"ECS", UCURR_COMMON
|UCURR_DEPRECATED
},
1781 {"ECV", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1782 {"EEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1783 {"EGP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1784 {"EQE", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1785 {"ERN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1786 {"ESA", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1787 {"ESB", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1788 {"ESP", UCURR_COMMON
|UCURR_DEPRECATED
},
1789 {"ETB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1790 {"EUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1791 {"FIM", UCURR_COMMON
|UCURR_DEPRECATED
},
1792 {"FJD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1793 {"FKP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1794 {"FRF", UCURR_COMMON
|UCURR_DEPRECATED
},
1795 {"GBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1796 {"GEK", UCURR_COMMON
|UCURR_DEPRECATED
},
1797 {"GEL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1798 {"GHC", UCURR_COMMON
|UCURR_DEPRECATED
},
1799 {"GHS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1800 {"GIP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1801 {"GMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1802 {"GNF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1803 {"GNS", UCURR_COMMON
|UCURR_DEPRECATED
},
1804 {"GQE", UCURR_COMMON
|UCURR_DEPRECATED
},
1805 {"GRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1806 {"GTQ", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1807 {"GWE", UCURR_COMMON
|UCURR_DEPRECATED
},
1808 {"GWP", UCURR_COMMON
|UCURR_DEPRECATED
},
1809 {"GYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1810 {"HKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1811 {"HNL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1812 {"HRD", UCURR_COMMON
|UCURR_DEPRECATED
},
1813 {"HRK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1814 {"HTG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1815 {"HUF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1816 {"IDR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1817 {"IEP", UCURR_COMMON
|UCURR_DEPRECATED
},
1818 {"ILP", UCURR_COMMON
|UCURR_DEPRECATED
},
1819 {"ILR", UCURR_COMMON
|UCURR_DEPRECATED
},
1820 {"ILS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1821 {"INR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1822 {"IQD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1823 {"IRR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1824 {"ISJ", UCURR_COMMON
|UCURR_DEPRECATED
},
1825 {"ISK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1826 {"ITL", UCURR_COMMON
|UCURR_DEPRECATED
},
1827 {"JMD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1828 {"JOD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1829 {"JPY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1830 {"KES", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1831 {"KGS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1832 {"KHR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1833 {"KMF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1834 {"KPW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1835 {"KRH", UCURR_COMMON
|UCURR_DEPRECATED
},
1836 {"KRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1837 {"KRW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1838 {"KWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1839 {"KYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1840 {"KZT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1841 {"LAK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1842 {"LBP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1843 {"LKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1844 {"LRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1845 {"LSL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1846 {"LSM", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1847 {"LTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1848 {"LTT", UCURR_COMMON
|UCURR_DEPRECATED
},
1849 {"LUC", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1850 {"LUF", UCURR_COMMON
|UCURR_DEPRECATED
},
1851 {"LUL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1852 {"LVL", UCURR_COMMON
|UCURR_DEPRECATED
},
1853 {"LVR", UCURR_COMMON
|UCURR_DEPRECATED
},
1854 {"LYD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1855 {"MAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1856 {"MAF", UCURR_COMMON
|UCURR_DEPRECATED
},
1857 {"MCF", UCURR_COMMON
|UCURR_DEPRECATED
},
1858 {"MDC", UCURR_COMMON
|UCURR_DEPRECATED
},
1859 {"MDL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1860 {"MGA", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1861 {"MGF", UCURR_COMMON
|UCURR_DEPRECATED
},
1862 {"MKD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1863 {"MKN", UCURR_COMMON
|UCURR_DEPRECATED
},
1864 {"MLF", UCURR_COMMON
|UCURR_DEPRECATED
},
1865 {"MMK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1866 {"MNT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1867 {"MOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1868 {"MRO", UCURR_COMMON
|UCURR_DEPRECATED
},
1869 {"MRU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1870 {"MTL", UCURR_COMMON
|UCURR_DEPRECATED
},
1871 {"MTP", UCURR_COMMON
|UCURR_DEPRECATED
},
1872 {"MUR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1873 {"MVP", UCURR_COMMON
|UCURR_DEPRECATED
}, // questionable, remove?
1874 {"MVR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1875 {"MWK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1876 {"MXN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1877 {"MXP", UCURR_COMMON
|UCURR_DEPRECATED
},
1878 {"MXV", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1879 {"MYR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1880 {"MZE", UCURR_COMMON
|UCURR_DEPRECATED
},
1881 {"MZM", UCURR_COMMON
|UCURR_DEPRECATED
},
1882 {"MZN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1883 {"NAD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1884 {"NGN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1885 {"NIC", UCURR_COMMON
|UCURR_DEPRECATED
},
1886 {"NIO", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1887 {"NLG", UCURR_COMMON
|UCURR_DEPRECATED
},
1888 {"NOK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1889 {"NPR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1890 {"NZD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1891 {"OMR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1892 {"PAB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1893 {"PEI", UCURR_COMMON
|UCURR_DEPRECATED
},
1894 {"PEN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1895 {"PES", UCURR_COMMON
|UCURR_DEPRECATED
},
1896 {"PGK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1897 {"PHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1898 {"PKR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1899 {"PLN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1900 {"PLZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1901 {"PTE", UCURR_COMMON
|UCURR_DEPRECATED
},
1902 {"PYG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1903 {"QAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1904 {"RHD", UCURR_COMMON
|UCURR_DEPRECATED
},
1905 {"ROL", UCURR_COMMON
|UCURR_DEPRECATED
},
1906 {"RON", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1907 {"RSD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1908 {"RUB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1909 {"RUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1910 {"RWF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1911 {"SAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1912 {"SBD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1913 {"SCR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1914 {"SDD", UCURR_COMMON
|UCURR_DEPRECATED
},
1915 {"SDG", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1916 {"SDP", UCURR_COMMON
|UCURR_DEPRECATED
},
1917 {"SEK", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1918 {"SGD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1919 {"SHP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1920 {"SIT", UCURR_COMMON
|UCURR_DEPRECATED
},
1921 {"SKK", UCURR_COMMON
|UCURR_DEPRECATED
},
1922 {"SLL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1923 {"SOS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1924 {"SRD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1925 {"SRG", UCURR_COMMON
|UCURR_DEPRECATED
},
1926 {"SSP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1927 {"STD", UCURR_COMMON
|UCURR_DEPRECATED
},
1928 {"STN", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1929 {"SUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1930 {"SVC", UCURR_COMMON
|UCURR_DEPRECATED
},
1931 {"SYP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1932 {"SZL", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1933 {"THB", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1934 {"TJR", UCURR_COMMON
|UCURR_DEPRECATED
},
1935 {"TJS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1936 {"TMM", UCURR_COMMON
|UCURR_DEPRECATED
},
1937 {"TMT", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1938 {"TND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1939 {"TOP", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1940 {"TPE", UCURR_COMMON
|UCURR_DEPRECATED
},
1941 {"TRL", UCURR_COMMON
|UCURR_DEPRECATED
},
1942 {"TRY", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1943 {"TTD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1944 {"TWD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1945 {"TZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1946 {"UAH", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1947 {"UAK", UCURR_COMMON
|UCURR_DEPRECATED
},
1948 {"UGS", UCURR_COMMON
|UCURR_DEPRECATED
},
1949 {"UGX", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1950 {"USD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1951 {"USN", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1952 {"USS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1953 {"UYI", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1954 {"UYP", UCURR_COMMON
|UCURR_DEPRECATED
},
1955 {"UYU", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1956 {"UZS", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1957 {"VEB", UCURR_COMMON
|UCURR_DEPRECATED
},
1958 {"VEF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1959 {"VND", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1960 {"VNN", UCURR_COMMON
|UCURR_DEPRECATED
},
1961 {"VUV", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1962 {"WST", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1963 {"XAF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1964 {"XAG", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1965 {"XAU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1966 {"XBA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1967 {"XBB", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1968 {"XBC", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1969 {"XBD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1970 {"XCD", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1971 {"XDR", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1972 {"XEU", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1973 {"XFO", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1974 {"XFU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1975 {"XOF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1976 {"XPD", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1977 {"XPF", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1978 {"XPT", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1979 {"XRE", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1980 {"XSU", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1981 {"XTS", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1982 {"XUA", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1983 {"XXX", UCURR_UNCOMMON
|UCURR_NON_DEPRECATED
},
1984 {"YDD", UCURR_COMMON
|UCURR_DEPRECATED
},
1985 {"YER", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1986 {"YUD", UCURR_COMMON
|UCURR_DEPRECATED
},
1987 {"YUM", UCURR_COMMON
|UCURR_DEPRECATED
},
1988 {"YUN", UCURR_COMMON
|UCURR_DEPRECATED
},
1989 {"YUR", UCURR_COMMON
|UCURR_DEPRECATED
},
1990 {"ZAL", UCURR_UNCOMMON
|UCURR_DEPRECATED
},
1991 {"ZAR", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1992 {"ZMK", UCURR_COMMON
|UCURR_DEPRECATED
},
1993 {"ZMW", UCURR_COMMON
|UCURR_NON_DEPRECATED
},
1994 {"ZRN", UCURR_COMMON
|UCURR_DEPRECATED
},
1995 {"ZRZ", UCURR_COMMON
|UCURR_DEPRECATED
},
1996 {"ZWD", UCURR_COMMON
|UCURR_DEPRECATED
},
1997 {"ZWL", UCURR_COMMON
|UCURR_DEPRECATED
},
1998 {"ZWR", UCURR_COMMON
|UCURR_DEPRECATED
},
1999 { NULL
, 0 } // Leave here to denote the end of the list.
2002 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
2003 ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
2005 static int32_t U_CALLCONV
2006 ucurr_countCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2007 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2008 uint32_t currType
= myContext
->currType
;
2011 /* Count the number of items matching the type we are looking for. */
2012 for (int32_t idx
= 0; gCurrencyList
[idx
].currency
!= NULL
; idx
++) {
2013 if (UCURR_MATCHES_BITMASK(gCurrencyList
[idx
].currType
, currType
)) {
2020 static const char* U_CALLCONV
2021 ucurr_nextCurrencyList(UEnumeration
*enumerator
,
2022 int32_t* resultLength
,
2023 UErrorCode
* /*pErrorCode*/)
2025 UCurrencyContext
*myContext
= (UCurrencyContext
*)(enumerator
->context
);
2027 /* Find the next in the list that matches the type we are looking for. */
2028 while (myContext
->listIdx
< UPRV_LENGTHOF(gCurrencyList
)-1) {
2029 const struct CurrencyList
*currItem
= &gCurrencyList
[myContext
->listIdx
++];
2030 if (UCURR_MATCHES_BITMASK(currItem
->currType
, myContext
->currType
))
2033 *resultLength
= 3; /* Currency codes are only 3 chars long */
2035 return currItem
->currency
;
2038 /* We enumerated too far. */
2045 static void U_CALLCONV
2046 ucurr_resetCurrencyList(UEnumeration
*enumerator
, UErrorCode
* /*pErrorCode*/) {
2047 ((UCurrencyContext
*)(enumerator
->context
))->listIdx
= 0;
2050 static void U_CALLCONV
2051 ucurr_closeCurrencyList(UEnumeration
*enumerator
) {
2052 uprv_free(enumerator
->context
);
2053 uprv_free(enumerator
);
2056 static void U_CALLCONV
2057 ucurr_createCurrencyList(UHashtable
*isoCodes
, UErrorCode
* status
){
2058 UErrorCode localStatus
= U_ZERO_ERROR
;
2060 // Look up the CurrencyMap element in the root bundle.
2061 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2062 UResourceBundle
*currencyMapArray
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2064 if (U_SUCCESS(localStatus
)) {
2065 // process each entry in currency map
2066 for (int32_t i
=0; i
<ures_getSize(currencyMapArray
); i
++) {
2067 // get the currency resource
2068 UResourceBundle
*currencyArray
= ures_getByIndex(currencyMapArray
, i
, NULL
, &localStatus
);
2069 // process each currency
2070 if (U_SUCCESS(localStatus
)) {
2071 for (int32_t j
=0; j
<ures_getSize(currencyArray
); j
++) {
2072 // get the currency resource
2073 UResourceBundle
*currencyRes
= ures_getByIndex(currencyArray
, j
, NULL
, &localStatus
);
2074 IsoCodeEntry
*entry
= (IsoCodeEntry
*)uprv_malloc(sizeof(IsoCodeEntry
));
2075 if (entry
== NULL
) {
2076 *status
= U_MEMORY_ALLOCATION_ERROR
;
2081 int32_t isoLength
= 0;
2082 UResourceBundle
*idRes
= ures_getByKey(currencyRes
, "id", NULL
, &localStatus
);
2083 if (idRes
== NULL
) {
2086 const UChar
*isoCode
= ures_getString(idRes
, &isoLength
, &localStatus
);
2089 UDate fromDate
= U_DATE_MIN
;
2090 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2092 if (U_SUCCESS(localStatus
)) {
2093 int32_t fromLength
= 0;
2094 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2095 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2096 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2097 fromDate
= (UDate
)currDate64
;
2099 ures_close(fromRes
);
2102 UDate toDate
= U_DATE_MAX
;
2103 localStatus
= U_ZERO_ERROR
;
2104 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2106 if (U_SUCCESS(localStatus
)) {
2107 int32_t toLength
= 0;
2108 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2109 int64_t currDate64
= (int64_t)toArray
[0] << 32;
2110 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2111 toDate
= (UDate
)currDate64
;
2116 ures_close(currencyRes
);
2118 entry
->isoCode
= isoCode
;
2119 entry
->from
= fromDate
;
2122 localStatus
= U_ZERO_ERROR
;
2123 uhash_put(isoCodes
, (UChar
*)isoCode
, entry
, &localStatus
);
2126 *status
= localStatus
;
2128 ures_close(currencyArray
);
2131 *status
= localStatus
;
2134 ures_close(currencyMapArray
);
2137 static const UEnumeration gEnumCurrencyList
= {
2140 ucurr_closeCurrencyList
,
2141 ucurr_countCurrencyList
,
2143 ucurr_nextCurrencyList
,
2144 ucurr_resetCurrencyList
2149 static void U_CALLCONV
initIsoCodes(UErrorCode
&status
) {
2150 U_ASSERT(gIsoCodes
== NULL
);
2151 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
2153 UHashtable
*isoCodes
= uhash_open(uhash_hashUChars
, uhash_compareUChars
, NULL
, &status
);
2154 if (U_FAILURE(status
)) {
2157 uhash_setValueDeleter(isoCodes
, deleteIsoCodeEntry
);
2159 ucurr_createCurrencyList(isoCodes
, &status
);
2160 if (U_FAILURE(status
)) {
2161 uhash_close(isoCodes
);
2164 gIsoCodes
= isoCodes
; // Note: gIsoCodes is const. Once set up here it is never altered,
2165 // and read only access is safe without synchronization.
2168 static void populateCurrSymbolsEquiv(icu::Hashtable
*hash
, UErrorCode
&status
) {
2169 if (U_FAILURE(status
)) { return; }
2170 for (auto& entry
: unisets::kCurrencyEntries
) {
2171 UnicodeString
exemplar(entry
.exemplar
);
2172 const UnicodeSet
* set
= unisets::get(entry
.key
);
2173 if (set
== nullptr) { return; }
2174 UnicodeSetIterator
it(*set
);
2176 UnicodeString value
= it
.getString();
2177 if (value
== exemplar
) {
2178 // No need to mark the exemplar character as an equivalent
2181 makeEquivalent(exemplar
, value
, hash
, status
);
2182 if (U_FAILURE(status
)) { return; }
2187 static void U_CALLCONV
initCurrSymbolsEquiv() {
2188 U_ASSERT(gCurrSymbolsEquiv
== NULL
);
2189 UErrorCode status
= U_ZERO_ERROR
;
2190 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY
, currency_cleanup
);
2191 icu::Hashtable
*temp
= new icu::Hashtable(status
);
2195 if (U_FAILURE(status
)) {
2199 temp
->setValueDeleter(deleteUnicode
);
2200 populateCurrSymbolsEquiv(temp
, status
);
2201 if (U_FAILURE(status
)) {
2205 gCurrSymbolsEquiv
= temp
;
2208 U_CAPI UBool U_EXPORT2
2209 ucurr_isAvailable(const UChar
* isoCode
, UDate from
, UDate to
, UErrorCode
* eErrorCode
) {
2210 umtx_initOnce(gIsoCodesInitOnce
, &initIsoCodes
, *eErrorCode
);
2211 if (U_FAILURE(*eErrorCode
)) {
2215 IsoCodeEntry
* result
= (IsoCodeEntry
*) uhash_get(gIsoCodes
, isoCode
);
2216 if (result
== NULL
) {
2218 } else if (from
> to
) {
2219 *eErrorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2221 } else if ((from
> result
->to
) || (to
< result
->from
)) {
2227 static const icu::Hashtable
* getCurrSymbolsEquiv() {
2228 umtx_initOnce(gCurrSymbolsEquivInitOnce
, &initCurrSymbolsEquiv
);
2229 return gCurrSymbolsEquiv
;
2232 U_CAPI UEnumeration
* U_EXPORT2
2233 ucurr_openISOCurrencies(uint32_t currType
, UErrorCode
*pErrorCode
) {
2234 UEnumeration
*myEnum
= NULL
;
2235 UCurrencyContext
*myContext
;
2237 myEnum
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2238 if (myEnum
== NULL
) {
2239 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2242 uprv_memcpy(myEnum
, &gEnumCurrencyList
, sizeof(UEnumeration
));
2243 myContext
= (UCurrencyContext
*)uprv_malloc(sizeof(UCurrencyContext
));
2244 if (myContext
== NULL
) {
2245 *pErrorCode
= U_MEMORY_ALLOCATION_ERROR
;
2249 myContext
->currType
= currType
;
2250 myContext
->listIdx
= 0;
2251 myEnum
->context
= myContext
;
2255 U_CAPI
int32_t U_EXPORT2
2256 ucurr_countCurrencies(const char* locale
,
2260 int32_t currCount
= 0;
2262 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2265 UErrorCode localStatus
= U_ZERO_ERROR
;
2266 char id
[ULOC_FULLNAME_CAPACITY
];
2267 uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2269 // get country or country_variant in `id'
2270 idForLocale(locale
, id
, sizeof(id
), ec
);
2277 // Remove variants, which is only needed for registration.
2278 char *idDelim
= strchr(id
, VAR_DELIM
);
2284 // Look up the CurrencyMap element in the root bundle.
2285 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2286 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2288 // Using the id derived from the local, get the currency data
2289 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2291 // process each currency to see which one is valid for the given date
2292 if (U_SUCCESS(localStatus
))
2294 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2296 // get the currency resource
2297 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2299 // get the from date
2300 int32_t fromLength
= 0;
2301 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2302 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2304 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2305 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2306 UDate fromDate
= (UDate
)currDate64
;
2308 if (ures_getSize(currencyRes
)> 2)
2310 int32_t toLength
= 0;
2311 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2312 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2314 currDate64
= (int64_t)toArray
[0] << 32;
2315 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2316 UDate toDate
= (UDate
)currDate64
;
2318 if ((fromDate
<= date
) && (date
< toDate
))
2327 if (fromDate
<= date
)
2333 // close open resources
2334 ures_close(currencyRes
);
2335 ures_close(fromRes
);
2338 } // end if (U_SUCCESS(localStatus))
2340 ures_close(countryArray
);
2343 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2345 // There is nothing to fallback to.
2346 // Report the failure/warning if possible.
2358 // If we got here, either error code is invalid or
2359 // some argument passed is no good.
2363 U_CAPI
int32_t U_EXPORT2
2364 ucurr_forLocaleAndDate(const char* locale
,
2368 int32_t buffCapacity
,
2372 int32_t currIndex
= 0;
2373 const UChar
* s
= NULL
;
2375 if (ec
!= NULL
&& U_SUCCESS(*ec
))
2377 // check the arguments passed
2378 if ((buff
&& buffCapacity
) || !buffCapacity
)
2381 UErrorCode localStatus
= U_ZERO_ERROR
;
2382 char id
[ULOC_FULLNAME_CAPACITY
];
2383 resLen
= uloc_getKeywordValue(locale
, "currency", id
, ULOC_FULLNAME_CAPACITY
, &localStatus
);
2385 // get country or country_variant in `id'
2386 idForLocale(locale
, id
, sizeof(id
), ec
);
2392 // Remove variants, which is only needed for registration.
2393 char *idDelim
= strchr(id
, VAR_DELIM
);
2399 // Look up the CurrencyMap element in the root bundle.
2400 UResourceBundle
*rb
= ures_openDirect(U_ICUDATA_CURR
, CURRENCY_DATA
, &localStatus
);
2401 UResourceBundle
*cm
= ures_getByKey(rb
, CURRENCY_MAP
, rb
, &localStatus
);
2403 // Using the id derived from the local, get the currency data
2404 UResourceBundle
*countryArray
= ures_getByKey(rb
, id
, cm
, &localStatus
);
2406 // process each currency to see which one is valid for the given date
2407 bool matchFound
= false;
2408 if (U_SUCCESS(localStatus
))
2410 if ((index
<= 0) || (index
> ures_getSize(countryArray
)))
2412 // requested index is out of bounds
2413 ures_close(countryArray
);
2417 for (int32_t i
=0; i
<ures_getSize(countryArray
); i
++)
2419 // get the currency resource
2420 UResourceBundle
*currencyRes
= ures_getByIndex(countryArray
, i
, NULL
, &localStatus
);
2421 s
= ures_getStringByKey(currencyRes
, "id", &resLen
, &localStatus
);
2423 // get the from date
2424 int32_t fromLength
= 0;
2425 UResourceBundle
*fromRes
= ures_getByKey(currencyRes
, "from", NULL
, &localStatus
);
2426 const int32_t *fromArray
= ures_getIntVector(fromRes
, &fromLength
, &localStatus
);
2428 int64_t currDate64
= (int64_t)fromArray
[0] << 32;
2429 currDate64
|= ((int64_t)fromArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2430 UDate fromDate
= (UDate
)currDate64
;
2432 if (ures_getSize(currencyRes
)> 2)
2434 int32_t toLength
= 0;
2435 UResourceBundle
*toRes
= ures_getByKey(currencyRes
, "to", NULL
, &localStatus
);
2436 const int32_t *toArray
= ures_getIntVector(toRes
, &toLength
, &localStatus
);
2438 currDate64
= (int64_t)toArray
[0] << 32;
2439 currDate64
|= ((int64_t)toArray
[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2440 UDate toDate
= (UDate
)currDate64
;
2442 if ((fromDate
<= date
) && (date
< toDate
))
2445 if (currIndex
== index
)
2455 if (fromDate
<= date
)
2458 if (currIndex
== index
)
2465 // close open resources
2466 ures_close(currencyRes
);
2467 ures_close(fromRes
);
2469 // check for loop exit
2478 ures_close(countryArray
);
2481 if (*ec
== U_ZERO_ERROR
|| localStatus
!= U_ZERO_ERROR
)
2483 // There is nothing to fallback to.
2484 // Report the failure/warning if possible.
2491 if((buffCapacity
> resLen
) && matchFound
)
2493 // write out the currency value
2502 // return null terminated currency string
2503 return u_terminateUChars(buff
, buffCapacity
, resLen
, ec
);
2507 // illegal argument encountered
2508 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2513 // If we got here, either error code is invalid or
2514 // some argument passed is no good.
2518 static const UEnumeration defaultKeywordValues
= {
2521 ulist_close_keyword_values_iterator
,
2522 ulist_count_keyword_values
,
2524 ulist_next_keyword_value
,
2525 ulist_reset_keyword_values_iterator
2528 U_CAPI UEnumeration
*U_EXPORT2
ucurr_getKeywordValuesForLocale(const char *key
, const char *locale
, UBool commonlyUsed
, UErrorCode
* status
) {
2530 char prefRegion
[ULOC_COUNTRY_CAPACITY
];
2531 ulocimp_getRegionForSupplementalData(locale
, TRUE
, prefRegion
, sizeof(prefRegion
), status
);
2533 // Read value from supplementalData
2534 UList
*values
= ulist_createEmptyList(status
);
2535 UList
*otherValues
= ulist_createEmptyList(status
);
2536 UEnumeration
*en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2537 if (U_FAILURE(*status
) || en
== NULL
) {
2539 *status
= U_MEMORY_ALLOCATION_ERROR
;
2543 ulist_deleteList(values
);
2544 ulist_deleteList(otherValues
);
2547 memcpy(en
, &defaultKeywordValues
, sizeof(UEnumeration
));
2548 en
->context
= values
;
2550 UResourceBundle
*bundle
= ures_openDirect(U_ICUDATA_CURR
, "supplementalData", status
);
2551 ures_getByKey(bundle
, "CurrencyMap", bundle
, status
);
2552 UResourceBundle bundlekey
, regbndl
, curbndl
, to
;
2553 ures_initStackObject(&bundlekey
);
2554 ures_initStackObject(®bndl
);
2555 ures_initStackObject(&curbndl
);
2556 ures_initStackObject(&to
);
2558 while (U_SUCCESS(*status
) && ures_hasNext(bundle
)) {
2559 ures_getNextResource(bundle
, &bundlekey
, status
);
2560 if (U_FAILURE(*status
)) {
2563 const char *region
= ures_getKey(&bundlekey
);
2564 UBool isPrefRegion
= uprv_strcmp(region
, prefRegion
) == 0 ? TRUE
: FALSE
;
2565 if (!isPrefRegion
&& commonlyUsed
) {
2566 // With commonlyUsed=true, we do not put
2567 // currencies for other regions in the
2571 ures_getByKey(bundle
, region
, ®bndl
, status
);
2572 if (U_FAILURE(*status
)) {
2575 while (U_SUCCESS(*status
) && ures_hasNext(®bndl
)) {
2576 ures_getNextResource(®bndl
, &curbndl
, status
);
2577 if (ures_getType(&curbndl
) != URES_TABLE
) {
2578 // Currently, an empty ARRAY is mixed in.
2581 char *curID
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2582 int32_t curIDLength
= ULOC_KEYWORDS_CAPACITY
;
2583 if (curID
== NULL
) {
2584 *status
= U_MEMORY_ALLOCATION_ERROR
;
2588 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
2589 ures_getUTF8StringByKey(&curbndl
, "id", curID
, &curIDLength
, TRUE
, status
);
2590 /* optimize - use the utf-8 string */
2593 const UChar
* defString
= ures_getStringByKey(&curbndl
, "id", &curIDLength
, status
);
2594 if(U_SUCCESS(*status
)) {
2595 if(curIDLength
+1 > ULOC_KEYWORDS_CAPACITY
) {
2596 *status
= U_BUFFER_OVERFLOW_ERROR
;
2598 u_UCharsToChars(defString
, curID
, curIDLength
+1);
2604 if (U_FAILURE(*status
)) {
2607 UBool hasTo
= FALSE
;
2608 ures_getByKey(&curbndl
, "to", &to
, status
);
2609 if (U_FAILURE(*status
)) {
2610 // Do nothing here...
2611 *status
= U_ZERO_ERROR
;
2615 if (isPrefRegion
&& !hasTo
&& !ulist_containsString(values
, curID
, (int32_t)uprv_strlen(curID
))) {
2616 // Currently active currency for the target country
2617 ulist_addItemEndList(values
, curID
, TRUE
, status
);
2618 } else if (!ulist_containsString(otherValues
, curID
, (int32_t)uprv_strlen(curID
)) && !commonlyUsed
) {
2619 ulist_addItemEndList(otherValues
, curID
, TRUE
, status
);
2626 if (U_SUCCESS(*status
)) {
2628 if (ulist_getListSize(values
) == 0) {
2629 // This could happen if no valid region is supplied in the input
2630 // locale. In this case, we use the CLDR's default.
2632 en
= ucurr_getKeywordValuesForLocale(key
, "und", TRUE
, status
);
2635 // Consolidate the list
2637 ulist_resetList(otherValues
);
2638 while ((value
= (char *)ulist_getNext(otherValues
)) != NULL
) {
2639 if (!ulist_containsString(values
, value
, (int32_t)uprv_strlen(value
))) {
2640 char *tmpValue
= (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY
);
2641 uprv_memcpy(tmpValue
, value
, uprv_strlen(value
) + 1);
2642 ulist_addItemEndList(values
, tmpValue
, TRUE
, status
);
2643 if (U_FAILURE(*status
)) {
2650 ulist_resetList((UList
*)(en
->context
));
2652 ulist_deleteList(values
);
2658 ures_close(&curbndl
);
2659 ures_close(®bndl
);
2660 ures_close(&bundlekey
);
2663 ulist_deleteList(otherValues
);
2669 U_CAPI
int32_t U_EXPORT2
2670 ucurr_getNumericCode(const UChar
* currency
) {
2672 if (currency
&& u_strlen(currency
) == ISO_CURRENCY_CODE_LENGTH
) {
2673 UErrorCode status
= U_ZERO_ERROR
;
2675 UResourceBundle
*bundle
= ures_openDirect(0, "currencyNumericCodes", &status
);
2676 ures_getByKey(bundle
, "codeMap", bundle
, &status
);
2677 if (U_SUCCESS(status
)) {
2678 char alphaCode
[ISO_CURRENCY_CODE_LENGTH
+1];
2679 myUCharsToChars(alphaCode
, currency
);
2680 T_CString_toUpperCase(alphaCode
);
2681 ures_getByKey(bundle
, alphaCode
, bundle
, &status
);
2682 int tmpCode
= ures_getInt(bundle
, &status
);
2683 if (U_SUCCESS(status
)) {
2691 #endif /* #if !UCONFIG_NO_FORMATTING */