]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/ucurr.cpp
ICU-6.2.15.tar.gz
[apple/icu.git] / icuSources / i18n / ucurr.cpp
CommitLineData
b75a7d8f
A
1/*
2**********************************************************************
374ca955 3* Copyright (c) 2002-2004, International Business Machines
b75a7d8f
A
4* Corporation and others. All Rights Reserved.
5**********************************************************************
6*/
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_FORMATTING
11
12#include "unicode/ucurr.h"
13#include "unicode/locid.h"
374ca955 14#include "unicode/ures.h"
b75a7d8f 15#include "unicode/ustring.h"
374ca955
A
16#include "unicode/choicfmt.h"
17#include "unicode/parsepos.h"
18#include "ustr_imp.h"
b75a7d8f
A
19#include "cmemory.h"
20#include "cstring.h"
21#include "uassert.h"
374ca955 22#include "mutex.h"
b75a7d8f
A
23#include "ucln_in.h"
24
25//------------------------------------------------------------
26// Constants
27
28// Default currency meta data of last resort. We try to use the
29// defaults encoded in the meta data resource bundle. If there is a
30// configuration/build error and these are not available, we use these
31// hard-coded defaults (which should be identical).
32static const int32_t LAST_RESORT_DATA[] = { 2, 0 };
33
34// POW10[i] = 10^i, i=0..MAX_POW10
35static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
36 1000000, 10000000, 100000000, 1000000000 };
37
38static const int32_t MAX_POW10 = (sizeof(POW10)/sizeof(POW10[0])) - 1;
39
40#define ISO_COUNTRY_CODE_LENGTH 3
41
42//------------------------------------------------------------
43// Resource tags
374ca955 44//
b75a7d8f 45
374ca955 46static const char CURRENCY_DATA[] = "CurrencyData";
b75a7d8f
A
47// Tag for meta-data, in root.
48static const char CURRENCY_META[] = "CurrencyMeta";
49
50// Tag for map from countries to currencies, in root.
51static const char CURRENCY_MAP[] = "CurrencyMap";
52
53// Tag for default meta-data, in CURRENCY_META
54static const char DEFAULT_META[] = "DEFAULT";
55
56// Variant for legacy pre-euro mapping in CurrencyMap
57static const char VAR_PRE_EURO[] = "PREEURO";
58
59// Variant for legacy euro mapping in CurrencyMap
60static const char VAR_EURO[] = "EURO";
61
62// Variant delimiter
63static const char VAR_DELIM[] = "_";
64
374ca955
A
65// Variant for legacy euro mapping in CurrencyMap
66static const char VAR_DELIM_EURO[] = "_EURO";
67
68#define VARIANT_IS_EMPTY 0
69#define VARIANT_IS_EURO 0x1
70#define VARIANT_IS_PREEURO 0x2
71
b75a7d8f
A
72// Tag for localized display names (symbols) of currencies
73static const char CURRENCIES[] = "Currencies";
74
75// Marker character indicating that a display name is a ChoiceFormat
76// pattern. Strings that start with one mark are ChoiceFormat
77// patterns. Strings that start with 2 marks are static strings, and
78// the first mark is deleted.
79static const UChar CHOICE_FORMAT_MARK = 0x003D; // Equals sign
80
81//------------------------------------------------------------
82// Code
83
84/**
85 * Unfortunately, we have to convert the UChar* currency code to char*
86 * to use it as a resource key.
87 */
88static inline char*
89myUCharsToChars(char* resultOfLen4, const UChar* currency) {
90 u_UCharsToChars(currency, resultOfLen4, ISO_COUNTRY_CODE_LENGTH);
91 resultOfLen4[ISO_COUNTRY_CODE_LENGTH] = 0;
92 return resultOfLen4;
93}
94
95/**
96 * Internal function to look up currency data. Result is an array of
97 * two integers. The first is the fraction digits. The second is the
98 * rounding increment, or 0 if none. The rounding increment is in
99 * units of 10^(-fraction_digits).
100 */
101static const int32_t*
374ca955
A
102_findMetaData(const UChar* currency, UErrorCode& ec) {
103
104 if (currency == 0 || *currency == 0) {
105 if (U_SUCCESS(ec)) {
106 ec = U_ILLEGAL_ARGUMENT_ERROR;
107 }
108 return LAST_RESORT_DATA;
109 }
b75a7d8f
A
110
111 // Get CurrencyMeta resource out of root locale file. [This may
112 // move out of the root locale file later; if it does, update this
113 // code.]
374ca955
A
114 UResourceBundle* currencyData = ures_openDirect(NULL, CURRENCY_DATA, &ec);
115 UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec);
116
b75a7d8f 117 if (U_FAILURE(ec)) {
374ca955 118 ures_close(currencyMeta);
b75a7d8f
A
119 // Config/build error; return hard-coded defaults
120 return LAST_RESORT_DATA;
121 }
122
123 // Look up our currency, or if that's not available, then DEFAULT
124 char buf[ISO_COUNTRY_CODE_LENGTH+1];
374ca955
A
125 UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure
126 UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), NULL, &ec2);
127 if (U_FAILURE(ec2)) {
128 ures_close(rb);
129 rb = ures_getByKey(currencyMeta,DEFAULT_META, NULL, &ec);
b75a7d8f 130 if (U_FAILURE(ec)) {
374ca955
A
131 ures_close(currencyMeta);
132 ures_close(rb);
b75a7d8f
A
133 // Config/build error; return hard-coded defaults
134 return LAST_RESORT_DATA;
135 }
136 }
137
138 int32_t len;
374ca955
A
139 const int32_t *data = ures_getIntVector(rb, &len, &ec);
140 if (U_FAILURE(ec) || len != 2) {
b75a7d8f 141 // Config/build error; return hard-coded defaults
374ca955
A
142 if (U_SUCCESS(ec)) {
143 ec = U_INVALID_FORMAT_ERROR;
144 }
145 ures_close(currencyMeta);
146 ures_close(rb);
b75a7d8f
A
147 return LAST_RESORT_DATA;
148 }
149
374ca955
A
150 ures_close(currencyMeta);
151 ures_close(rb);
b75a7d8f
A
152 return data;
153}
154
374ca955
A
155// -------------------------------------
156
157/**
158 * @see VARIANT_IS_EURO
159 * @see VARIANT_IS_PREEURO
160 */
161static uint32_t
162idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec)
163{
164 uint32_t variantType = 0;
165 // !!! this is internal only, assumes buffer is not null and capacity is sufficient
166 // Extract the country name and variant name. We only
167 // recognize two variant names, EURO and PREEURO.
168 char variant[ULOC_FULLNAME_CAPACITY];
169 uloc_getCountry(locale, countryAndVariant, capacity, ec);
170 uloc_getVariant(locale, variant, sizeof(variant), ec);
171 if (variant[0] != 0) {
172 variantType = (0 == uprv_strcmp(variant, VAR_EURO))
173 | ((0 == uprv_strcmp(variant, VAR_PRE_EURO)) << 1);
174 if (variantType)
175 {
176 uprv_strcat(countryAndVariant, VAR_DELIM);
177 uprv_strcat(countryAndVariant, variant);
178 }
179 }
180 return variantType;
181}
b75a7d8f
A
182
183// ------------------------------------------
184//
185// Registration
186//
187//-------------------------------------------
188
189// don't use ICUService since we don't need fallback
190
374ca955
A
191#if !UCONFIG_NO_SERVICE
192U_CDECL_BEGIN
193static UBool U_CALLCONV currency_cleanup(void);
194U_CDECL_END
b75a7d8f
A
195struct CReg;
196
197/* Remember to call umtx_init(&gCRegLock) before using it! */
198static UMTX gCRegLock = 0;
199static CReg* gCRegHead = 0;
200
201struct CReg : public UMemory {
202 CReg *next;
203 UChar iso[ISO_COUNTRY_CODE_LENGTH+1];
204 char id[ULOC_FULLNAME_CAPACITY];
374ca955 205
b75a7d8f
A
206 CReg(const UChar* _iso, const char* _id)
207 : next(0)
208 {
374ca955 209 int32_t len = (int32_t)uprv_strlen(_id);
b75a7d8f
A
210 if (len > (int32_t)(sizeof(id)-1)) {
211 len = (sizeof(id)-1);
212 }
213 uprv_strncpy(id, _id, len);
214 id[len] = 0;
215 uprv_memcpy(iso, _iso, ISO_COUNTRY_CODE_LENGTH * sizeof(const UChar));
216 iso[ISO_COUNTRY_CODE_LENGTH] = 0;
217 }
374ca955 218
b75a7d8f
A
219 static UCurrRegistryKey reg(const UChar* _iso, const char* _id, UErrorCode* status)
220 {
221 if (status && U_SUCCESS(*status) && _iso && _id) {
222 CReg* n = new CReg(_iso, _id);
223 if (n) {
224 umtx_init(&gCRegLock);
225 Mutex mutex(&gCRegLock);
226 if (!gCRegHead) {
374ca955
A
227 /* register for the first time */
228 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
b75a7d8f
A
229 }
230 n->next = gCRegHead;
231 gCRegHead = n;
232 return n;
233 }
234 *status = U_MEMORY_ALLOCATION_ERROR;
235 }
236 return 0;
237 }
374ca955 238
b75a7d8f
A
239 static UBool unreg(UCurrRegistryKey key) {
240 umtx_init(&gCRegLock);
241 Mutex mutex(&gCRegLock);
242 if (gCRegHead == key) {
243 gCRegHead = gCRegHead->next;
244 delete (CReg*)key;
245 return TRUE;
246 }
374ca955 247
b75a7d8f
A
248 CReg* p = gCRegHead;
249 while (p) {
250 if (p->next == key) {
251 p->next = ((CReg*)key)->next;
374ca955 252 delete (CReg*)key;
b75a7d8f
A
253 return TRUE;
254 }
255 p = p->next;
256 }
374ca955 257
b75a7d8f
A
258 return FALSE;
259 }
374ca955 260
b75a7d8f
A
261 static const UChar* get(const char* id) {
262 umtx_init(&gCRegLock);
263 Mutex mutex(&gCRegLock);
264 CReg* p = gCRegHead;
374ca955
A
265
266 /* register cleanup of the mutex */
267 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
b75a7d8f
A
268 while (p) {
269 if (uprv_strcmp(id, p->id) == 0) {
270 return p->iso;
271 }
272 p = p->next;
273 }
274 return NULL;
275 }
276
277 /* This doesn't need to be thread safe. It's for u_cleanup only. */
278 static void cleanup(void) {
279 while (gCRegHead) {
280 CReg* n = gCRegHead;
281 gCRegHead = gCRegHead->next;
282 delete n;
283 }
284 umtx_destroy(&gCRegLock);
285 }
286};
287
374ca955
A
288/**
289 * Release all static memory held by currency.
290 */
291U_CDECL_BEGIN
292static UBool U_CALLCONV currency_cleanup(void) {
293#if !UCONFIG_NO_SERVICE
294 CReg::cleanup();
295#endif
296 return TRUE;
b75a7d8f 297}
374ca955 298U_CDECL_END
b75a7d8f
A
299
300// -------------------------------------
301
302U_CAPI UCurrRegistryKey U_EXPORT2
374ca955 303ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status)
b75a7d8f
A
304{
305 if (status && U_SUCCESS(*status)) {
306 char id[ULOC_FULLNAME_CAPACITY];
307 idForLocale(locale, id, sizeof(id), status);
308 return CReg::reg(isoCode, id, status);
309 }
310 return NULL;
311}
312
313// -------------------------------------
314
315U_CAPI UBool U_EXPORT2
374ca955 316ucurr_unregister(UCurrRegistryKey key, UErrorCode* status)
b75a7d8f
A
317{
318 if (status && U_SUCCESS(*status)) {
319 return CReg::unreg(key);
320 }
321 return FALSE;
322}
374ca955 323#endif /* UCONFIG_NO_SERVICE */
b75a7d8f
A
324
325// -------------------------------------
326
374ca955
A
327U_CAPI int32_t U_EXPORT2
328ucurr_forLocale(const char* locale,
329 UChar* buff,
330 int32_t buffCapacity,
331 UErrorCode* ec)
332{
333 int32_t resLen = 0;
334 const UChar* s = NULL;
b75a7d8f 335 if (ec != NULL && U_SUCCESS(*ec)) {
374ca955
A
336 if ((buff && buffCapacity) || !buffCapacity) {
337 UErrorCode localStatus = U_ZERO_ERROR;
338 char id[ULOC_FULLNAME_CAPACITY];
339 if ((resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus))) {
340 // there is a currency keyword. Try to see if it's valid
341 if(buffCapacity > resLen) {
342 u_charsToUChars(id, buff, resLen);
343 }
344 } else {
345 // get country or country_variant in `id'
346 uint32_t variantType = idForLocale(locale, id, sizeof(id), ec);
347
348 if (U_FAILURE(*ec)) {
349 return 0;
350 }
351
352#if !UCONFIG_NO_SERVICE
353 const UChar* result = CReg::get(id);
354 if (result) {
355 if(buffCapacity > u_strlen(result)) {
356 u_strcpy(buff, result);
357 }
358 return u_strlen(result);
359 }
360#endif
361
362 // Look up the CurrencyMap element in the root bundle.
363 UResourceBundle *rb = ures_openDirect(NULL, CURRENCY_DATA, &localStatus);
364 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
365 s = ures_getStringByKey(cm, id, &resLen, &localStatus);
366
367 if ((s == NULL || U_FAILURE(localStatus)) && variantType != VARIANT_IS_EMPTY
368 && (id[0] != 0))
369 {
370 // We don't know about it. Check to see if we support the variant.
371 if (variantType & VARIANT_IS_EURO) {
372 s = ures_getStringByKey(cm, VAR_DELIM_EURO, &resLen, ec);
373 }
374 else {
375 uloc_getParent(locale, id, sizeof(id), ec);
376 *ec = U_USING_FALLBACK_WARNING;
377 ures_close(cm);
378 return ucurr_forLocale(id, buff, buffCapacity, ec);
379 }
380 }
381 else if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
382 // There is nothing to fallback to. Report the failure/warning if possible.
383 *ec = localStatus;
384 }
385 if (U_SUCCESS(*ec)) {
386 if(buffCapacity > resLen) {
387 u_strcpy(buff, s);
388 }
389 }
390 ures_close(cm);
391 }
392 return u_terminateUChars(buff, buffCapacity, resLen, ec);
393 } else {
394 *ec = U_ILLEGAL_ARGUMENT_ERROR;
b75a7d8f
A
395 }
396 }
374ca955 397 return resLen;
b75a7d8f
A
398}
399
400// end registration
401
402/**
403 * Modify the given locale name by removing the rightmost _-delimited
404 * element. If there is none, empty the string ("" == root).
405 * NOTE: The string "root" is not recognized; do not use it.
406 * @return TRUE if the fallback happened; FALSE if locale is already
407 * root ("").
408 */
409static UBool fallback(char *loc) {
410 if (!*loc) {
411 return FALSE;
412 }
413 UErrorCode status = U_ZERO_ERROR;
374ca955 414 uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status);
b75a7d8f
A
415 /*
416 char *i = uprv_strrchr(loc, '_');
417 if (i == NULL) {
418 i = loc;
419 }
420 *i = 0;
421 */
422 return TRUE;
423}
424
425
426U_CAPI const UChar* U_EXPORT2
427ucurr_getName(const UChar* currency,
428 const char* locale,
429 UCurrNameStyle nameStyle,
430 UBool* isChoiceFormat, // fillin
431 int32_t* len, // fillin
432 UErrorCode* ec) {
433
434 // Look up the Currencies resource for the given locale. The
435 // Currencies locale data looks like this:
436 //|en {
374ca955 437 //| Currencies {
b75a7d8f
A
438 //| USD { "US$", "US Dollar" }
439 //| CHF { "Sw F", "Swiss Franc" }
440 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
441 //| //...
442 //| }
443 //|}
444
374ca955 445 if (U_FAILURE(*ec)) {
b75a7d8f
A
446 return 0;
447 }
448
449 int32_t choice = (int32_t) nameStyle;
450 if (choice < 0 || choice > 1) {
451 *ec = U_ILLEGAL_ARGUMENT_ERROR;
452 return 0;
453 }
374ca955 454
b75a7d8f
A
455 // In the future, resource bundles may implement multi-level
456 // fallback. That is, if a currency is not found in the en_US
457 // Currencies data, then the en Currencies data will be searched.
458 // Currently, if a Currencies datum exists in en_US and en, the
459 // en_US entry hides that in en.
460
461 // We want multi-level fallback for this resource, so we implement
462 // it manually.
463
464 // Use a separate UErrorCode here that does not propagate out of
465 // this function.
466 UErrorCode ec2 = U_ZERO_ERROR;
467
468 char loc[ULOC_FULLNAME_CAPACITY];
469 uloc_getName(locale, loc, sizeof(loc), &ec2);
470 if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
471 *ec = U_ILLEGAL_ARGUMENT_ERROR;
472 return 0;
473 }
474
475 char buf[ISO_COUNTRY_CODE_LENGTH+1];
476 myUCharsToChars(buf, currency);
477
478 const UChar* s = NULL;
479
480 // Multi-level resource inheritance fallback loop
481 for (;;) {
482 ec2 = U_ZERO_ERROR;
483 UResourceBundle* rb = ures_open(NULL, loc, &ec2);
374ca955
A
484 rb = ures_getByKey(rb, CURRENCIES, rb, &ec2);
485 rb = ures_getByKey(rb, buf, rb, &ec2);
486 s = ures_getStringByIndex(rb, choice, len, &ec2);
b75a7d8f
A
487 ures_close(rb);
488
489 // If we've succeeded we're done. Otherwise, try to fallback.
490 // If that fails (because we are already at root) then exit.
491 if (U_SUCCESS(ec2) || !fallback(loc)) {
374ca955
A
492 if (ec2 == U_USING_DEFAULT_WARNING
493 || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
494 *ec = ec2;
495 }
b75a7d8f 496 break;
374ca955
A
497 } else if (strlen(loc) == 0) {
498 *ec = U_USING_DEFAULT_WARNING;
499 } else if (*ec != U_USING_DEFAULT_WARNING) {
500 *ec = U_USING_FALLBACK_WARNING;
b75a7d8f
A
501 }
502 }
503
504 // Determine if this is a ChoiceFormat pattern. One leading mark
505 // indicates a ChoiceFormat. Two indicates a static string that
506 // starts with a mark. In either case, the first mark is ignored,
507 // if present. Marks in the rest of the string have no special
508 // meaning.
509 *isChoiceFormat = FALSE;
510 if (U_SUCCESS(ec2)) {
511 U_ASSERT(s != NULL);
512 int32_t i=0;
513 while (i < *len && s[i] == CHOICE_FORMAT_MARK && i < 2) {
514 ++i;
515 }
516 *isChoiceFormat = (i == 1);
517 if (i != 0) ++s; // Skip over first mark
518 return s;
519 }
520
521 // If we fail to find a match, use the ISO 4217 code
522 *len = u_strlen(currency); // Should == ISO_COUNTRY_CODE_LENGTH, but maybe not...?
374ca955 523 *ec = U_USING_DEFAULT_WARNING;
b75a7d8f
A
524 return currency;
525}
526
374ca955
A
527U_NAMESPACE_BEGIN
528
529void
530uprv_parseCurrency(const char* locale,
531 const UnicodeString& text,
532 ParsePosition& pos,
533 UChar* result,
534 UErrorCode& ec) {
535
536 // TODO: There is a slight problem with the pseudo-multi-level
537 // fallback implemented here. More-specific locales don't
538 // properly shield duplicate entries in less-specific locales.
539 // This problem will go away when real multi-level fallback is
540 // implemented. We could also fix this by recording (in a
541 // hash) which codes are used at each level of fallback, but
542 // this doesn't seem warranted.
543
544 if (U_FAILURE(ec)) {
545 return;
546 }
547
548 // Look up the Currencies resource for the given locale. The
549 // Currencies locale data looks like this:
550 //|en {
551 //| Currencies {
552 //| USD { "US$", "US Dollar" }
553 //| CHF { "Sw F", "Swiss Franc" }
554 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
555 //| //...
556 //| }
557 //|}
558
559 // In the future, resource bundles may implement multi-level
560 // fallback. That is, if a currency is not found in the en_US
561 // Currencies data, then the en Currencies data will be searched.
562 // Currently, if a Currencies datum exists in en_US and en, the
563 // en_US entry hides that in en.
564
565 // We want multi-level fallback for this resource, so we implement
566 // it manually.
567
568 // Use a separate UErrorCode here that does not propagate out of
569 // this function.
570 UErrorCode ec2 = U_ZERO_ERROR;
571
572 char loc[ULOC_FULLNAME_CAPACITY];
573 uloc_getName(locale, loc, sizeof(loc), &ec2);
574 if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
575 ec = U_ILLEGAL_ARGUMENT_ERROR;
576 return;
577 }
578
579 int32_t start = pos.getIndex();
580 const UChar* s = NULL;
581
582 const char* iso = NULL;
583 int32_t max = 0;
584
585 // Multi-level resource inheritance fallback loop
586 for (;;) {
587 ec2 = U_ZERO_ERROR;
588 UResourceBundle* rb = ures_open(NULL, loc, &ec2);
589 UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
590 int32_t n = ures_getSize(curr);
591 for (int32_t i=0; i<n; ++i) {
592 UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2);
593 int32_t len;
594 s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
595 UBool isChoice = FALSE;
596 if (len > 0 && s[0] == CHOICE_FORMAT_MARK) {
597 ++s;
598 --len;
599 if (len > 0 && s[0] != CHOICE_FORMAT_MARK) {
600 isChoice = TRUE;
601 }
602 }
603 if (isChoice) {
604 Formattable temp;
605 ChoiceFormat fmt(s, ec2);
606 fmt.parse(text, temp, pos);
607 len = pos.getIndex() - start;
608 pos.setIndex(start);
609 } else if (len > max &&
610 text.compare(pos.getIndex(), len, s) != 0) {
611 len = 0;
612 }
613 if (len > max) {
614 iso = ures_getKey(names);
615 max = len;
616 }
617 ures_close(names);
618 }
619 ures_close(curr);
620 ures_close(rb);
621
622 // Try to fallback. If that fails (because we are already at
623 // root) then exit.
624 if (!fallback(loc)) {
625 break;
626 }
627 }
628
629 if (iso != NULL) {
630 u_charsToUChars(iso, result, 4);
631 }
632
633 // If display name parse fails or if it matches fewer than 3
634 // characters, try to parse 3-letter ISO. Do this after the
635 // display name processing so 3-letter display names are
636 // preferred. Consider /[A-Z]{3}/ to be valid ISO, and parse
637 // it manually--UnicodeSet/regex are too slow and heavy.
638 if (max < 3 && (text.length() - start) >= 3) {
639 UBool valid = TRUE;
640 for (int32_t k=0; k<3; ++k) {
641 UChar ch = text.charAt(start + k); // 16-bit ok
642 if (ch < 0x41/*'A'*/ || ch > 0x5A/*'Z'*/) {
643 valid = FALSE;
644 break;
645 }
646 }
647 if (valid) {
648 text.extract(start, 3, result);
649 result[3] = 0;
650 max = 3;
651 }
652 }
653
654 pos.setIndex(start + max);
655}
656
657U_NAMESPACE_END
658
659/**
660 * Internal method. Given a currency ISO code and a locale, return
661 * the "static" currency name. This is usually the same as the
662 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
663 * format is applied to the number 2.0 (to yield the more common
664 * plural) to return a static name.
665 *
666 * This is used for backward compatibility with old currency logic in
667 * DecimalFormat and DecimalFormatSymbols.
668 */
669U_CAPI void
670uprv_getStaticCurrencyName(const UChar* iso, const char* loc,
671 UnicodeString& result, UErrorCode& ec)
672{
673 UBool isChoiceFormat;
674 int32_t len;
675 const UChar* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME,
676 &isChoiceFormat, &len, &ec);
677 if (U_SUCCESS(ec)) {
678 // If this is a ChoiceFormat currency, then format an
679 // arbitrary value; pick something != 1; more common.
680 result.truncate(0);
681 if (isChoiceFormat) {
682 ChoiceFormat f(currname, ec);
683 if (U_SUCCESS(ec)) {
684 f.format(2.0, result);
685 } else {
686 result = iso;
687 }
688 } else {
689 result = currname;
690 }
691 }
692}
b75a7d8f
A
693
694U_CAPI int32_t U_EXPORT2
374ca955
A
695ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) {
696 return (_findMetaData(currency, *ec))[0];
b75a7d8f
A
697}
698
699U_CAPI double U_EXPORT2
374ca955
A
700ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) {
701 const int32_t *data = _findMetaData(currency, *ec);
b75a7d8f 702
374ca955
A
703 // If the meta data is invalid, return 0.0.
704 if (data[0] < 0 || data[0] > MAX_POW10) {
705 if (U_SUCCESS(*ec)) {
706 *ec = U_INVALID_FORMAT_ERROR;
707 }
708 return 0.0;
709 }
710
711 // If there is no rounding, return 0.0 to indicate no rounding. A
712 // rounding value (data[1]) of 0 or 1 indicates no rounding.
713 if (data[1] < 2) {
b75a7d8f
A
714 return 0.0;
715 }
716
717 // Return data[1] / 10^(data[0]). The only actual rounding data,
718 // as of this writing, is CHF { 2, 5 }.
719 return double(data[1]) / POW10[data[0]];
720}
721
b75a7d8f
A
722#endif /* #if !UCONFIG_NO_FORMATTING */
723
724//eof