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