]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ******************************************************************************* | |
729e4ab9 | 3 | * Copyright (C) 1997-2010, International Business Machines Corporation and * |
b75a7d8f A |
4 | * others. All Rights Reserved. * |
5 | ******************************************************************************* | |
6 | * | |
7 | * File NUMFMT.CPP | |
8 | * | |
9 | * Modification History: | |
10 | * | |
11 | * Date Name Description | |
12 | * 02/19/97 aliu Converted from java. | |
13 | * 03/18/97 clhuang Implemented with C++ APIs. | |
14 | * 04/17/97 aliu Enlarged MAX_INTEGER_DIGITS to fully accomodate the | |
15 | * largest double, by default. | |
16 | * Changed DigitCount to int per code review. | |
17 | * 07/20/98 stephen Changed operator== to check for grouping | |
18 | * Changed setMaxIntegerDigits per Java implementation. | |
19 | * Changed setMinIntegerDigits per Java implementation. | |
20 | * Changed setMinFractionDigits per Java implementation. | |
21 | * Changed setMaxFractionDigits per Java implementation. | |
22 | ******************************************************************************** | |
23 | */ | |
24 | ||
25 | #include "unicode/utypes.h" | |
26 | ||
27 | #if !UCONFIG_NO_FORMATTING | |
28 | ||
29 | #include "unicode/numfmt.h" | |
30 | #include "unicode/locid.h" | |
b75a7d8f A |
31 | #include "unicode/dcfmtsym.h" |
32 | #include "unicode/decimfmt.h" | |
33 | #include "unicode/ustring.h" | |
374ca955 A |
34 | #include "unicode/ucurr.h" |
35 | #include "unicode/curramt.h" | |
729e4ab9 A |
36 | #include "unicode/numsys.h" |
37 | #include "unicode/rbnf.h" | |
73c04bcf A |
38 | #include "winnmfmt.h" |
39 | #include "uresimp.h" | |
b75a7d8f | 40 | #include "uhash.h" |
374ca955 | 41 | #include "cmemory.h" |
73c04bcf | 42 | #include "servloc.h" |
b75a7d8f | 43 | #include "ucln_in.h" |
374ca955 A |
44 | #include "cstring.h" |
45 | #include "putilimp.h" | |
729e4ab9 A |
46 | #include "umutex.h" |
47 | #include "digitlst.h" | |
b75a7d8f A |
48 | #include <float.h> |
49 | ||
374ca955 A |
50 | //#define FMT_DEBUG |
51 | ||
52 | #ifdef FMT_DEBUG | |
53 | #include <stdio.h> | |
54 | static void debugout(UnicodeString s) { | |
55 | char buf[2000]; | |
56 | s.extract((int32_t) 0, s.length(), buf); | |
57 | printf("%s", buf); | |
58 | } | |
59 | #define debug(x) printf("%s", x); | |
60 | #else | |
61 | #define debugout(x) | |
62 | #define debug(x) | |
63 | #endif | |
64 | ||
b75a7d8f A |
65 | // If no number pattern can be located for a locale, this is the last |
66 | // resort. | |
67 | static const UChar gLastResortDecimalPat[] = { | |
68 | 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x3B, 0x2D, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#0.###;-#0.###" */ | |
69 | }; | |
70 | static const UChar gLastResortCurrencyPat[] = { | |
71 | 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x3B, 0x28, 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x29, 0 /* "$#0.00;($#0.00)" */ | |
72 | }; | |
73 | static const UChar gLastResortPercentPat[] = { | |
74 | 0x23, 0x30, 0x25, 0 /* "#0%" */ | |
75 | }; | |
76 | static const UChar gLastResortScientificPat[] = { | |
77 | 0x23, 0x45, 0x30, 0 /* "#E0" */ | |
78 | }; | |
729e4ab9 A |
79 | static const UChar gLastResortIsoCurrencyPat[] = { |
80 | 0xA4, 0xA4, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x3B, 0x28, 0xA4, 0xA4, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x29, 0 /* "\u00A4\u00A4#0.00;(\u00A4\u00A4#0.00)" */ | |
81 | }; | |
82 | static const UChar gLastResortPluralCurrencyPat[] = { | |
83 | 0x23, 0x30, 0x2E, 0x30, 0x30, 0xA0, 0xA4, 0xA4, 0xA4, 0 /* "#0.00\u00A0\u00A4\u00A4\u00A4*/ | |
84 | }; | |
85 | ||
86 | static const UChar gSingleCurrencySign[] = {0xA4, 0}; | |
87 | static const UChar gDoubleCurrencySign[] = {0xA4, 0xA4, 0}; | |
88 | ||
89 | static const UChar gSlash = 0x2f; | |
b75a7d8f | 90 | |
b75a7d8f A |
91 | // If the maximum base 10 exponent were 4, then the largest number would |
92 | // be 99,999 which has 5 digits. | |
46f4442e A |
93 | // On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit |
94 | static const int32_t gMaxIntegerDigits = DBL_MAX_10_EXP + DBL_DIG + 1; | |
73c04bcf | 95 | static const int32_t gMinIntegerDigits = 127; |
b75a7d8f | 96 | |
73c04bcf | 97 | static const UChar * const gLastResortNumberPatterns[] = |
b75a7d8f A |
98 | { |
99 | gLastResortDecimalPat, | |
100 | gLastResortCurrencyPat, | |
101 | gLastResortPercentPat, | |
729e4ab9 A |
102 | gLastResortScientificPat, |
103 | gLastResortIsoCurrencyPat, | |
104 | gLastResortPluralCurrencyPat, | |
b75a7d8f A |
105 | }; |
106 | ||
729e4ab9 A |
107 | // Keys used for accessing resource bundles |
108 | ||
109 | static const char *gNumberElements = "NumberElements"; | |
110 | static const char *gLatn = "latn"; | |
111 | static const char *gPatterns = "patterns"; | |
112 | static const char *gFormatKeys[] = { "decimalFormat", "currencyFormat", "percentFormat", "scientificFormat" }; | |
113 | ||
114 | // Static hashtable cache of NumberingSystem objects used by NumberFormat | |
115 | static UHashtable * NumberingSystem_cache = NULL; | |
116 | ||
117 | static UMTX nscacheMutex = NULL; | |
118 | ||
119 | #if !UCONFIG_NO_SERVICE | |
120 | static U_NAMESPACE_QUALIFIER ICULocaleService* gService = NULL; | |
121 | #endif | |
122 | ||
123 | /** | |
124 | * Release all static memory held by Number Format. | |
125 | */ | |
126 | U_CDECL_BEGIN | |
127 | static void U_CALLCONV | |
128 | deleteNumberingSystem(void *obj) { | |
129 | delete (U_NAMESPACE_QUALIFIER NumberingSystem *)obj; | |
130 | } | |
131 | ||
132 | static UBool U_CALLCONV numfmt_cleanup(void) { | |
133 | #if !UCONFIG_NO_SERVICE | |
134 | if (gService) { | |
135 | delete gService; | |
136 | gService = NULL; | |
137 | } | |
138 | #endif | |
139 | if (NumberingSystem_cache) { | |
140 | // delete NumberingSystem_cache; | |
141 | uhash_close(NumberingSystem_cache); | |
142 | NumberingSystem_cache = NULL; | |
143 | } | |
144 | ||
145 | return TRUE; | |
146 | } | |
147 | U_CDECL_END | |
148 | ||
73c04bcf A |
149 | // ***************************************************************************** |
150 | // class NumberFormat | |
151 | // ***************************************************************************** | |
152 | ||
153 | U_NAMESPACE_BEGIN | |
154 | ||
155 | UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(NumberFormat) | |
156 | ||
374ca955 | 157 | #if !UCONFIG_NO_SERVICE |
b75a7d8f | 158 | // ------------------------------------- |
374ca955 A |
159 | // SimpleNumberFormatFactory implementation |
160 | NumberFormatFactory::~NumberFormatFactory() {} | |
161 | SimpleNumberFormatFactory::SimpleNumberFormatFactory(const Locale& locale, UBool visible) | |
162 | : _visible(visible) | |
374ca955 | 163 | { |
73c04bcf | 164 | LocaleUtility::initNameFromLocale(locale, _id); |
374ca955 A |
165 | } |
166 | ||
167 | SimpleNumberFormatFactory::~SimpleNumberFormatFactory() {} | |
168 | ||
169 | UBool SimpleNumberFormatFactory::visible(void) const { | |
170 | return _visible; | |
171 | } | |
172 | ||
173 | const UnicodeString * | |
729e4ab9 | 174 | SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const |
374ca955 A |
175 | { |
176 | if (U_SUCCESS(status)) { | |
177 | count = 1; | |
178 | return &_id; | |
179 | } | |
180 | count = 0; | |
181 | return NULL; | |
182 | } | |
183 | #endif /* #if !UCONFIG_NO_SERVICE */ | |
b75a7d8f | 184 | |
374ca955 A |
185 | // ------------------------------------- |
186 | // default constructor | |
b75a7d8f A |
187 | NumberFormat::NumberFormat() |
188 | : fGroupingUsed(TRUE), | |
73c04bcf | 189 | fMaxIntegerDigits(gMaxIntegerDigits), |
b75a7d8f A |
190 | fMinIntegerDigits(1), |
191 | fMaxFractionDigits(3), // invariant, >= minFractionDigits | |
192 | fMinFractionDigits(0), | |
46f4442e A |
193 | fParseIntegerOnly(FALSE), |
194 | fParseStrict(TRUE) // TODO: Should this be FALSE? | |
b75a7d8f | 195 | { |
374ca955 | 196 | fCurrency[0] = 0; |
b75a7d8f A |
197 | } |
198 | ||
199 | // ------------------------------------- | |
200 | ||
201 | NumberFormat::~NumberFormat() | |
202 | { | |
203 | } | |
204 | ||
205 | // ------------------------------------- | |
206 | // copy constructor | |
207 | ||
208 | NumberFormat::NumberFormat(const NumberFormat &source) | |
209 | : Format(source) | |
210 | { | |
211 | *this = source; | |
212 | } | |
213 | ||
214 | // ------------------------------------- | |
215 | // assignment operator | |
216 | ||
217 | NumberFormat& | |
218 | NumberFormat::operator=(const NumberFormat& rhs) | |
219 | { | |
220 | if (this != &rhs) | |
221 | { | |
729e4ab9 | 222 | Format::operator=(rhs); |
b75a7d8f A |
223 | fGroupingUsed = rhs.fGroupingUsed; |
224 | fMaxIntegerDigits = rhs.fMaxIntegerDigits; | |
225 | fMinIntegerDigits = rhs.fMinIntegerDigits; | |
226 | fMaxFractionDigits = rhs.fMaxFractionDigits; | |
227 | fMinFractionDigits = rhs.fMinFractionDigits; | |
228 | fParseIntegerOnly = rhs.fParseIntegerOnly; | |
374ca955 | 229 | u_strncpy(fCurrency, rhs.fCurrency, 4); |
b75a7d8f A |
230 | } |
231 | return *this; | |
232 | } | |
233 | ||
234 | // ------------------------------------- | |
235 | ||
236 | UBool | |
237 | NumberFormat::operator==(const Format& that) const | |
238 | { | |
374ca955 | 239 | // Format::operator== guarantees this cast is safe |
b75a7d8f A |
240 | NumberFormat* other = (NumberFormat*)&that; |
241 | ||
374ca955 A |
242 | #ifdef FMT_DEBUG |
243 | // This code makes it easy to determine why two format objects that should | |
244 | // be equal aren't. | |
245 | UBool first = TRUE; | |
246 | if (!Format::operator==(that)) { | |
247 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
248 | debug("Format::!="); | |
249 | } | |
250 | if (!(fMaxIntegerDigits == other->fMaxIntegerDigits && | |
251 | fMinIntegerDigits == other->fMinIntegerDigits)) { | |
252 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
253 | debug("Integer digits !="); | |
254 | } | |
255 | if (!(fMaxFractionDigits == other->fMaxFractionDigits && | |
256 | fMinFractionDigits == other->fMinFractionDigits)) { | |
257 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
258 | debug("Fraction digits !="); | |
259 | } | |
260 | if (!(fGroupingUsed == other->fGroupingUsed)) { | |
261 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
262 | debug("fGroupingUsed != "); | |
263 | } | |
264 | if (!(fParseIntegerOnly == other->fParseIntegerOnly)) { | |
265 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
266 | debug("fParseIntegerOnly != "); | |
267 | } | |
268 | if (!(u_strcmp(fCurrency, other->fCurrency) == 0)) { | |
269 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
270 | debug("fCurrency !="); | |
271 | } | |
729e4ab9 | 272 | if (!first) { printf(" ]"); } |
374ca955 A |
273 | #endif |
274 | ||
b75a7d8f A |
275 | return ((this == &that) || |
276 | ((Format::operator==(that) && | |
b75a7d8f A |
277 | fMaxIntegerDigits == other->fMaxIntegerDigits && |
278 | fMinIntegerDigits == other->fMinIntegerDigits && | |
279 | fMaxFractionDigits == other->fMaxFractionDigits && | |
280 | fMinFractionDigits == other->fMinFractionDigits && | |
281 | fGroupingUsed == other->fGroupingUsed && | |
374ca955 A |
282 | fParseIntegerOnly == other->fParseIntegerOnly && |
283 | u_strcmp(fCurrency, other->fCurrency) == 0))); | |
b75a7d8f A |
284 | } |
285 | ||
729e4ab9 A |
286 | // ------------------------------------- |
287 | // Default implementation sets unsupported error; subclasses should | |
288 | // override. | |
b75a7d8f A |
289 | |
290 | UnicodeString& | |
729e4ab9 A |
291 | NumberFormat::format(double /* unused number */, |
292 | UnicodeString& toAppendTo, | |
293 | FieldPositionIterator* /* unused posIter */, | |
294 | UErrorCode& status) const | |
b75a7d8f | 295 | { |
729e4ab9 A |
296 | if (!U_FAILURE(status)) { |
297 | status = U_UNSUPPORTED_ERROR; | |
298 | } | |
299 | return toAppendTo; | |
300 | } | |
301 | ||
302 | // ------------------------------------- | |
303 | // Default implementation sets unsupported error; subclasses should | |
304 | // override. | |
305 | ||
306 | UnicodeString& | |
307 | NumberFormat::format(int32_t /* unused number */, | |
308 | UnicodeString& toAppendTo, | |
309 | FieldPositionIterator* /* unused posIter */, | |
310 | UErrorCode& status) const | |
311 | { | |
312 | if (!U_FAILURE(status)) { | |
313 | status = U_UNSUPPORTED_ERROR; | |
314 | } | |
315 | return toAppendTo; | |
316 | } | |
317 | ||
318 | // ------------------------------------- | |
319 | // Default implementation sets unsupported error; subclasses should | |
320 | // override. | |
321 | ||
322 | UnicodeString& | |
323 | NumberFormat::format(int64_t /* unused number */, | |
324 | UnicodeString& toAppendTo, | |
325 | FieldPositionIterator* /* unused posIter */, | |
326 | UErrorCode& status) const | |
327 | { | |
328 | if (!U_FAILURE(status)) { | |
329 | status = U_UNSUPPORTED_ERROR; | |
330 | } | |
331 | return toAppendTo; | |
332 | } | |
b75a7d8f | 333 | |
729e4ab9 A |
334 | // ------------------------------------- |
335 | // Decimal Number format() default implementation | |
336 | // Subclasses do not normally override this function, but rather the DigitList | |
337 | // formatting functions.. | |
338 | // The expected call chain from here is | |
339 | // this function -> | |
340 | // NumberFormat::format(Formattable -> | |
341 | // DecimalFormat::format(DigitList | |
342 | // | |
343 | // Or, for subclasses of Formattable that do not know about DigitList, | |
344 | // this Function -> | |
345 | // NumberFormat::format(Formattable -> | |
346 | // NumberFormat::format(DigitList -> | |
347 | // XXXFormat::format(double | |
348 | ||
349 | UnicodeString& | |
350 | NumberFormat::format(const StringPiece &decimalNum, | |
351 | UnicodeString& toAppendTo, | |
352 | FieldPositionIterator* fpi, | |
353 | UErrorCode& status) const | |
354 | { | |
355 | Formattable f; | |
356 | f.setDecimalNumber(decimalNum, status); | |
357 | format(f, toAppendTo, fpi, status); | |
358 | return toAppendTo; | |
359 | } | |
360 | ||
361 | // ------------------------------------- | |
362 | // Formats the number object and save the format | |
363 | // result in the toAppendTo string buffer. | |
364 | ||
365 | // utility to save/restore state, used in two overloads | |
366 | // of format(const Formattable&...) below. | |
367 | ||
368 | class ArgExtractor { | |
369 | NumberFormat *ncnf; | |
370 | const Formattable* num; | |
371 | UBool setCurr; | |
372 | UChar save[4]; | |
373 | ||
374 | public: | |
375 | ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status); | |
376 | ~ArgExtractor(); | |
377 | ||
378 | const Formattable* number(void) const; | |
379 | }; | |
380 | ||
381 | inline const Formattable* | |
382 | ArgExtractor::number(void) const { | |
383 | return num; | |
384 | } | |
385 | ||
386 | ArgExtractor::ArgExtractor(const NumberFormat& nf, const Formattable& obj, UErrorCode& status) | |
387 | : ncnf((NumberFormat*) &nf), num(&obj), setCurr(FALSE) { | |
374ca955 | 388 | |
374ca955 | 389 | const UObject* o = obj.getObject(); // most commonly o==NULL |
729e4ab9 A |
390 | const CurrencyAmount* amt; |
391 | if (o != NULL && (amt = dynamic_cast<const CurrencyAmount*>(o)) != NULL) { | |
374ca955 A |
392 | // getISOCurrency() returns a pointer to internal storage, so we |
393 | // copy it to retain it across the call to setCurrency(). | |
374ca955 | 394 | const UChar* curr = amt->getISOCurrency(); |
729e4ab9 | 395 | u_strcpy(save, nf.getCurrency()); |
374ca955 A |
396 | setCurr = (u_strcmp(curr, save) != 0); |
397 | if (setCurr) { | |
729e4ab9 | 398 | ncnf->setCurrency(curr, status); |
374ca955 | 399 | } |
729e4ab9 | 400 | num = &amt->getNumber(); |
b75a7d8f | 401 | } |
729e4ab9 | 402 | } |
374ca955 | 403 | |
729e4ab9 | 404 | ArgExtractor::~ArgExtractor() { |
374ca955 A |
405 | if (setCurr) { |
406 | UErrorCode ok = U_ZERO_ERROR; | |
729e4ab9 A |
407 | ncnf->setCurrency(save, ok); // always restore currency |
408 | } | |
409 | } | |
410 | ||
411 | UnicodeString& NumberFormat::format(const DigitList &number, | |
412 | UnicodeString& appendTo, | |
413 | FieldPositionIterator* posIter, | |
414 | UErrorCode& status) const { | |
415 | // DecimalFormat overrides this function, and handles DigitList based big decimals. | |
416 | // Other subclasses (ChoiceFormat, RuleBasedNumberFormat) do not (yet) handle DigitLists, | |
417 | // so this default implementation falls back to formatting decimal numbers as doubles. | |
418 | if (U_FAILURE(status)) { | |
419 | return appendTo; | |
420 | } | |
421 | double dnum = number.getDouble(); | |
422 | format(dnum, appendTo, posIter, status); | |
423 | return appendTo; | |
424 | } | |
425 | ||
426 | ||
427 | ||
428 | UnicodeString& | |
429 | NumberFormat::format(const DigitList &number, | |
430 | UnicodeString& appendTo, | |
431 | FieldPosition& pos, | |
432 | UErrorCode &status) const { | |
433 | // DecimalFormat overrides this function, and handles DigitList based big decimals. | |
434 | // Other subclasses (ChoiceFormat, RuleBasedNumberFormat) do not (yet) handle DigitLists, | |
435 | // so this default implementation falls back to formatting decimal numbers as doubles. | |
436 | if (U_FAILURE(status)) { | |
437 | return appendTo; | |
438 | } | |
439 | double dnum = number.getDouble(); | |
440 | format(dnum, appendTo, pos, status); | |
441 | return appendTo; | |
442 | } | |
443 | ||
444 | UnicodeString& | |
445 | NumberFormat::format(const Formattable& obj, | |
446 | UnicodeString& appendTo, | |
447 | FieldPosition& pos, | |
448 | UErrorCode& status) const | |
449 | { | |
450 | if (U_FAILURE(status)) return appendTo; | |
451 | ||
452 | ArgExtractor arg(*this, obj, status); | |
453 | const Formattable *n = arg.number(); | |
454 | ||
455 | if (n->isNumeric() && n->getDigitList() != NULL) { | |
456 | // Decimal Number. We will have a DigitList available if the value was | |
457 | // set to a decimal number, or if the value originated with a parse. | |
458 | // | |
459 | // The default implementation for formatting a DigitList converts it | |
460 | // to a double, and formats that, allowing formatting classes that don't | |
461 | // know about DigitList to continue to operate as they had. | |
462 | // | |
463 | // DecimalFormat overrides the DigitList formatting functions. | |
464 | format(*n->getDigitList(), appendTo, pos, status); | |
465 | } else { | |
466 | switch (n->getType()) { | |
467 | case Formattable::kDouble: | |
468 | format(n->getDouble(), appendTo, pos); | |
469 | break; | |
470 | case Formattable::kLong: | |
471 | format(n->getLong(), appendTo, pos); | |
472 | break; | |
473 | case Formattable::kInt64: | |
474 | format(n->getInt64(), appendTo, pos); | |
475 | break; | |
476 | default: | |
477 | status = U_INVALID_FORMAT_ERROR; | |
478 | break; | |
479 | } | |
480 | } | |
481 | ||
482 | return appendTo; | |
483 | } | |
484 | ||
485 | // -------------------------------------x | |
486 | // Formats the number object and save the format | |
487 | // result in the toAppendTo string buffer. | |
488 | ||
489 | UnicodeString& | |
490 | NumberFormat::format(const Formattable& obj, | |
491 | UnicodeString& appendTo, | |
492 | FieldPositionIterator* posIter, | |
493 | UErrorCode& status) const | |
494 | { | |
495 | if (U_FAILURE(status)) return appendTo; | |
496 | ||
497 | ArgExtractor arg(*this, obj, status); | |
498 | const Formattable *n = arg.number(); | |
499 | ||
500 | if (n->isNumeric() && n->getDigitList() != NULL) { | |
501 | // Decimal Number | |
502 | format(*n->getDigitList(), appendTo, posIter, status); | |
503 | } else { | |
504 | switch (n->getType()) { | |
505 | case Formattable::kDouble: | |
506 | format(n->getDouble(), appendTo, posIter, status); | |
507 | break; | |
508 | case Formattable::kLong: | |
509 | format(n->getLong(), appendTo, posIter, status); | |
510 | break; | |
511 | case Formattable::kInt64: | |
512 | format(n->getInt64(), appendTo, posIter, status); | |
513 | break; | |
514 | default: | |
515 | status = U_INVALID_FORMAT_ERROR; | |
516 | break; | |
517 | } | |
374ca955 | 518 | } |
729e4ab9 | 519 | |
374ca955 A |
520 | return appendTo; |
521 | } | |
522 | ||
523 | // ------------------------------------- | |
524 | ||
729e4ab9 | 525 | UnicodeString& |
374ca955 A |
526 | NumberFormat::format(int64_t number, |
527 | UnicodeString& appendTo, | |
528 | FieldPosition& pos) const | |
529 | { | |
530 | // default so we don't introduce a new abstract method | |
531 | return format((int32_t)number, appendTo, pos); | |
b75a7d8f A |
532 | } |
533 | ||
534 | // ------------------------------------- | |
535 | // Parses the string and save the result object as well | |
536 | // as the final parsed position. | |
537 | ||
538 | void | |
539 | NumberFormat::parseObject(const UnicodeString& source, | |
540 | Formattable& result, | |
541 | ParsePosition& parse_pos) const | |
542 | { | |
543 | parse(source, result, parse_pos); | |
544 | } | |
545 | ||
546 | // ------------------------------------- | |
547 | // Formats a double number and save the result in a string. | |
548 | ||
549 | UnicodeString& | |
550 | NumberFormat::format(double number, UnicodeString& appendTo) const | |
551 | { | |
552 | FieldPosition pos(0); | |
553 | return format(number, appendTo, pos); | |
554 | } | |
555 | ||
556 | // ------------------------------------- | |
557 | // Formats a long number and save the result in a string. | |
558 | ||
559 | UnicodeString& | |
560 | NumberFormat::format(int32_t number, UnicodeString& appendTo) const | |
561 | { | |
562 | FieldPosition pos(0); | |
563 | return format(number, appendTo, pos); | |
564 | } | |
565 | ||
374ca955 A |
566 | // ------------------------------------- |
567 | // Formats a long number and save the result in a string. | |
568 | ||
569 | UnicodeString& | |
570 | NumberFormat::format(int64_t number, UnicodeString& appendTo) const | |
571 | { | |
572 | FieldPosition pos(0); | |
573 | return format(number, appendTo, pos); | |
574 | } | |
575 | ||
b75a7d8f A |
576 | // ------------------------------------- |
577 | // Parses the text and save the result object. If the returned | |
578 | // parse position is 0, that means the parsing failed, the status | |
579 | // code needs to be set to failure. Ignores the returned parse | |
580 | // position, otherwise. | |
581 | ||
582 | void | |
583 | NumberFormat::parse(const UnicodeString& text, | |
584 | Formattable& result, | |
585 | UErrorCode& status) const | |
586 | { | |
587 | if (U_FAILURE(status)) return; | |
588 | ||
589 | ParsePosition parsePosition(0); | |
590 | parse(text, result, parsePosition); | |
591 | if (parsePosition.getIndex() == 0) { | |
592 | status = U_INVALID_FORMAT_ERROR; | |
593 | } | |
594 | } | |
595 | ||
374ca955 A |
596 | Formattable& NumberFormat::parseCurrency(const UnicodeString& text, |
597 | Formattable& result, | |
598 | ParsePosition& pos) const { | |
599 | // Default implementation only -- subclasses should override | |
600 | int32_t start = pos.getIndex(); | |
601 | parse(text, result, pos); | |
602 | if (pos.getIndex() != start) { | |
603 | UChar curr[4]; | |
604 | UErrorCode ec = U_ZERO_ERROR; | |
605 | getEffectiveCurrency(curr, ec); | |
606 | if (U_SUCCESS(ec)) { | |
607 | Formattable n(result); | |
46f4442e A |
608 | CurrencyAmount *tempCurAmnt = new CurrencyAmount(n, curr, ec); // Use for null testing. |
609 | if (U_FAILURE(ec) || tempCurAmnt == NULL) { | |
374ca955 | 610 | pos.setIndex(start); // indicate failure |
46f4442e A |
611 | } else { |
612 | result.adoptObject(tempCurAmnt); | |
374ca955 A |
613 | } |
614 | } | |
615 | } | |
616 | return result; | |
617 | } | |
618 | ||
b75a7d8f A |
619 | // ------------------------------------- |
620 | // Sets to only parse integers. | |
621 | ||
622 | void | |
623 | NumberFormat::setParseIntegerOnly(UBool value) | |
624 | { | |
625 | fParseIntegerOnly = value; | |
626 | } | |
627 | ||
46f4442e A |
628 | // ------------------------------------- |
629 | // Sets whether or not parse is strict. | |
630 | ||
631 | void | |
632 | NumberFormat::setParseStrict(UBool value) | |
633 | { | |
634 | fParseStrict = value; | |
635 | } | |
636 | ||
b75a7d8f A |
637 | // ------------------------------------- |
638 | // Create a number style NumberFormat instance with the default locale. | |
639 | ||
374ca955 | 640 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
641 | NumberFormat::createInstance(UErrorCode& status) |
642 | { | |
643 | return createInstance(Locale::getDefault(), kNumberStyle, status); | |
644 | } | |
645 | ||
646 | // ------------------------------------- | |
647 | // Create a number style NumberFormat instance with the inLocale locale. | |
648 | ||
374ca955 | 649 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
650 | NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) |
651 | { | |
652 | return createInstance(inLocale, kNumberStyle, status); | |
653 | } | |
654 | ||
655 | // ------------------------------------- | |
656 | // Create a currency style NumberFormat instance with the default locale. | |
657 | ||
374ca955 | 658 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
659 | NumberFormat::createCurrencyInstance(UErrorCode& status) |
660 | { | |
374ca955 | 661 | return createCurrencyInstance(Locale::getDefault(), status); |
b75a7d8f A |
662 | } |
663 | ||
664 | // ------------------------------------- | |
665 | // Create a currency style NumberFormat instance with the inLocale locale. | |
666 | ||
374ca955 | 667 | NumberFormat* U_EXPORT2 |
b75a7d8f | 668 | NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) |
729e4ab9 | 669 | { |
b75a7d8f A |
670 | return createInstance(inLocale, kCurrencyStyle, status); |
671 | } | |
672 | ||
673 | // ------------------------------------- | |
674 | // Create a percent style NumberFormat instance with the default locale. | |
675 | ||
374ca955 | 676 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
677 | NumberFormat::createPercentInstance(UErrorCode& status) |
678 | { | |
679 | return createInstance(Locale::getDefault(), kPercentStyle, status); | |
680 | } | |
681 | ||
682 | // ------------------------------------- | |
683 | // Create a percent style NumberFormat instance with the inLocale locale. | |
684 | ||
374ca955 | 685 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
686 | NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) |
687 | { | |
688 | return createInstance(inLocale, kPercentStyle, status); | |
689 | } | |
690 | ||
691 | // ------------------------------------- | |
692 | // Create a scientific style NumberFormat instance with the default locale. | |
693 | ||
374ca955 | 694 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
695 | NumberFormat::createScientificInstance(UErrorCode& status) |
696 | { | |
697 | return createInstance(Locale::getDefault(), kScientificStyle, status); | |
698 | } | |
699 | ||
700 | // ------------------------------------- | |
701 | // Create a scientific style NumberFormat instance with the inLocale locale. | |
702 | ||
374ca955 | 703 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
704 | NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) |
705 | { | |
706 | return createInstance(inLocale, kScientificStyle, status); | |
707 | } | |
708 | ||
709 | // ------------------------------------- | |
710 | ||
374ca955 | 711 | const Locale* U_EXPORT2 |
b75a7d8f A |
712 | NumberFormat::getAvailableLocales(int32_t& count) |
713 | { | |
714 | return Locale::getAvailableLocales(count); | |
715 | } | |
716 | ||
717 | // ------------------------------------------ | |
718 | // | |
719 | // Registration | |
720 | // | |
721 | //------------------------------------------- | |
722 | ||
374ca955 | 723 | #if !UCONFIG_NO_SERVICE |
374ca955 | 724 | |
b75a7d8f A |
725 | // ------------------------------------- |
726 | ||
727 | class ICUNumberFormatFactory : public ICUResourceBundleFactory { | |
728 | protected: | |
374ca955 A |
729 | virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /* service */, UErrorCode& status) const { |
730 | // !!! kind is not an EStyles, need to determine how to handle this | |
731 | return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); | |
732 | } | |
b75a7d8f A |
733 | }; |
734 | ||
735 | // ------------------------------------- | |
736 | ||
737 | class NFFactory : public LocaleKeyFactory { | |
738 | private: | |
374ca955 A |
739 | NumberFormatFactory* _delegate; |
740 | Hashtable* _ids; | |
b75a7d8f A |
741 | |
742 | public: | |
729e4ab9 | 743 | NFFactory(NumberFormatFactory* delegate) |
374ca955 A |
744 | : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) |
745 | , _delegate(delegate) | |
746 | , _ids(NULL) | |
747 | { | |
748 | } | |
b75a7d8f | 749 | |
374ca955 A |
750 | virtual ~NFFactory() |
751 | { | |
752 | delete _delegate; | |
753 | delete _ids; | |
754 | } | |
b75a7d8f | 755 | |
374ca955 A |
756 | virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const |
757 | { | |
758 | if (handlesKey(key, status)) { | |
759 | const LocaleKey& lkey = (const LocaleKey&)key; | |
760 | Locale loc; | |
761 | lkey.canonicalLocale(loc); | |
762 | int32_t kind = lkey.kind(); | |
763 | ||
764 | UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)(kind+1)); | |
765 | if (result == NULL) { | |
766 | result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status); | |
767 | } | |
768 | return result; | |
769 | } | |
770 | return NULL; | |
b75a7d8f | 771 | } |
b75a7d8f A |
772 | |
773 | protected: | |
374ca955 | 774 | /** |
729e4ab9 | 775 | * Return the set of ids that this factory supports (visible or |
374ca955 A |
776 | * otherwise). This can be called often and might need to be |
777 | * cached if it is expensive to create. | |
778 | */ | |
779 | virtual const Hashtable* getSupportedIDs(UErrorCode& status) const | |
780 | { | |
781 | if (U_SUCCESS(status)) { | |
782 | if (!_ids) { | |
783 | int32_t count = 0; | |
784 | const UnicodeString * const idlist = _delegate->getSupportedIDs(count, status); | |
785 | ((NFFactory*)this)->_ids = new Hashtable(status); /* cast away const */ | |
786 | if (_ids) { | |
787 | for (int i = 0; i < count; ++i) { | |
788 | _ids->put(idlist[i], (void*)this, status); | |
789 | } | |
790 | } | |
791 | } | |
792 | return _ids; | |
b75a7d8f | 793 | } |
374ca955 | 794 | return NULL; |
b75a7d8f | 795 | } |
b75a7d8f A |
796 | }; |
797 | ||
798 | class ICUNumberFormatService : public ICULocaleService { | |
799 | public: | |
374ca955 | 800 | ICUNumberFormatService() |
73c04bcf | 801 | : ICULocaleService(UNICODE_STRING_SIMPLE("Number Format")) |
374ca955 A |
802 | { |
803 | UErrorCode status = U_ZERO_ERROR; | |
804 | registerFactory(new ICUNumberFormatFactory(), status); | |
805 | } | |
b75a7d8f | 806 | |
374ca955 A |
807 | virtual UObject* cloneInstance(UObject* instance) const { |
808 | return ((NumberFormat*)instance)->clone(); | |
809 | } | |
b75a7d8f | 810 | |
374ca955 A |
811 | virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /* actualID */, UErrorCode& status) const { |
812 | LocaleKey& lkey = (LocaleKey&)key; | |
813 | int32_t kind = lkey.kind(); | |
814 | Locale loc; | |
815 | lkey.currentLocale(loc); | |
816 | return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); | |
817 | } | |
b75a7d8f | 818 | |
374ca955 A |
819 | virtual UBool isDefault() const { |
820 | return countFactories() == 1; | |
821 | } | |
b75a7d8f A |
822 | }; |
823 | ||
824 | // ------------------------------------- | |
825 | ||
729e4ab9 | 826 | static ICULocaleService* |
374ca955 | 827 | getNumberFormatService(void) |
b75a7d8f A |
828 | { |
829 | UBool needInit; | |
46f4442e | 830 | UMTX_CHECK(NULL, (UBool)(gService == NULL), needInit); |
b75a7d8f A |
831 | if (needInit) { |
832 | ICULocaleService * newservice = new ICUNumberFormatService(); | |
833 | if (newservice) { | |
46f4442e | 834 | umtx_lock(NULL); |
b75a7d8f A |
835 | if (gService == NULL) { |
836 | gService = newservice; | |
837 | newservice = NULL; | |
838 | } | |
46f4442e | 839 | umtx_unlock(NULL); |
b75a7d8f A |
840 | } |
841 | if (newservice) { | |
842 | delete newservice; | |
843 | } else { | |
844 | // we won the contention, this thread can register cleanup. | |
374ca955 | 845 | ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); |
b75a7d8f A |
846 | } |
847 | } | |
848 | return gService; | |
849 | } | |
850 | ||
851 | // ------------------------------------- | |
852 | ||
374ca955 | 853 | URegistryKey U_EXPORT2 |
b75a7d8f A |
854 | NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) |
855 | { | |
374ca955 | 856 | ICULocaleService *service = getNumberFormatService(); |
b75a7d8f | 857 | if (service) { |
46f4442e A |
858 | NFFactory *tempnnf = new NFFactory(toAdopt); |
859 | if (tempnnf != NULL) { | |
860 | return service->registerFactory(tempnnf, status); | |
861 | } | |
b75a7d8f A |
862 | } |
863 | status = U_MEMORY_ALLOCATION_ERROR; | |
864 | return NULL; | |
865 | } | |
866 | ||
867 | // ------------------------------------- | |
868 | ||
374ca955 | 869 | UBool U_EXPORT2 |
b75a7d8f A |
870 | NumberFormat::unregister(URegistryKey key, UErrorCode& status) |
871 | { | |
872 | if (U_SUCCESS(status)) { | |
46f4442e A |
873 | UBool haveService; |
874 | UMTX_CHECK(NULL, gService != NULL, haveService); | |
b75a7d8f A |
875 | if (haveService) { |
876 | return gService->unregister(key, status); | |
877 | } | |
878 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
879 | } | |
880 | return FALSE; | |
881 | } | |
882 | ||
883 | // ------------------------------------- | |
374ca955 | 884 | StringEnumeration* U_EXPORT2 |
b75a7d8f A |
885 | NumberFormat::getAvailableLocales(void) |
886 | { | |
374ca955 | 887 | ICULocaleService *service = getNumberFormatService(); |
b75a7d8f A |
888 | if (service) { |
889 | return service->getAvailableLocales(); | |
890 | } | |
891 | return NULL; // no way to return error condition | |
892 | } | |
374ca955 A |
893 | #endif /* UCONFIG_NO_SERVICE */ |
894 | // ------------------------------------- | |
895 | ||
896 | NumberFormat* U_EXPORT2 | |
897 | NumberFormat::createInstance(const Locale& loc, EStyles kind, UErrorCode& status) | |
898 | { | |
899 | #if !UCONFIG_NO_SERVICE | |
46f4442e A |
900 | UBool haveService; |
901 | UMTX_CHECK(NULL, gService != NULL, haveService); | |
374ca955 A |
902 | if (haveService) { |
903 | return (NumberFormat*)gService->get(loc, kind, status); | |
904 | } | |
905 | else | |
906 | #endif | |
907 | { | |
908 | return makeInstance(loc, kind, status); | |
909 | } | |
910 | } | |
911 | ||
b75a7d8f A |
912 | |
913 | // ------------------------------------- | |
914 | // Checks if the thousand/10 thousand grouping is used in the | |
915 | // NumberFormat instance. | |
916 | ||
917 | UBool | |
918 | NumberFormat::isGroupingUsed() const | |
919 | { | |
920 | return fGroupingUsed; | |
921 | } | |
922 | ||
923 | // ------------------------------------- | |
924 | // Sets to use the thousand/10 thousand grouping in the | |
925 | // NumberFormat instance. | |
926 | ||
927 | void | |
928 | NumberFormat::setGroupingUsed(UBool newValue) | |
929 | { | |
930 | fGroupingUsed = newValue; | |
931 | } | |
932 | ||
933 | // ------------------------------------- | |
934 | // Gets the maximum number of digits for the integral part for | |
935 | // this NumberFormat instance. | |
936 | ||
937 | int32_t NumberFormat::getMaximumIntegerDigits() const | |
938 | { | |
939 | return fMaxIntegerDigits; | |
940 | } | |
941 | ||
942 | // ------------------------------------- | |
943 | // Sets the maximum number of digits for the integral part for | |
944 | // this NumberFormat instance. | |
945 | ||
946 | void | |
947 | NumberFormat::setMaximumIntegerDigits(int32_t newValue) | |
948 | { | |
73c04bcf | 949 | fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); |
b75a7d8f A |
950 | if(fMinIntegerDigits > fMaxIntegerDigits) |
951 | fMinIntegerDigits = fMaxIntegerDigits; | |
952 | } | |
953 | ||
954 | // ------------------------------------- | |
955 | // Gets the minimum number of digits for the integral part for | |
956 | // this NumberFormat instance. | |
957 | ||
958 | int32_t | |
959 | NumberFormat::getMinimumIntegerDigits() const | |
960 | { | |
961 | return fMinIntegerDigits; | |
962 | } | |
963 | ||
964 | // ------------------------------------- | |
965 | // Sets the minimum number of digits for the integral part for | |
966 | // this NumberFormat instance. | |
967 | ||
968 | void | |
969 | NumberFormat::setMinimumIntegerDigits(int32_t newValue) | |
970 | { | |
73c04bcf | 971 | fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); |
b75a7d8f A |
972 | if(fMinIntegerDigits > fMaxIntegerDigits) |
973 | fMaxIntegerDigits = fMinIntegerDigits; | |
974 | } | |
975 | ||
976 | // ------------------------------------- | |
977 | // Gets the maximum number of digits for the fractional part for | |
978 | // this NumberFormat instance. | |
979 | ||
980 | int32_t | |
981 | NumberFormat::getMaximumFractionDigits() const | |
982 | { | |
983 | return fMaxFractionDigits; | |
984 | } | |
985 | ||
986 | // ------------------------------------- | |
987 | // Sets the maximum number of digits for the fractional part for | |
988 | // this NumberFormat instance. | |
989 | ||
990 | void | |
991 | NumberFormat::setMaximumFractionDigits(int32_t newValue) | |
992 | { | |
73c04bcf | 993 | fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); |
b75a7d8f A |
994 | if(fMaxFractionDigits < fMinFractionDigits) |
995 | fMinFractionDigits = fMaxFractionDigits; | |
996 | } | |
997 | ||
998 | // ------------------------------------- | |
999 | // Gets the minimum number of digits for the fractional part for | |
1000 | // this NumberFormat instance. | |
1001 | ||
1002 | int32_t | |
1003 | NumberFormat::getMinimumFractionDigits() const | |
1004 | { | |
1005 | return fMinFractionDigits; | |
1006 | } | |
1007 | ||
1008 | // ------------------------------------- | |
1009 | // Sets the minimum number of digits for the fractional part for | |
1010 | // this NumberFormat instance. | |
1011 | ||
1012 | void | |
1013 | NumberFormat::setMinimumFractionDigits(int32_t newValue) | |
1014 | { | |
73c04bcf | 1015 | fMinFractionDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); |
b75a7d8f A |
1016 | if (fMaxFractionDigits < fMinFractionDigits) |
1017 | fMaxFractionDigits = fMinFractionDigits; | |
1018 | } | |
1019 | ||
1020 | // ------------------------------------- | |
1021 | ||
374ca955 A |
1022 | void NumberFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { |
1023 | if (U_FAILURE(ec)) { | |
1024 | return; | |
1025 | } | |
b75a7d8f | 1026 | if (theCurrency) { |
374ca955 A |
1027 | u_strncpy(fCurrency, theCurrency, 3); |
1028 | fCurrency[3] = 0; | |
b75a7d8f | 1029 | } else { |
374ca955 | 1030 | fCurrency[0] = 0; |
b75a7d8f A |
1031 | } |
1032 | } | |
1033 | ||
1034 | const UChar* NumberFormat::getCurrency() const { | |
374ca955 A |
1035 | return fCurrency; |
1036 | } | |
1037 | ||
1038 | void NumberFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { | |
1039 | const UChar* c = getCurrency(); | |
1040 | if (*c != 0) { | |
1041 | u_strncpy(result, c, 3); | |
1042 | result[3] = 0; | |
1043 | } else { | |
1044 | const char* loc = getLocaleID(ULOC_VALID_LOCALE, ec); | |
1045 | if (loc == NULL) { | |
1046 | loc = uloc_getDefault(); | |
1047 | } | |
1048 | ucurr_forLocale(loc, result, 4, &ec); | |
1049 | } | |
b75a7d8f A |
1050 | } |
1051 | ||
1052 | // ------------------------------------- | |
1053 | // Creates the NumberFormat instance of the specified style (number, currency, | |
1054 | // or percent) for the desired locale. | |
1055 | ||
1056 | NumberFormat* | |
1057 | NumberFormat::makeInstance(const Locale& desiredLocale, | |
1058 | EStyles style, | |
1059 | UErrorCode& status) | |
1060 | { | |
1061 | if (U_FAILURE(status)) return NULL; | |
1062 | ||
1063 | if (style < 0 || style >= kStyleCount) { | |
1064 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
1065 | return NULL; | |
1066 | } | |
1067 | ||
73c04bcf A |
1068 | #ifdef U_WINDOWS |
1069 | char buffer[8]; | |
1070 | int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); | |
1071 | ||
1072 | // if the locale has "@compat=host", create a host-specific NumberFormat | |
1073 | if (count > 0 && uprv_strcmp(buffer, "host") == 0) { | |
1074 | Win32NumberFormat *f = NULL; | |
1075 | UBool curr = TRUE; | |
1076 | ||
1077 | switch (style) { | |
1078 | case kNumberStyle: | |
1079 | curr = FALSE; | |
1080 | // fall-through | |
1081 | ||
1082 | case kCurrencyStyle: | |
729e4ab9 A |
1083 | case kIsoCurrencyStyle: // do not support plural formatting here |
1084 | case kPluralCurrencyStyle: | |
73c04bcf A |
1085 | f = new Win32NumberFormat(desiredLocale, curr, status); |
1086 | ||
1087 | if (U_SUCCESS(status)) { | |
1088 | return f; | |
1089 | } | |
1090 | ||
1091 | delete f; | |
1092 | break; | |
729e4ab9 | 1093 | |
73c04bcf A |
1094 | default: |
1095 | break; | |
1096 | } | |
1097 | } | |
1098 | #endif | |
1099 | ||
374ca955 A |
1100 | NumberFormat* f = NULL; |
1101 | DecimalFormatSymbols* symbolsToAdopt = NULL; | |
1102 | UnicodeString pattern; | |
729e4ab9 A |
1103 | UResourceBundle *resource = ures_open(NULL, desiredLocale.getName(), &status); |
1104 | NumberingSystem *ns = NULL; | |
1105 | UBool deleteSymbols = TRUE; | |
1106 | UHashtable * cache = NULL; | |
1107 | int32_t hashKey; | |
1108 | UBool getCache = FALSE; | |
1109 | UBool deleteNS = FALSE; | |
b75a7d8f | 1110 | |
374ca955 | 1111 | if (U_FAILURE(status)) { |
b75a7d8f A |
1112 | // We don't appear to have resource data available -- use the last-resort data |
1113 | status = U_USING_FALLBACK_WARNING; | |
374ca955 A |
1114 | // When the data is unavailable, and locale isn't passed in, last resort data is used. |
1115 | symbolsToAdopt = new DecimalFormatSymbols(status); | |
b75a7d8f A |
1116 | |
1117 | // Creates a DecimalFormat instance with the last resort number patterns. | |
73c04bcf | 1118 | pattern.setTo(TRUE, gLastResortNumberPatterns[style], -1); |
b75a7d8f | 1119 | } |
374ca955 | 1120 | else { |
374ca955 A |
1121 | // Loads the decimal symbols of the desired locale. |
1122 | symbolsToAdopt = new DecimalFormatSymbols(desiredLocale, status); | |
b75a7d8f | 1123 | |
374ca955 | 1124 | int32_t patLen = 0; |
729e4ab9 A |
1125 | |
1126 | /* for ISOCURRENCYSTYLE and PLURALCURRENCYSTYLE, | |
1127 | * the pattern is the same as the pattern of CURRENCYSTYLE | |
1128 | * but by replacing the single currency sign with | |
1129 | * double currency sign or triple currency sign. | |
1130 | */ | |
1131 | int styleInNumberPattern = ((style == kIsoCurrencyStyle || | |
1132 | style == kPluralCurrencyStyle) ? | |
1133 | kCurrencyStyle : style); | |
1134 | ||
1135 | resource = ures_getByKeyWithFallback(resource, gNumberElements, resource, &status); | |
1136 | // TODO : Get patterns on a per numbering system basis, for right now assumes "latn" for patterns | |
1137 | resource = ures_getByKeyWithFallback(resource, gLatn, resource, &status); | |
1138 | resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); | |
1139 | ||
1140 | const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[styleInNumberPattern], &patLen, &status); | |
1141 | ||
374ca955 A |
1142 | // Creates the specified decimal format style of the desired locale. |
1143 | pattern.setTo(TRUE, patResStr, patLen); | |
b75a7d8f | 1144 | } |
374ca955 A |
1145 | if (U_FAILURE(status) || symbolsToAdopt == NULL) { |
1146 | goto cleanup; | |
b75a7d8f | 1147 | } |
729e4ab9 | 1148 | if(style==kCurrencyStyle || style == kIsoCurrencyStyle){ |
73c04bcf A |
1149 | const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); |
1150 | if(currPattern!=NULL){ | |
1151 | pattern.setTo(currPattern, u_strlen(currPattern)); | |
b75a7d8f A |
1152 | } |
1153 | } | |
729e4ab9 A |
1154 | |
1155 | // Use numbering system cache hashtable | |
1156 | UMTX_CHECK(&nscacheMutex, (UBool)(cache != NumberingSystem_cache), getCache); | |
1157 | if (getCache) { | |
1158 | umtx_lock(&nscacheMutex); | |
1159 | cache = NumberingSystem_cache; | |
1160 | umtx_unlock(&nscacheMutex); | |
1161 | } | |
1162 | ||
1163 | // Check cache we got, create if non-existant | |
1164 | status = U_ZERO_ERROR; | |
1165 | if (cache == NULL) { | |
1166 | cache = uhash_open(uhash_hashLong, | |
1167 | uhash_compareLong, | |
1168 | NULL, | |
1169 | &status); | |
1170 | ||
1171 | if (cache == NULL || U_FAILURE(status)) { | |
1172 | // cache not created - out of memory | |
1173 | cache = NULL; | |
1174 | } | |
1175 | else { | |
1176 | // cache created | |
1177 | uhash_setValueDeleter(cache, deleteNumberingSystem); | |
1178 | ||
1179 | // set final NumberingSystem_cache value | |
1180 | UHashtable* h = NULL; | |
1181 | ||
1182 | UMTX_CHECK(&nscacheMutex, (UBool)(h != NumberingSystem_cache), getCache); | |
1183 | if (getCache) { | |
1184 | umtx_lock(&nscacheMutex); | |
1185 | h = NumberingSystem_cache; | |
1186 | umtx_unlock(&nscacheMutex); | |
1187 | } | |
1188 | if (h == NULL) { | |
1189 | umtx_lock(&nscacheMutex); | |
1190 | NumberingSystem_cache = h = cache; | |
1191 | cache = NULL; | |
1192 | ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); | |
1193 | umtx_unlock(&nscacheMutex); | |
1194 | } | |
1195 | ||
1196 | if(cache != NULL) { | |
1197 | uhash_close(cache); | |
1198 | } | |
1199 | cache = h; | |
1200 | } | |
1201 | } | |
1202 | ||
1203 | // Get cached numbering system | |
1204 | if (cache != NULL) { | |
1205 | hashKey = desiredLocale.hashCode(); | |
1206 | ||
1207 | umtx_lock(&nscacheMutex); | |
1208 | ns = (NumberingSystem *)uhash_iget(cache, hashKey); | |
1209 | if (ns == NULL) { | |
1210 | ns = NumberingSystem::createInstance(desiredLocale,status); | |
1211 | uhash_iput(cache, hashKey, (void*)ns, &status); | |
1212 | } | |
1213 | umtx_unlock(&nscacheMutex); | |
1214 | } | |
1215 | else { | |
1216 | ns = NumberingSystem::createInstance(desiredLocale,status); | |
1217 | deleteNS = TRUE; | |
1218 | } | |
1219 | ||
1220 | // check results of getting a numbering system | |
1221 | if ((ns == NULL) || (U_FAILURE(status))) { | |
374ca955 | 1222 | goto cleanup; |
b75a7d8f A |
1223 | } |
1224 | ||
729e4ab9 A |
1225 | if (ns->isAlgorithmic()) { |
1226 | UnicodeString nsDesc; | |
1227 | UnicodeString nsRuleSetGroup; | |
1228 | UnicodeString nsRuleSetName; | |
1229 | Locale nsLoc; | |
1230 | URBNFRuleSetTag desiredRulesType = URBNF_NUMBERING_SYSTEM; | |
1231 | ||
1232 | nsDesc.setTo(ns->getDescription()); | |
1233 | int32_t firstSlash = nsDesc.indexOf(gSlash); | |
1234 | int32_t lastSlash = nsDesc.lastIndexOf(gSlash); | |
1235 | if ( lastSlash > firstSlash ) { | |
1236 | char nsLocID[ULOC_FULLNAME_CAPACITY]; | |
1237 | ||
1238 | nsDesc.extract(0,firstSlash,nsLocID,ULOC_FULLNAME_CAPACITY,US_INV); | |
1239 | nsRuleSetGroup.setTo(nsDesc,firstSlash+1,lastSlash-firstSlash-1); | |
1240 | nsRuleSetName.setTo(nsDesc,lastSlash+1); | |
1241 | ||
1242 | nsLoc = Locale::createFromName(nsLocID); | |
1243 | ||
1244 | UnicodeString SpelloutRules = UNICODE_STRING_SIMPLE("SpelloutRules"); | |
1245 | if ( nsRuleSetGroup.compare(SpelloutRules) == 0 ) { | |
1246 | desiredRulesType = URBNF_SPELLOUT; | |
1247 | } | |
1248 | } else { | |
1249 | nsLoc = desiredLocale; | |
1250 | nsRuleSetName.setTo(nsDesc); | |
1251 | } | |
1252 | ||
1253 | RuleBasedNumberFormat *r = new RuleBasedNumberFormat(desiredRulesType,nsLoc,status); | |
1254 | ||
1255 | if (U_FAILURE(status) || r == NULL) { | |
1256 | goto cleanup; | |
1257 | } | |
1258 | r->setDefaultRuleSet(nsRuleSetName,status); | |
1259 | f = (NumberFormat *) r; | |
1260 | ||
1261 | } else { | |
1262 | // replace single currency sign in the pattern with double currency sign | |
1263 | // if the style is kIsoCurrencyStyle | |
1264 | if (style == kIsoCurrencyStyle) { | |
1265 | pattern.findAndReplace(gSingleCurrencySign, gDoubleCurrencySign); | |
1266 | } | |
1267 | ||
1268 | f = new DecimalFormat(pattern, symbolsToAdopt, style, status); | |
1269 | if (U_FAILURE(status) || f == NULL) { | |
1270 | goto cleanup; | |
1271 | } | |
1272 | deleteSymbols = FALSE; | |
1273 | } | |
1274 | ||
1275 | f->setLocaleIDs(ures_getLocaleByType(resource, ULOC_VALID_LOCALE, &status), | |
1276 | ures_getLocaleByType(resource, ULOC_ACTUAL_LOCALE, &status)); | |
1277 | ||
374ca955 | 1278 | cleanup: |
374ca955 | 1279 | ures_close(resource); |
729e4ab9 A |
1280 | |
1281 | if (deleteNS && ns) { | |
1282 | delete ns; | |
1283 | } | |
1284 | ||
374ca955 A |
1285 | if (U_FAILURE(status)) { |
1286 | /* If f exists, then it will delete the symbols */ | |
1287 | if (f==NULL) { | |
1288 | delete symbolsToAdopt; | |
1289 | } | |
1290 | else { | |
1291 | delete f; | |
1292 | } | |
b75a7d8f A |
1293 | return NULL; |
1294 | } | |
374ca955 A |
1295 | if (f == NULL || symbolsToAdopt == NULL) { |
1296 | status = U_MEMORY_ALLOCATION_ERROR; | |
b75a7d8f A |
1297 | f = NULL; |
1298 | } | |
729e4ab9 A |
1299 | if (deleteSymbols && symbolsToAdopt != NULL) { |
1300 | delete symbolsToAdopt; | |
1301 | } | |
b75a7d8f A |
1302 | return f; |
1303 | } | |
1304 | ||
1305 | U_NAMESPACE_END | |
1306 | ||
b75a7d8f A |
1307 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
1308 | ||
1309 | //eof |