2 **********************************************************************
3 * Copyright (c) 2002-2003, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/ucurr.h"
13 #include "unicode/locid.h"
14 #include "unicode/resbund.h"
15 #include "unicode/ustring.h"
22 //------------------------------------------------------------
25 // Default currency meta data of last resort. We try to use the
26 // defaults encoded in the meta data resource bundle. If there is a
27 // configuration/build error and these are not available, we use these
28 // hard-coded defaults (which should be identical).
29 static const int32_t LAST_RESORT_DATA
[] = { 2, 0 };
31 // POW10[i] = 10^i, i=0..MAX_POW10
32 static const int32_t POW10
[] = { 1, 10, 100, 1000, 10000, 100000,
33 1000000, 10000000, 100000000, 1000000000 };
35 static const int32_t MAX_POW10
= (sizeof(POW10
)/sizeof(POW10
[0])) - 1;
37 #define ISO_COUNTRY_CODE_LENGTH 3
39 //------------------------------------------------------------
42 // Tag for meta-data, in root.
43 static const char CURRENCY_META
[] = "CurrencyMeta";
45 // Tag for map from countries to currencies, in root.
46 static const char CURRENCY_MAP
[] = "CurrencyMap";
48 // Tag for default meta-data, in CURRENCY_META
49 static const char DEFAULT_META
[] = "DEFAULT";
51 // Variant for legacy pre-euro mapping in CurrencyMap
52 static const char VAR_PRE_EURO
[] = "PREEURO";
54 // Variant for legacy euro mapping in CurrencyMap
55 static const char VAR_EURO
[] = "EURO";
58 static const char VAR_DELIM
[] = "_";
60 // Tag for localized display names (symbols) of currencies
61 static const char CURRENCIES
[] = "Currencies";
63 // Marker character indicating that a display name is a ChoiceFormat
64 // pattern. Strings that start with one mark are ChoiceFormat
65 // patterns. Strings that start with 2 marks are static strings, and
66 // the first mark is deleted.
67 static const UChar CHOICE_FORMAT_MARK
= 0x003D; // Equals sign
69 //------------------------------------------------------------
73 * Unfortunately, we have to convert the UChar* currency code to char*
74 * to use it as a resource key.
77 myUCharsToChars(char* resultOfLen4
, const UChar
* currency
) {
78 u_UCharsToChars(currency
, resultOfLen4
, ISO_COUNTRY_CODE_LENGTH
);
79 resultOfLen4
[ISO_COUNTRY_CODE_LENGTH
] = 0;
84 * Internal function to look up currency data. Result is an array of
85 * two integers. The first is the fraction digits. The second is the
86 * rounding increment, or 0 if none. The rounding increment is in
87 * units of 10^(-fraction_digits).
90 _findMetaData(const UChar
* currency
) {
92 // Get CurrencyMeta resource out of root locale file. [This may
93 // move out of the root locale file later; if it does, update this
95 UErrorCode ec
= U_ZERO_ERROR
;
96 ResourceBundle currencyMeta
=
97 ResourceBundle((char*)0, Locale(""), ec
).get(CURRENCY_META
, ec
);
100 // Config/build error; return hard-coded defaults
101 return LAST_RESORT_DATA
;
104 // Look up our currency, or if that's not available, then DEFAULT
105 char buf
[ISO_COUNTRY_CODE_LENGTH
+1];
106 ResourceBundle rb
= currencyMeta
.get(myUCharsToChars(buf
, currency
), ec
);
108 rb
= currencyMeta
.get(DEFAULT_META
, ec
);
110 // Config/build error; return hard-coded defaults
111 return LAST_RESORT_DATA
;
116 const int32_t *data
= rb
.getIntVector(len
, ec
);
117 if (U_FAILURE(ec
) || len
< 2) {
118 // Config/build error; return hard-coded defaults
119 return LAST_RESORT_DATA
;
126 // ------------------------------------------
130 //-------------------------------------------
132 // don't use ICUService since we don't need fallback
136 /* Remember to call umtx_init(&gCRegLock) before using it! */
137 static UMTX gCRegLock
= 0;
138 static CReg
* gCRegHead
= 0;
140 struct CReg
: public UMemory
{
142 UChar iso
[ISO_COUNTRY_CODE_LENGTH
+1];
143 char id
[ULOC_FULLNAME_CAPACITY
];
145 CReg(const UChar
* _iso
, const char* _id
)
148 int32_t len
= uprv_strlen(_id
);
149 if (len
> (int32_t)(sizeof(id
)-1)) {
150 len
= (sizeof(id
)-1);
152 uprv_strncpy(id
, _id
, len
);
154 uprv_memcpy(iso
, _iso
, ISO_COUNTRY_CODE_LENGTH
* sizeof(const UChar
));
155 iso
[ISO_COUNTRY_CODE_LENGTH
] = 0;
158 static UCurrRegistryKey
reg(const UChar
* _iso
, const char* _id
, UErrorCode
* status
)
160 if (status
&& U_SUCCESS(*status
) && _iso
&& _id
) {
161 CReg
* n
= new CReg(_iso
, _id
);
163 umtx_init(&gCRegLock
);
164 Mutex
mutex(&gCRegLock
);
166 ucln_i18n_registerCleanup();
172 *status
= U_MEMORY_ALLOCATION_ERROR
;
177 static UBool
unreg(UCurrRegistryKey key
) {
178 umtx_init(&gCRegLock
);
179 Mutex
mutex(&gCRegLock
);
180 if (gCRegHead
== key
) {
181 gCRegHead
= gCRegHead
->next
;
188 if (p
->next
== key
) {
189 p
->next
= ((CReg
*)key
)->next
;
199 static const UChar
* get(const char* id
) {
200 umtx_init(&gCRegLock
);
201 Mutex
mutex(&gCRegLock
);
204 if (uprv_strcmp(id
, p
->id
) == 0) {
212 /* This doesn't need to be thread safe. It's for u_cleanup only. */
213 static void cleanup(void) {
216 gCRegHead
= gCRegHead
->next
;
219 umtx_destroy(&gCRegLock
);
223 // -------------------------------------
226 idForLocale(const char* locale
, char* buffer
, int capacity
, UErrorCode
* ec
)
228 // !!! this is internal only, assumes buffer is not null and capacity is sufficient
229 // Extract the country name and variant name. We only
230 // recognize two variant names, EURO and PREEURO.
231 char variant
[ULOC_FULLNAME_CAPACITY
];
232 uloc_getCountry(locale
, buffer
, capacity
, ec
);
233 uloc_getVariant(locale
, variant
, sizeof(variant
), ec
);
234 if (0 == uprv_strcmp(variant
, VAR_PRE_EURO
) ||
235 0 == uprv_strcmp(variant
, VAR_EURO
))
237 uprv_strcat(buffer
, VAR_DELIM
);
238 uprv_strcat(buffer
, variant
);
242 // -------------------------------------
244 U_CAPI UCurrRegistryKey U_EXPORT2
245 ucurr_register(const UChar
* isoCode
, const char* locale
, UErrorCode
*status
)
247 if (status
&& U_SUCCESS(*status
)) {
248 char id
[ULOC_FULLNAME_CAPACITY
];
249 idForLocale(locale
, id
, sizeof(id
), status
);
250 return CReg::reg(isoCode
, id
, status
);
255 // -------------------------------------
257 U_CAPI UBool U_EXPORT2
258 ucurr_unregister(UCurrRegistryKey key
, UErrorCode
* status
)
260 if (status
&& U_SUCCESS(*status
)) {
261 return CReg::unreg(key
);
266 // -------------------------------------
268 U_CAPI
const UChar
* U_EXPORT2
269 ucurr_forLocale(const char* locale
, UErrorCode
* ec
) {
270 if (ec
!= NULL
&& U_SUCCESS(*ec
)) {
271 char id
[ULOC_FULLNAME_CAPACITY
];
272 idForLocale(locale
, id
, sizeof(id
), ec
);
273 if (U_FAILURE(*ec
)) {
277 const UChar
* result
= CReg::get(id
);
282 // Look up the CurrencyMap element in the root bundle.
283 UResourceBundle
* rb
= ures_open(NULL
, "", ec
);
284 UResourceBundle
* cm
= ures_getByKey(rb
, CURRENCY_MAP
, NULL
, ec
);
286 const UChar
* s
= ures_getStringByKey(cm
, id
, &len
, ec
);
290 if (U_SUCCESS(*ec
)) {
300 * Modify the given locale name by removing the rightmost _-delimited
301 * element. If there is none, empty the string ("" == root).
302 * NOTE: The string "root" is not recognized; do not use it.
303 * @return TRUE if the fallback happened; FALSE if locale is already
306 static UBool
fallback(char *loc
) {
310 UErrorCode status
= U_ZERO_ERROR
;
311 uloc_getParent(loc
, loc
, uprv_strlen(loc
), &status
);
313 char *i = uprv_strrchr(loc, '_');
323 U_CAPI
const UChar
* U_EXPORT2
324 ucurr_getName(const UChar
* currency
,
326 UCurrNameStyle nameStyle
,
327 UBool
* isChoiceFormat
, // fillin
328 int32_t* len
, // fillin
331 // Look up the Currencies resource for the given locale. The
332 // Currencies locale data looks like this:
335 //| USD { "US$", "US Dollar" }
336 //| CHF { "Sw F", "Swiss Franc" }
337 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
342 if (ec
== NULL
|| U_FAILURE(*ec
)) {
346 int32_t choice
= (int32_t) nameStyle
;
347 if (choice
< 0 || choice
> 1) {
348 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
352 // In the future, resource bundles may implement multi-level
353 // fallback. That is, if a currency is not found in the en_US
354 // Currencies data, then the en Currencies data will be searched.
355 // Currently, if a Currencies datum exists in en_US and en, the
356 // en_US entry hides that in en.
358 // We want multi-level fallback for this resource, so we implement
361 // Use a separate UErrorCode here that does not propagate out of
363 UErrorCode ec2
= U_ZERO_ERROR
;
365 char loc
[ULOC_FULLNAME_CAPACITY
];
366 uloc_getName(locale
, loc
, sizeof(loc
), &ec2
);
367 if (U_FAILURE(ec2
) || ec2
== U_STRING_NOT_TERMINATED_WARNING
) {
368 *ec
= U_ILLEGAL_ARGUMENT_ERROR
;
372 char buf
[ISO_COUNTRY_CODE_LENGTH
+1];
373 myUCharsToChars(buf
, currency
);
375 const UChar
* s
= NULL
;
377 // Multi-level resource inheritance fallback loop
380 UResourceBundle
* rb
= ures_open(NULL
, loc
, &ec2
);
381 UResourceBundle
* curr
= ures_getByKey(rb
, CURRENCIES
, NULL
, &ec2
);
382 UResourceBundle
* names
= ures_getByKey(curr
, buf
, NULL
, &ec2
);
383 s
= ures_getStringByIndex(names
, choice
, len
, &ec2
);
388 // If we've succeeded we're done. Otherwise, try to fallback.
389 // If that fails (because we are already at root) then exit.
390 if (U_SUCCESS(ec2
) || !fallback(loc
)) {
395 // Determine if this is a ChoiceFormat pattern. One leading mark
396 // indicates a ChoiceFormat. Two indicates a static string that
397 // starts with a mark. In either case, the first mark is ignored,
398 // if present. Marks in the rest of the string have no special
400 *isChoiceFormat
= FALSE
;
401 if (U_SUCCESS(ec2
)) {
404 while (i
< *len
&& s
[i
] == CHOICE_FORMAT_MARK
&& i
< 2) {
407 *isChoiceFormat
= (i
== 1);
408 if (i
!= 0) ++s
; // Skip over first mark
412 // If we fail to find a match, use the ISO 4217 code
413 *len
= u_strlen(currency
); // Should == ISO_COUNTRY_CODE_LENGTH, but maybe not...?
417 //!// This API is now redundant. It predates ucurr_getName, which
418 //!// replaces and extends it.
419 //!U_CAPI const UChar* U_EXPORT2
420 //!ucurr_getSymbol(const UChar* currency,
421 //! const char* locale,
422 //! int32_t* len, // fillin
423 //! UErrorCode* ec) {
424 //! UBool isChoiceFormat;
425 //! const UChar* s = ucurr_getName(currency, locale, UCURR_SYMBOL_NAME,
426 //! &isChoiceFormat, len, ec);
427 //! if (isChoiceFormat) {
428 //! // Don't let ChoiceFormat patterns out through this API
429 //! *len = u_strlen(currency); // Should == 3, but maybe not...?
435 U_CAPI
int32_t U_EXPORT2
436 ucurr_getDefaultFractionDigits(const UChar
* currency
) {
437 return (_findMetaData(currency
))[0];
440 U_CAPI
double U_EXPORT2
441 ucurr_getRoundingIncrement(const UChar
* currency
) {
442 const int32_t *data
= _findMetaData(currency
);
444 // If there is no rounding, or if the meta data is invalid,
445 // return 0.0 to indicate no rounding. A rounding value
446 // (data[1]) of 0 or 1 indicates no rounding.
447 if (data
[1] < 2 || data
[0] < 0 || data
[0] > MAX_POW10
) {
451 // Return data[1] / 10^(data[0]). The only actual rounding data,
452 // as of this writing, is CHF { 2, 5 }.
453 return double(data
[1]) / POW10
[data
[0]];
457 * Release all static memory held by currency.
459 U_CFUNC UBool
currency_cleanup(void) {
464 #endif /* #if !UCONFIG_NO_FORMATTING */