]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ******************************************************************************* | |
46f4442e | 3 | * Copyright (C) 1997-2009, 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" | |
73c04bcf A |
36 | #include "winnmfmt.h" |
37 | #include "uresimp.h" | |
b75a7d8f | 38 | #include "uhash.h" |
374ca955 | 39 | #include "cmemory.h" |
73c04bcf | 40 | #include "servloc.h" |
b75a7d8f | 41 | #include "ucln_in.h" |
374ca955 A |
42 | #include "cstring.h" |
43 | #include "putilimp.h" | |
b75a7d8f A |
44 | #include <float.h> |
45 | ||
374ca955 A |
46 | //#define FMT_DEBUG |
47 | ||
48 | #ifdef FMT_DEBUG | |
49 | #include <stdio.h> | |
50 | static void debugout(UnicodeString s) { | |
51 | char buf[2000]; | |
52 | s.extract((int32_t) 0, s.length(), buf); | |
53 | printf("%s", buf); | |
54 | } | |
55 | #define debug(x) printf("%s", x); | |
56 | #else | |
57 | #define debugout(x) | |
58 | #define debug(x) | |
59 | #endif | |
60 | ||
b75a7d8f A |
61 | // If no number pattern can be located for a locale, this is the last |
62 | // resort. | |
63 | static const UChar gLastResortDecimalPat[] = { | |
64 | 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0x3B, 0x2D, 0x23, 0x30, 0x2E, 0x23, 0x23, 0x23, 0 /* "#0.###;-#0.###" */ | |
65 | }; | |
66 | static const UChar gLastResortCurrencyPat[] = { | |
67 | 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x3B, 0x28, 0x24, 0x23, 0x30, 0x2E, 0x30, 0x30, 0x29, 0 /* "$#0.00;($#0.00)" */ | |
68 | }; | |
69 | static const UChar gLastResortPercentPat[] = { | |
70 | 0x23, 0x30, 0x25, 0 /* "#0%" */ | |
71 | }; | |
72 | static const UChar gLastResortScientificPat[] = { | |
73 | 0x23, 0x45, 0x30, 0 /* "#E0" */ | |
74 | }; | |
b75a7d8f | 75 | |
b75a7d8f A |
76 | // If the maximum base 10 exponent were 4, then the largest number would |
77 | // be 99,999 which has 5 digits. | |
46f4442e A |
78 | // On IEEE754 systems gMaxIntegerDigits is 308 + possible denormalized 15 digits + rounding digit |
79 | static const int32_t gMaxIntegerDigits = DBL_MAX_10_EXP + DBL_DIG + 1; | |
73c04bcf | 80 | static const int32_t gMinIntegerDigits = 127; |
b75a7d8f | 81 | |
73c04bcf | 82 | static const UChar * const gLastResortNumberPatterns[] = |
b75a7d8f A |
83 | { |
84 | gLastResortDecimalPat, | |
85 | gLastResortCurrencyPat, | |
86 | gLastResortPercentPat, | |
87 | gLastResortScientificPat | |
88 | }; | |
89 | ||
73c04bcf A |
90 | // ***************************************************************************** |
91 | // class NumberFormat | |
92 | // ***************************************************************************** | |
93 | ||
94 | U_NAMESPACE_BEGIN | |
95 | ||
96 | UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(NumberFormat) | |
97 | ||
374ca955 | 98 | #if !UCONFIG_NO_SERVICE |
b75a7d8f | 99 | // ------------------------------------- |
374ca955 A |
100 | // SimpleNumberFormatFactory implementation |
101 | NumberFormatFactory::~NumberFormatFactory() {} | |
102 | SimpleNumberFormatFactory::SimpleNumberFormatFactory(const Locale& locale, UBool visible) | |
103 | : _visible(visible) | |
374ca955 | 104 | { |
73c04bcf | 105 | LocaleUtility::initNameFromLocale(locale, _id); |
374ca955 A |
106 | } |
107 | ||
108 | SimpleNumberFormatFactory::~SimpleNumberFormatFactory() {} | |
109 | ||
110 | UBool SimpleNumberFormatFactory::visible(void) const { | |
111 | return _visible; | |
112 | } | |
113 | ||
114 | const UnicodeString * | |
115 | SimpleNumberFormatFactory::getSupportedIDs(int32_t &count, UErrorCode& status) const | |
116 | { | |
117 | if (U_SUCCESS(status)) { | |
118 | count = 1; | |
119 | return &_id; | |
120 | } | |
121 | count = 0; | |
122 | return NULL; | |
123 | } | |
124 | #endif /* #if !UCONFIG_NO_SERVICE */ | |
b75a7d8f | 125 | |
374ca955 A |
126 | // ------------------------------------- |
127 | // default constructor | |
b75a7d8f A |
128 | NumberFormat::NumberFormat() |
129 | : fGroupingUsed(TRUE), | |
73c04bcf | 130 | fMaxIntegerDigits(gMaxIntegerDigits), |
b75a7d8f A |
131 | fMinIntegerDigits(1), |
132 | fMaxFractionDigits(3), // invariant, >= minFractionDigits | |
133 | fMinFractionDigits(0), | |
46f4442e A |
134 | fParseIntegerOnly(FALSE), |
135 | fParseStrict(TRUE) // TODO: Should this be FALSE? | |
b75a7d8f | 136 | { |
374ca955 | 137 | fCurrency[0] = 0; |
b75a7d8f A |
138 | } |
139 | ||
140 | // ------------------------------------- | |
141 | ||
142 | NumberFormat::~NumberFormat() | |
143 | { | |
144 | } | |
145 | ||
146 | // ------------------------------------- | |
147 | // copy constructor | |
148 | ||
149 | NumberFormat::NumberFormat(const NumberFormat &source) | |
150 | : Format(source) | |
151 | { | |
152 | *this = source; | |
153 | } | |
154 | ||
155 | // ------------------------------------- | |
156 | // assignment operator | |
157 | ||
158 | NumberFormat& | |
159 | NumberFormat::operator=(const NumberFormat& rhs) | |
160 | { | |
161 | if (this != &rhs) | |
162 | { | |
163 | fGroupingUsed = rhs.fGroupingUsed; | |
164 | fMaxIntegerDigits = rhs.fMaxIntegerDigits; | |
165 | fMinIntegerDigits = rhs.fMinIntegerDigits; | |
166 | fMaxFractionDigits = rhs.fMaxFractionDigits; | |
167 | fMinFractionDigits = rhs.fMinFractionDigits; | |
168 | fParseIntegerOnly = rhs.fParseIntegerOnly; | |
374ca955 | 169 | u_strncpy(fCurrency, rhs.fCurrency, 4); |
b75a7d8f A |
170 | } |
171 | return *this; | |
172 | } | |
173 | ||
174 | // ------------------------------------- | |
175 | ||
176 | UBool | |
177 | NumberFormat::operator==(const Format& that) const | |
178 | { | |
374ca955 | 179 | // Format::operator== guarantees this cast is safe |
b75a7d8f A |
180 | NumberFormat* other = (NumberFormat*)&that; |
181 | ||
374ca955 A |
182 | #ifdef FMT_DEBUG |
183 | // This code makes it easy to determine why two format objects that should | |
184 | // be equal aren't. | |
185 | UBool first = TRUE; | |
186 | if (!Format::operator==(that)) { | |
187 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
188 | debug("Format::!="); | |
189 | } | |
190 | if (!(fMaxIntegerDigits == other->fMaxIntegerDigits && | |
191 | fMinIntegerDigits == other->fMinIntegerDigits)) { | |
192 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
193 | debug("Integer digits !="); | |
194 | } | |
195 | if (!(fMaxFractionDigits == other->fMaxFractionDigits && | |
196 | fMinFractionDigits == other->fMinFractionDigits)) { | |
197 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
198 | debug("Fraction digits !="); | |
199 | } | |
200 | if (!(fGroupingUsed == other->fGroupingUsed)) { | |
201 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
202 | debug("fGroupingUsed != "); | |
203 | } | |
204 | if (!(fParseIntegerOnly == other->fParseIntegerOnly)) { | |
205 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
206 | debug("fParseIntegerOnly != "); | |
207 | } | |
208 | if (!(u_strcmp(fCurrency, other->fCurrency) == 0)) { | |
209 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
210 | debug("fCurrency !="); | |
211 | } | |
212 | if (!first) { printf(" ]"); } | |
213 | #endif | |
214 | ||
b75a7d8f A |
215 | return ((this == &that) || |
216 | ((Format::operator==(that) && | |
b75a7d8f A |
217 | fMaxIntegerDigits == other->fMaxIntegerDigits && |
218 | fMinIntegerDigits == other->fMinIntegerDigits && | |
219 | fMaxFractionDigits == other->fMaxFractionDigits && | |
220 | fMinFractionDigits == other->fMinFractionDigits && | |
221 | fGroupingUsed == other->fGroupingUsed && | |
374ca955 A |
222 | fParseIntegerOnly == other->fParseIntegerOnly && |
223 | u_strcmp(fCurrency, other->fCurrency) == 0))); | |
b75a7d8f A |
224 | } |
225 | ||
374ca955 | 226 | // -------------------------------------x |
b75a7d8f A |
227 | // Formats the number object and save the format |
228 | // result in the toAppendTo string buffer. | |
229 | ||
230 | UnicodeString& | |
231 | NumberFormat::format(const Formattable& obj, | |
232 | UnicodeString& appendTo, | |
233 | FieldPosition& pos, | |
234 | UErrorCode& status) const | |
235 | { | |
236 | if (U_FAILURE(status)) return appendTo; | |
237 | ||
374ca955 A |
238 | NumberFormat* nonconst = (NumberFormat*) this; |
239 | const Formattable* n = &obj; | |
240 | ||
241 | UChar save[4]; | |
242 | UBool setCurr = FALSE; | |
243 | const UObject* o = obj.getObject(); // most commonly o==NULL | |
244 | if (o != NULL && | |
245 | o->getDynamicClassID() == CurrencyAmount::getStaticClassID()) { | |
246 | // getISOCurrency() returns a pointer to internal storage, so we | |
247 | // copy it to retain it across the call to setCurrency(). | |
248 | const CurrencyAmount* amt = (const CurrencyAmount*) o; | |
249 | const UChar* curr = amt->getISOCurrency(); | |
250 | u_strcpy(save, getCurrency()); | |
251 | setCurr = (u_strcmp(curr, save) != 0); | |
252 | if (setCurr) { | |
253 | nonconst->setCurrency(curr, status); | |
254 | } | |
255 | n = &amt->getNumber(); | |
b75a7d8f | 256 | } |
374ca955 A |
257 | |
258 | switch (n->getType()) { | |
259 | case Formattable::kDouble: | |
260 | format(n->getDouble(), appendTo, pos); | |
261 | break; | |
262 | case Formattable::kLong: | |
263 | format(n->getLong(), appendTo, pos); | |
264 | break; | |
265 | case Formattable::kInt64: | |
266 | format(n->getInt64(), appendTo, pos); | |
267 | break; | |
268 | default: | |
b75a7d8f | 269 | status = U_INVALID_FORMAT_ERROR; |
374ca955 | 270 | break; |
b75a7d8f | 271 | } |
374ca955 A |
272 | |
273 | if (setCurr) { | |
274 | UErrorCode ok = U_ZERO_ERROR; | |
275 | nonconst->setCurrency(save, ok); // always restore currency | |
276 | } | |
277 | return appendTo; | |
278 | } | |
279 | ||
280 | // ------------------------------------- | |
281 | ||
282 | UnicodeString& | |
283 | NumberFormat::format(int64_t number, | |
284 | UnicodeString& appendTo, | |
285 | FieldPosition& pos) const | |
286 | { | |
287 | // default so we don't introduce a new abstract method | |
288 | return format((int32_t)number, appendTo, pos); | |
b75a7d8f A |
289 | } |
290 | ||
291 | // ------------------------------------- | |
292 | // Parses the string and save the result object as well | |
293 | // as the final parsed position. | |
294 | ||
295 | void | |
296 | NumberFormat::parseObject(const UnicodeString& source, | |
297 | Formattable& result, | |
298 | ParsePosition& parse_pos) const | |
299 | { | |
300 | parse(source, result, parse_pos); | |
301 | } | |
302 | ||
303 | // ------------------------------------- | |
304 | // Formats a double number and save the result in a string. | |
305 | ||
306 | UnicodeString& | |
307 | NumberFormat::format(double number, UnicodeString& appendTo) const | |
308 | { | |
309 | FieldPosition pos(0); | |
310 | return format(number, appendTo, pos); | |
311 | } | |
312 | ||
313 | // ------------------------------------- | |
314 | // Formats a long number and save the result in a string. | |
315 | ||
316 | UnicodeString& | |
317 | NumberFormat::format(int32_t number, UnicodeString& appendTo) const | |
318 | { | |
319 | FieldPosition pos(0); | |
320 | return format(number, appendTo, pos); | |
321 | } | |
322 | ||
374ca955 A |
323 | // ------------------------------------- |
324 | // Formats a long number and save the result in a string. | |
325 | ||
326 | UnicodeString& | |
327 | NumberFormat::format(int64_t number, UnicodeString& appendTo) const | |
328 | { | |
329 | FieldPosition pos(0); | |
330 | return format(number, appendTo, pos); | |
331 | } | |
332 | ||
b75a7d8f A |
333 | // ------------------------------------- |
334 | // Parses the text and save the result object. If the returned | |
335 | // parse position is 0, that means the parsing failed, the status | |
336 | // code needs to be set to failure. Ignores the returned parse | |
337 | // position, otherwise. | |
338 | ||
339 | void | |
340 | NumberFormat::parse(const UnicodeString& text, | |
341 | Formattable& result, | |
342 | UErrorCode& status) const | |
343 | { | |
344 | if (U_FAILURE(status)) return; | |
345 | ||
346 | ParsePosition parsePosition(0); | |
347 | parse(text, result, parsePosition); | |
348 | if (parsePosition.getIndex() == 0) { | |
349 | status = U_INVALID_FORMAT_ERROR; | |
350 | } | |
351 | } | |
352 | ||
374ca955 A |
353 | Formattable& NumberFormat::parseCurrency(const UnicodeString& text, |
354 | Formattable& result, | |
355 | ParsePosition& pos) const { | |
356 | // Default implementation only -- subclasses should override | |
357 | int32_t start = pos.getIndex(); | |
358 | parse(text, result, pos); | |
359 | if (pos.getIndex() != start) { | |
360 | UChar curr[4]; | |
361 | UErrorCode ec = U_ZERO_ERROR; | |
362 | getEffectiveCurrency(curr, ec); | |
363 | if (U_SUCCESS(ec)) { | |
364 | Formattable n(result); | |
46f4442e A |
365 | CurrencyAmount *tempCurAmnt = new CurrencyAmount(n, curr, ec); // Use for null testing. |
366 | if (U_FAILURE(ec) || tempCurAmnt == NULL) { | |
374ca955 | 367 | pos.setIndex(start); // indicate failure |
46f4442e A |
368 | } else { |
369 | result.adoptObject(tempCurAmnt); | |
374ca955 A |
370 | } |
371 | } | |
372 | } | |
373 | return result; | |
374 | } | |
375 | ||
b75a7d8f A |
376 | // ------------------------------------- |
377 | // Sets to only parse integers. | |
378 | ||
379 | void | |
380 | NumberFormat::setParseIntegerOnly(UBool value) | |
381 | { | |
382 | fParseIntegerOnly = value; | |
383 | } | |
384 | ||
46f4442e A |
385 | // ------------------------------------- |
386 | // Sets whether or not parse is strict. | |
387 | ||
388 | void | |
389 | NumberFormat::setParseStrict(UBool value) | |
390 | { | |
391 | fParseStrict = value; | |
392 | } | |
393 | ||
b75a7d8f A |
394 | // ------------------------------------- |
395 | // Create a number style NumberFormat instance with the default locale. | |
396 | ||
374ca955 | 397 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
398 | NumberFormat::createInstance(UErrorCode& status) |
399 | { | |
400 | return createInstance(Locale::getDefault(), kNumberStyle, status); | |
401 | } | |
402 | ||
403 | // ------------------------------------- | |
404 | // Create a number style NumberFormat instance with the inLocale locale. | |
405 | ||
374ca955 | 406 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
407 | NumberFormat::createInstance(const Locale& inLocale, UErrorCode& status) |
408 | { | |
409 | return createInstance(inLocale, kNumberStyle, status); | |
410 | } | |
411 | ||
412 | // ------------------------------------- | |
413 | // Create a currency style NumberFormat instance with the default locale. | |
414 | ||
374ca955 | 415 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
416 | NumberFormat::createCurrencyInstance(UErrorCode& status) |
417 | { | |
374ca955 | 418 | return createCurrencyInstance(Locale::getDefault(), status); |
b75a7d8f A |
419 | } |
420 | ||
421 | // ------------------------------------- | |
422 | // Create a currency style NumberFormat instance with the inLocale locale. | |
423 | ||
374ca955 | 424 | NumberFormat* U_EXPORT2 |
b75a7d8f | 425 | NumberFormat::createCurrencyInstance(const Locale& inLocale, UErrorCode& status) |
374ca955 | 426 | { |
b75a7d8f A |
427 | return createInstance(inLocale, kCurrencyStyle, status); |
428 | } | |
429 | ||
430 | // ------------------------------------- | |
431 | // Create a percent style NumberFormat instance with the default locale. | |
432 | ||
374ca955 | 433 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
434 | NumberFormat::createPercentInstance(UErrorCode& status) |
435 | { | |
436 | return createInstance(Locale::getDefault(), kPercentStyle, status); | |
437 | } | |
438 | ||
439 | // ------------------------------------- | |
440 | // Create a percent style NumberFormat instance with the inLocale locale. | |
441 | ||
374ca955 | 442 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
443 | NumberFormat::createPercentInstance(const Locale& inLocale, UErrorCode& status) |
444 | { | |
445 | return createInstance(inLocale, kPercentStyle, status); | |
446 | } | |
447 | ||
448 | // ------------------------------------- | |
449 | // Create a scientific style NumberFormat instance with the default locale. | |
450 | ||
374ca955 | 451 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
452 | NumberFormat::createScientificInstance(UErrorCode& status) |
453 | { | |
454 | return createInstance(Locale::getDefault(), kScientificStyle, status); | |
455 | } | |
456 | ||
457 | // ------------------------------------- | |
458 | // Create a scientific style NumberFormat instance with the inLocale locale. | |
459 | ||
374ca955 | 460 | NumberFormat* U_EXPORT2 |
b75a7d8f A |
461 | NumberFormat::createScientificInstance(const Locale& inLocale, UErrorCode& status) |
462 | { | |
463 | return createInstance(inLocale, kScientificStyle, status); | |
464 | } | |
465 | ||
466 | // ------------------------------------- | |
467 | ||
374ca955 | 468 | const Locale* U_EXPORT2 |
b75a7d8f A |
469 | NumberFormat::getAvailableLocales(int32_t& count) |
470 | { | |
471 | return Locale::getAvailableLocales(count); | |
472 | } | |
473 | ||
474 | // ------------------------------------------ | |
475 | // | |
476 | // Registration | |
477 | // | |
478 | //------------------------------------------- | |
479 | ||
374ca955 | 480 | #if !UCONFIG_NO_SERVICE |
b75a7d8f A |
481 | static ICULocaleService* gService = NULL; |
482 | ||
374ca955 A |
483 | /** |
484 | * Release all static memory held by numberformat. | |
485 | */ | |
486 | U_CDECL_BEGIN | |
487 | static UBool U_CALLCONV numfmt_cleanup(void) { | |
488 | if (gService) { | |
489 | delete gService; | |
490 | gService = NULL; | |
491 | } | |
492 | return TRUE; | |
493 | } | |
494 | U_CDECL_END | |
495 | ||
b75a7d8f A |
496 | // ------------------------------------- |
497 | ||
498 | class ICUNumberFormatFactory : public ICUResourceBundleFactory { | |
499 | protected: | |
374ca955 A |
500 | virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* /* service */, UErrorCode& status) const { |
501 | // !!! kind is not an EStyles, need to determine how to handle this | |
502 | return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); | |
503 | } | |
b75a7d8f A |
504 | }; |
505 | ||
506 | // ------------------------------------- | |
507 | ||
508 | class NFFactory : public LocaleKeyFactory { | |
509 | private: | |
374ca955 A |
510 | NumberFormatFactory* _delegate; |
511 | Hashtable* _ids; | |
b75a7d8f A |
512 | |
513 | public: | |
374ca955 A |
514 | NFFactory(NumberFormatFactory* delegate) |
515 | : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE) | |
516 | , _delegate(delegate) | |
517 | , _ids(NULL) | |
518 | { | |
519 | } | |
b75a7d8f | 520 | |
374ca955 A |
521 | virtual ~NFFactory() |
522 | { | |
523 | delete _delegate; | |
524 | delete _ids; | |
525 | } | |
b75a7d8f | 526 | |
374ca955 A |
527 | virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const |
528 | { | |
529 | if (handlesKey(key, status)) { | |
530 | const LocaleKey& lkey = (const LocaleKey&)key; | |
531 | Locale loc; | |
532 | lkey.canonicalLocale(loc); | |
533 | int32_t kind = lkey.kind(); | |
534 | ||
535 | UObject* result = _delegate->createFormat(loc, (UNumberFormatStyle)(kind+1)); | |
536 | if (result == NULL) { | |
537 | result = service->getKey((ICUServiceKey&)key /* cast away const */, NULL, this, status); | |
538 | } | |
539 | return result; | |
540 | } | |
541 | return NULL; | |
b75a7d8f | 542 | } |
b75a7d8f A |
543 | |
544 | protected: | |
374ca955 A |
545 | /** |
546 | * Return the set of ids that this factory supports (visible or | |
547 | * otherwise). This can be called often and might need to be | |
548 | * cached if it is expensive to create. | |
549 | */ | |
550 | virtual const Hashtable* getSupportedIDs(UErrorCode& status) const | |
551 | { | |
552 | if (U_SUCCESS(status)) { | |
553 | if (!_ids) { | |
554 | int32_t count = 0; | |
555 | const UnicodeString * const idlist = _delegate->getSupportedIDs(count, status); | |
556 | ((NFFactory*)this)->_ids = new Hashtable(status); /* cast away const */ | |
557 | if (_ids) { | |
558 | for (int i = 0; i < count; ++i) { | |
559 | _ids->put(idlist[i], (void*)this, status); | |
560 | } | |
561 | } | |
562 | } | |
563 | return _ids; | |
b75a7d8f | 564 | } |
374ca955 | 565 | return NULL; |
b75a7d8f | 566 | } |
b75a7d8f A |
567 | }; |
568 | ||
569 | class ICUNumberFormatService : public ICULocaleService { | |
570 | public: | |
374ca955 | 571 | ICUNumberFormatService() |
73c04bcf | 572 | : ICULocaleService(UNICODE_STRING_SIMPLE("Number Format")) |
374ca955 A |
573 | { |
574 | UErrorCode status = U_ZERO_ERROR; | |
575 | registerFactory(new ICUNumberFormatFactory(), status); | |
576 | } | |
b75a7d8f | 577 | |
374ca955 A |
578 | virtual UObject* cloneInstance(UObject* instance) const { |
579 | return ((NumberFormat*)instance)->clone(); | |
580 | } | |
b75a7d8f | 581 | |
374ca955 A |
582 | virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /* actualID */, UErrorCode& status) const { |
583 | LocaleKey& lkey = (LocaleKey&)key; | |
584 | int32_t kind = lkey.kind(); | |
585 | Locale loc; | |
586 | lkey.currentLocale(loc); | |
587 | return NumberFormat::makeInstance(loc, (NumberFormat::EStyles)kind, status); | |
588 | } | |
b75a7d8f | 589 | |
374ca955 A |
590 | virtual UBool isDefault() const { |
591 | return countFactories() == 1; | |
592 | } | |
b75a7d8f A |
593 | }; |
594 | ||
595 | // ------------------------------------- | |
596 | ||
597 | static ICULocaleService* | |
374ca955 | 598 | getNumberFormatService(void) |
b75a7d8f A |
599 | { |
600 | UBool needInit; | |
46f4442e | 601 | UMTX_CHECK(NULL, (UBool)(gService == NULL), needInit); |
b75a7d8f A |
602 | if (needInit) { |
603 | ICULocaleService * newservice = new ICUNumberFormatService(); | |
604 | if (newservice) { | |
46f4442e | 605 | umtx_lock(NULL); |
b75a7d8f A |
606 | if (gService == NULL) { |
607 | gService = newservice; | |
608 | newservice = NULL; | |
609 | } | |
46f4442e | 610 | umtx_unlock(NULL); |
b75a7d8f A |
611 | } |
612 | if (newservice) { | |
613 | delete newservice; | |
614 | } else { | |
615 | // we won the contention, this thread can register cleanup. | |
374ca955 | 616 | ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup); |
b75a7d8f A |
617 | } |
618 | } | |
619 | return gService; | |
620 | } | |
621 | ||
622 | // ------------------------------------- | |
623 | ||
374ca955 | 624 | URegistryKey U_EXPORT2 |
b75a7d8f A |
625 | NumberFormat::registerFactory(NumberFormatFactory* toAdopt, UErrorCode& status) |
626 | { | |
374ca955 | 627 | ICULocaleService *service = getNumberFormatService(); |
b75a7d8f | 628 | if (service) { |
46f4442e A |
629 | NFFactory *tempnnf = new NFFactory(toAdopt); |
630 | if (tempnnf != NULL) { | |
631 | return service->registerFactory(tempnnf, status); | |
632 | } | |
b75a7d8f A |
633 | } |
634 | status = U_MEMORY_ALLOCATION_ERROR; | |
635 | return NULL; | |
636 | } | |
637 | ||
638 | // ------------------------------------- | |
639 | ||
374ca955 | 640 | UBool U_EXPORT2 |
b75a7d8f A |
641 | NumberFormat::unregister(URegistryKey key, UErrorCode& status) |
642 | { | |
643 | if (U_SUCCESS(status)) { | |
46f4442e A |
644 | UBool haveService; |
645 | UMTX_CHECK(NULL, gService != NULL, haveService); | |
b75a7d8f A |
646 | if (haveService) { |
647 | return gService->unregister(key, status); | |
648 | } | |
649 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
650 | } | |
651 | return FALSE; | |
652 | } | |
653 | ||
654 | // ------------------------------------- | |
374ca955 | 655 | StringEnumeration* U_EXPORT2 |
b75a7d8f A |
656 | NumberFormat::getAvailableLocales(void) |
657 | { | |
374ca955 | 658 | ICULocaleService *service = getNumberFormatService(); |
b75a7d8f A |
659 | if (service) { |
660 | return service->getAvailableLocales(); | |
661 | } | |
662 | return NULL; // no way to return error condition | |
663 | } | |
374ca955 A |
664 | #endif /* UCONFIG_NO_SERVICE */ |
665 | // ------------------------------------- | |
666 | ||
667 | NumberFormat* U_EXPORT2 | |
668 | NumberFormat::createInstance(const Locale& loc, EStyles kind, UErrorCode& status) | |
669 | { | |
670 | #if !UCONFIG_NO_SERVICE | |
46f4442e A |
671 | UBool haveService; |
672 | UMTX_CHECK(NULL, gService != NULL, haveService); | |
374ca955 A |
673 | if (haveService) { |
674 | return (NumberFormat*)gService->get(loc, kind, status); | |
675 | } | |
676 | else | |
677 | #endif | |
678 | { | |
679 | return makeInstance(loc, kind, status); | |
680 | } | |
681 | } | |
682 | ||
b75a7d8f A |
683 | |
684 | // ------------------------------------- | |
685 | // Checks if the thousand/10 thousand grouping is used in the | |
686 | // NumberFormat instance. | |
687 | ||
688 | UBool | |
689 | NumberFormat::isGroupingUsed() const | |
690 | { | |
691 | return fGroupingUsed; | |
692 | } | |
693 | ||
694 | // ------------------------------------- | |
695 | // Sets to use the thousand/10 thousand grouping in the | |
696 | // NumberFormat instance. | |
697 | ||
698 | void | |
699 | NumberFormat::setGroupingUsed(UBool newValue) | |
700 | { | |
701 | fGroupingUsed = newValue; | |
702 | } | |
703 | ||
704 | // ------------------------------------- | |
705 | // Gets the maximum number of digits for the integral part for | |
706 | // this NumberFormat instance. | |
707 | ||
708 | int32_t NumberFormat::getMaximumIntegerDigits() const | |
709 | { | |
710 | return fMaxIntegerDigits; | |
711 | } | |
712 | ||
713 | // ------------------------------------- | |
714 | // Sets the maximum number of digits for the integral part for | |
715 | // this NumberFormat instance. | |
716 | ||
717 | void | |
718 | NumberFormat::setMaximumIntegerDigits(int32_t newValue) | |
719 | { | |
73c04bcf | 720 | fMaxIntegerDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); |
b75a7d8f A |
721 | if(fMinIntegerDigits > fMaxIntegerDigits) |
722 | fMinIntegerDigits = fMaxIntegerDigits; | |
723 | } | |
724 | ||
725 | // ------------------------------------- | |
726 | // Gets the minimum number of digits for the integral part for | |
727 | // this NumberFormat instance. | |
728 | ||
729 | int32_t | |
730 | NumberFormat::getMinimumIntegerDigits() const | |
731 | { | |
732 | return fMinIntegerDigits; | |
733 | } | |
734 | ||
735 | // ------------------------------------- | |
736 | // Sets the minimum number of digits for the integral part for | |
737 | // this NumberFormat instance. | |
738 | ||
739 | void | |
740 | NumberFormat::setMinimumIntegerDigits(int32_t newValue) | |
741 | { | |
73c04bcf | 742 | fMinIntegerDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); |
b75a7d8f A |
743 | if(fMinIntegerDigits > fMaxIntegerDigits) |
744 | fMaxIntegerDigits = fMinIntegerDigits; | |
745 | } | |
746 | ||
747 | // ------------------------------------- | |
748 | // Gets the maximum number of digits for the fractional part for | |
749 | // this NumberFormat instance. | |
750 | ||
751 | int32_t | |
752 | NumberFormat::getMaximumFractionDigits() const | |
753 | { | |
754 | return fMaxFractionDigits; | |
755 | } | |
756 | ||
757 | // ------------------------------------- | |
758 | // Sets the maximum number of digits for the fractional part for | |
759 | // this NumberFormat instance. | |
760 | ||
761 | void | |
762 | NumberFormat::setMaximumFractionDigits(int32_t newValue) | |
763 | { | |
73c04bcf | 764 | fMaxFractionDigits = uprv_max(0, uprv_min(newValue, gMaxIntegerDigits)); |
b75a7d8f A |
765 | if(fMaxFractionDigits < fMinFractionDigits) |
766 | fMinFractionDigits = fMaxFractionDigits; | |
767 | } | |
768 | ||
769 | // ------------------------------------- | |
770 | // Gets the minimum number of digits for the fractional part for | |
771 | // this NumberFormat instance. | |
772 | ||
773 | int32_t | |
774 | NumberFormat::getMinimumFractionDigits() const | |
775 | { | |
776 | return fMinFractionDigits; | |
777 | } | |
778 | ||
779 | // ------------------------------------- | |
780 | // Sets the minimum number of digits for the fractional part for | |
781 | // this NumberFormat instance. | |
782 | ||
783 | void | |
784 | NumberFormat::setMinimumFractionDigits(int32_t newValue) | |
785 | { | |
73c04bcf | 786 | fMinFractionDigits = uprv_max(0, uprv_min(newValue, gMinIntegerDigits)); |
b75a7d8f A |
787 | if (fMaxFractionDigits < fMinFractionDigits) |
788 | fMaxFractionDigits = fMinFractionDigits; | |
789 | } | |
790 | ||
791 | // ------------------------------------- | |
792 | ||
374ca955 A |
793 | void NumberFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { |
794 | if (U_FAILURE(ec)) { | |
795 | return; | |
796 | } | |
b75a7d8f | 797 | if (theCurrency) { |
374ca955 A |
798 | u_strncpy(fCurrency, theCurrency, 3); |
799 | fCurrency[3] = 0; | |
b75a7d8f | 800 | } else { |
374ca955 | 801 | fCurrency[0] = 0; |
b75a7d8f A |
802 | } |
803 | } | |
804 | ||
805 | const UChar* NumberFormat::getCurrency() const { | |
374ca955 A |
806 | return fCurrency; |
807 | } | |
808 | ||
809 | void NumberFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { | |
810 | const UChar* c = getCurrency(); | |
811 | if (*c != 0) { | |
812 | u_strncpy(result, c, 3); | |
813 | result[3] = 0; | |
814 | } else { | |
815 | const char* loc = getLocaleID(ULOC_VALID_LOCALE, ec); | |
816 | if (loc == NULL) { | |
817 | loc = uloc_getDefault(); | |
818 | } | |
819 | ucurr_forLocale(loc, result, 4, &ec); | |
820 | } | |
b75a7d8f A |
821 | } |
822 | ||
823 | // ------------------------------------- | |
824 | // Creates the NumberFormat instance of the specified style (number, currency, | |
825 | // or percent) for the desired locale. | |
826 | ||
827 | NumberFormat* | |
828 | NumberFormat::makeInstance(const Locale& desiredLocale, | |
829 | EStyles style, | |
830 | UErrorCode& status) | |
831 | { | |
832 | if (U_FAILURE(status)) return NULL; | |
833 | ||
834 | if (style < 0 || style >= kStyleCount) { | |
835 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
836 | return NULL; | |
837 | } | |
838 | ||
73c04bcf A |
839 | #ifdef U_WINDOWS |
840 | char buffer[8]; | |
841 | int32_t count = desiredLocale.getKeywordValue("compat", buffer, sizeof(buffer), status); | |
842 | ||
843 | // if the locale has "@compat=host", create a host-specific NumberFormat | |
844 | if (count > 0 && uprv_strcmp(buffer, "host") == 0) { | |
845 | Win32NumberFormat *f = NULL; | |
846 | UBool curr = TRUE; | |
847 | ||
848 | switch (style) { | |
849 | case kNumberStyle: | |
850 | curr = FALSE; | |
851 | // fall-through | |
852 | ||
853 | case kCurrencyStyle: | |
854 | f = new Win32NumberFormat(desiredLocale, curr, status); | |
855 | ||
856 | if (U_SUCCESS(status)) { | |
857 | return f; | |
858 | } | |
859 | ||
860 | delete f; | |
861 | break; | |
862 | ||
863 | default: | |
864 | break; | |
865 | } | |
866 | } | |
867 | #endif | |
868 | ||
374ca955 A |
869 | NumberFormat* f = NULL; |
870 | DecimalFormatSymbols* symbolsToAdopt = NULL; | |
871 | UnicodeString pattern; | |
872 | UResourceBundle *resource = ures_open((char *)0, desiredLocale.getName(), &status); | |
873 | UResourceBundle *numberPatterns = ures_getByKey(resource, DecimalFormat::fgNumberPatterns, NULL, &status); | |
b75a7d8f | 874 | |
374ca955 | 875 | if (U_FAILURE(status)) { |
b75a7d8f A |
876 | // We don't appear to have resource data available -- use the last-resort data |
877 | status = U_USING_FALLBACK_WARNING; | |
374ca955 A |
878 | // When the data is unavailable, and locale isn't passed in, last resort data is used. |
879 | symbolsToAdopt = new DecimalFormatSymbols(status); | |
b75a7d8f A |
880 | |
881 | // Creates a DecimalFormat instance with the last resort number patterns. | |
73c04bcf | 882 | pattern.setTo(TRUE, gLastResortNumberPatterns[style], -1); |
b75a7d8f | 883 | } |
374ca955 A |
884 | else { |
885 | // If not all the styled patterns exists for the NumberFormat in this locale, | |
886 | // sets the status code to failure and returns nil. | |
73c04bcf | 887 | if (ures_getSize(numberPatterns) < (int32_t)(sizeof(gLastResortNumberPatterns)/sizeof(gLastResortNumberPatterns[0]))) { |
374ca955 A |
888 | status = U_INVALID_FORMAT_ERROR; |
889 | goto cleanup; | |
890 | } | |
b75a7d8f | 891 | |
374ca955 A |
892 | // Loads the decimal symbols of the desired locale. |
893 | symbolsToAdopt = new DecimalFormatSymbols(desiredLocale, status); | |
b75a7d8f | 894 | |
374ca955 A |
895 | int32_t patLen = 0; |
896 | const UChar *patResStr = ures_getStringByIndex(numberPatterns, (int32_t)style, &patLen, &status); | |
897 | // Creates the specified decimal format style of the desired locale. | |
898 | pattern.setTo(TRUE, patResStr, patLen); | |
b75a7d8f | 899 | } |
374ca955 A |
900 | if (U_FAILURE(status) || symbolsToAdopt == NULL) { |
901 | goto cleanup; | |
b75a7d8f | 902 | } |
374ca955 | 903 | if(style==kCurrencyStyle){ |
73c04bcf A |
904 | const UChar* currPattern = symbolsToAdopt->getCurrencyPattern(); |
905 | if(currPattern!=NULL){ | |
906 | pattern.setTo(currPattern, u_strlen(currPattern)); | |
b75a7d8f A |
907 | } |
908 | } | |
374ca955 A |
909 | f = new DecimalFormat(pattern, symbolsToAdopt, status); |
910 | if (U_FAILURE(status) || f == NULL) { | |
911 | goto cleanup; | |
b75a7d8f A |
912 | } |
913 | ||
374ca955 A |
914 | f->setLocaleIDs(ures_getLocaleByType(numberPatterns, ULOC_VALID_LOCALE, &status), |
915 | ures_getLocaleByType(numberPatterns, ULOC_ACTUAL_LOCALE, &status)); | |
916 | cleanup: | |
917 | ures_close(numberPatterns); | |
918 | ures_close(resource); | |
919 | if (U_FAILURE(status)) { | |
920 | /* If f exists, then it will delete the symbols */ | |
921 | if (f==NULL) { | |
922 | delete symbolsToAdopt; | |
923 | } | |
924 | else { | |
925 | delete f; | |
926 | } | |
b75a7d8f A |
927 | return NULL; |
928 | } | |
374ca955 A |
929 | if (f == NULL || symbolsToAdopt == NULL) { |
930 | status = U_MEMORY_ALLOCATION_ERROR; | |
b75a7d8f A |
931 | f = NULL; |
932 | } | |
933 | return f; | |
934 | } | |
935 | ||
936 | U_NAMESPACE_END | |
937 | ||
b75a7d8f A |
938 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
939 | ||
940 | //eof |