]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/ucurr.cpp
ICU-6.2.14.tar.gz
[apple/icu.git] / icuSources / i18n / ucurr.cpp
1 /*
2 **********************************************************************
3 * Copyright (c) 2002-2004, International Business Machines
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"
14 #include "unicode/ures.h"
15 #include "unicode/ustring.h"
16 #include "unicode/choicfmt.h"
17 #include "unicode/parsepos.h"
18 #include "ustr_imp.h"
19 #include "cmemory.h"
20 #include "cstring.h"
21 #include "uassert.h"
22 #include "mutex.h"
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).
32 static const int32_t LAST_RESORT_DATA[] = { 2, 0 };
33
34 // POW10[i] = 10^i, i=0..MAX_POW10
35 static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
36 1000000, 10000000, 100000000, 1000000000 };
37
38 static const int32_t MAX_POW10 = (sizeof(POW10)/sizeof(POW10[0])) - 1;
39
40 #define ISO_COUNTRY_CODE_LENGTH 3
41
42 //------------------------------------------------------------
43 // Resource tags
44 //
45
46 static const char CURRENCY_DATA[] = "CurrencyData";
47 // Tag for meta-data, in root.
48 static const char CURRENCY_META[] = "CurrencyMeta";
49
50 // Tag for map from countries to currencies, in root.
51 static const char CURRENCY_MAP[] = "CurrencyMap";
52
53 // Tag for default meta-data, in CURRENCY_META
54 static const char DEFAULT_META[] = "DEFAULT";
55
56 // Variant for legacy pre-euro mapping in CurrencyMap
57 static const char VAR_PRE_EURO[] = "PREEURO";
58
59 // Variant for legacy euro mapping in CurrencyMap
60 static const char VAR_EURO[] = "EURO";
61
62 // Variant delimiter
63 static const char VAR_DELIM[] = "_";
64
65 // Variant for legacy euro mapping in CurrencyMap
66 static 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
72 // Tag for localized display names (symbols) of currencies
73 static 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.
79 static 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 */
88 static inline char*
89 myUCharsToChars(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 */
101 static const int32_t*
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 }
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.]
114 UResourceBundle* currencyData = ures_openDirect(NULL, CURRENCY_DATA, &ec);
115 UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec);
116
117 if (U_FAILURE(ec)) {
118 ures_close(currencyMeta);
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];
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);
130 if (U_FAILURE(ec)) {
131 ures_close(currencyMeta);
132 ures_close(rb);
133 // Config/build error; return hard-coded defaults
134 return LAST_RESORT_DATA;
135 }
136 }
137
138 int32_t len;
139 const int32_t *data = ures_getIntVector(rb, &len, &ec);
140 if (U_FAILURE(ec) || len != 2) {
141 // Config/build error; return hard-coded defaults
142 if (U_SUCCESS(ec)) {
143 ec = U_INVALID_FORMAT_ERROR;
144 }
145 ures_close(currencyMeta);
146 ures_close(rb);
147 return LAST_RESORT_DATA;
148 }
149
150 ures_close(currencyMeta);
151 ures_close(rb);
152 return data;
153 }
154
155 // -------------------------------------
156
157 /**
158 * @see VARIANT_IS_EURO
159 * @see VARIANT_IS_PREEURO
160 */
161 static uint32_t
162 idForLocale(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 }
182
183 // ------------------------------------------
184 //
185 // Registration
186 //
187 //-------------------------------------------
188
189 // don't use ICUService since we don't need fallback
190
191 #if !UCONFIG_NO_SERVICE
192 U_CDECL_BEGIN
193 static UBool U_CALLCONV currency_cleanup(void);
194 U_CDECL_END
195 struct CReg;
196
197 /* Remember to call umtx_init(&gCRegLock) before using it! */
198 static UMTX gCRegLock = 0;
199 static CReg* gCRegHead = 0;
200
201 struct CReg : public UMemory {
202 CReg *next;
203 UChar iso[ISO_COUNTRY_CODE_LENGTH+1];
204 char id[ULOC_FULLNAME_CAPACITY];
205
206 CReg(const UChar* _iso, const char* _id)
207 : next(0)
208 {
209 int32_t len = (int32_t)uprv_strlen(_id);
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 }
218
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) {
227 /* register for the first time */
228 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
229 }
230 n->next = gCRegHead;
231 gCRegHead = n;
232 return n;
233 }
234 *status = U_MEMORY_ALLOCATION_ERROR;
235 }
236 return 0;
237 }
238
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 }
247
248 CReg* p = gCRegHead;
249 while (p) {
250 if (p->next == key) {
251 p->next = ((CReg*)key)->next;
252 delete (CReg*)key;
253 return TRUE;
254 }
255 p = p->next;
256 }
257
258 return FALSE;
259 }
260
261 static const UChar* get(const char* id) {
262 umtx_init(&gCRegLock);
263 Mutex mutex(&gCRegLock);
264 CReg* p = gCRegHead;
265
266 /* register cleanup of the mutex */
267 ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
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
288 /**
289 * Release all static memory held by currency.
290 */
291 U_CDECL_BEGIN
292 static UBool U_CALLCONV currency_cleanup(void) {
293 #if !UCONFIG_NO_SERVICE
294 CReg::cleanup();
295 #endif
296 return TRUE;
297 }
298 U_CDECL_END
299
300 // -------------------------------------
301
302 U_CAPI UCurrRegistryKey U_EXPORT2
303 ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status)
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
315 U_CAPI UBool U_EXPORT2
316 ucurr_unregister(UCurrRegistryKey key, UErrorCode* status)
317 {
318 if (status && U_SUCCESS(*status)) {
319 return CReg::unreg(key);
320 }
321 return FALSE;
322 }
323 #endif /* UCONFIG_NO_SERVICE */
324
325 // -------------------------------------
326
327 U_CAPI int32_t U_EXPORT2
328 ucurr_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;
335 if (ec != NULL && U_SUCCESS(*ec)) {
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;
395 }
396 }
397 return resLen;
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 */
409 static UBool fallback(char *loc) {
410 if (!*loc) {
411 return FALSE;
412 }
413 UErrorCode status = U_ZERO_ERROR;
414 uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status);
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
426 U_CAPI const UChar* U_EXPORT2
427 ucurr_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 {
437 //| Currencies {
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
445 if (U_FAILURE(*ec)) {
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 }
454
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);
484 rb = ures_getByKey(rb, CURRENCIES, rb, &ec2);
485 rb = ures_getByKey(rb, buf, rb, &ec2);
486 s = ures_getStringByIndex(rb, choice, len, &ec2);
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)) {
492 if (ec2 == U_USING_DEFAULT_WARNING
493 || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
494 *ec = ec2;
495 }
496 break;
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;
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...?
523 *ec = U_USING_DEFAULT_WARNING;
524 return currency;
525 }
526
527 U_NAMESPACE_BEGIN
528
529 void
530 uprv_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
657 U_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 */
669 U_CAPI void
670 uprv_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 }
693
694 U_CAPI int32_t U_EXPORT2
695 ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) {
696 return (_findMetaData(currency, *ec))[0];
697 }
698
699 U_CAPI double U_EXPORT2
700 ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) {
701 const int32_t *data = _findMetaData(currency, *ec);
702
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) {
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
722 #endif /* #if !UCONFIG_NO_FORMATTING */
723
724 //eof