]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ******************************************************************************* | |
51004dcb | 3 | * Copyright (C) 1997-2013, International Business Machines Corporation and * |
b75a7d8f A |
4 | * others. All Rights Reserved. * |
5 | ******************************************************************************* | |
6 | * | |
7 | * File DECIMFMT.CPP | |
8 | * | |
9 | * Modification History: | |
10 | * | |
11 | * Date Name Description | |
12 | * 02/19/97 aliu Converted from java. | |
13 | * 03/20/97 clhuang Implemented with new APIs. | |
14 | * 03/31/97 aliu Moved isLONG_MIN to DigitList, and fixed it. | |
15 | * 04/3/97 aliu Rewrote parsing and formatting completely, and | |
16 | * cleaned up and debugged. Actually works now. | |
17 | * Implemented NAN and INF handling, for both parsing | |
18 | * and formatting. Extensive testing & debugging. | |
19 | * 04/10/97 aliu Modified to compile on AIX. | |
20 | * 04/16/97 aliu Rewrote to use DigitList, which has been resurrected. | |
21 | * Changed DigitCount to int per code review. | |
22 | * 07/09/97 helena Made ParsePosition into a class. | |
23 | * 08/26/97 aliu Extensive changes to applyPattern; completely | |
24 | * rewritten from the Java. | |
25 | * 09/09/97 aliu Ported over support for exponential formats. | |
26 | * 07/20/98 stephen JDK 1.2 sync up. | |
27 | * Various instances of '0' replaced with 'NULL' | |
28 | * Check for grouping size in subFormat() | |
29 | * Brought subParse() in line with Java 1.2 | |
30 | * Added method appendAffix() | |
31 | * 08/24/1998 srl Removed Mutex calls. This is not a thread safe class! | |
32 | * 02/22/99 stephen Removed character literals for EBCDIC safety | |
33 | * 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes | |
34 | * 06/28/99 stephen Fixed bugs in toPattern(). | |
729e4ab9 | 35 | * 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, |
b75a7d8f A |
36 | * fPadPosition |
37 | ******************************************************************************** | |
38 | */ | |
729e4ab9 | 39 | |
b75a7d8f A |
40 | #include "unicode/utypes.h" |
41 | ||
42 | #if !UCONFIG_NO_FORMATTING | |
43 | ||
729e4ab9 | 44 | #include "fphdlimp.h" |
b75a7d8f A |
45 | #include "unicode/decimfmt.h" |
46 | #include "unicode/choicfmt.h" | |
47 | #include "unicode/ucurr.h" | |
48 | #include "unicode/ustring.h" | |
49 | #include "unicode/dcfmtsym.h" | |
374ca955 | 50 | #include "unicode/ures.h" |
b75a7d8f | 51 | #include "unicode/uchar.h" |
46f4442e | 52 | #include "unicode/uniset.h" |
374ca955 | 53 | #include "unicode/curramt.h" |
729e4ab9 A |
54 | #include "unicode/currpinf.h" |
55 | #include "unicode/plurrule.h" | |
4388f060 A |
56 | #include "unicode/utf16.h" |
57 | #include "unicode/numsys.h" | |
58 | #include "unicode/localpointer.h" | |
59 | #include "uresimp.h" | |
374ca955 | 60 | #include "ucurrimp.h" |
729e4ab9 A |
61 | #include "charstr.h" |
62 | #include "cmemory.h" | |
4388f060 | 63 | #include "patternprops.h" |
b75a7d8f | 64 | #include "digitlst.h" |
b75a7d8f A |
65 | #include "cstring.h" |
66 | #include "umutex.h" | |
67 | #include "uassert.h" | |
374ca955 | 68 | #include "putilimp.h" |
729e4ab9 A |
69 | #include <math.h> |
70 | #include "hash.h" | |
4388f060 | 71 | #include "decfmtst.h" |
51004dcb | 72 | #include "dcfmtimp.h" |
729e4ab9 | 73 | |
51004dcb A |
74 | /* |
75 | * On certain platforms, round is a macro defined in math.h | |
76 | * This undefine is to avoid conflict between the macro and | |
77 | * the function defined below. | |
78 | */ | |
79 | #ifdef round | |
80 | #undef round | |
81 | #endif | |
b75a7d8f A |
82 | |
83 | U_NAMESPACE_BEGIN | |
84 | ||
51004dcb A |
85 | |
86 | /* == Fastpath calculation. == | |
87 | */ | |
88 | #if UCONFIG_FORMAT_FASTPATHS_49 | |
89 | inline DecimalFormatInternal& internalData(uint8_t *reserved) { | |
90 | return *reinterpret_cast<DecimalFormatInternal*>(reserved); | |
91 | } | |
92 | inline const DecimalFormatInternal& internalData(const uint8_t *reserved) { | |
93 | return *reinterpret_cast<const DecimalFormatInternal*>(reserved); | |
94 | } | |
95 | #else | |
96 | #endif | |
97 | ||
729e4ab9 A |
98 | /* For currency parsing purose, |
99 | * Need to remember all prefix patterns and suffix patterns of | |
100 | * every currency format pattern, | |
101 | * including the pattern of default currecny style | |
102 | * and plural currency style. And the patterns are set through applyPattern. | |
103 | */ | |
104 | struct AffixPatternsForCurrency : public UMemory { | |
105 | // negative prefix pattern | |
106 | UnicodeString negPrefixPatternForCurrency; | |
107 | // negative suffix pattern | |
108 | UnicodeString negSuffixPatternForCurrency; | |
109 | // positive prefix pattern | |
110 | UnicodeString posPrefixPatternForCurrency; | |
111 | // positive suffix pattern | |
112 | UnicodeString posSuffixPatternForCurrency; | |
113 | int8_t patternType; | |
114 | ||
115 | AffixPatternsForCurrency(const UnicodeString& negPrefix, | |
116 | const UnicodeString& negSuffix, | |
117 | const UnicodeString& posPrefix, | |
118 | const UnicodeString& posSuffix, | |
119 | int8_t type) { | |
120 | negPrefixPatternForCurrency = negPrefix; | |
121 | negSuffixPatternForCurrency = negSuffix; | |
122 | posPrefixPatternForCurrency = posPrefix; | |
123 | posSuffixPatternForCurrency = posSuffix; | |
124 | patternType = type; | |
125 | } | |
126 | }; | |
127 | ||
128 | /* affix for currency formatting when the currency sign in the pattern | |
129 | * equals to 3, such as the pattern contains 3 currency sign or | |
130 | * the formatter style is currency plural format style. | |
131 | */ | |
132 | struct AffixesForCurrency : public UMemory { | |
133 | // negative prefix | |
134 | UnicodeString negPrefixForCurrency; | |
135 | // negative suffix | |
136 | UnicodeString negSuffixForCurrency; | |
137 | // positive prefix | |
138 | UnicodeString posPrefixForCurrency; | |
139 | // positive suffix | |
140 | UnicodeString posSuffixForCurrency; | |
141 | ||
142 | int32_t formatWidth; | |
143 | ||
144 | AffixesForCurrency(const UnicodeString& negPrefix, | |
145 | const UnicodeString& negSuffix, | |
146 | const UnicodeString& posPrefix, | |
147 | const UnicodeString& posSuffix) { | |
148 | negPrefixForCurrency = negPrefix; | |
149 | negSuffixForCurrency = negSuffix; | |
150 | posPrefixForCurrency = posPrefix; | |
151 | posSuffixForCurrency = posSuffix; | |
152 | } | |
153 | }; | |
154 | ||
155 | U_CDECL_BEGIN | |
156 | ||
157 | /** | |
158 | * @internal ICU 4.2 | |
159 | */ | |
160 | static UBool U_CALLCONV decimfmtAffixValueComparator(UHashTok val1, UHashTok val2); | |
161 | ||
162 | /** | |
163 | * @internal ICU 4.2 | |
164 | */ | |
165 | static UBool U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2); | |
166 | ||
167 | ||
168 | static UBool | |
169 | U_CALLCONV decimfmtAffixValueComparator(UHashTok val1, UHashTok val2) { | |
170 | const AffixesForCurrency* affix_1 = | |
171 | (AffixesForCurrency*)val1.pointer; | |
172 | const AffixesForCurrency* affix_2 = | |
173 | (AffixesForCurrency*)val2.pointer; | |
174 | return affix_1->negPrefixForCurrency == affix_2->negPrefixForCurrency && | |
175 | affix_1->negSuffixForCurrency == affix_2->negSuffixForCurrency && | |
176 | affix_1->posPrefixForCurrency == affix_2->posPrefixForCurrency && | |
177 | affix_1->posSuffixForCurrency == affix_2->posSuffixForCurrency; | |
178 | } | |
179 | ||
180 | ||
181 | static UBool | |
182 | U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2) { | |
183 | const AffixPatternsForCurrency* affix_1 = | |
184 | (AffixPatternsForCurrency*)val1.pointer; | |
185 | const AffixPatternsForCurrency* affix_2 = | |
186 | (AffixPatternsForCurrency*)val2.pointer; | |
187 | return affix_1->negPrefixPatternForCurrency == | |
188 | affix_2->negPrefixPatternForCurrency && | |
189 | affix_1->negSuffixPatternForCurrency == | |
190 | affix_2->negSuffixPatternForCurrency && | |
191 | affix_1->posPrefixPatternForCurrency == | |
192 | affix_2->posPrefixPatternForCurrency && | |
193 | affix_1->posSuffixPatternForCurrency == | |
194 | affix_2->posSuffixPatternForCurrency && | |
195 | affix_1->patternType == affix_2->patternType; | |
196 | } | |
197 | ||
198 | U_CDECL_END | |
199 | ||
b75a7d8f A |
200 | #ifdef FMT_DEBUG |
201 | #include <stdio.h> | |
51004dcb | 202 | static void _debugout(const char *f, int l, const UnicodeString& s) { |
b75a7d8f A |
203 | char buf[2000]; |
204 | s.extract((int32_t) 0, s.length(), buf); | |
51004dcb | 205 | printf("%s:%d: %s\n", f,l, buf); |
b75a7d8f | 206 | } |
51004dcb A |
207 | #define debugout(x) _debugout(__FILE__,__LINE__,x) |
208 | #define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x); | |
209 | static const UnicodeString dbg_null("<NULL>",""); | |
210 | #define DEREFSTR(x) ((x!=NULL)?(*x):(dbg_null)) | |
b75a7d8f A |
211 | #else |
212 | #define debugout(x) | |
213 | #define debug(x) | |
214 | #endif | |
215 | ||
4388f060 | 216 | |
46f4442e | 217 | |
b75a7d8f A |
218 | // ***************************************************************************** |
219 | // class DecimalFormat | |
220 | // ***************************************************************************** | |
221 | ||
374ca955 | 222 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) |
b75a7d8f A |
223 | |
224 | // Constants for characters used in programmatic (unlocalized) patterns. | |
374ca955 A |
225 | #define kPatternZeroDigit ((UChar)0x0030) /*'0'*/ |
226 | #define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/ | |
227 | #define kPatternGroupingSeparator ((UChar)0x002C) /*','*/ | |
228 | #define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/ | |
229 | #define kPatternPerMill ((UChar)0x2030) | |
230 | #define kPatternPercent ((UChar)0x0025) /*'%'*/ | |
231 | #define kPatternDigit ((UChar)0x0023) /*'#'*/ | |
232 | #define kPatternSeparator ((UChar)0x003B) /*';'*/ | |
233 | #define kPatternExponent ((UChar)0x0045) /*'E'*/ | |
234 | #define kPatternPlus ((UChar)0x002B) /*'+'*/ | |
235 | #define kPatternMinus ((UChar)0x002D) /*'-'*/ | |
236 | #define kPatternPadEscape ((UChar)0x002A) /*'*'*/ | |
237 | #define kQuote ((UChar)0x0027) /*'\''*/ | |
238 | /** | |
239 | * The CURRENCY_SIGN is the standard Unicode symbol for currency. It | |
240 | * is used in patterns and substitued with either the currency symbol, | |
241 | * or if it is doubled, with the international currency symbol. If the | |
242 | * CURRENCY_SIGN is seen in a pattern, then the decimal separator is | |
243 | * replaced with the monetary decimal separator. | |
244 | */ | |
245 | #define kCurrencySign ((UChar)0x00A4) | |
246 | #define kDefaultPad ((UChar)0x0020) /* */ | |
b75a7d8f A |
247 | |
248 | const int32_t DecimalFormat::kDoubleIntegerDigits = 309; | |
249 | const int32_t DecimalFormat::kDoubleFractionDigits = 340; | |
250 | ||
374ca955 A |
251 | const int32_t DecimalFormat::kMaxScientificIntegerDigits = 8; |
252 | ||
b75a7d8f A |
253 | /** |
254 | * These are the tags we expect to see in normal resource bundle files associated | |
255 | * with a locale. | |
256 | */ | |
729e4ab9 A |
257 | const char DecimalFormat::fgNumberPatterns[]="NumberPatterns"; // Deprecated - not used |
258 | static const char fgNumberElements[]="NumberElements"; | |
259 | static const char fgLatn[]="latn"; | |
260 | static const char fgPatterns[]="patterns"; | |
261 | static const char fgDecimalFormat[]="decimalFormat"; | |
262 | static const char fgCurrencyFormat[]="currencyFormat"; | |
263 | static const UChar fgTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; | |
b75a7d8f | 264 | |
374ca955 A |
265 | inline int32_t _min(int32_t a, int32_t b) { return (a<b) ? a : b; } |
266 | inline int32_t _max(int32_t a, int32_t b) { return (a<b) ? b : a; } | |
b75a7d8f A |
267 | |
268 | //------------------------------------------------------------------------------ | |
269 | // Constructs a DecimalFormat instance in the default locale. | |
729e4ab9 A |
270 | |
271 | DecimalFormat::DecimalFormat(UErrorCode& status) { | |
51004dcb | 272 | init(status); |
b75a7d8f A |
273 | UParseError parseError; |
274 | construct(status, parseError); | |
275 | } | |
276 | ||
277 | //------------------------------------------------------------------------------ | |
278 | // Constructs a DecimalFormat instance with the specified number format | |
279 | // pattern in the default locale. | |
280 | ||
281 | DecimalFormat::DecimalFormat(const UnicodeString& pattern, | |
729e4ab9 | 282 | UErrorCode& status) { |
51004dcb | 283 | init(status); |
b75a7d8f A |
284 | UParseError parseError; |
285 | construct(status, parseError, &pattern); | |
286 | } | |
287 | ||
288 | //------------------------------------------------------------------------------ | |
289 | // Constructs a DecimalFormat instance with the specified number format | |
290 | // pattern and the number format symbols in the default locale. The | |
291 | // created instance owns the symbols. | |
292 | ||
293 | DecimalFormat::DecimalFormat(const UnicodeString& pattern, | |
294 | DecimalFormatSymbols* symbolsToAdopt, | |
729e4ab9 | 295 | UErrorCode& status) { |
51004dcb | 296 | init(status); |
b75a7d8f A |
297 | UParseError parseError; |
298 | if (symbolsToAdopt == NULL) | |
299 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
300 | construct(status, parseError, &pattern, symbolsToAdopt); | |
301 | } | |
729e4ab9 | 302 | |
b75a7d8f A |
303 | DecimalFormat::DecimalFormat( const UnicodeString& pattern, |
304 | DecimalFormatSymbols* symbolsToAdopt, | |
305 | UParseError& parseErr, | |
729e4ab9 | 306 | UErrorCode& status) { |
51004dcb | 307 | init(status); |
b75a7d8f A |
308 | if (symbolsToAdopt == NULL) |
309 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
310 | construct(status,parseErr, &pattern, symbolsToAdopt); | |
311 | } | |
729e4ab9 | 312 | |
b75a7d8f A |
313 | //------------------------------------------------------------------------------ |
314 | // Constructs a DecimalFormat instance with the specified number format | |
315 | // pattern and the number format symbols in the default locale. The | |
316 | // created instance owns the clone of the symbols. | |
729e4ab9 | 317 | |
b75a7d8f A |
318 | DecimalFormat::DecimalFormat(const UnicodeString& pattern, |
319 | const DecimalFormatSymbols& symbols, | |
729e4ab9 | 320 | UErrorCode& status) { |
51004dcb | 321 | init(status); |
b75a7d8f A |
322 | UParseError parseError; |
323 | construct(status, parseError, &pattern, new DecimalFormatSymbols(symbols)); | |
324 | } | |
325 | ||
729e4ab9 A |
326 | //------------------------------------------------------------------------------ |
327 | // Constructs a DecimalFormat instance with the specified number format | |
328 | // pattern, the number format symbols, and the number format style. | |
329 | // The created instance owns the clone of the symbols. | |
330 | ||
331 | DecimalFormat::DecimalFormat(const UnicodeString& pattern, | |
332 | DecimalFormatSymbols* symbolsToAdopt, | |
4388f060 | 333 | UNumberFormatStyle style, |
729e4ab9 | 334 | UErrorCode& status) { |
51004dcb | 335 | init(status); |
729e4ab9 A |
336 | fStyle = style; |
337 | UParseError parseError; | |
338 | construct(status, parseError, &pattern, symbolsToAdopt); | |
339 | } | |
340 | ||
341 | //----------------------------------------------------------------------------- | |
342 | // Common DecimalFormat initialization. | |
343 | // Put all fields of an uninitialized object into a known state. | |
344 | // Common code, shared by all constructors. | |
345 | void | |
51004dcb | 346 | DecimalFormat::init(UErrorCode &status) { |
729e4ab9 A |
347 | fPosPrefixPattern = 0; |
348 | fPosSuffixPattern = 0; | |
349 | fNegPrefixPattern = 0; | |
350 | fNegSuffixPattern = 0; | |
351 | fCurrencyChoice = 0; | |
352 | fMultiplier = NULL; | |
51004dcb | 353 | fScale = 0; |
729e4ab9 A |
354 | fGroupingSize = 0; |
355 | fGroupingSize2 = 0; | |
356 | fDecimalSeparatorAlwaysShown = FALSE; | |
357 | fSymbols = NULL; | |
358 | fUseSignificantDigits = FALSE; | |
359 | fMinSignificantDigits = 1; | |
360 | fMaxSignificantDigits = 6; | |
361 | fUseExponentialNotation = FALSE; | |
362 | fMinExponentDigits = 0; | |
363 | fExponentSignAlwaysShown = FALSE; | |
51004dcb | 364 | fBoolFlags.clear(); |
729e4ab9 A |
365 | fRoundingIncrement = 0; |
366 | fRoundingMode = kRoundHalfEven; | |
367 | fPad = 0; | |
368 | fFormatWidth = 0; | |
369 | fPadPosition = kPadBeforePrefix; | |
4388f060 | 370 | fStyle = UNUM_DECIMAL; |
729e4ab9 A |
371 | fCurrencySignCount = 0; |
372 | fAffixPatternsForCurrency = NULL; | |
373 | fAffixesForCurrency = NULL; | |
374 | fPluralAffixesForCurrency = NULL; | |
375 | fCurrencyPluralInfo = NULL; | |
51004dcb A |
376 | #if UCONFIG_HAVE_PARSEALLINPUT |
377 | fParseAllInput = UNUM_MAYBE; | |
378 | #endif | |
379 | ||
380 | #if UCONFIG_FORMAT_FASTPATHS_49 | |
381 | DecimalFormatInternal &data = internalData(fReserved); | |
382 | data.fFastFormatStatus=kFastpathUNKNOWN; // don't try to calculate the fastpath until later. | |
383 | data.fFastParseStatus=kFastpathUNKNOWN; // don't try to calculate the fastpath until later. | |
384 | #endif | |
385 | // only do this once per obj. | |
386 | DecimalFormatStaticSets::initSets(&status); | |
729e4ab9 A |
387 | } |
388 | ||
b75a7d8f A |
389 | //------------------------------------------------------------------------------ |
390 | // Constructs a DecimalFormat instance with the specified number format | |
391 | // pattern and the number format symbols in the desired locale. The | |
392 | // created instance owns the symbols. | |
393 | ||
394 | void | |
395 | DecimalFormat::construct(UErrorCode& status, | |
396 | UParseError& parseErr, | |
397 | const UnicodeString* pattern, | |
398 | DecimalFormatSymbols* symbolsToAdopt) | |
399 | { | |
400 | fSymbols = symbolsToAdopt; // Do this BEFORE aborting on status failure!!! | |
b75a7d8f | 401 | fRoundingIncrement = NULL; |
b75a7d8f A |
402 | fRoundingMode = kRoundHalfEven; |
403 | fPad = kPatternPadEscape; | |
404 | fPadPosition = kPadBeforePrefix; | |
405 | if (U_FAILURE(status)) | |
406 | return; | |
407 | ||
408 | fPosPrefixPattern = fPosSuffixPattern = NULL; | |
409 | fNegPrefixPattern = fNegSuffixPattern = NULL; | |
729e4ab9 | 410 | setMultiplier(1); |
b75a7d8f A |
411 | fGroupingSize = 3; |
412 | fGroupingSize2 = 0; | |
413 | fDecimalSeparatorAlwaysShown = FALSE; | |
b75a7d8f A |
414 | fUseExponentialNotation = FALSE; |
415 | fMinExponentDigits = 0; | |
416 | ||
417 | if (fSymbols == NULL) | |
418 | { | |
419 | fSymbols = new DecimalFormatSymbols(Locale::getDefault(), status); | |
420 | /* test for NULL */ | |
421 | if (fSymbols == 0) { | |
422 | status = U_MEMORY_ALLOCATION_ERROR; | |
423 | return; | |
424 | } | |
425 | } | |
4388f060 A |
426 | UErrorCode nsStatus = U_ZERO_ERROR; |
427 | NumberingSystem *ns = NumberingSystem::createInstance(nsStatus); | |
428 | if (U_FAILURE(nsStatus)) { | |
429 | status = nsStatus; | |
430 | return; | |
431 | } | |
b75a7d8f A |
432 | |
433 | UnicodeString str; | |
434 | // Uses the default locale's number format pattern if there isn't | |
435 | // one specified. | |
436 | if (pattern == NULL) | |
437 | { | |
374ca955 | 438 | int32_t len = 0; |
4388f060 A |
439 | UResourceBundle *top = ures_open(NULL, Locale::getDefault().getName(), &status); |
440 | ||
441 | UResourceBundle *resource = ures_getByKeyWithFallback(top, fgNumberElements, NULL, &status); | |
442 | resource = ures_getByKeyWithFallback(resource, ns->getName(), resource, &status); | |
443 | resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); | |
444 | const UChar *resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); | |
445 | if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(fgLatn,ns->getName())) { | |
446 | status = U_ZERO_ERROR; | |
447 | resource = ures_getByKeyWithFallback(top, fgNumberElements, resource, &status); | |
448 | resource = ures_getByKeyWithFallback(resource, fgLatn, resource, &status); | |
449 | resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); | |
450 | resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); | |
451 | } | |
374ca955 | 452 | str.setTo(TRUE, resStr, len); |
b75a7d8f | 453 | pattern = &str; |
374ca955 | 454 | ures_close(resource); |
4388f060 | 455 | ures_close(top); |
b75a7d8f A |
456 | } |
457 | ||
4388f060 A |
458 | delete ns; |
459 | ||
b75a7d8f A |
460 | if (U_FAILURE(status)) |
461 | { | |
462 | return; | |
463 | } | |
464 | ||
374ca955 A |
465 | if (pattern->indexOf((UChar)kCurrencySign) >= 0) { |
466 | // If it looks like we are going to use a currency pattern | |
467 | // then do the time consuming lookup. | |
73c04bcf | 468 | setCurrencyForSymbols(); |
b75a7d8f | 469 | } else { |
729e4ab9 A |
470 | setCurrencyInternally(NULL, status); |
471 | } | |
472 | ||
473 | const UnicodeString* patternUsed; | |
474 | UnicodeString currencyPluralPatternForOther; | |
475 | // apply pattern | |
4388f060 | 476 | if (fStyle == UNUM_CURRENCY_PLURAL) { |
729e4ab9 A |
477 | fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); |
478 | if (U_FAILURE(status)) { | |
479 | return; | |
480 | } | |
481 | ||
482 | // the pattern used in format is not fixed until formatting, | |
483 | // in which, the number is known and | |
484 | // will be used to pick the right pattern based on plural count. | |
485 | // Here, set the pattern as the pattern of plural count == "other". | |
486 | // For most locale, the patterns are probably the same for all | |
487 | // plural count. If not, the right pattern need to be re-applied | |
488 | // during format. | |
4388f060 | 489 | fCurrencyPluralInfo->getCurrencyPluralPattern(UNICODE_STRING("other", 5), currencyPluralPatternForOther); |
729e4ab9 A |
490 | patternUsed = ¤cyPluralPatternForOther; |
491 | // TODO: not needed? | |
492 | setCurrencyForSymbols(); | |
493 | ||
494 | } else { | |
495 | patternUsed = pattern; | |
496 | } | |
497 | ||
498 | if (patternUsed->indexOf(kCurrencySign) != -1) { | |
499 | // initialize for currency, not only for plural format, | |
500 | // but also for mix parsing | |
501 | if (fCurrencyPluralInfo == NULL) { | |
502 | fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); | |
503 | if (U_FAILURE(status)) { | |
504 | return; | |
505 | } | |
506 | } | |
507 | // need it for mix parsing | |
508 | setupCurrencyAffixPatterns(status); | |
509 | // expanded affixes for plural names | |
4388f060 | 510 | if (patternUsed->indexOf(fgTripleCurrencySign, 3, 0) != -1) { |
729e4ab9 A |
511 | setupCurrencyAffixes(*patternUsed, TRUE, TRUE, status); |
512 | } | |
513 | } | |
514 | ||
515 | applyPatternWithoutExpandAffix(*patternUsed,FALSE, parseErr, status); | |
516 | ||
517 | // expand affixes | |
518 | if (fCurrencySignCount != fgCurrencySignCountInPluralFormat) { | |
519 | expandAffixAdjustWidth(NULL); | |
b75a7d8f A |
520 | } |
521 | ||
73c04bcf A |
522 | // If it was a currency format, apply the appropriate rounding by |
523 | // resetting the currency. NOTE: this copies fCurrency on top of itself. | |
729e4ab9 A |
524 | if (fCurrencySignCount > fgCurrencySignCountZero) { |
525 | setCurrencyInternally(getCurrency(), status); | |
b75a7d8f | 526 | } |
51004dcb A |
527 | #if UCONFIG_FORMAT_FASTPATHS_49 |
528 | DecimalFormatInternal &data = internalData(fReserved); | |
529 | data.fFastFormatStatus = kFastpathNO; // allow it to be calculated | |
530 | data.fFastParseStatus = kFastpathNO; // allow it to be calculated | |
531 | handleChanged(); | |
532 | #endif | |
b75a7d8f A |
533 | } |
534 | ||
729e4ab9 A |
535 | |
536 | void | |
537 | DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) { | |
538 | if (U_FAILURE(status)) { | |
539 | return; | |
540 | } | |
541 | UParseError parseErr; | |
542 | fAffixPatternsForCurrency = initHashForAffixPattern(status); | |
543 | if (U_FAILURE(status)) { | |
544 | return; | |
545 | } | |
546 | ||
4388f060 A |
547 | NumberingSystem *ns = NumberingSystem::createInstance(fSymbols->getLocale(),status); |
548 | if (U_FAILURE(status)) { | |
549 | return; | |
550 | } | |
551 | ||
729e4ab9 A |
552 | // Save the default currency patterns of this locale. |
553 | // Here, chose onlyApplyPatternWithoutExpandAffix without | |
554 | // expanding the affix patterns into affixes. | |
555 | UnicodeString currencyPattern; | |
556 | UErrorCode error = U_ZERO_ERROR; | |
557 | ||
558 | UResourceBundle *resource = ures_open(NULL, fSymbols->getLocale().getName(), &error); | |
4388f060 A |
559 | UResourceBundle *numElements = ures_getByKeyWithFallback(resource, fgNumberElements, NULL, &error); |
560 | resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &error); | |
561 | resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); | |
729e4ab9 | 562 | int32_t patLen = 0; |
4388f060 A |
563 | const UChar *patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); |
564 | if ( error == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),fgLatn)) { | |
565 | error = U_ZERO_ERROR; | |
566 | resource = ures_getByKeyWithFallback(numElements, fgLatn, resource, &error); | |
567 | resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); | |
568 | patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); | |
569 | } | |
570 | ures_close(numElements); | |
729e4ab9 | 571 | ures_close(resource); |
4388f060 | 572 | delete ns; |
729e4ab9 A |
573 | |
574 | if (U_SUCCESS(error)) { | |
575 | applyPatternWithoutExpandAffix(UnicodeString(patResStr, patLen), false, | |
576 | parseErr, status); | |
577 | AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( | |
578 | *fNegPrefixPattern, | |
579 | *fNegSuffixPattern, | |
580 | *fPosPrefixPattern, | |
581 | *fPosSuffixPattern, | |
582 | UCURR_SYMBOL_NAME); | |
4388f060 | 583 | fAffixPatternsForCurrency->put(UNICODE_STRING("default", 7), affixPtn, status); |
729e4ab9 A |
584 | } |
585 | ||
586 | // save the unique currency plural patterns of this locale. | |
587 | Hashtable* pluralPtn = fCurrencyPluralInfo->fPluralCountToCurrencyUnitPattern; | |
588 | const UHashElement* element = NULL; | |
589 | int32_t pos = -1; | |
590 | Hashtable pluralPatternSet; | |
591 | while ((element = pluralPtn->nextElement(pos)) != NULL) { | |
592 | const UHashTok valueTok = element->value; | |
593 | const UnicodeString* value = (UnicodeString*)valueTok.pointer; | |
594 | const UHashTok keyTok = element->key; | |
595 | const UnicodeString* key = (UnicodeString*)keyTok.pointer; | |
596 | if (pluralPatternSet.geti(*value) != 1) { | |
597 | pluralPatternSet.puti(*value, 1, status); | |
598 | applyPatternWithoutExpandAffix(*value, false, parseErr, status); | |
599 | AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( | |
600 | *fNegPrefixPattern, | |
601 | *fNegSuffixPattern, | |
602 | *fPosPrefixPattern, | |
603 | *fPosSuffixPattern, | |
604 | UCURR_LONG_NAME); | |
605 | fAffixPatternsForCurrency->put(*key, affixPtn, status); | |
606 | } | |
607 | } | |
608 | } | |
609 | ||
610 | ||
611 | void | |
612 | DecimalFormat::setupCurrencyAffixes(const UnicodeString& pattern, | |
613 | UBool setupForCurrentPattern, | |
614 | UBool setupForPluralPattern, | |
615 | UErrorCode& status) { | |
616 | if (U_FAILURE(status)) { | |
617 | return; | |
618 | } | |
619 | UParseError parseErr; | |
620 | if (setupForCurrentPattern) { | |
621 | if (fAffixesForCurrency) { | |
622 | deleteHashForAffix(fAffixesForCurrency); | |
623 | } | |
624 | fAffixesForCurrency = initHashForAffix(status); | |
625 | if (U_SUCCESS(status)) { | |
626 | applyPatternWithoutExpandAffix(pattern, false, parseErr, status); | |
627 | const PluralRules* pluralRules = fCurrencyPluralInfo->getPluralRules(); | |
628 | StringEnumeration* keywords = pluralRules->getKeywords(status); | |
629 | if (U_SUCCESS(status)) { | |
4388f060 A |
630 | const UnicodeString* pluralCount; |
631 | while ((pluralCount = keywords->snext(status)) != NULL) { | |
729e4ab9 | 632 | if ( U_SUCCESS(status) ) { |
4388f060 | 633 | expandAffixAdjustWidth(pluralCount); |
729e4ab9 A |
634 | AffixesForCurrency* affix = new AffixesForCurrency( |
635 | fNegativePrefix, fNegativeSuffix, fPositivePrefix, fPositiveSuffix); | |
4388f060 | 636 | fAffixesForCurrency->put(*pluralCount, affix, status); |
729e4ab9 A |
637 | } |
638 | } | |
639 | } | |
640 | delete keywords; | |
641 | } | |
642 | } | |
643 | ||
644 | if (U_FAILURE(status)) { | |
645 | return; | |
646 | } | |
647 | ||
648 | if (setupForPluralPattern) { | |
649 | if (fPluralAffixesForCurrency) { | |
650 | deleteHashForAffix(fPluralAffixesForCurrency); | |
651 | } | |
652 | fPluralAffixesForCurrency = initHashForAffix(status); | |
653 | if (U_SUCCESS(status)) { | |
654 | const PluralRules* pluralRules = fCurrencyPluralInfo->getPluralRules(); | |
655 | StringEnumeration* keywords = pluralRules->getKeywords(status); | |
656 | if (U_SUCCESS(status)) { | |
4388f060 A |
657 | const UnicodeString* pluralCount; |
658 | while ((pluralCount = keywords->snext(status)) != NULL) { | |
729e4ab9 | 659 | if ( U_SUCCESS(status) ) { |
729e4ab9 | 660 | UnicodeString ptn; |
4388f060 A |
661 | fCurrencyPluralInfo->getCurrencyPluralPattern(*pluralCount, ptn); |
662 | applyPatternInternally(*pluralCount, ptn, false, parseErr, status); | |
729e4ab9 A |
663 | AffixesForCurrency* affix = new AffixesForCurrency( |
664 | fNegativePrefix, fNegativeSuffix, fPositivePrefix, fPositiveSuffix); | |
4388f060 | 665 | fPluralAffixesForCurrency->put(*pluralCount, affix, status); |
729e4ab9 A |
666 | } |
667 | } | |
668 | } | |
669 | delete keywords; | |
670 | } | |
671 | } | |
672 | } | |
673 | ||
674 | ||
b75a7d8f A |
675 | //------------------------------------------------------------------------------ |
676 | ||
677 | DecimalFormat::~DecimalFormat() | |
678 | { | |
b75a7d8f A |
679 | delete fPosPrefixPattern; |
680 | delete fPosSuffixPattern; | |
681 | delete fNegPrefixPattern; | |
682 | delete fNegSuffixPattern; | |
683 | delete fCurrencyChoice; | |
729e4ab9 | 684 | delete fMultiplier; |
b75a7d8f A |
685 | delete fSymbols; |
686 | delete fRoundingIncrement; | |
729e4ab9 A |
687 | deleteHashForAffixPattern(); |
688 | deleteHashForAffix(fAffixesForCurrency); | |
689 | deleteHashForAffix(fPluralAffixesForCurrency); | |
690 | delete fCurrencyPluralInfo; | |
b75a7d8f A |
691 | } |
692 | ||
693 | //------------------------------------------------------------------------------ | |
694 | // copy constructor | |
695 | ||
729e4ab9 A |
696 | DecimalFormat::DecimalFormat(const DecimalFormat &source) : |
697 | NumberFormat(source) { | |
51004dcb A |
698 | UErrorCode status = U_ZERO_ERROR; |
699 | init(status); // if this fails, 'source' isn't initialized properly either. | |
b75a7d8f A |
700 | *this = source; |
701 | } | |
702 | ||
703 | //------------------------------------------------------------------------------ | |
704 | // assignment operator | |
b75a7d8f | 705 | |
51004dcb A |
706 | template <class T> |
707 | static void _copy_ptr(T** pdest, const T* source) { | |
b75a7d8f A |
708 | if (source == NULL) { |
709 | delete *pdest; | |
710 | *pdest = NULL; | |
711 | } else if (*pdest == NULL) { | |
51004dcb A |
712 | *pdest = new T(*source); |
713 | } else { | |
714 | **pdest = *source; | |
715 | } | |
716 | } | |
717 | ||
718 | template <class T> | |
719 | static void _clone_ptr(T** pdest, const T* source) { | |
720 | delete *pdest; | |
721 | if (source == NULL) { | |
722 | *pdest = NULL; | |
b75a7d8f | 723 | } else { |
51004dcb | 724 | *pdest = static_cast<T*>(source->clone()); |
b75a7d8f A |
725 | } |
726 | } | |
727 | ||
728 | DecimalFormat& | |
729 | DecimalFormat::operator=(const DecimalFormat& rhs) | |
730 | { | |
374ca955 A |
731 | if(this != &rhs) { |
732 | NumberFormat::operator=(rhs); | |
733 | fPositivePrefix = rhs.fPositivePrefix; | |
734 | fPositiveSuffix = rhs.fPositiveSuffix; | |
735 | fNegativePrefix = rhs.fNegativePrefix; | |
736 | fNegativeSuffix = rhs.fNegativeSuffix; | |
51004dcb A |
737 | _copy_ptr(&fPosPrefixPattern, rhs.fPosPrefixPattern); |
738 | _copy_ptr(&fPosSuffixPattern, rhs.fPosSuffixPattern); | |
739 | _copy_ptr(&fNegPrefixPattern, rhs.fNegPrefixPattern); | |
740 | _copy_ptr(&fNegSuffixPattern, rhs.fNegSuffixPattern); | |
741 | _clone_ptr(&fCurrencyChoice, rhs.fCurrencyChoice); | |
729e4ab9 | 742 | setRoundingIncrement(rhs.getRoundingIncrement()); |
03115e54 | 743 | fRoundingMode = rhs.fRoundingMode; |
729e4ab9 | 744 | setMultiplier(rhs.getMultiplier()); |
374ca955 A |
745 | fGroupingSize = rhs.fGroupingSize; |
746 | fGroupingSize2 = rhs.fGroupingSize2; | |
747 | fDecimalSeparatorAlwaysShown = rhs.fDecimalSeparatorAlwaysShown; | |
51004dcb | 748 | _copy_ptr(&fSymbols, rhs.fSymbols); |
374ca955 A |
749 | fUseExponentialNotation = rhs.fUseExponentialNotation; |
750 | fExponentSignAlwaysShown = rhs.fExponentSignAlwaysShown; | |
51004dcb | 751 | fBoolFlags = rhs.fBoolFlags; |
374ca955 | 752 | /*Bertrand A. D. Update 98.03.17*/ |
729e4ab9 | 753 | fCurrencySignCount = rhs.fCurrencySignCount; |
374ca955 A |
754 | /*end of Update*/ |
755 | fMinExponentDigits = rhs.fMinExponentDigits; | |
729e4ab9 | 756 | |
374ca955 A |
757 | /* sfb 990629 */ |
758 | fFormatWidth = rhs.fFormatWidth; | |
759 | fPad = rhs.fPad; | |
760 | fPadPosition = rhs.fPadPosition; | |
761 | /* end sfb */ | |
762 | fMinSignificantDigits = rhs.fMinSignificantDigits; | |
763 | fMaxSignificantDigits = rhs.fMaxSignificantDigits; | |
764 | fUseSignificantDigits = rhs.fUseSignificantDigits; | |
729e4ab9 A |
765 | fFormatPattern = rhs.fFormatPattern; |
766 | fStyle = rhs.fStyle; | |
767 | fCurrencySignCount = rhs.fCurrencySignCount; | |
51004dcb A |
768 | _clone_ptr(&fCurrencyPluralInfo, rhs.fCurrencyPluralInfo); |
769 | deleteHashForAffixPattern(); | |
729e4ab9 A |
770 | if (rhs.fAffixPatternsForCurrency) { |
771 | UErrorCode status = U_ZERO_ERROR; | |
729e4ab9 A |
772 | fAffixPatternsForCurrency = initHashForAffixPattern(status); |
773 | copyHashForAffixPattern(rhs.fAffixPatternsForCurrency, | |
774 | fAffixPatternsForCurrency, status); | |
775 | } | |
51004dcb | 776 | deleteHashForAffix(fAffixesForCurrency); |
729e4ab9 A |
777 | if (rhs.fAffixesForCurrency) { |
778 | UErrorCode status = U_ZERO_ERROR; | |
729e4ab9 A |
779 | fAffixesForCurrency = initHashForAffixPattern(status); |
780 | copyHashForAffix(rhs.fAffixesForCurrency, fAffixesForCurrency, status); | |
781 | } | |
51004dcb | 782 | deleteHashForAffix(fPluralAffixesForCurrency); |
729e4ab9 A |
783 | if (rhs.fPluralAffixesForCurrency) { |
784 | UErrorCode status = U_ZERO_ERROR; | |
729e4ab9 A |
785 | fPluralAffixesForCurrency = initHashForAffixPattern(status); |
786 | copyHashForAffix(rhs.fPluralAffixesForCurrency, fPluralAffixesForCurrency, status); | |
787 | } | |
374ca955 | 788 | } |
51004dcb A |
789 | #if UCONFIG_FORMAT_FASTPATHS_49 |
790 | handleChanged(); | |
791 | #endif | |
374ca955 | 792 | return *this; |
b75a7d8f A |
793 | } |
794 | ||
795 | //------------------------------------------------------------------------------ | |
796 | ||
797 | UBool | |
798 | DecimalFormat::operator==(const Format& that) const | |
799 | { | |
800 | if (this == &that) | |
801 | return TRUE; | |
802 | ||
374ca955 | 803 | // NumberFormat::operator== guarantees this cast is safe |
b75a7d8f A |
804 | const DecimalFormat* other = (DecimalFormat*)&that; |
805 | ||
806 | #ifdef FMT_DEBUG | |
807 | // This code makes it easy to determine why two format objects that should | |
808 | // be equal aren't. | |
809 | UBool first = TRUE; | |
810 | if (!NumberFormat::operator==(that)) { | |
811 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
812 | debug("NumberFormat::!="); | |
729e4ab9 | 813 | } else { |
b75a7d8f A |
814 | if (!((fPosPrefixPattern == other->fPosPrefixPattern && // both null |
815 | fPositivePrefix == other->fPositivePrefix) | |
816 | || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && | |
817 | *fPosPrefixPattern == *other->fPosPrefixPattern))) { | |
818 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
819 | debug("Pos Prefix !="); | |
820 | } | |
821 | if (!((fPosSuffixPattern == other->fPosSuffixPattern && // both null | |
822 | fPositiveSuffix == other->fPositiveSuffix) | |
823 | || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && | |
824 | *fPosSuffixPattern == *other->fPosSuffixPattern))) { | |
825 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
826 | debug("Pos Suffix !="); | |
827 | } | |
828 | if (!((fNegPrefixPattern == other->fNegPrefixPattern && // both null | |
829 | fNegativePrefix == other->fNegativePrefix) | |
830 | || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && | |
831 | *fNegPrefixPattern == *other->fNegPrefixPattern))) { | |
832 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
833 | debug("Neg Prefix "); | |
834 | if (fNegPrefixPattern == NULL) { | |
835 | debug("NULL("); | |
836 | debugout(fNegativePrefix); | |
837 | debug(")"); | |
838 | } else { | |
839 | debugout(*fNegPrefixPattern); | |
840 | } | |
841 | debug(" != "); | |
842 | if (other->fNegPrefixPattern == NULL) { | |
843 | debug("NULL("); | |
844 | debugout(other->fNegativePrefix); | |
845 | debug(")"); | |
846 | } else { | |
847 | debugout(*other->fNegPrefixPattern); | |
848 | } | |
849 | } | |
850 | if (!((fNegSuffixPattern == other->fNegSuffixPattern && // both null | |
851 | fNegativeSuffix == other->fNegativeSuffix) | |
852 | || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && | |
853 | *fNegSuffixPattern == *other->fNegSuffixPattern))) { | |
854 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
855 | debug("Neg Suffix "); | |
856 | if (fNegSuffixPattern == NULL) { | |
857 | debug("NULL("); | |
858 | debugout(fNegativeSuffix); | |
859 | debug(")"); | |
860 | } else { | |
861 | debugout(*fNegSuffixPattern); | |
862 | } | |
863 | debug(" != "); | |
864 | if (other->fNegSuffixPattern == NULL) { | |
865 | debug("NULL("); | |
866 | debugout(other->fNegativeSuffix); | |
867 | debug(")"); | |
868 | } else { | |
869 | debugout(*other->fNegSuffixPattern); | |
870 | } | |
871 | } | |
872 | if (!((fRoundingIncrement == other->fRoundingIncrement) // both null | |
873 | || (fRoundingIncrement != NULL && | |
874 | other->fRoundingIncrement != NULL && | |
875 | *fRoundingIncrement == *other->fRoundingIncrement))) { | |
876 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
877 | debug("Rounding Increment !="); | |
878 | } | |
729e4ab9 | 879 | if (getMultiplier() != other->getMultiplier()) { |
b75a7d8f | 880 | if (first) { printf("[ "); first = FALSE; } |
729e4ab9 | 881 | printf("Multiplier %ld != %ld", getMultiplier(), other->getMultiplier()); |
b75a7d8f A |
882 | } |
883 | if (fGroupingSize != other->fGroupingSize) { | |
884 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
885 | printf("Grouping Size %ld != %ld", fGroupingSize, other->fGroupingSize); | |
886 | } | |
887 | if (fGroupingSize2 != other->fGroupingSize2) { | |
888 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
889 | printf("Secondary Grouping Size %ld != %ld", fGroupingSize2, other->fGroupingSize2); | |
890 | } | |
891 | if (fDecimalSeparatorAlwaysShown != other->fDecimalSeparatorAlwaysShown) { | |
892 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
893 | printf("Dec Sep Always %d != %d", fDecimalSeparatorAlwaysShown, other->fDecimalSeparatorAlwaysShown); | |
894 | } | |
895 | if (fUseExponentialNotation != other->fUseExponentialNotation) { | |
896 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
897 | debug("Use Exp !="); | |
898 | } | |
899 | if (!(!fUseExponentialNotation || | |
900 | fMinExponentDigits != other->fMinExponentDigits)) { | |
901 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
902 | debug("Exp Digits !="); | |
903 | } | |
904 | if (*fSymbols != *(other->fSymbols)) { | |
905 | if (first) { printf("[ "); first = FALSE; } else { printf(", "); } | |
906 | debug("Symbols !="); | |
907 | } | |
374ca955 | 908 | // TODO Add debug stuff for significant digits here |
729e4ab9 A |
909 | if (fUseSignificantDigits != other->fUseSignificantDigits) { |
910 | debug("fUseSignificantDigits !="); | |
911 | } | |
912 | if (fUseSignificantDigits && | |
913 | fMinSignificantDigits != other->fMinSignificantDigits) { | |
914 | debug("fMinSignificantDigits !="); | |
915 | } | |
916 | if (fUseSignificantDigits && | |
917 | fMaxSignificantDigits != other->fMaxSignificantDigits) { | |
918 | debug("fMaxSignificantDigits !="); | |
919 | } | |
920 | ||
b75a7d8f | 921 | if (!first) { printf(" ]"); } |
729e4ab9 A |
922 | if (fCurrencySignCount != other->fCurrencySignCount) { |
923 | debug("fCurrencySignCount !="); | |
924 | } | |
925 | if (fCurrencyPluralInfo == other->fCurrencyPluralInfo) { | |
926 | debug("fCurrencyPluralInfo == "); | |
927 | if (fCurrencyPluralInfo == NULL) { | |
928 | debug("fCurrencyPluralInfo == NULL"); | |
929 | } | |
930 | } | |
931 | if (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && | |
932 | *fCurrencyPluralInfo != *(other->fCurrencyPluralInfo)) { | |
933 | debug("fCurrencyPluralInfo !="); | |
934 | } | |
935 | if (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo == NULL || | |
936 | fCurrencyPluralInfo == NULL && other->fCurrencyPluralInfo != NULL) { | |
937 | debug("fCurrencyPluralInfo one NULL, the other not"); | |
938 | } | |
939 | if (fCurrencyPluralInfo == NULL && other->fCurrencyPluralInfo == NULL) { | |
940 | debug("fCurrencyPluralInfo == "); | |
941 | } | |
942 | } | |
b75a7d8f A |
943 | #endif |
944 | ||
945 | return (NumberFormat::operator==(that) && | |
729e4ab9 A |
946 | ((fCurrencySignCount == fgCurrencySignCountInPluralFormat) ? |
947 | (fAffixPatternsForCurrency->equals(*other->fAffixPatternsForCurrency)) : | |
948 | (((fPosPrefixPattern == other->fPosPrefixPattern && // both null | |
b75a7d8f A |
949 | fPositivePrefix == other->fPositivePrefix) |
950 | || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && | |
951 | *fPosPrefixPattern == *other->fPosPrefixPattern)) && | |
952 | ((fPosSuffixPattern == other->fPosSuffixPattern && // both null | |
953 | fPositiveSuffix == other->fPositiveSuffix) | |
954 | || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && | |
955 | *fPosSuffixPattern == *other->fPosSuffixPattern)) && | |
956 | ((fNegPrefixPattern == other->fNegPrefixPattern && // both null | |
957 | fNegativePrefix == other->fNegativePrefix) | |
958 | || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && | |
959 | *fNegPrefixPattern == *other->fNegPrefixPattern)) && | |
960 | ((fNegSuffixPattern == other->fNegSuffixPattern && // both null | |
961 | fNegativeSuffix == other->fNegativeSuffix) | |
962 | || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && | |
729e4ab9 | 963 | *fNegSuffixPattern == *other->fNegSuffixPattern)))) && |
b75a7d8f A |
964 | ((fRoundingIncrement == other->fRoundingIncrement) // both null |
965 | || (fRoundingIncrement != NULL && | |
966 | other->fRoundingIncrement != NULL && | |
967 | *fRoundingIncrement == *other->fRoundingIncrement)) && | |
729e4ab9 | 968 | getMultiplier() == other->getMultiplier() && |
b75a7d8f A |
969 | fGroupingSize == other->fGroupingSize && |
970 | fGroupingSize2 == other->fGroupingSize2 && | |
971 | fDecimalSeparatorAlwaysShown == other->fDecimalSeparatorAlwaysShown && | |
972 | fUseExponentialNotation == other->fUseExponentialNotation && | |
973 | (!fUseExponentialNotation || | |
974 | fMinExponentDigits == other->fMinExponentDigits) && | |
374ca955 A |
975 | *fSymbols == *(other->fSymbols) && |
976 | fUseSignificantDigits == other->fUseSignificantDigits && | |
977 | (!fUseSignificantDigits || | |
978 | (fMinSignificantDigits == other->fMinSignificantDigits && | |
729e4ab9 A |
979 | fMaxSignificantDigits == other->fMaxSignificantDigits)) && |
980 | fCurrencySignCount == other->fCurrencySignCount && | |
981 | ((fCurrencyPluralInfo == other->fCurrencyPluralInfo && | |
982 | fCurrencyPluralInfo == NULL) || | |
983 | (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && | |
984 | *fCurrencyPluralInfo == *(other->fCurrencyPluralInfo)))); | |
b75a7d8f A |
985 | } |
986 | ||
987 | //------------------------------------------------------------------------------ | |
988 | ||
989 | Format* | |
990 | DecimalFormat::clone() const | |
991 | { | |
992 | return new DecimalFormat(*this); | |
993 | } | |
994 | ||
995 | //------------------------------------------------------------------------------ | |
729e4ab9 | 996 | |
b75a7d8f A |
997 | UnicodeString& |
998 | DecimalFormat::format(int32_t number, | |
999 | UnicodeString& appendTo, | |
1000 | FieldPosition& fieldPosition) const | |
374ca955 A |
1001 | { |
1002 | return format((int64_t)number, appendTo, fieldPosition); | |
1003 | } | |
1004 | ||
51004dcb A |
1005 | UnicodeString& |
1006 | DecimalFormat::format(int32_t number, | |
1007 | UnicodeString& appendTo, | |
1008 | FieldPosition& fieldPosition, | |
1009 | UErrorCode& status) const | |
1010 | { | |
1011 | return format((int64_t)number, appendTo, fieldPosition, status); | |
1012 | } | |
1013 | ||
729e4ab9 A |
1014 | UnicodeString& |
1015 | DecimalFormat::format(int32_t number, | |
1016 | UnicodeString& appendTo, | |
1017 | FieldPositionIterator* posIter, | |
1018 | UErrorCode& status) const | |
1019 | { | |
1020 | return format((int64_t)number, appendTo, posIter, status); | |
1021 | } | |
1022 | ||
51004dcb A |
1023 | |
1024 | #if UCONFIG_FORMAT_FASTPATHS_49 | |
1025 | void DecimalFormat::handleChanged() { | |
1026 | DecimalFormatInternal &data = internalData(fReserved); | |
1027 | ||
1028 | if(data.fFastFormatStatus == kFastpathUNKNOWN || data.fFastParseStatus == kFastpathUNKNOWN) { | |
1029 | return; // still constructing. Wait. | |
1030 | } | |
1031 | ||
1032 | data.fFastParseStatus = data.fFastFormatStatus = kFastpathNO; | |
1033 | ||
1034 | #if UCONFIG_HAVE_PARSEALLINPUT | |
1035 | if(fParseAllInput == UNUM_NO) { | |
1036 | debug("No Parse fastpath: fParseAllInput==UNUM_NO"); | |
1037 | } else | |
1038 | #endif | |
1039 | if (fFormatWidth!=0) { | |
1040 | debug("No Parse fastpath: fFormatWidth"); | |
1041 | } else if(fPositivePrefix.length()>0) { | |
1042 | debug("No Parse fastpath: positive prefix"); | |
1043 | } else if(fPositiveSuffix.length()>0) { | |
1044 | debug("No Parse fastpath: positive suffix"); | |
1045 | } else if(fNegativePrefix.length()>1 | |
1046 | || ((fNegativePrefix.length()==1) && (fNegativePrefix.charAt(0)!=0x002D))) { | |
1047 | debug("No Parse fastpath: negative prefix that isn't '-'"); | |
1048 | } else if(fNegativeSuffix.length()>0) { | |
1049 | debug("No Parse fastpath: negative suffix"); | |
1050 | } else { | |
1051 | data.fFastParseStatus = kFastpathYES; | |
1052 | debug("parse fastpath: YES"); | |
1053 | } | |
1054 | ||
1055 | if (fGroupingSize!=0 && isGroupingUsed()) { | |
1056 | debug("No format fastpath: fGroupingSize!=0 and grouping is used"); | |
1057 | #ifdef FMT_DEBUG | |
1058 | printf("groupingsize=%d\n", fGroupingSize); | |
1059 | #endif | |
1060 | } else if(fGroupingSize2!=0 && isGroupingUsed()) { | |
1061 | debug("No format fastpath: fGroupingSize2!=0"); | |
1062 | } else if(fUseExponentialNotation) { | |
1063 | debug("No format fastpath: fUseExponentialNotation"); | |
1064 | } else if(fFormatWidth!=0) { | |
1065 | debug("No format fastpath: fFormatWidth!=0"); | |
1066 | } else if(fMinSignificantDigits!=1) { | |
1067 | debug("No format fastpath: fMinSignificantDigits!=1"); | |
1068 | } else if(fMultiplier!=NULL) { | |
1069 | debug("No format fastpath: fMultiplier!=NULL"); | |
1070 | } else if(fScale!=0) { | |
1071 | debug("No format fastpath: fScale!=0"); | |
1072 | } else if(0x0030 != getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0)) { | |
1073 | debug("No format fastpath: 0x0030 != getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0)"); | |
1074 | } else if(fDecimalSeparatorAlwaysShown) { | |
1075 | debug("No format fastpath: fDecimalSeparatorAlwaysShown"); | |
1076 | } else if(getMinimumFractionDigits()>0) { | |
1077 | debug("No format fastpath: fMinFractionDigits>0"); | |
1078 | } else if(fCurrencySignCount > fgCurrencySignCountZero) { | |
1079 | debug("No format fastpath: fCurrencySignCount > fgCurrencySignCountZero"); | |
1080 | } else if(fRoundingIncrement!=0) { | |
1081 | debug("No format fastpath: fRoundingIncrement!=0"); | |
1082 | } else { | |
1083 | data.fFastFormatStatus = kFastpathYES; | |
1084 | debug("format:kFastpathYES!"); | |
1085 | } | |
1086 | ||
1087 | ||
1088 | } | |
1089 | #endif | |
374ca955 | 1090 | //------------------------------------------------------------------------------ |
729e4ab9 | 1091 | |
374ca955 A |
1092 | UnicodeString& |
1093 | DecimalFormat::format(int64_t number, | |
1094 | UnicodeString& appendTo, | |
1095 | FieldPosition& fieldPosition) const | |
51004dcb A |
1096 | { |
1097 | UErrorCode status = U_ZERO_ERROR; /* ignored */ | |
1098 | FieldPositionOnlyHandler handler(fieldPosition); | |
1099 | return _format(number, appendTo, handler, status); | |
1100 | } | |
1101 | ||
1102 | UnicodeString& | |
1103 | DecimalFormat::format(int64_t number, | |
1104 | UnicodeString& appendTo, | |
1105 | FieldPosition& fieldPosition, | |
1106 | UErrorCode& status) const | |
b75a7d8f | 1107 | { |
729e4ab9 | 1108 | FieldPositionOnlyHandler handler(fieldPosition); |
51004dcb | 1109 | return _format(number, appendTo, handler, status); |
729e4ab9 | 1110 | } |
b75a7d8f | 1111 | |
729e4ab9 A |
1112 | UnicodeString& |
1113 | DecimalFormat::format(int64_t number, | |
1114 | UnicodeString& appendTo, | |
1115 | FieldPositionIterator* posIter, | |
1116 | UErrorCode& status) const | |
1117 | { | |
1118 | FieldPositionIteratorHandler handler(posIter, status); | |
51004dcb | 1119 | return _format(number, appendTo, handler, status); |
729e4ab9 | 1120 | } |
b75a7d8f | 1121 | |
729e4ab9 A |
1122 | UnicodeString& |
1123 | DecimalFormat::_format(int64_t number, | |
1124 | UnicodeString& appendTo, | |
51004dcb A |
1125 | FieldPositionHandler& handler, |
1126 | UErrorCode &status) const | |
729e4ab9 | 1127 | { |
51004dcb A |
1128 | // Bottleneck function for formatting int64_t |
1129 | if (U_FAILURE(status)) { | |
1130 | return appendTo; | |
1131 | } | |
1132 | ||
1133 | #if UCONFIG_FORMAT_FASTPATHS_49 | |
1134 | // const UnicodeString *posPrefix = fPosPrefixPattern; | |
1135 | // const UnicodeString *posSuffix = fPosSuffixPattern; | |
1136 | // const UnicodeString *negSuffix = fNegSuffixPattern; | |
1137 | ||
1138 | const DecimalFormatInternal &data = internalData(fReserved); | |
1139 | ||
1140 | #ifdef FMT_DEBUG | |
1141 | data.dump(); | |
1142 | printf("fastpath? [%d]\n", number); | |
1143 | #endif | |
1144 | ||
1145 | if( data.fFastFormatStatus==kFastpathYES) { | |
1146 | ||
1147 | #define kZero 0x0030 | |
1148 | const int32_t MAX_IDX = MAX_DIGITS+2; | |
1149 | UChar outputStr[MAX_IDX]; | |
1150 | int32_t destIdx = MAX_IDX; | |
1151 | outputStr[--destIdx] = 0; // term | |
1152 | ||
1153 | int64_t n = number; | |
1154 | if (number < 1) { | |
1155 | // Negative numbers are slightly larger than positive | |
1156 | // output the first digit (or the leading zero) | |
1157 | outputStr[--destIdx] = (-(n % 10) + kZero); | |
1158 | n /= -10; | |
1159 | } | |
1160 | // get any remaining digits | |
1161 | while (n > 0) { | |
1162 | outputStr[--destIdx] = (n % 10) + kZero; | |
1163 | n /= 10; | |
1164 | } | |
1165 | ||
1166 | ||
1167 | // Slide the number to the start of the output str | |
1168 | U_ASSERT(destIdx >= 0); | |
1169 | int32_t length = MAX_IDX - destIdx -1; | |
1170 | /*int32_t prefixLen = */ appendAffix(appendTo, number, handler, number<0, TRUE); | |
1171 | int32_t maxIntDig = getMaximumIntegerDigits(); | |
1172 | int32_t destlength = length<=maxIntDig?length:maxIntDig; // dest length pinned to max int digits | |
1173 | ||
1174 | if(length>maxIntDig && fBoolFlags.contains(UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)) { | |
1175 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
1176 | } | |
1177 | ||
1178 | int32_t prependZero = getMinimumIntegerDigits() - destlength; | |
1179 | ||
1180 | #ifdef FMT_DEBUG | |
1181 | printf("prependZero=%d, length=%d, minintdig=%d maxintdig=%d destlength=%d skip=%d\n", prependZero, length, getMinimumIntegerDigits(), maxIntDig, destlength, length-destlength); | |
1182 | #endif | |
1183 | int32_t intBegin = appendTo.length(); | |
1184 | ||
1185 | while((prependZero--)>0) { | |
1186 | appendTo.append((UChar)0x0030); // '0' | |
1187 | } | |
1188 | ||
1189 | appendTo.append(outputStr+destIdx+ | |
1190 | (length-destlength), // skip any leading digits | |
1191 | destlength); | |
1192 | handler.addAttribute(kIntegerField, intBegin, appendTo.length()); | |
1193 | ||
1194 | /*int32_t suffixLen =*/ appendAffix(appendTo, number, handler, number<0, FALSE); | |
1195 | ||
1196 | //outputStr[length]=0; | |
1197 | ||
1198 | #ifdef FMT_DEBUG | |
1199 | printf("Writing [%s] length [%d] max %d for [%d]\n", outputStr+destIdx, length, MAX_IDX, number); | |
1200 | #endif | |
1201 | ||
1202 | #undef kZero | |
1203 | ||
1204 | return appendTo; | |
1205 | } // end fastpath | |
1206 | #endif | |
1207 | ||
1208 | // Else the slow way - via DigitList | |
729e4ab9 A |
1209 | DigitList digits; |
1210 | digits.set(number); | |
1211 | return _format(digits, appendTo, handler, status); | |
b75a7d8f | 1212 | } |
729e4ab9 | 1213 | |
b75a7d8f A |
1214 | //------------------------------------------------------------------------------ |
1215 | ||
1216 | UnicodeString& | |
1217 | DecimalFormat::format( double number, | |
1218 | UnicodeString& appendTo, | |
1219 | FieldPosition& fieldPosition) const | |
1220 | { | |
51004dcb | 1221 | UErrorCode status = U_ZERO_ERROR; /* ignored */ |
729e4ab9 | 1222 | FieldPositionOnlyHandler handler(fieldPosition); |
51004dcb A |
1223 | return _format(number, appendTo, handler, status); |
1224 | } | |
1225 | ||
1226 | UnicodeString& | |
1227 | DecimalFormat::format( double number, | |
1228 | UnicodeString& appendTo, | |
1229 | FieldPosition& fieldPosition, | |
1230 | UErrorCode& status) const | |
1231 | { | |
1232 | FieldPositionOnlyHandler handler(fieldPosition); | |
1233 | return _format(number, appendTo, handler, status); | |
729e4ab9 | 1234 | } |
b75a7d8f | 1235 | |
729e4ab9 A |
1236 | UnicodeString& |
1237 | DecimalFormat::format( double number, | |
1238 | UnicodeString& appendTo, | |
1239 | FieldPositionIterator* posIter, | |
1240 | UErrorCode& status) const | |
1241 | { | |
1242 | FieldPositionIteratorHandler handler(posIter, status); | |
51004dcb | 1243 | return _format(number, appendTo, handler, status); |
729e4ab9 A |
1244 | } |
1245 | ||
1246 | UnicodeString& | |
1247 | DecimalFormat::_format( double number, | |
1248 | UnicodeString& appendTo, | |
51004dcb A |
1249 | FieldPositionHandler& handler, |
1250 | UErrorCode &status) const | |
729e4ab9 | 1251 | { |
51004dcb A |
1252 | if (U_FAILURE(status)) { |
1253 | return appendTo; | |
1254 | } | |
b75a7d8f A |
1255 | // Special case for NaN, sets the begin and end index to be the |
1256 | // the string length of localized name of NaN. | |
729e4ab9 | 1257 | // TODO: let NaNs go through DigitList. |
b75a7d8f A |
1258 | if (uprv_isNaN(number)) |
1259 | { | |
729e4ab9 A |
1260 | int begin = appendTo.length(); |
1261 | appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); | |
1262 | ||
1263 | handler.addAttribute(kIntegerField, begin, appendTo.length()); | |
1264 | ||
1265 | addPadding(appendTo, handler, 0, 0); | |
1266 | return appendTo; | |
1267 | } | |
1268 | ||
729e4ab9 A |
1269 | DigitList digits; |
1270 | digits.set(number); | |
1271 | _format(digits, appendTo, handler, status); | |
1272 | // No way to return status from here. | |
1273 | return appendTo; | |
1274 | } | |
1275 | ||
1276 | //------------------------------------------------------------------------------ | |
1277 | ||
1278 | ||
1279 | UnicodeString& | |
1280 | DecimalFormat::format(const StringPiece &number, | |
1281 | UnicodeString &toAppendTo, | |
1282 | FieldPositionIterator *posIter, | |
1283 | UErrorCode &status) const | |
1284 | { | |
51004dcb A |
1285 | #if UCONFIG_FORMAT_FASTPATHS_49 |
1286 | // don't bother if the int64 path is not optimized | |
1287 | int32_t len = number.length(); | |
1288 | ||
1289 | if(len>0&&len<10) { /* 10 or more digits may not be an int64 */ | |
1290 | const char *data = number.data(); | |
1291 | int64_t num = 0; | |
1292 | UBool neg = FALSE; | |
1293 | UBool ok = TRUE; | |
1294 | ||
1295 | int32_t start = 0; | |
1296 | ||
1297 | if(data[start]=='+') { | |
1298 | start++; | |
1299 | } else if(data[start]=='-') { | |
1300 | neg=TRUE; | |
1301 | start++; | |
1302 | } | |
1303 | ||
1304 | int32_t place = 1; /* 1, 10, ... */ | |
1305 | for(int32_t i=len-1;i>=start;i--) { | |
1306 | if(data[i]>='0'&&data[i]<='9') { | |
1307 | num+=place*(int64_t)(data[i]-'0'); | |
1308 | } else { | |
1309 | ok=FALSE; | |
1310 | break; | |
1311 | } | |
1312 | place *= 10; | |
1313 | } | |
1314 | ||
1315 | if(ok) { | |
1316 | if(neg) { | |
1317 | num = -num;// add minus bit | |
1318 | } | |
1319 | // format as int64_t | |
1320 | return format(num, toAppendTo, posIter, status); | |
1321 | } | |
1322 | // else fall through | |
1323 | } | |
1324 | #endif | |
1325 | ||
729e4ab9 A |
1326 | DigitList dnum; |
1327 | dnum.set(number, status); | |
1328 | if (U_FAILURE(status)) { | |
1329 | return toAppendTo; | |
1330 | } | |
1331 | FieldPositionIteratorHandler handler(posIter, status); | |
1332 | _format(dnum, toAppendTo, handler, status); | |
1333 | return toAppendTo; | |
1334 | } | |
1335 | ||
1336 | ||
1337 | UnicodeString& | |
1338 | DecimalFormat::format(const DigitList &number, | |
1339 | UnicodeString &appendTo, | |
1340 | FieldPositionIterator *posIter, | |
1341 | UErrorCode &status) const { | |
1342 | FieldPositionIteratorHandler handler(posIter, status); | |
1343 | _format(number, appendTo, handler, status); | |
1344 | return appendTo; | |
1345 | } | |
1346 | ||
1347 | ||
1348 | ||
1349 | UnicodeString& | |
1350 | DecimalFormat::format(const DigitList &number, | |
1351 | UnicodeString& appendTo, | |
1352 | FieldPosition& pos, | |
1353 | UErrorCode &status) const { | |
1354 | FieldPositionOnlyHandler handler(pos); | |
1355 | _format(number, appendTo, handler, status); | |
1356 | return appendTo; | |
1357 | } | |
b75a7d8f | 1358 | |
51004dcb A |
1359 | DigitList& |
1360 | DecimalFormat::_round(const DigitList &number, DigitList &adjustedNum, UBool& isNegative, UErrorCode &status) const { | |
1361 | if (U_FAILURE(status)) { | |
1362 | return adjustedNum; | |
1363 | } | |
1364 | adjustedNum = number; | |
1365 | isNegative = false; | |
1366 | if (number.isNaN()) { | |
1367 | return adjustedNum; | |
b75a7d8f A |
1368 | } |
1369 | ||
46f4442e A |
1370 | // Do this BEFORE checking to see if value is infinite or negative! Sets the |
1371 | // begin and end index to be length of the string composed of | |
1372 | // localized name of Infinite and the positive/negative localized | |
1373 | // signs. | |
1374 | ||
729e4ab9 A |
1375 | adjustedNum.setRoundingMode(fRoundingMode); |
1376 | if (fMultiplier != NULL) { | |
1377 | adjustedNum.mult(*fMultiplier, status); | |
51004dcb A |
1378 | if (U_FAILURE(status)) { |
1379 | return adjustedNum; | |
1380 | } | |
729e4ab9 | 1381 | } |
46f4442e | 1382 | |
51004dcb A |
1383 | if (fScale != 0) { |
1384 | DigitList ten; | |
1385 | ten.set(10); | |
1386 | if (fScale > 0) { | |
1387 | for (int32_t i = fScale ; i > 0 ; i--) { | |
1388 | adjustedNum.mult(ten, status); | |
1389 | if (U_FAILURE(status)) { | |
1390 | return adjustedNum; | |
1391 | } | |
1392 | } | |
1393 | } else { | |
1394 | for (int32_t i = fScale ; i < 0 ; i++) { | |
1395 | adjustedNum.div(ten, status); | |
1396 | if (U_FAILURE(status)) { | |
1397 | return adjustedNum; | |
1398 | } | |
1399 | } | |
1400 | } | |
1401 | } | |
1402 | ||
1403 | /* | |
729e4ab9 A |
1404 | * Note: sign is important for zero as well as non-zero numbers. |
1405 | * Proper detection of -0.0 is needed to deal with the | |
b75a7d8f A |
1406 | * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98. |
1407 | */ | |
51004dcb | 1408 | isNegative = !adjustedNum.isPositive(); |
b75a7d8f | 1409 | |
b75a7d8f | 1410 | // Apply rounding after multiplier |
51004dcb | 1411 | |
4388f060 | 1412 | adjustedNum.fContext.status &= ~DEC_Inexact; |
b75a7d8f | 1413 | if (fRoundingIncrement != NULL) { |
729e4ab9 A |
1414 | adjustedNum.div(*fRoundingIncrement, status); |
1415 | adjustedNum.toIntegralValue(); | |
1416 | adjustedNum.mult(*fRoundingIncrement, status); | |
1417 | adjustedNum.trim(); | |
51004dcb A |
1418 | if (U_FAILURE(status)) { |
1419 | return adjustedNum; | |
1420 | } | |
b75a7d8f | 1421 | } |
4388f060 A |
1422 | if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) { |
1423 | status = U_FORMAT_INEXACT_ERROR; | |
51004dcb | 1424 | return adjustedNum; |
4388f060 | 1425 | } |
b75a7d8f | 1426 | |
729e4ab9 | 1427 | if (adjustedNum.isInfinite()) { |
51004dcb | 1428 | return adjustedNum; |
b75a7d8f A |
1429 | } |
1430 | ||
729e4ab9 A |
1431 | if (fUseExponentialNotation || areSignificantDigitsUsed()) { |
1432 | int32_t sigDigits = precision(); | |
1433 | if (sigDigits > 0) { | |
1434 | adjustedNum.round(sigDigits); | |
b75a7d8f | 1435 | } |
729e4ab9 A |
1436 | } else { |
1437 | // Fixed point format. Round to a set number of fraction digits. | |
1438 | int32_t numFractionDigits = precision(); | |
1439 | adjustedNum.roundFixedPoint(numFractionDigits); | |
b75a7d8f | 1440 | } |
4388f060 A |
1441 | if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) { |
1442 | status = U_FORMAT_INEXACT_ERROR; | |
51004dcb | 1443 | return adjustedNum; |
4388f060 | 1444 | } |
51004dcb | 1445 | return adjustedNum; |
b75a7d8f A |
1446 | } |
1447 | ||
51004dcb A |
1448 | UnicodeString& |
1449 | DecimalFormat::_format(const DigitList &number, | |
1450 | UnicodeString& appendTo, | |
1451 | FieldPositionHandler& handler, | |
1452 | UErrorCode &status) const | |
1453 | { | |
1454 | if (U_FAILURE(status)) { | |
1455 | return appendTo; | |
1456 | } | |
1457 | ||
1458 | // Special case for NaN, sets the begin and end index to be the | |
1459 | // the string length of localized name of NaN. | |
1460 | if (number.isNaN()) | |
1461 | { | |
1462 | int begin = appendTo.length(); | |
1463 | appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); | |
1464 | ||
1465 | handler.addAttribute(kIntegerField, begin, appendTo.length()); | |
1466 | ||
1467 | addPadding(appendTo, handler, 0, 0); | |
1468 | return appendTo; | |
1469 | } | |
1470 | ||
1471 | DigitList adjustedNum; | |
1472 | UBool isNegative; | |
1473 | _round(number, adjustedNum, isNegative, status); | |
1474 | if (U_FAILURE(status)) { | |
1475 | return appendTo; | |
1476 | } | |
1477 | ||
1478 | // Special case for INFINITE, | |
1479 | if (adjustedNum.isInfinite()) { | |
1480 | int32_t prefixLen = appendAffix(appendTo, adjustedNum.getDouble(), handler, isNegative, TRUE); | |
1481 | ||
1482 | int begin = appendTo.length(); | |
1483 | appendTo += getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); | |
1484 | ||
1485 | handler.addAttribute(kIntegerField, begin, appendTo.length()); | |
1486 | ||
1487 | int32_t suffixLen = appendAffix(appendTo, adjustedNum.getDouble(), handler, isNegative, FALSE); | |
1488 | ||
1489 | addPadding(appendTo, handler, prefixLen, suffixLen); | |
1490 | return appendTo; | |
1491 | } | |
1492 | return subformat(appendTo, handler, adjustedNum, FALSE, status); | |
1493 | } | |
729e4ab9 | 1494 | |
b75a7d8f A |
1495 | UnicodeString& |
1496 | DecimalFormat::format( const Formattable& obj, | |
1497 | UnicodeString& appendTo, | |
1498 | FieldPosition& fieldPosition, | |
1499 | UErrorCode& status) const | |
1500 | { | |
1501 | return NumberFormat::format(obj, appendTo, fieldPosition, status); | |
1502 | } | |
1503 | ||
1504 | /** | |
1505 | * Return true if a grouping separator belongs at the given | |
1506 | * position, based on whether grouping is in use and the values of | |
1507 | * the primary and secondary grouping interval. | |
1508 | * @param pos the number of integer digits to the right of | |
1509 | * the current position. Zero indicates the position after the | |
1510 | * rightmost integer digit. | |
1511 | * @return true if a grouping character belongs at the current | |
1512 | * position. | |
1513 | */ | |
1514 | UBool DecimalFormat::isGroupingPosition(int32_t pos) const { | |
1515 | UBool result = FALSE; | |
1516 | if (isGroupingUsed() && (pos > 0) && (fGroupingSize > 0)) { | |
1517 | if ((fGroupingSize2 > 0) && (pos > fGroupingSize)) { | |
1518 | result = ((pos - fGroupingSize) % fGroupingSize2) == 0; | |
1519 | } else { | |
1520 | result = pos % fGroupingSize == 0; | |
1521 | } | |
1522 | } | |
1523 | return result; | |
1524 | } | |
1525 | ||
1526 | //------------------------------------------------------------------------------ | |
1527 | ||
1528 | /** | |
729e4ab9 | 1529 | * Complete the formatting of a finite number. On entry, the DigitList must |
b75a7d8f A |
1530 | * be filled in with the correct digits. |
1531 | */ | |
1532 | UnicodeString& | |
1533 | DecimalFormat::subformat(UnicodeString& appendTo, | |
729e4ab9 | 1534 | FieldPositionHandler& handler, |
b75a7d8f | 1535 | DigitList& digits, |
51004dcb A |
1536 | UBool isInteger, |
1537 | UErrorCode& status) const | |
b75a7d8f | 1538 | { |
729e4ab9 A |
1539 | // char zero = '0'; |
1540 | // DigitList returns digits as '0' thru '9', so we will need to | |
1541 | // always need to subtract the character 0 to get the numeric value to use for indexing. | |
1542 | ||
1543 | UChar32 localizedDigits[10]; | |
1544 | localizedDigits[0] = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); | |
1545 | localizedDigits[1] = getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0); | |
1546 | localizedDigits[2] = getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0); | |
1547 | localizedDigits[3] = getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0); | |
1548 | localizedDigits[4] = getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0); | |
1549 | localizedDigits[5] = getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0); | |
1550 | localizedDigits[6] = getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0); | |
1551 | localizedDigits[7] = getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0); | |
1552 | localizedDigits[8] = getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0); | |
1553 | localizedDigits[9] = getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0); | |
1554 | ||
73c04bcf | 1555 | const UnicodeString *grouping ; |
729e4ab9 | 1556 | if(fCurrencySignCount > fgCurrencySignCountZero) { |
73c04bcf A |
1557 | grouping = &getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); |
1558 | }else{ | |
1559 | grouping = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); | |
1560 | } | |
b75a7d8f | 1561 | const UnicodeString *decimal; |
729e4ab9 | 1562 | if(fCurrencySignCount > fgCurrencySignCountZero) { |
b75a7d8f A |
1563 | decimal = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); |
1564 | } else { | |
1565 | decimal = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); | |
1566 | } | |
374ca955 | 1567 | UBool useSigDig = areSignificantDigitsUsed(); |
b75a7d8f A |
1568 | int32_t maxIntDig = getMaximumIntegerDigits(); |
1569 | int32_t minIntDig = getMinimumIntegerDigits(); | |
1570 | ||
b75a7d8f A |
1571 | // Appends the prefix. |
1572 | double doubleValue = digits.getDouble(); | |
729e4ab9 | 1573 | int32_t prefixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), TRUE); |
b75a7d8f A |
1574 | |
1575 | if (fUseExponentialNotation) | |
1576 | { | |
729e4ab9 A |
1577 | int currentLength = appendTo.length(); |
1578 | int intBegin = currentLength; | |
1579 | int intEnd = -1; | |
1580 | int fracBegin = -1; | |
b75a7d8f | 1581 | |
374ca955 A |
1582 | int32_t minFracDig = 0; |
1583 | if (useSigDig) { | |
1584 | maxIntDig = minIntDig = 1; | |
1585 | minFracDig = getMinimumSignificantDigits() - 1; | |
1586 | } else { | |
1587 | minFracDig = getMinimumFractionDigits(); | |
1588 | if (maxIntDig > kMaxScientificIntegerDigits) { | |
1589 | maxIntDig = 1; | |
1590 | if (maxIntDig < minIntDig) { | |
1591 | maxIntDig = minIntDig; | |
1592 | } | |
1593 | } | |
1594 | if (maxIntDig > minIntDig) { | |
1595 | minIntDig = 1; | |
1596 | } | |
1597 | } | |
1598 | ||
b75a7d8f A |
1599 | // Minimum integer digits are handled in exponential format by |
1600 | // adjusting the exponent. For example, 0.01234 with 3 minimum | |
1601 | // integer digits is "123.4E-4". | |
1602 | ||
1603 | // Maximum integer digits are interpreted as indicating the | |
1604 | // repeating range. This is useful for engineering notation, in | |
1605 | // which the exponent is restricted to a multiple of 3. For | |
1606 | // example, 0.01234 with 3 maximum integer digits is "12.34e-3". | |
1607 | // If maximum integer digits are defined and are larger than | |
1608 | // minimum integer digits, then minimum integer digits are | |
1609 | // ignored. | |
729e4ab9 A |
1610 | digits.reduce(); // Removes trailing zero digits. |
1611 | int32_t exponent = digits.getDecimalAt(); | |
b75a7d8f A |
1612 | if (maxIntDig > 1 && maxIntDig != minIntDig) { |
1613 | // A exponent increment is defined; adjust to it. | |
1614 | exponent = (exponent > 0) ? (exponent - 1) / maxIntDig | |
1615 | : (exponent / maxIntDig) - 1; | |
1616 | exponent *= maxIntDig; | |
1617 | } else { | |
1618 | // No exponent increment is defined; use minimum integer digits. | |
1619 | // If none is specified, as in "#E0", generate 1 integer digit. | |
374ca955 | 1620 | exponent -= (minIntDig > 0 || minFracDig > 0) |
b75a7d8f A |
1621 | ? minIntDig : 1; |
1622 | } | |
1623 | ||
1624 | // We now output a minimum number of digits, and more if there | |
1625 | // are more digits, up to the maximum number of digits. We | |
1626 | // place the decimal point after the "integer" digits, which | |
1627 | // are the first (decimalAt - exponent) digits. | |
374ca955 | 1628 | int32_t minimumDigits = minIntDig + minFracDig; |
b75a7d8f A |
1629 | // The number of integer digits is handled specially if the number |
1630 | // is zero, since then there may be no digits. | |
1631 | int32_t integerDigits = digits.isZero() ? minIntDig : | |
729e4ab9 A |
1632 | digits.getDecimalAt() - exponent; |
1633 | int32_t totalDigits = digits.getCount(); | |
b75a7d8f A |
1634 | if (minimumDigits > totalDigits) |
1635 | totalDigits = minimumDigits; | |
1636 | if (integerDigits > totalDigits) | |
1637 | totalDigits = integerDigits; | |
1638 | ||
1639 | // totalDigits records total number of digits needs to be processed | |
1640 | int32_t i; | |
1641 | for (i=0; i<totalDigits; ++i) | |
1642 | { | |
1643 | if (i == integerDigits) | |
1644 | { | |
729e4ab9 A |
1645 | intEnd = appendTo.length(); |
1646 | handler.addAttribute(kIntegerField, intBegin, intEnd); | |
b75a7d8f A |
1647 | |
1648 | appendTo += *decimal; | |
1649 | ||
729e4ab9 A |
1650 | fracBegin = appendTo.length(); |
1651 | handler.addAttribute(kDecimalSeparatorField, fracBegin - 1, fracBegin); | |
b75a7d8f A |
1652 | } |
1653 | // Restores the digit character or pads the buffer with zeros. | |
729e4ab9 A |
1654 | UChar32 c = (UChar32)((i < digits.getCount()) ? |
1655 | localizedDigits[digits.getDigitValue(i)] : | |
1656 | localizedDigits[0]); | |
b75a7d8f A |
1657 | appendTo += c; |
1658 | } | |
1659 | ||
729e4ab9 A |
1660 | currentLength = appendTo.length(); |
1661 | ||
1662 | if (intEnd < 0) { | |
1663 | handler.addAttribute(kIntegerField, intBegin, currentLength); | |
b75a7d8f | 1664 | } |
729e4ab9 A |
1665 | if (fracBegin > 0) { |
1666 | handler.addAttribute(kFractionField, fracBegin, currentLength); | |
b75a7d8f A |
1667 | } |
1668 | ||
1669 | // The exponent is output using the pattern-specified minimum | |
1670 | // exponent digits. There is no maximum limit to the exponent | |
1671 | // digits, since truncating the exponent would appendTo in an | |
1672 | // unacceptable inaccuracy. | |
1673 | appendTo += getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); | |
1674 | ||
729e4ab9 A |
1675 | handler.addAttribute(kExponentSymbolField, currentLength, appendTo.length()); |
1676 | currentLength = appendTo.length(); | |
1677 | ||
b75a7d8f A |
1678 | // For zero values, we force the exponent to zero. We |
1679 | // must do this here, and not earlier, because the value | |
1680 | // is used to determine integer digit count above. | |
1681 | if (digits.isZero()) | |
1682 | exponent = 0; | |
1683 | ||
1684 | if (exponent < 0) { | |
1685 | appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); | |
729e4ab9 | 1686 | handler.addAttribute(kExponentSignField, currentLength, appendTo.length()); |
b75a7d8f A |
1687 | } else if (fExponentSignAlwaysShown) { |
1688 | appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); | |
729e4ab9 | 1689 | handler.addAttribute(kExponentSignField, currentLength, appendTo.length()); |
b75a7d8f A |
1690 | } |
1691 | ||
729e4ab9 A |
1692 | currentLength = appendTo.length(); |
1693 | ||
b75a7d8f A |
1694 | DigitList expDigits; |
1695 | expDigits.set(exponent); | |
374ca955 A |
1696 | { |
1697 | int expDig = fMinExponentDigits; | |
1698 | if (fUseExponentialNotation && expDig < 1) { | |
1699 | expDig = 1; | |
1700 | } | |
729e4ab9 A |
1701 | for (i=expDigits.getDecimalAt(); i<expDig; ++i) |
1702 | appendTo += (localizedDigits[0]); | |
374ca955 | 1703 | } |
729e4ab9 | 1704 | for (i=0; i<expDigits.getDecimalAt(); ++i) |
b75a7d8f | 1705 | { |
729e4ab9 A |
1706 | UChar32 c = (UChar32)((i < expDigits.getCount()) ? |
1707 | localizedDigits[expDigits.getDigitValue(i)] : | |
1708 | localizedDigits[0]); | |
b75a7d8f A |
1709 | appendTo += c; |
1710 | } | |
729e4ab9 A |
1711 | |
1712 | handler.addAttribute(kExponentField, currentLength, appendTo.length()); | |
b75a7d8f A |
1713 | } |
1714 | else // Not using exponential notation | |
1715 | { | |
729e4ab9 A |
1716 | int currentLength = appendTo.length(); |
1717 | int intBegin = currentLength; | |
b75a7d8f | 1718 | |
374ca955 A |
1719 | int32_t sigCount = 0; |
1720 | int32_t minSigDig = getMinimumSignificantDigits(); | |
1721 | int32_t maxSigDig = getMaximumSignificantDigits(); | |
1722 | if (!useSigDig) { | |
1723 | minSigDig = 0; | |
1724 | maxSigDig = INT32_MAX; | |
1725 | } | |
1726 | ||
b75a7d8f A |
1727 | // Output the integer portion. Here 'count' is the total |
1728 | // number of integer digits we will display, including both | |
1729 | // leading zeros required to satisfy getMinimumIntegerDigits, | |
1730 | // and actual digits present in the number. | |
374ca955 | 1731 | int32_t count = useSigDig ? |
729e4ab9 A |
1732 | _max(1, digits.getDecimalAt()) : minIntDig; |
1733 | if (digits.getDecimalAt() > 0 && count < digits.getDecimalAt()) { | |
1734 | count = digits.getDecimalAt(); | |
374ca955 | 1735 | } |
b75a7d8f A |
1736 | |
1737 | // Handle the case where getMaximumIntegerDigits() is smaller | |
1738 | // than the real number of integer digits. If this is so, we | |
1739 | // output the least significant max integer digits. For example, | |
1740 | // the value 1997 printed with 2 max integer digits is just "97". | |
1741 | ||
374ca955 A |
1742 | int32_t digitIndex = 0; // Index into digitList.fDigits[] |
1743 | if (count > maxIntDig && maxIntDig >= 0) { | |
b75a7d8f | 1744 | count = maxIntDig; |
729e4ab9 | 1745 | digitIndex = digits.getDecimalAt() - count; |
51004dcb A |
1746 | if(fBoolFlags.contains(UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)) { |
1747 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
1748 | } | |
b75a7d8f A |
1749 | } |
1750 | ||
1751 | int32_t sizeBeforeIntegerPart = appendTo.length(); | |
1752 | ||
1753 | int32_t i; | |
1754 | for (i=count-1; i>=0; --i) | |
1755 | { | |
729e4ab9 | 1756 | if (i < digits.getDecimalAt() && digitIndex < digits.getCount() && |
374ca955 | 1757 | sigCount < maxSigDig) { |
b75a7d8f | 1758 | // Output a real digit |
729e4ab9 | 1759 | appendTo += (UChar32)localizedDigits[digits.getDigitValue(digitIndex++)]; |
374ca955 | 1760 | ++sigCount; |
b75a7d8f A |
1761 | } |
1762 | else | |
1763 | { | |
374ca955 | 1764 | // Output a zero (leading or trailing) |
729e4ab9 | 1765 | appendTo += localizedDigits[0]; |
374ca955 A |
1766 | if (sigCount > 0) { |
1767 | ++sigCount; | |
1768 | } | |
b75a7d8f A |
1769 | } |
1770 | ||
1771 | // Output grouping separator if necessary. | |
1772 | if (isGroupingPosition(i)) { | |
729e4ab9 | 1773 | currentLength = appendTo.length(); |
b75a7d8f | 1774 | appendTo.append(*grouping); |
729e4ab9 | 1775 | handler.addAttribute(kGroupingSeparatorField, currentLength, appendTo.length()); |
b75a7d8f A |
1776 | } |
1777 | } | |
1778 | ||
729e4ab9 A |
1779 | // TODO(dlf): this looks like it was a bug, we marked the int field as ending |
1780 | // before the zero was generated. | |
b75a7d8f | 1781 | // Record field information for caller. |
729e4ab9 A |
1782 | // if (fieldPosition.getField() == NumberFormat::kIntegerField) |
1783 | // fieldPosition.setEndIndex(appendTo.length()); | |
b75a7d8f A |
1784 | |
1785 | // Determine whether or not there are any printable fractional | |
1786 | // digits. If we've used up the digits we know there aren't. | |
729e4ab9 | 1787 | UBool fractionPresent = (!isInteger && digitIndex < digits.getCount()) || |
374ca955 | 1788 | (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0)); |
b75a7d8f A |
1789 | |
1790 | // If there is no fraction present, and we haven't printed any | |
1791 | // integer digits, then print a zero. Otherwise we won't print | |
1792 | // _any_ digits, and we won't be able to parse this string. | |
1793 | if (!fractionPresent && appendTo.length() == sizeBeforeIntegerPart) | |
729e4ab9 A |
1794 | appendTo += localizedDigits[0]; |
1795 | ||
1796 | currentLength = appendTo.length(); | |
1797 | handler.addAttribute(kIntegerField, intBegin, currentLength); | |
b75a7d8f A |
1798 | |
1799 | // Output the decimal separator if we always do so. | |
729e4ab9 | 1800 | if (fDecimalSeparatorAlwaysShown || fractionPresent) { |
b75a7d8f | 1801 | appendTo += *decimal; |
729e4ab9 A |
1802 | handler.addAttribute(kDecimalSeparatorField, currentLength, appendTo.length()); |
1803 | currentLength = appendTo.length(); | |
1804 | } | |
b75a7d8f | 1805 | |
729e4ab9 | 1806 | int fracBegin = currentLength; |
b75a7d8f | 1807 | |
374ca955 A |
1808 | count = useSigDig ? INT32_MAX : getMaximumFractionDigits(); |
1809 | if (useSigDig && (sigCount == maxSigDig || | |
729e4ab9 | 1810 | (sigCount >= minSigDig && digitIndex == digits.getCount()))) { |
374ca955 A |
1811 | count = 0; |
1812 | } | |
1813 | ||
1814 | for (i=0; i < count; ++i) { | |
1815 | // Here is where we escape from the loop. We escape | |
1816 | // if we've output the maximum fraction digits | |
1817 | // (specified in the for expression above). We also | |
1818 | // stop when we've output the minimum digits and | |
1819 | // either: we have an integer, so there is no | |
1820 | // fractional stuff to display, or we're out of | |
1821 | // significant digits. | |
1822 | if (!useSigDig && i >= getMinimumFractionDigits() && | |
729e4ab9 | 1823 | (isInteger || digitIndex >= digits.getCount())) { |
374ca955 | 1824 | break; |
b75a7d8f | 1825 | } |
b75a7d8f | 1826 | |
374ca955 A |
1827 | // Output leading fractional zeros. These are zeros |
1828 | // that come after the decimal but before any | |
1829 | // significant digits. These are only output if | |
1830 | // abs(number being formatted) < 1.0. | |
729e4ab9 A |
1831 | if (-1-i > (digits.getDecimalAt()-1)) { |
1832 | appendTo += localizedDigits[0]; | |
374ca955 A |
1833 | continue; |
1834 | } | |
1835 | ||
1836 | // Output a digit, if we have any precision left, or a | |
1837 | // zero if we don't. We don't want to output noise digits. | |
729e4ab9 A |
1838 | if (!isInteger && digitIndex < digits.getCount()) { |
1839 | appendTo += (UChar32)localizedDigits[digits.getDigitValue(digitIndex++)]; | |
374ca955 | 1840 | } else { |
729e4ab9 | 1841 | appendTo += localizedDigits[0]; |
374ca955 A |
1842 | } |
1843 | ||
1844 | // If we reach the maximum number of significant | |
1845 | // digits, or if we output all the real digits and | |
1846 | // reach the minimum, then we are done. | |
1847 | ++sigCount; | |
1848 | if (useSigDig && | |
1849 | (sigCount == maxSigDig || | |
729e4ab9 | 1850 | (digitIndex == digits.getCount() && sigCount >= minSigDig))) { |
374ca955 | 1851 | break; |
b75a7d8f A |
1852 | } |
1853 | } | |
1854 | ||
729e4ab9 | 1855 | handler.addAttribute(kFractionField, fracBegin, appendTo.length()); |
b75a7d8f A |
1856 | } |
1857 | ||
729e4ab9 | 1858 | int32_t suffixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), FALSE); |
b75a7d8f | 1859 | |
729e4ab9 | 1860 | addPadding(appendTo, handler, prefixLen, suffixLen); |
b75a7d8f A |
1861 | return appendTo; |
1862 | } | |
1863 | ||
1864 | /** | |
1865 | * Inserts the character fPad as needed to expand result to fFormatWidth. | |
1866 | * @param result the string to be padded | |
1867 | */ | |
1868 | void DecimalFormat::addPadding(UnicodeString& appendTo, | |
729e4ab9 | 1869 | FieldPositionHandler& handler, |
b75a7d8f A |
1870 | int32_t prefixLen, |
1871 | int32_t suffixLen) const | |
1872 | { | |
1873 | if (fFormatWidth > 0) { | |
1874 | int32_t len = fFormatWidth - appendTo.length(); | |
1875 | if (len > 0) { | |
1876 | UnicodeString padding; | |
1877 | for (int32_t i=0; i<len; ++i) { | |
1878 | padding += fPad; | |
1879 | } | |
1880 | switch (fPadPosition) { | |
1881 | case kPadAfterPrefix: | |
1882 | appendTo.insert(prefixLen, padding); | |
1883 | break; | |
1884 | case kPadBeforePrefix: | |
1885 | appendTo.insert(0, padding); | |
1886 | break; | |
1887 | case kPadBeforeSuffix: | |
1888 | appendTo.insert(appendTo.length() - suffixLen, padding); | |
1889 | break; | |
1890 | case kPadAfterSuffix: | |
1891 | appendTo += padding; | |
1892 | break; | |
1893 | } | |
729e4ab9 A |
1894 | if (fPadPosition == kPadBeforePrefix || fPadPosition == kPadAfterPrefix) { |
1895 | handler.shiftLast(len); | |
b75a7d8f A |
1896 | } |
1897 | } | |
1898 | } | |
1899 | } | |
1900 | ||
1901 | //------------------------------------------------------------------------------ | |
729e4ab9 | 1902 | |
b75a7d8f A |
1903 | void |
1904 | DecimalFormat::parse(const UnicodeString& text, | |
1905 | Formattable& result, | |
1906 | UErrorCode& status) const | |
1907 | { | |
1908 | NumberFormat::parse(text, result, status); | |
1909 | } | |
1910 | ||
1911 | void | |
1912 | DecimalFormat::parse(const UnicodeString& text, | |
1913 | Formattable& result, | |
374ca955 | 1914 | ParsePosition& parsePosition) const { |
4388f060 | 1915 | parse(text, result, parsePosition, NULL); |
374ca955 A |
1916 | } |
1917 | ||
4388f060 A |
1918 | CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, |
1919 | ParsePosition& pos) const { | |
1920 | Formattable parseResult; | |
1921 | int32_t start = pos.getIndex(); | |
51004dcb | 1922 | UChar curbuf[4] = {}; |
4388f060 A |
1923 | parse(text, parseResult, pos, curbuf); |
1924 | if (pos.getIndex() != start) { | |
1925 | UErrorCode ec = U_ZERO_ERROR; | |
1926 | LocalPointer<CurrencyAmount> currAmt(new CurrencyAmount(parseResult, curbuf, ec)); | |
1927 | if (U_FAILURE(ec)) { | |
1928 | pos.setIndex(start); // indicate failure | |
1929 | } else { | |
1930 | return currAmt.orphan(); | |
1931 | } | |
1932 | } | |
1933 | return NULL; | |
374ca955 A |
1934 | } |
1935 | ||
1936 | /** | |
4388f060 | 1937 | * Parses the given text as a number, optionally providing a currency amount. |
374ca955 | 1938 | * @param text the string to parse |
4388f060 | 1939 | * @param result output parameter for the numeric result. |
374ca955 A |
1940 | * @param parsePosition input-output position; on input, the |
1941 | * position within text to match; must have 0 <= pos.getIndex() < | |
1942 | * text.length(); on output, the position after the last matched | |
1943 | * character. If the parse fails, the position in unchanged upon | |
1944 | * output. | |
4388f060 A |
1945 | * @param currency if non-NULL, it should point to a 4-UChar buffer. |
1946 | * In this case the text is parsed as a currency format, and the | |
1947 | * ISO 4217 code for the parsed currency is put into the buffer. | |
1948 | * Otherwise the text is parsed as a non-currency format. | |
374ca955 A |
1949 | */ |
1950 | void DecimalFormat::parse(const UnicodeString& text, | |
1951 | Formattable& result, | |
1952 | ParsePosition& parsePosition, | |
4388f060 A |
1953 | UChar* currency) const { |
1954 | int32_t startIdx, backup; | |
1955 | int32_t i = startIdx = backup = parsePosition.getIndex(); | |
b75a7d8f | 1956 | |
729e4ab9 A |
1957 | // clear any old contents in the result. In particular, clears any DigitList |
1958 | // that it may be holding. | |
1959 | result.setLong(0); | |
1960 | ||
b75a7d8f | 1961 | // Handle NaN as a special case: |
729e4ab9 | 1962 | |
b75a7d8f A |
1963 | // Skip padding characters, if around prefix |
1964 | if (fFormatWidth > 0 && (fPadPosition == kPadBeforePrefix || | |
1965 | fPadPosition == kPadAfterPrefix)) { | |
1966 | i = skipPadding(text, i); | |
1967 | } | |
729e4ab9 | 1968 | |
4388f060 A |
1969 | if (isLenient()) { |
1970 | // skip any leading whitespace | |
1971 | i = backup = skipUWhiteSpace(text, i); | |
46f4442e | 1972 | } |
729e4ab9 | 1973 | |
b75a7d8f A |
1974 | // If the text is composed of the representation of NaN, returns NaN.length |
1975 | const UnicodeString *nan = &getConstSymbol(DecimalFormatSymbols::kNaNSymbol); | |
1976 | int32_t nanLen = (text.compare(i, nan->length(), *nan) | |
1977 | ? 0 : nan->length()); | |
1978 | if (nanLen) { | |
1979 | i += nanLen; | |
1980 | if (fFormatWidth > 0 && (fPadPosition == kPadBeforeSuffix || | |
1981 | fPadPosition == kPadAfterSuffix)) { | |
1982 | i = skipPadding(text, i); | |
1983 | } | |
1984 | parsePosition.setIndex(i); | |
1985 | result.setDouble(uprv_getNaN()); | |
1986 | return; | |
1987 | } | |
729e4ab9 | 1988 | |
b75a7d8f A |
1989 | // NaN parse failed; start over |
1990 | i = backup; | |
46f4442e | 1991 | parsePosition.setIndex(i); |
b75a7d8f A |
1992 | |
1993 | // status is used to record whether a number is infinite. | |
1994 | UBool status[fgStatusLength]; | |
51004dcb A |
1995 | |
1996 | DigitList *digits = result.getInternalDigitList(); // get one from the stack buffer | |
729e4ab9 A |
1997 | if (digits == NULL) { |
1998 | return; // no way to report error from here. | |
1999 | } | |
b75a7d8f | 2000 | |
729e4ab9 A |
2001 | if (fCurrencySignCount > fgCurrencySignCountZero) { |
2002 | if (!parseForCurrency(text, parsePosition, *digits, | |
2003 | status, currency)) { | |
51004dcb | 2004 | return; |
729e4ab9 A |
2005 | } |
2006 | } else { | |
2007 | if (!subparse(text, | |
2008 | fNegPrefixPattern, fNegSuffixPattern, | |
2009 | fPosPrefixPattern, fPosSuffixPattern, | |
2010 | FALSE, UCURR_SYMBOL_NAME, | |
2011 | parsePosition, *digits, status, currency)) { | |
51004dcb | 2012 | debug("!subparse(...) - rewind"); |
4388f060 | 2013 | parsePosition.setIndex(startIdx); |
729e4ab9 A |
2014 | return; |
2015 | } | |
b75a7d8f A |
2016 | } |
2017 | ||
2018 | // Handle infinity | |
2019 | if (status[fgStatusInfinite]) { | |
2020 | double inf = uprv_getInfinity(); | |
729e4ab9 | 2021 | result.setDouble(digits->isPositive() ? inf : -inf); |
51004dcb | 2022 | // TODO: set the dl to infinity, and let it fall into the code below. |
b75a7d8f A |
2023 | } |
2024 | ||
374ca955 | 2025 | else { |
374ca955 | 2026 | |
729e4ab9 A |
2027 | if (fMultiplier != NULL) { |
2028 | UErrorCode ec = U_ZERO_ERROR; | |
2029 | digits->div(*fMultiplier, ec); | |
b75a7d8f | 2030 | } |
729e4ab9 | 2031 | |
51004dcb A |
2032 | if (fScale != 0) { |
2033 | DigitList ten; | |
2034 | ten.set(10); | |
2035 | if (fScale > 0) { | |
2036 | for (int32_t i = fScale; i > 0; i--) { | |
2037 | UErrorCode ec = U_ZERO_ERROR; | |
2038 | digits->div(ten,ec); | |
2039 | } | |
2040 | } else { | |
2041 | for (int32_t i = fScale; i < 0; i++) { | |
2042 | UErrorCode ec = U_ZERO_ERROR; | |
2043 | digits->mult(ten,ec); | |
2044 | } | |
2045 | } | |
2046 | } | |
2047 | ||
729e4ab9 A |
2048 | // Negative zero special case: |
2049 | // if parsing integerOnly, change to +0, which goes into an int32 in a Formattable. | |
2050 | // if not parsing integerOnly, leave as -0, which a double can represent. | |
2051 | if (digits->isZero() && !digits->isPositive() && isParseIntegerOnly()) { | |
2052 | digits->setPositive(TRUE); | |
b75a7d8f | 2053 | } |
729e4ab9 | 2054 | result.adoptDigitList(digits); |
b75a7d8f | 2055 | } |
b75a7d8f A |
2056 | } |
2057 | ||
2058 | ||
374ca955 | 2059 | |
729e4ab9 A |
2060 | UBool |
2061 | DecimalFormat::parseForCurrency(const UnicodeString& text, | |
2062 | ParsePosition& parsePosition, | |
2063 | DigitList& digits, | |
2064 | UBool* status, | |
2065 | UChar* currency) const { | |
2066 | int origPos = parsePosition.getIndex(); | |
2067 | int maxPosIndex = origPos; | |
2068 | int maxErrorPos = -1; | |
2069 | // First, parse against current pattern. | |
2070 | // Since current pattern could be set by applyPattern(), | |
2071 | // it could be an arbitrary pattern, and it may not be the one | |
2072 | // defined in current locale. | |
2073 | UBool tmpStatus[fgStatusLength]; | |
2074 | ParsePosition tmpPos(origPos); | |
2075 | DigitList tmpDigitList; | |
2076 | UBool found; | |
4388f060 | 2077 | if (fStyle == UNUM_CURRENCY_PLURAL) { |
729e4ab9 A |
2078 | found = subparse(text, |
2079 | fNegPrefixPattern, fNegSuffixPattern, | |
2080 | fPosPrefixPattern, fPosSuffixPattern, | |
2081 | TRUE, UCURR_LONG_NAME, | |
2082 | tmpPos, tmpDigitList, tmpStatus, currency); | |
2083 | } else { | |
2084 | found = subparse(text, | |
2085 | fNegPrefixPattern, fNegSuffixPattern, | |
2086 | fPosPrefixPattern, fPosSuffixPattern, | |
2087 | TRUE, UCURR_SYMBOL_NAME, | |
2088 | tmpPos, tmpDigitList, tmpStatus, currency); | |
2089 | } | |
2090 | if (found) { | |
2091 | if (tmpPos.getIndex() > maxPosIndex) { | |
2092 | maxPosIndex = tmpPos.getIndex(); | |
2093 | for (int32_t i = 0; i < fgStatusLength; ++i) { | |
2094 | status[i] = tmpStatus[i]; | |
2095 | } | |
2096 | digits = tmpDigitList; | |
2097 | } | |
2098 | } else { | |
2099 | maxErrorPos = tmpPos.getErrorIndex(); | |
2100 | } | |
2101 | // Then, parse against affix patterns. | |
2102 | // Those are currency patterns and currency plural patterns. | |
2103 | int32_t pos = -1; | |
2104 | const UHashElement* element = NULL; | |
2105 | while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { | |
729e4ab9 A |
2106 | const UHashTok valueTok = element->value; |
2107 | const AffixPatternsForCurrency* affixPtn = (AffixPatternsForCurrency*)valueTok.pointer; | |
2108 | UBool tmpStatus[fgStatusLength]; | |
2109 | ParsePosition tmpPos(origPos); | |
2110 | DigitList tmpDigitList; | |
2111 | UBool result = subparse(text, | |
2112 | &affixPtn->negPrefixPatternForCurrency, | |
2113 | &affixPtn->negSuffixPatternForCurrency, | |
2114 | &affixPtn->posPrefixPatternForCurrency, | |
2115 | &affixPtn->posSuffixPatternForCurrency, | |
2116 | TRUE, affixPtn->patternType, | |
2117 | tmpPos, tmpDigitList, tmpStatus, currency); | |
2118 | if (result) { | |
2119 | found = true; | |
2120 | if (tmpPos.getIndex() > maxPosIndex) { | |
2121 | maxPosIndex = tmpPos.getIndex(); | |
2122 | for (int32_t i = 0; i < fgStatusLength; ++i) { | |
2123 | status[i] = tmpStatus[i]; | |
2124 | } | |
2125 | digits = tmpDigitList; | |
2126 | } | |
2127 | } else { | |
2128 | maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? | |
2129 | tmpPos.getErrorIndex() : maxErrorPos; | |
2130 | } | |
2131 | } | |
2132 | // Finally, parse against simple affix to find the match. | |
2133 | // For example, in TestMonster suite, | |
2134 | // if the to-be-parsed text is "-\u00A40,00". | |
2135 | // complexAffixCompare will not find match, | |
2136 | // since there is no ISO code matches "\u00A4", | |
2137 | // and the parse stops at "\u00A4". | |
2138 | // We will just use simple affix comparison (look for exact match) | |
2139 | // to pass it. | |
2140 | UBool tmpStatus_2[fgStatusLength]; | |
2141 | ParsePosition tmpPos_2(origPos); | |
2142 | DigitList tmpDigitList_2; | |
2143 | // set currencySignCount to 0 so that compareAffix function will | |
2144 | // fall to compareSimpleAffix path, not compareComplexAffix path. | |
2145 | // ?? TODO: is it right? need "false"? | |
2146 | UBool result = subparse(text, | |
2147 | &fNegativePrefix, &fNegativeSuffix, | |
2148 | &fPositivePrefix, &fPositiveSuffix, | |
2149 | FALSE, UCURR_SYMBOL_NAME, | |
2150 | tmpPos_2, tmpDigitList_2, tmpStatus_2, | |
2151 | currency); | |
2152 | if (result) { | |
2153 | if (tmpPos_2.getIndex() > maxPosIndex) { | |
2154 | maxPosIndex = tmpPos_2.getIndex(); | |
2155 | for (int32_t i = 0; i < fgStatusLength; ++i) { | |
2156 | status[i] = tmpStatus_2[i]; | |
2157 | } | |
2158 | digits = tmpDigitList_2; | |
2159 | } | |
2160 | found = true; | |
2161 | } else { | |
2162 | maxErrorPos = (tmpPos_2.getErrorIndex() > maxErrorPos) ? | |
2163 | tmpPos_2.getErrorIndex() : maxErrorPos; | |
2164 | } | |
2165 | ||
2166 | if (!found) { | |
2167 | //parsePosition.setIndex(origPos); | |
2168 | parsePosition.setErrorIndex(maxErrorPos); | |
2169 | } else { | |
2170 | parsePosition.setIndex(maxPosIndex); | |
2171 | parsePosition.setErrorIndex(-1); | |
2172 | } | |
2173 | return found; | |
2174 | } | |
2175 | ||
374ca955 | 2176 | |
b75a7d8f A |
2177 | /** |
2178 | * Parse the given text into a number. The text is parsed beginning at | |
2179 | * parsePosition, until an unparseable character is seen. | |
374ca955 | 2180 | * @param text the string to parse. |
729e4ab9 A |
2181 | * @param negPrefix negative prefix. |
2182 | * @param negSuffix negative suffix. | |
2183 | * @param posPrefix positive prefix. | |
2184 | * @param posSuffix positive suffix. | |
2185 | * @param currencyParsing whether it is currency parsing or not. | |
2186 | * @param type the currency type to parse against, LONG_NAME only or not. | |
b75a7d8f | 2187 | * @param parsePosition The position at which to being parsing. Upon |
374ca955 A |
2188 | * return, the first unparsed character. |
2189 | * @param digits the DigitList to set to the parsed value. | |
2190 | * @param status output param containing boolean status flags indicating | |
b75a7d8f | 2191 | * whether the value was infinite and whether it was positive. |
374ca955 A |
2192 | * @param currency return value for parsed currency, for generic |
2193 | * currency parsing mode, or NULL for normal parsing. In generic | |
2194 | * currency parsing mode, any currency is parsed, not just the | |
2195 | * currency that this formatter is set to. | |
b75a7d8f | 2196 | */ |
729e4ab9 A |
2197 | UBool DecimalFormat::subparse(const UnicodeString& text, |
2198 | const UnicodeString* negPrefix, | |
2199 | const UnicodeString* negSuffix, | |
2200 | const UnicodeString* posPrefix, | |
2201 | const UnicodeString* posSuffix, | |
2202 | UBool currencyParsing, | |
2203 | int8_t type, | |
2204 | ParsePosition& parsePosition, | |
374ca955 A |
2205 | DigitList& digits, UBool* status, |
2206 | UChar* currency) const | |
b75a7d8f | 2207 | { |
729e4ab9 A |
2208 | // The parsing process builds up the number as char string, in the neutral format that |
2209 | // will be acceptable to the decNumber library, then at the end passes that string | |
2210 | // off for conversion to a decNumber. | |
2211 | UErrorCode err = U_ZERO_ERROR; | |
2212 | CharString parsedNum; | |
2213 | digits.setToZero(); | |
2214 | ||
b75a7d8f A |
2215 | int32_t position = parsePosition.getIndex(); |
2216 | int32_t oldStart = position; | |
51004dcb | 2217 | int32_t textLength = text.length(); // One less pointer to follow |
4388f060 | 2218 | UBool strictParse = !isLenient(); |
51004dcb A |
2219 | UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); |
2220 | const UnicodeString *groupingString = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); | |
2221 | UChar32 groupingChar = groupingString->char32At(0); | |
2222 | int32_t groupingStringLength = groupingString->length(); | |
2223 | int32_t groupingCharLength = U16_LENGTH(groupingChar); | |
2224 | UBool groupingUsed = isGroupingUsed(); | |
2225 | #ifdef FMT_DEBUG | |
2226 | UChar dbgbuf[300]; | |
2227 | UnicodeString s(dbgbuf,0,300);; | |
2228 | s.append((UnicodeString)"PARSE \"").append(text.tempSubString(position)).append((UnicodeString)"\" " ); | |
2229 | #define DBGAPPD(x) if(x) { s.append(UnicodeString(#x "=")); if(x->isEmpty()) { s.append(UnicodeString("<empty>")); } else { s.append(*x); } s.append(UnicodeString(" ")); } else { s.append(UnicodeString(#x "=NULL ")); } | |
2230 | DBGAPPD(negPrefix); | |
2231 | DBGAPPD(negSuffix); | |
2232 | DBGAPPD(posPrefix); | |
2233 | DBGAPPD(posSuffix); | |
2234 | debugout(s); | |
2235 | printf("currencyParsing=%d, fFormatWidth=%d, isParseIntegerOnly=%c text.length=%d negPrefLen=%d\n", currencyParsing, fFormatWidth, (isParseIntegerOnly())?'Y':'N', text.length(), negPrefix!=NULL?negPrefix->length():-1); | |
2236 | #endif | |
2237 | ||
2238 | UBool fastParseOk = false; /* TRUE iff fast parse is OK */ | |
2239 | // UBool fastParseHadDecimal = FALSE; /* true if fast parse saw a decimal point. */ | |
2240 | const DecimalFormatInternal &data = internalData(fReserved); | |
2241 | if((data.fFastParseStatus==kFastpathYES) && | |
2242 | !currencyParsing && | |
2243 | // (negPrefix!=NULL&&negPrefix->isEmpty()) || | |
2244 | text.length()>0 && | |
2245 | text.length()<32 && | |
2246 | (posPrefix==NULL||posPrefix->isEmpty()) && | |
2247 | (posSuffix==NULL||posSuffix->isEmpty()) && | |
2248 | // (negPrefix==NULL||negPrefix->isEmpty()) && | |
2249 | // (negSuffix==NULL||(negSuffix->isEmpty()) ) && | |
2250 | TRUE) { // optimized path | |
2251 | int j=position; | |
2252 | int l=text.length(); | |
2253 | int digitCount=0; | |
2254 | UChar32 ch = text.char32At(j); | |
2255 | const UnicodeString *decimalString = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); | |
2256 | UChar32 decimalChar = 0; | |
2257 | UBool intOnly = FALSE; | |
2258 | UChar32 lookForGroup = (groupingUsed&&intOnly&&strictParse)?groupingChar:0; | |
2259 | ||
2260 | int32_t decimalCount = decimalString->countChar32(0,3); | |
2261 | if(isParseIntegerOnly()) { | |
2262 | decimalChar = 0; // not allowed | |
2263 | intOnly = TRUE; // Don't look for decimals. | |
2264 | } else if(decimalCount==1) { | |
2265 | decimalChar = decimalString->char32At(0); // Look for this decimal | |
2266 | } else if(decimalCount==0) { | |
2267 | decimalChar=0; // NO decimal set | |
2268 | } else { | |
2269 | j=l+1;//Set counter to end of line, so that we break. Unknown decimal situation. | |
2270 | } | |
b75a7d8f | 2271 | |
51004dcb A |
2272 | #ifdef FMT_DEBUG |
2273 | printf("Preparing to do fastpath parse: decimalChar=U+%04X, groupingChar=U+%04X, first ch=U+%04X intOnly=%c strictParse=%c\n", | |
2274 | decimalChar, groupingChar, ch, | |
2275 | (intOnly)?'y':'n', | |
2276 | (strictParse)?'y':'n'); | |
2277 | #endif | |
2278 | if(ch==0x002D) { // '-' | |
2279 | j=l+1;//=break - negative number. | |
2280 | ||
2281 | /* | |
2282 | parsedNum.append('-',err); | |
2283 | j+=U16_LENGTH(ch); | |
2284 | if(j<l) ch = text.char32At(j); | |
2285 | */ | |
2286 | } else { | |
2287 | parsedNum.append('+',err); | |
2288 | } | |
2289 | while(j<l) { | |
2290 | int32_t digit = ch - zero; | |
2291 | if(digit >=0 && digit <= 9) { | |
2292 | parsedNum.append((char)(digit + '0'), err); | |
2293 | if((digitCount>0) || digit!=0 || j==(l-1)) { | |
2294 | digitCount++; | |
2295 | } | |
2296 | } else if(ch == 0) { // break out | |
2297 | digitCount=-1; | |
2298 | break; | |
2299 | } else if(ch == decimalChar) { | |
2300 | parsedNum.append((char)('.'), err); | |
2301 | decimalChar=0; // no more decimals. | |
2302 | // fastParseHadDecimal=TRUE; | |
2303 | } else if(ch == lookForGroup) { | |
2304 | // ignore grouping char. No decimals, so it has to be an ignorable grouping sep | |
2305 | } else if(intOnly && (lookForGroup!=0) && !u_isdigit(ch)) { | |
2306 | // parsing integer only and can fall through | |
2307 | } else { | |
2308 | digitCount=-1; // fail - fall through to slow parse | |
2309 | break; | |
2310 | } | |
2311 | j+=U16_LENGTH(ch); | |
2312 | ch = text.char32At(j); // for next | |
2313 | } | |
2314 | if( | |
2315 | ((j==l)||intOnly) // end OR only parsing integer | |
2316 | && (digitCount>0)) { // and have at least one digit | |
2317 | #ifdef FMT_DEBUG | |
2318 | printf("PP -> %d, good = [%s] digitcount=%d, fGroupingSize=%d fGroupingSize2=%d!\n", j, parsedNum.data(), digitCount, fGroupingSize, fGroupingSize2); | |
2319 | #endif | |
2320 | fastParseOk=true; // Fast parse OK! | |
2321 | ||
2322 | #ifdef SKIP_OPT | |
2323 | debug("SKIP_OPT"); | |
2324 | /* for testing, try it the slow way. also */ | |
2325 | fastParseOk=false; | |
2326 | parsedNum.clear(); | |
2327 | #else | |
2328 | parsePosition.setIndex(position=j); | |
2329 | status[fgStatusInfinite]=false; | |
2330 | #endif | |
2331 | } else { | |
2332 | // was not OK. reset, retry | |
2333 | #ifdef FMT_DEBUG | |
2334 | printf("Fall through: j=%d, l=%d, digitCount=%d\n", j, l, digitCount); | |
2335 | #endif | |
2336 | parsedNum.clear(); | |
2337 | } | |
2338 | } else { | |
2339 | #ifdef FMT_DEBUG | |
2340 | printf("Could not fastpath parse. "); | |
2341 | printf("fFormatWidth=%d ", fFormatWidth); | |
2342 | printf("text.length()=%d ", text.length()); | |
2343 | printf("posPrefix=%p posSuffix=%p ", posPrefix, posSuffix); | |
2344 | ||
2345 | printf("\n"); | |
2346 | #endif | |
2347 | } | |
2348 | ||
2349 | if(!fastParseOk | |
2350 | #if UCONFIG_HAVE_PARSEALLINPUT | |
2351 | && fParseAllInput!=UNUM_YES | |
2352 | #endif | |
2353 | ) | |
2354 | { | |
b75a7d8f A |
2355 | // Match padding before prefix |
2356 | if (fFormatWidth > 0 && fPadPosition == kPadBeforePrefix) { | |
2357 | position = skipPadding(text, position); | |
2358 | } | |
2359 | ||
2360 | // Match positive and negative prefixes; prefer longest match. | |
729e4ab9 A |
2361 | int32_t posMatch = compareAffix(text, position, FALSE, TRUE, posPrefix, currencyParsing, type, currency); |
2362 | int32_t negMatch = compareAffix(text, position, TRUE, TRUE, negPrefix, currencyParsing, type, currency); | |
b75a7d8f A |
2363 | if (posMatch >= 0 && negMatch >= 0) { |
2364 | if (posMatch > negMatch) { | |
2365 | negMatch = -1; | |
2366 | } else if (negMatch > posMatch) { | |
2367 | posMatch = -1; | |
729e4ab9 | 2368 | } |
b75a7d8f A |
2369 | } |
2370 | if (posMatch >= 0) { | |
2371 | position += posMatch; | |
729e4ab9 | 2372 | parsedNum.append('+', err); |
b75a7d8f A |
2373 | } else if (negMatch >= 0) { |
2374 | position += negMatch; | |
729e4ab9 A |
2375 | parsedNum.append('-', err); |
2376 | } else if (strictParse){ | |
b75a7d8f A |
2377 | parsePosition.setErrorIndex(position); |
2378 | return FALSE; | |
4388f060 A |
2379 | } else { |
2380 | // Temporary set positive. This might be changed after checking suffix | |
2381 | parsedNum.append('+', err); | |
b75a7d8f A |
2382 | } |
2383 | ||
2384 | // Match padding before prefix | |
2385 | if (fFormatWidth > 0 && fPadPosition == kPadAfterPrefix) { | |
2386 | position = skipPadding(text, position); | |
2387 | } | |
729e4ab9 | 2388 | |
46f4442e | 2389 | if (! strictParse) { |
4388f060 | 2390 | position = skipUWhiteSpace(text, position); |
46f4442e | 2391 | } |
729e4ab9 | 2392 | |
b75a7d8f A |
2393 | // process digits or Inf, find decimal position |
2394 | const UnicodeString *inf = &getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); | |
2395 | int32_t infLen = (text.compare(position, inf->length(), *inf) | |
2396 | ? 0 : inf->length()); | |
2397 | position += infLen; // infLen is non-zero when it does equal to infinity | |
46f4442e | 2398 | status[fgStatusInfinite] = infLen != 0; |
729e4ab9 | 2399 | |
4388f060 | 2400 | if (infLen != 0) { |
729e4ab9 A |
2401 | parsedNum.append("Infinity", err); |
2402 | } else { | |
b75a7d8f A |
2403 | // We now have a string of digits, possibly with grouping symbols, |
2404 | // and decimal points. We want to process these into a DigitList. | |
2405 | // We don't want to put a bunch of leading zeros into the DigitList | |
2406 | // though, so we keep track of the location of the decimal point, | |
2407 | // put only significant digits into the DigitList, and adjust the | |
2408 | // exponent as needed. | |
2409 | ||
b75a7d8f | 2410 | |
46f4442e | 2411 | UBool strictFail = FALSE; // did we exit with a strict parse failure? |
46f4442e A |
2412 | int32_t lastGroup = -1; // where did we last see a grouping separator? |
2413 | int32_t digitStart = position; | |
2414 | int32_t gs2 = fGroupingSize2 == 0 ? fGroupingSize : fGroupingSize2; | |
729e4ab9 | 2415 | |
46f4442e | 2416 | const UnicodeString *decimalString; |
729e4ab9 | 2417 | if (fCurrencySignCount > fgCurrencySignCountZero) { |
4388f060 | 2418 | decimalString = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); |
b75a7d8f | 2419 | } else { |
4388f060 | 2420 | decimalString = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); |
b75a7d8f | 2421 | } |
46f4442e | 2422 | UChar32 decimalChar = decimalString->char32At(0); |
51004dcb A |
2423 | int32_t decimalStringLength = decimalString->length(); |
2424 | int32_t decimalCharLength = U16_LENGTH(decimalChar); | |
729e4ab9 | 2425 | |
b75a7d8f | 2426 | UBool sawDecimal = FALSE; |
4388f060 A |
2427 | UChar32 sawDecimalChar = 0xFFFF; |
2428 | UBool sawGrouping = FALSE; | |
2429 | UChar32 sawGroupingChar = 0xFFFF; | |
b75a7d8f A |
2430 | UBool sawDigit = FALSE; |
2431 | int32_t backup = -1; | |
2432 | int32_t digit; | |
729e4ab9 | 2433 | |
46f4442e | 2434 | // equivalent grouping and decimal support |
4388f060 A |
2435 | const UnicodeSet *decimalSet = NULL; |
2436 | const UnicodeSet *groupingSet = NULL; | |
729e4ab9 A |
2437 | |
2438 | if (decimalCharLength == decimalStringLength) { | |
4388f060 | 2439 | decimalSet = DecimalFormatStaticSets::getSimilarDecimals(decimalChar, strictParse); |
729e4ab9 A |
2440 | } |
2441 | ||
2442 | if (groupingCharLength == groupingStringLength) { | |
2443 | if (strictParse) { | |
4388f060 | 2444 | groupingSet = DecimalFormatStaticSets::gStaticSets->fStrictDefaultGroupingSeparators; |
729e4ab9 | 2445 | } else { |
4388f060 | 2446 | groupingSet = DecimalFormatStaticSets::gStaticSets->fDefaultGroupingSeparators; |
46f4442e | 2447 | } |
46f4442e | 2448 | } |
729e4ab9 | 2449 | |
4388f060 A |
2450 | // We need to test groupingChar and decimalChar separately from groupingSet and decimalSet, if the sets are even initialized. |
2451 | // If sawDecimal is TRUE, only consider sawDecimalChar and NOT decimalSet | |
2452 | // If a character matches decimalSet, don't consider it to be a member of the groupingSet. | |
729e4ab9 | 2453 | |
b75a7d8f A |
2454 | // We have to track digitCount ourselves, because digits.fCount will |
2455 | // pin when the maximum allowable digits is reached. | |
2456 | int32_t digitCount = 0; | |
729e4ab9 | 2457 | int32_t integerDigitCount = 0; |
b75a7d8f A |
2458 | |
2459 | for (; position < textLength; ) | |
2460 | { | |
2461 | UChar32 ch = text.char32At(position); | |
2462 | ||
2463 | /* We recognize all digit ranges, not only the Latin digit range | |
2464 | * '0'..'9'. We do so by using the Character.digit() method, | |
2465 | * which converts a valid Unicode digit to the range 0..9. | |
2466 | * | |
2467 | * The character 'ch' may be a digit. If so, place its value | |
2468 | * from 0 to 9 in 'digit'. First try using the locale digit, | |
2469 | * which may or MAY NOT be a standard Unicode digit range. If | |
2470 | * this fails, try using the standard Unicode digit ranges by | |
729e4ab9 | 2471 | * calling Character.digit(). If this also fails, digit will |
b75a7d8f A |
2472 | * have a value outside the range 0..9. |
2473 | */ | |
2474 | digit = ch - zero; | |
2475 | if (digit < 0 || digit > 9) | |
2476 | { | |
2477 | digit = u_charDigitValue(ch); | |
2478 | } | |
729e4ab9 A |
2479 | |
2480 | // As a last resort, look through the localized digits if the zero digit | |
2481 | // is not a "standard" Unicode digit. | |
2482 | if ( (digit < 0 || digit > 9) && u_charDigitValue(zero) != 0) { | |
2483 | digit = 0; | |
2484 | // Already check above (digit = ch - zero) for ch==zero; the only check we need to do here is: | |
2485 | // if \u3007 is treated as 0 for parsing, \u96F6 should be too. Otherwise check for nonzero digits. | |
2486 | if ( zero!=0x3007 || ch!=0x96F6 ) { | |
2487 | for (digit = 1 ; digit < 10 ; digit++ ) { | |
2488 | if ( getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kOneDigitSymbol+digit-1)).char32At(0) == ch ) { | |
46f4442e A |
2489 | break; |
2490 | } | |
46f4442e A |
2491 | } |
2492 | } | |
b75a7d8f | 2493 | } |
b75a7d8f | 2494 | |
729e4ab9 A |
2495 | if (digit >= 0 && digit <= 9) |
2496 | { | |
46f4442e A |
2497 | if (strictParse && backup != -1) { |
2498 | // comma followed by digit, so group before comma is a | |
2499 | // secondary group. If there was a group separator | |
2500 | // before that, the group must == the secondary group | |
2501 | // length, else it can be <= the the secondary group | |
2502 | // length. | |
2503 | if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) || | |
2504 | (lastGroup == -1 && position - digitStart - 1 > gs2)) { | |
2505 | strictFail = TRUE; | |
2506 | break; | |
2507 | } | |
729e4ab9 | 2508 | |
46f4442e | 2509 | lastGroup = backup; |
b75a7d8f | 2510 | } |
729e4ab9 A |
2511 | |
2512 | // Cancel out backup setting (see grouping handler below) | |
46f4442e A |
2513 | backup = -1; |
2514 | sawDigit = TRUE; | |
729e4ab9 A |
2515 | |
2516 | // Note: this will append leading zeros | |
2517 | parsedNum.append((char)(digit + '0'), err); | |
2518 | ||
2519 | // count any digit that's not a leading zero | |
2520 | if (digit > 0 || digitCount > 0 || sawDecimal) { | |
2521 | digitCount += 1; | |
2522 | ||
2523 | // count any integer digit that's not a leading zero | |
2524 | if (! sawDecimal) { | |
2525 | integerDigitCount += 1; | |
2526 | } | |
b75a7d8f | 2527 | } |
729e4ab9 | 2528 | |
b75a7d8f A |
2529 | position += U16_LENGTH(ch); |
2530 | } | |
4388f060 A |
2531 | else if (groupingStringLength > 0 && |
2532 | matchGrouping(groupingChar, sawGrouping, sawGroupingChar, groupingSet, | |
2533 | decimalChar, decimalSet, | |
51004dcb | 2534 | ch) && groupingUsed) |
b75a7d8f | 2535 | { |
46f4442e A |
2536 | if (sawDecimal) { |
2537 | break; | |
2538 | } | |
729e4ab9 | 2539 | |
46f4442e | 2540 | if (strictParse) { |
729e4ab9 A |
2541 | if ( (!sawDigit && groupingSet!=NULL && u_isWhitespace(ch)) || backup != -1 ) { |
2542 | // We differ from the ICU4J code by allowing a leading group sep in strict mode (for | |
2543 | // backward compatibility) as long as it is not one of the breaking whitespace characters | |
2544 | // that is only treated as a group separator because of the equivalence set. If we get | |
2545 | // here it is because the leading sep was such a breaking space, or there were multiple | |
2546 | // group separators in a row. Note that the DecimalFormat documentation says | |
2547 | // "During parsing, grouping separators are ignored" and that was for strict parsing, | |
2548 | // so we may need to further revisit this strictParse restriction to ensure compatibility. | |
2549 | // Also note: u_isWhitespace is true for all Zs/Zl/Zp except the no-break ones: 00A0,2007,202F. | |
2550 | // In CLDR, all locales that have space as a group separator use 00A0 (NBSP). | |
46f4442e A |
2551 | strictFail = TRUE; |
2552 | break; | |
2553 | } | |
2554 | } | |
729e4ab9 | 2555 | |
b75a7d8f A |
2556 | // Ignore grouping characters, if we are using them, but require |
2557 | // that they be followed by a digit. Otherwise we backup and | |
2558 | // reprocess them. | |
2559 | backup = position; | |
46f4442e | 2560 | position += groupingStringLength; |
4388f060 A |
2561 | sawGrouping=TRUE; |
2562 | // Once we see a grouping character, we only accept that grouping character from then on. | |
2563 | sawGroupingChar=ch; | |
b75a7d8f | 2564 | } |
4388f060 | 2565 | else if (matchDecimal(decimalChar,sawDecimal,sawDecimalChar, decimalSet, ch)) |
b75a7d8f | 2566 | { |
46f4442e A |
2567 | if (strictParse) { |
2568 | if (backup != -1 || | |
2569 | (lastGroup != -1 && position - lastGroup != fGroupingSize + 1)) { | |
2570 | strictFail = TRUE; | |
2571 | break; | |
2572 | } | |
2573 | } | |
729e4ab9 A |
2574 | |
2575 | // If we're only parsing integers, or if we ALREADY saw the | |
b75a7d8f | 2576 | // decimal, then don't parse this one. |
46f4442e | 2577 | if (isParseIntegerOnly() || sawDecimal) { |
4388f060 | 2578 | break; |
46f4442e | 2579 | } |
729e4ab9 A |
2580 | |
2581 | parsedNum.append('.', err); | |
46f4442e | 2582 | position += decimalStringLength; |
b75a7d8f | 2583 | sawDecimal = TRUE; |
4388f060 A |
2584 | // Once we see a decimal character, we only accept that decimal character from then on. |
2585 | sawDecimalChar=ch; | |
2586 | // decimalSet is considered to consist of (ch,ch) | |
b75a7d8f | 2587 | } |
4388f060 | 2588 | else { |
b75a7d8f | 2589 | |
51004dcb A |
2590 | if(!fBoolFlags.contains(UNUM_PARSE_NO_EXPONENT) || // don't parse if this is set unless.. |
2591 | fUseExponentialNotation /* should be: isScientificNotation() but it is not const (?!) see bug #9619 */) { // .. it's an exponent format - ignore setting and parse anyways | |
2592 | const UnicodeString *tmp; | |
2593 | tmp = &getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); | |
2594 | // TODO: CASE | |
2595 | if (!text.caseCompare(position, tmp->length(), *tmp, U_FOLD_CASE_DEFAULT)) // error code is set below if !sawDigit | |
b75a7d8f | 2596 | { |
51004dcb A |
2597 | // Parse sign, if present |
2598 | int32_t pos = position + tmp->length(); | |
2599 | char exponentSign = '+'; | |
2600 | ||
2601 | if (pos < textLength) | |
b75a7d8f | 2602 | { |
51004dcb | 2603 | tmp = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); |
b75a7d8f A |
2604 | if (!text.compare(pos, tmp->length(), *tmp)) |
2605 | { | |
2606 | pos += tmp->length(); | |
b75a7d8f | 2607 | } |
51004dcb A |
2608 | else { |
2609 | tmp = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); | |
2610 | if (!text.compare(pos, tmp->length(), *tmp)) | |
2611 | { | |
2612 | exponentSign = '-'; | |
2613 | pos += tmp->length(); | |
2614 | } | |
2615 | } | |
b75a7d8f | 2616 | } |
b75a7d8f | 2617 | |
51004dcb A |
2618 | UBool sawExponentDigit = FALSE; |
2619 | while (pos < textLength) { | |
2620 | ch = text[(int32_t)pos]; | |
2621 | digit = ch - zero; | |
b75a7d8f | 2622 | |
51004dcb A |
2623 | if (digit < 0 || digit > 9) { |
2624 | digit = u_charDigitValue(ch); | |
2625 | } | |
2626 | if (0 <= digit && digit <= 9) { | |
2627 | if (!sawExponentDigit) { | |
2628 | parsedNum.append('E', err); | |
2629 | parsedNum.append(exponentSign, err); | |
2630 | sawExponentDigit = TRUE; | |
2631 | } | |
2632 | ++pos; | |
2633 | parsedNum.append((char)(digit + '0'), err); | |
2634 | } else { | |
2635 | break; | |
729e4ab9 | 2636 | } |
b75a7d8f | 2637 | } |
b75a7d8f | 2638 | |
51004dcb A |
2639 | if (sawExponentDigit) { |
2640 | position = pos; // Advance past the exponent | |
2641 | } | |
b75a7d8f | 2642 | |
51004dcb A |
2643 | break; // Whether we fail or succeed, we exit this loop |
2644 | } else { | |
2645 | break; | |
2646 | } | |
2647 | } else { // not parsing exponent | |
b75a7d8f | 2648 | break; |
51004dcb | 2649 | } |
b75a7d8f A |
2650 | } |
2651 | } | |
729e4ab9 | 2652 | |
b75a7d8f A |
2653 | if (backup != -1) |
2654 | { | |
2655 | position = backup; | |
2656 | } | |
2657 | ||
46f4442e A |
2658 | if (strictParse && !sawDecimal) { |
2659 | if (lastGroup != -1 && position - lastGroup != fGroupingSize + 1) { | |
2660 | strictFail = TRUE; | |
2661 | } | |
2662 | } | |
46f4442e A |
2663 | |
2664 | if (strictFail) { | |
4388f060 | 2665 | // only set with strictParse and a grouping separator error |
729e4ab9 | 2666 | |
46f4442e A |
2667 | parsePosition.setIndex(oldStart); |
2668 | parsePosition.setErrorIndex(position); | |
51004dcb | 2669 | debug("strictFail!"); |
46f4442e A |
2670 | return FALSE; |
2671 | } | |
729e4ab9 | 2672 | |
b75a7d8f | 2673 | // If there was no decimal point we have an integer |
b75a7d8f A |
2674 | |
2675 | // If none of the text string was recognized. For example, parse | |
2676 | // "x" with pattern "#0.00" (return index and error index both 0) | |
2677 | // parse "$" with pattern "$#0.00". (return index 0 and error index | |
2678 | // 1). | |
2679 | if (!sawDigit && digitCount == 0) { | |
51004dcb A |
2680 | #ifdef FMT_DEBUG |
2681 | debug("none of text rec"); | |
2682 | printf("position=%d\n",position); | |
2683 | #endif | |
b75a7d8f A |
2684 | parsePosition.setIndex(oldStart); |
2685 | parsePosition.setErrorIndex(oldStart); | |
2686 | return FALSE; | |
2687 | } | |
2688 | } | |
2689 | ||
2690 | // Match padding before suffix | |
2691 | if (fFormatWidth > 0 && fPadPosition == kPadBeforeSuffix) { | |
2692 | position = skipPadding(text, position); | |
2693 | } | |
2694 | ||
46f4442e | 2695 | int32_t posSuffixMatch = -1, negSuffixMatch = -1; |
729e4ab9 | 2696 | |
b75a7d8f | 2697 | // Match positive and negative suffixes; prefer longest match. |
46f4442e | 2698 | if (posMatch >= 0 || (!strictParse && negMatch < 0)) { |
4388f060 | 2699 | posSuffixMatch = compareAffix(text, position, FALSE, FALSE, posSuffix, currencyParsing, type, currency); |
b75a7d8f A |
2700 | } |
2701 | if (negMatch >= 0) { | |
4388f060 | 2702 | negSuffixMatch = compareAffix(text, position, TRUE, FALSE, negSuffix, currencyParsing, type, currency); |
b75a7d8f | 2703 | } |
46f4442e A |
2704 | if (posSuffixMatch >= 0 && negSuffixMatch >= 0) { |
2705 | if (posSuffixMatch > negSuffixMatch) { | |
4388f060 | 2706 | negSuffixMatch = -1; |
46f4442e | 2707 | } else if (negSuffixMatch > posSuffixMatch) { |
4388f060 | 2708 | posSuffixMatch = -1; |
729e4ab9 | 2709 | } |
b75a7d8f A |
2710 | } |
2711 | ||
2712 | // Fail if neither or both | |
46f4442e | 2713 | if (strictParse && ((posSuffixMatch >= 0) == (negSuffixMatch >= 0))) { |
b75a7d8f | 2714 | parsePosition.setErrorIndex(position); |
51004dcb | 2715 | debug("neither or both"); |
b75a7d8f A |
2716 | return FALSE; |
2717 | } | |
2718 | ||
729e4ab9 | 2719 | position += (posSuffixMatch >= 0 ? posSuffixMatch : (negSuffixMatch >= 0 ? negSuffixMatch : 0)); |
b75a7d8f A |
2720 | |
2721 | // Match padding before suffix | |
2722 | if (fFormatWidth > 0 && fPadPosition == kPadAfterSuffix) { | |
2723 | position = skipPadding(text, position); | |
2724 | } | |
2725 | ||
2726 | parsePosition.setIndex(position); | |
2727 | ||
729e4ab9 | 2728 | parsedNum.data()[0] = (posSuffixMatch >= 0 || (!strictParse && negMatch < 0 && negSuffixMatch < 0)) ? '+' : '-'; |
51004dcb A |
2729 | #ifdef FMT_DEBUG |
2730 | printf("PP -> %d, SLOW = [%s]! pp=%d, os=%d, err=%s\n", position, parsedNum.data(), parsePosition.getIndex(),oldStart,u_errorName(err)); | |
2731 | #endif | |
2732 | } /* end SLOW parse */ | |
2733 | if(parsePosition.getIndex() == oldStart) | |
2734 | { | |
2735 | #ifdef FMT_DEBUG | |
2736 | printf(" PP didnt move, err\n"); | |
2737 | #endif | |
2738 | parsePosition.setErrorIndex(position); | |
2739 | return FALSE; | |
2740 | } | |
2741 | #if UCONFIG_HAVE_PARSEALLINPUT | |
2742 | else if (fParseAllInput==UNUM_YES&&parsePosition.getIndex()!=textLength) | |
b75a7d8f | 2743 | { |
51004dcb A |
2744 | #ifdef FMT_DEBUG |
2745 | printf(" PP didnt consume all (UNUM_YES), err\n"); | |
2746 | #endif | |
b75a7d8f A |
2747 | parsePosition.setErrorIndex(position); |
2748 | return FALSE; | |
2749 | } | |
51004dcb A |
2750 | #endif |
2751 | // uint32_t bits = (fastParseOk?kFastpathOk:0) | | |
2752 | // (fastParseHadDecimal?0:kNoDecimal); | |
2753 | //printf("FPOK=%d, FPHD=%d, bits=%08X\n", fastParseOk, fastParseHadDecimal, bits); | |
2754 | digits.set(parsedNum.toStringPiece(), | |
2755 | err, | |
2756 | 0//bits | |
2757 | ); | |
729e4ab9 A |
2758 | |
2759 | if (U_FAILURE(err)) { | |
51004dcb A |
2760 | #ifdef FMT_DEBUG |
2761 | printf(" err setting %s\n", u_errorName(err)); | |
2762 | #endif | |
729e4ab9 A |
2763 | parsePosition.setErrorIndex(position); |
2764 | return FALSE; | |
2765 | } | |
b75a7d8f A |
2766 | return TRUE; |
2767 | } | |
2768 | ||
2769 | /** | |
2770 | * Starting at position, advance past a run of pad characters, if any. | |
2771 | * Return the index of the first character after position that is not a pad | |
2772 | * character. Result is >= position. | |
2773 | */ | |
2774 | int32_t DecimalFormat::skipPadding(const UnicodeString& text, int32_t position) const { | |
2775 | int32_t padLen = U16_LENGTH(fPad); | |
2776 | while (position < text.length() && | |
2777 | text.char32At(position) == fPad) { | |
2778 | position += padLen; | |
2779 | } | |
2780 | return position; | |
2781 | } | |
2782 | ||
2783 | /** | |
2784 | * Return the length matched by the given affix, or -1 if none. | |
2785 | * Runs of white space in the affix, match runs of white space in | |
2786 | * the input. Pattern white space and input white space are | |
2787 | * determined differently; see code. | |
2788 | * @param text input text | |
2789 | * @param pos offset into input at which to begin matching | |
2790 | * @param isNegative | |
2791 | * @param isPrefix | |
729e4ab9 A |
2792 | * @param affixPat affix pattern used for currency affix comparison. |
2793 | * @param currencyParsing whether it is currency parsing or not | |
2794 | * @param type the currency type to parse against, LONG_NAME only or not. | |
374ca955 A |
2795 | * @param currency return value for parsed currency, for generic |
2796 | * currency parsing mode, or null for normal parsing. In generic | |
2797 | * currency parsing mode, any currency is parsed, not just the | |
2798 | * currency that this formatter is set to. | |
b75a7d8f A |
2799 | * @return length of input that matches, or -1 if match failure |
2800 | */ | |
2801 | int32_t DecimalFormat::compareAffix(const UnicodeString& text, | |
2802 | int32_t pos, | |
2803 | UBool isNegative, | |
374ca955 | 2804 | UBool isPrefix, |
729e4ab9 A |
2805 | const UnicodeString* affixPat, |
2806 | UBool currencyParsing, | |
2807 | int8_t type, | |
46f4442e A |
2808 | UChar* currency) const |
2809 | { | |
2810 | const UnicodeString *patternToCompare; | |
729e4ab9 A |
2811 | if (fCurrencyChoice != NULL || currency != NULL || |
2812 | (fCurrencySignCount > fgCurrencySignCountZero && currencyParsing)) { | |
2813 | ||
2814 | if (affixPat != NULL) { | |
2815 | return compareComplexAffix(*affixPat, text, pos, type, currency); | |
46f4442e | 2816 | } |
b75a7d8f | 2817 | } |
729e4ab9 | 2818 | |
46f4442e A |
2819 | if (isNegative) { |
2820 | if (isPrefix) { | |
2821 | patternToCompare = &fNegativePrefix; | |
2822 | } | |
2823 | else { | |
2824 | patternToCompare = &fNegativeSuffix; | |
2825 | } | |
b75a7d8f | 2826 | } |
46f4442e A |
2827 | else { |
2828 | if (isPrefix) { | |
2829 | patternToCompare = &fPositivePrefix; | |
2830 | } | |
2831 | else { | |
2832 | patternToCompare = &fPositiveSuffix; | |
2833 | } | |
2834 | } | |
4388f060 | 2835 | return compareSimpleAffix(*patternToCompare, text, pos, isLenient()); |
b75a7d8f A |
2836 | } |
2837 | ||
2838 | /** | |
2839 | * Return the length matched by the given affix, or -1 if none. | |
2840 | * Runs of white space in the affix, match runs of white space in | |
2841 | * the input. Pattern white space and input white space are | |
2842 | * determined differently; see code. | |
2843 | * @param affix pattern string, taken as a literal | |
2844 | * @param input input text | |
2845 | * @param pos offset into input at which to begin matching | |
2846 | * @return length of input that matches, or -1 if match failure | |
2847 | */ | |
2848 | int32_t DecimalFormat::compareSimpleAffix(const UnicodeString& affix, | |
2849 | const UnicodeString& input, | |
46f4442e | 2850 | int32_t pos, |
4388f060 | 2851 | UBool lenient) { |
b75a7d8f | 2852 | int32_t start = pos; |
7393aa2f A |
2853 | UChar32 affixChar = affix.char32At(0); |
2854 | int32_t affixLength = affix.length(); | |
2855 | int32_t inputLength = input.length(); | |
2856 | int32_t affixCharLength = U16_LENGTH(affixChar); | |
2857 | UnicodeSet *affixSet; | |
729e4ab9 | 2858 | |
4388f060 | 2859 | if (!lenient) { |
7393aa2f A |
2860 | affixSet = DecimalFormatStaticSets::gStaticSets->fStrictDashEquivalents; |
2861 | ||
2862 | // If the affix is exactly one character long and that character | |
2863 | // is in the dash set and the very next input character is also | |
2864 | // in the dash set, return a match. | |
2865 | if (affixCharLength == affixLength && affixSet->contains(affixChar)) { | |
2866 | if (affixSet->contains(input.char32At(pos))) { | |
2867 | return 1; | |
2868 | } | |
2869 | } | |
729e4ab9 | 2870 | |
4388f060 A |
2871 | for (int32_t i = 0; i < affixLength; ) { |
2872 | UChar32 c = affix.char32At(i); | |
2873 | int32_t len = U16_LENGTH(c); | |
2874 | if (PatternProps::isWhiteSpace(c)) { | |
2875 | // We may have a pattern like: \u200F \u0020 | |
2876 | // and input text like: \u200F \u0020 | |
2877 | // Note that U+200F and U+0020 are Pattern_White_Space but only | |
2878 | // U+0020 is UWhiteSpace. So we have to first do a direct | |
2879 | // match of the run of Pattern_White_Space in the pattern, | |
2880 | // then match any extra characters. | |
2881 | UBool literalMatch = FALSE; | |
2882 | while (pos < inputLength && | |
2883 | input.char32At(pos) == c) { | |
2884 | literalMatch = TRUE; | |
2885 | i += len; | |
2886 | pos += len; | |
2887 | if (i == affixLength) { | |
2888 | break; | |
2889 | } | |
2890 | c = affix.char32At(i); | |
2891 | len = U16_LENGTH(c); | |
2892 | if (!PatternProps::isWhiteSpace(c)) { | |
2893 | break; | |
2894 | } | |
2895 | } | |
2896 | ||
2897 | // Advance over run in pattern | |
2898 | i = skipPatternWhiteSpace(affix, i); | |
51004dcb A |
2899 | |
2900 | UBool patternWhitespaceWasJustMark = (i == 1 && (c == 0x200E || c == 0x200F)); | |
4388f060 A |
2901 | |
2902 | // Advance over run in input text | |
2903 | // Must see at least one white space char in input, | |
51004dcb A |
2904 | // unless we've already matched some characters literally, |
2905 | // or unless the pattern whitespace was just LRM/RLM | |
4388f060 A |
2906 | int32_t s = pos; |
2907 | pos = skipUWhiteSpace(input, pos); | |
51004dcb | 2908 | if (pos == s && !literalMatch && !patternWhitespaceWasJustMark) { |
4388f060 A |
2909 | return -1; |
2910 | } | |
2911 | ||
2912 | // If we skip UWhiteSpace in the input text, we need to skip it in the pattern. | |
2913 | // Otherwise, the previous lines may have skipped over text (such as U+00A0) that | |
2914 | // is also in the affix. | |
2915 | i = skipUWhiteSpace(affix, i); | |
2916 | } else { | |
2917 | if (pos < inputLength && | |
2918 | input.char32At(pos) == c) { | |
2919 | i += len; | |
2920 | pos += len; | |
2921 | } else { | |
2922 | return -1; | |
2923 | } | |
2924 | } | |
2925 | } | |
729e4ab9 | 2926 | } else { |
4388f060 | 2927 | UBool match = FALSE; |
7393aa2f A |
2928 | |
2929 | affixSet = DecimalFormatStaticSets::gStaticSets->fDashEquivalents; | |
729e4ab9 | 2930 | |
7393aa2f A |
2931 | if (affixCharLength == affixLength && affixSet->contains(affixChar)) { |
2932 | pos = skipUWhiteSpace(input, pos); | |
2933 | ||
2934 | if (affixSet->contains(input.char32At(pos))) { | |
2935 | return pos - start + 1; | |
2936 | } | |
2937 | } | |
729e4ab9 | 2938 | |
4388f060 A |
2939 | for (int32_t i = 0; i < affixLength; ) |
2940 | { | |
2941 | //i = skipRuleWhiteSpace(affix, i); | |
2942 | i = skipUWhiteSpace(affix, i); | |
2943 | pos = skipUWhiteSpace(input, pos); | |
729e4ab9 | 2944 | |
4388f060 A |
2945 | if (i >= affixLength || pos >= inputLength) { |
2946 | break; | |
2947 | } | |
729e4ab9 | 2948 | |
4388f060 A |
2949 | UChar32 c = affix.char32At(i); |
2950 | int32_t len = U16_LENGTH(c); | |
729e4ab9 | 2951 | |
4388f060 A |
2952 | if (input.char32At(pos) != c) { |
2953 | return -1; | |
2954 | } | |
729e4ab9 | 2955 | |
4388f060 A |
2956 | match = TRUE; |
2957 | i += len; | |
2958 | pos += len; | |
2959 | } | |
729e4ab9 | 2960 | |
4388f060 A |
2961 | if (affixLength > 0 && ! match) { |
2962 | return -1; | |
2963 | } | |
729e4ab9 | 2964 | } |
b75a7d8f A |
2965 | return pos - start; |
2966 | } | |
2967 | ||
2968 | /** | |
4388f060 | 2969 | * Skip over a run of zero or more Pattern_White_Space characters at |
b75a7d8f A |
2970 | * pos in text. |
2971 | */ | |
4388f060 A |
2972 | int32_t DecimalFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) { |
2973 | const UChar* s = text.getBuffer(); | |
2974 | return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s); | |
b75a7d8f A |
2975 | } |
2976 | ||
2977 | /** | |
2978 | * Skip over a run of zero or more isUWhiteSpace() characters at pos | |
2979 | * in text. | |
2980 | */ | |
2981 | int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { | |
2982 | while (pos < text.length()) { | |
2983 | UChar32 c = text.char32At(pos); | |
51004dcb | 2984 | if (!u_isUWhiteSpace(c) && c!=0x200E && c!=0x200F) { // u_isUWhiteSpace does not include LRM,RLM |
b75a7d8f A |
2985 | break; |
2986 | } | |
2987 | pos += U16_LENGTH(c); | |
2988 | } | |
2989 | return pos; | |
2990 | } | |
2991 | ||
2992 | /** | |
2993 | * Return the length matched by the given affix, or -1 if none. | |
2994 | * @param affixPat pattern string | |
2995 | * @param input input text | |
2996 | * @param pos offset into input at which to begin matching | |
729e4ab9 | 2997 | * @param type the currency type to parse against, LONG_NAME only or not. |
374ca955 A |
2998 | * @param currency return value for parsed currency, for generic |
2999 | * currency parsing mode, or null for normal parsing. In generic | |
3000 | * currency parsing mode, any currency is parsed, not just the | |
3001 | * currency that this formatter is set to. | |
b75a7d8f A |
3002 | * @return length of input that matches, or -1 if match failure |
3003 | */ | |
3004 | int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, | |
3005 | const UnicodeString& text, | |
374ca955 | 3006 | int32_t pos, |
729e4ab9 | 3007 | int8_t type, |
73c04bcf A |
3008 | UChar* currency) const |
3009 | { | |
3010 | int32_t start = pos; | |
374ca955 | 3011 | U_ASSERT(currency != NULL || |
729e4ab9 A |
3012 | (fCurrencyChoice != NULL && *getCurrency() != 0) || |
3013 | fCurrencySignCount > fgCurrencySignCountZero); | |
b75a7d8f | 3014 | |
729e4ab9 A |
3015 | for (int32_t i=0; |
3016 | i<affixPat.length() && pos >= 0; ) { | |
b75a7d8f A |
3017 | UChar32 c = affixPat.char32At(i); |
3018 | i += U16_LENGTH(c); | |
3019 | ||
3020 | if (c == kQuote) { | |
3021 | U_ASSERT(i <= affixPat.length()); | |
3022 | c = affixPat.char32At(i); | |
3023 | i += U16_LENGTH(c); | |
3024 | ||
3025 | const UnicodeString* affix = NULL; | |
3026 | ||
3027 | switch (c) { | |
3028 | case kCurrencySign: { | |
729e4ab9 A |
3029 | // since the currency names in choice format is saved |
3030 | // the same way as other currency names, | |
3031 | // do not need to do currency choice parsing here. | |
3032 | // the general currency parsing parse against all names, | |
3033 | // including names in choice format. | |
b75a7d8f A |
3034 | UBool intl = i<affixPat.length() && |
3035 | affixPat.char32At(i) == kCurrencySign; | |
729e4ab9 A |
3036 | if (intl) { |
3037 | ++i; | |
3038 | } | |
3039 | UBool plural = i<affixPat.length() && | |
3040 | affixPat.char32At(i) == kCurrencySign; | |
3041 | if (plural) { | |
3042 | ++i; | |
3043 | intl = FALSE; | |
3044 | } | |
374ca955 A |
3045 | // Parse generic currency -- anything for which we |
3046 | // have a display name, or any 3-letter ISO code. | |
729e4ab9 A |
3047 | // Try to parse display name for our locale; first |
3048 | // determine our locale. | |
3049 | const char* loc = fCurrencyPluralInfo->getLocale().getName(); | |
3050 | ParsePosition ppos(pos); | |
3051 | UChar curr[4]; | |
3052 | UErrorCode ec = U_ZERO_ERROR; | |
3053 | // Delegate parse of display name => ISO code to Currency | |
3054 | uprv_parseCurrency(loc, text, ppos, type, curr, ec); | |
3055 | ||
3056 | // If parse succeeds, populate currency[0] | |
3057 | if (U_SUCCESS(ec) && ppos.getIndex() != pos) { | |
3058 | if (currency) { | |
374ca955 | 3059 | u_strcpy(currency, curr); |
4388f060 | 3060 | } else { |
729e4ab9 A |
3061 | // The formatter is currency-style but the client has not requested |
3062 | // the value of the parsed currency. In this case, if that value does | |
3063 | // not match the formatter's current value, then the parse fails. | |
3064 | UChar effectiveCurr[4]; | |
3065 | getEffectiveCurrency(effectiveCurr, ec); | |
4388f060 | 3066 | if ( U_FAILURE(ec) || u_strncmp(curr,effectiveCurr,4) != 0 ) { |
729e4ab9 A |
3067 | pos = -1; |
3068 | continue; | |
3069 | } | |
374ca955 | 3070 | } |
729e4ab9 | 3071 | pos = ppos.getIndex(); |
4388f060 | 3072 | } else if (!isLenient()){ |
729e4ab9 | 3073 | pos = -1; |
b75a7d8f A |
3074 | } |
3075 | continue; | |
3076 | } | |
3077 | case kPatternPercent: | |
3078 | affix = &getConstSymbol(DecimalFormatSymbols::kPercentSymbol); | |
3079 | break; | |
3080 | case kPatternPerMill: | |
3081 | affix = &getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); | |
3082 | break; | |
3083 | case kPatternPlus: | |
3084 | affix = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); | |
3085 | break; | |
3086 | case kPatternMinus: | |
3087 | affix = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); | |
3088 | break; | |
3089 | default: | |
3090 | // fall through to affix!=0 test, which will fail | |
3091 | break; | |
3092 | } | |
3093 | ||
3094 | if (affix != NULL) { | |
3095 | pos = match(text, pos, *affix); | |
3096 | continue; | |
3097 | } | |
3098 | } | |
3099 | ||
3100 | pos = match(text, pos, c); | |
4388f060 A |
3101 | if (PatternProps::isWhiteSpace(c)) { |
3102 | i = skipPatternWhiteSpace(affixPat, i); | |
b75a7d8f A |
3103 | } |
3104 | } | |
73c04bcf | 3105 | return pos - start; |
b75a7d8f A |
3106 | } |
3107 | ||
3108 | /** | |
3109 | * Match a single character at text[pos] and return the index of the | |
3110 | * next character upon success. Return -1 on failure. If | |
4388f060 | 3111 | * ch is a Pattern_White_Space then match a run of white space in text. |
b75a7d8f A |
3112 | */ |
3113 | int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, UChar32 ch) { | |
4388f060 | 3114 | if (PatternProps::isWhiteSpace(ch)) { |
b75a7d8f A |
3115 | // Advance over run of white space in input text |
3116 | // Must see at least one white space char in input | |
3117 | int32_t s = pos; | |
4388f060 | 3118 | pos = skipPatternWhiteSpace(text, pos); |
b75a7d8f A |
3119 | if (pos == s) { |
3120 | return -1; | |
3121 | } | |
3122 | return pos; | |
3123 | } | |
3124 | return (pos >= 0 && text.char32At(pos) == ch) ? | |
3125 | (pos + U16_LENGTH(ch)) : -1; | |
3126 | } | |
3127 | ||
3128 | /** | |
3129 | * Match a string at text[pos] and return the index of the next | |
3130 | * character upon success. Return -1 on failure. Match a run of | |
3131 | * white space in str with a run of white space in text. | |
3132 | */ | |
3133 | int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const UnicodeString& str) { | |
3134 | for (int32_t i=0; i<str.length() && pos >= 0; ) { | |
3135 | UChar32 ch = str.char32At(i); | |
3136 | i += U16_LENGTH(ch); | |
4388f060 A |
3137 | if (PatternProps::isWhiteSpace(ch)) { |
3138 | i = skipPatternWhiteSpace(str, i); | |
b75a7d8f A |
3139 | } |
3140 | pos = match(text, pos, ch); | |
3141 | } | |
3142 | return pos; | |
3143 | } | |
3144 | ||
46f4442e | 3145 | UBool DecimalFormat::matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, |
729e4ab9 | 3146 | UnicodeSet *sset, UChar32 schar) |
46f4442e | 3147 | { |
4388f060 A |
3148 | if (sset != NULL) { |
3149 | return sset->contains(schar); | |
3150 | } | |
729e4ab9 | 3151 | |
4388f060 | 3152 | return text.compare(position, length, symbol) == 0; |
46f4442e A |
3153 | } |
3154 | ||
4388f060 A |
3155 | UBool DecimalFormat::matchDecimal(UChar32 symbolChar, |
3156 | UBool sawDecimal, UChar32 sawDecimalChar, | |
3157 | const UnicodeSet *sset, UChar32 schar) { | |
3158 | if(sawDecimal) { | |
3159 | return schar==sawDecimalChar; | |
3160 | } else if(schar==symbolChar) { | |
3161 | return TRUE; | |
3162 | } else if(sset!=NULL) { | |
3163 | return sset->contains(schar); | |
3164 | } else { | |
3165 | return FALSE; | |
3166 | } | |
3167 | } | |
3168 | ||
3169 | UBool DecimalFormat::matchGrouping(UChar32 groupingChar, | |
3170 | UBool sawGrouping, UChar32 sawGroupingChar, | |
3171 | const UnicodeSet *sset, | |
3172 | UChar32 /*decimalChar*/, const UnicodeSet *decimalSet, | |
3173 | UChar32 schar) { | |
3174 | if(sawGrouping) { | |
3175 | return schar==sawGroupingChar; // previously found | |
3176 | } else if(schar==groupingChar) { | |
3177 | return TRUE; // char from symbols | |
3178 | } else if(sset!=NULL) { | |
3179 | return sset->contains(schar) && // in groupingSet but... | |
3180 | ((decimalSet==NULL) || !decimalSet->contains(schar)); // Exclude decimalSet from groupingSet | |
3181 | } else { | |
3182 | return FALSE; | |
3183 | } | |
3184 | } | |
3185 | ||
3186 | ||
729e4ab9 | 3187 | |
b75a7d8f A |
3188 | //------------------------------------------------------------------------------ |
3189 | // Gets the pointer to the localized decimal format symbols | |
3190 | ||
3191 | const DecimalFormatSymbols* | |
3192 | DecimalFormat::getDecimalFormatSymbols() const | |
3193 | { | |
3194 | return fSymbols; | |
3195 | } | |
3196 | ||
3197 | //------------------------------------------------------------------------------ | |
3198 | // De-owning the current localized symbols and adopt the new symbols. | |
3199 | ||
3200 | void | |
3201 | DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) | |
3202 | { | |
374ca955 A |
3203 | if (symbolsToAdopt == NULL) { |
3204 | return; // do not allow caller to set fSymbols to NULL | |
3205 | } | |
3206 | ||
3207 | UBool sameSymbols = FALSE; | |
3208 | if (fSymbols != NULL) { | |
3209 | sameSymbols = (UBool)(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) == | |
3210 | symbolsToAdopt->getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) && | |
3211 | getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == | |
3212 | symbolsToAdopt->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); | |
b75a7d8f | 3213 | delete fSymbols; |
374ca955 | 3214 | } |
b75a7d8f A |
3215 | |
3216 | fSymbols = symbolsToAdopt; | |
374ca955 A |
3217 | if (!sameSymbols) { |
3218 | // If the currency symbols are the same, there is no need to recalculate. | |
3219 | setCurrencyForSymbols(); | |
3220 | } | |
729e4ab9 | 3221 | expandAffixes(NULL); |
51004dcb A |
3222 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3223 | handleChanged(); | |
3224 | #endif | |
b75a7d8f A |
3225 | } |
3226 | //------------------------------------------------------------------------------ | |
3227 | // Setting the symbols is equlivalent to adopting a newly created localized | |
3228 | // symbols. | |
3229 | ||
3230 | void | |
3231 | DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) | |
3232 | { | |
3233 | adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); | |
51004dcb A |
3234 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3235 | handleChanged(); | |
3236 | #endif | |
b75a7d8f | 3237 | } |
729e4ab9 | 3238 | |
4388f060 | 3239 | |
729e4ab9 A |
3240 | const CurrencyPluralInfo* |
3241 | DecimalFormat::getCurrencyPluralInfo(void) const | |
3242 | { | |
3243 | return fCurrencyPluralInfo; | |
3244 | } | |
3245 | ||
4388f060 | 3246 | |
729e4ab9 A |
3247 | void |
3248 | DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) | |
3249 | { | |
3250 | if (toAdopt != NULL) { | |
3251 | delete fCurrencyPluralInfo; | |
3252 | fCurrencyPluralInfo = toAdopt; | |
3253 | // re-set currency affix patterns and currency affixes. | |
3254 | if (fCurrencySignCount > fgCurrencySignCountZero) { | |
3255 | UErrorCode status = U_ZERO_ERROR; | |
3256 | if (fAffixPatternsForCurrency) { | |
3257 | deleteHashForAffixPattern(); | |
3258 | } | |
3259 | setupCurrencyAffixPatterns(status); | |
3260 | if (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { | |
3261 | // only setup the affixes of the plural pattern. | |
3262 | setupCurrencyAffixes(fFormatPattern, FALSE, TRUE, status); | |
3263 | } | |
3264 | } | |
3265 | } | |
51004dcb A |
3266 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3267 | handleChanged(); | |
3268 | #endif | |
729e4ab9 A |
3269 | } |
3270 | ||
3271 | void | |
3272 | DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) | |
3273 | { | |
3274 | adoptCurrencyPluralInfo(info.clone()); | |
51004dcb A |
3275 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3276 | handleChanged(); | |
3277 | #endif | |
729e4ab9 A |
3278 | } |
3279 | ||
3280 | ||
b75a7d8f A |
3281 | /** |
3282 | * Update the currency object to match the symbols. This method | |
3283 | * is used only when the caller has passed in a symbols object | |
3284 | * that may not be the default object for its locale. | |
3285 | */ | |
3286 | void | |
3287 | DecimalFormat::setCurrencyForSymbols() { | |
3288 | /*Bug 4212072 | |
3289 | Update the affix strings accroding to symbols in order to keep | |
3290 | the affix strings up to date. | |
3291 | [Richard/GCL] | |
3292 | */ | |
3293 | ||
3294 | // With the introduction of the Currency object, the currency | |
3295 | // symbols in the DFS object are ignored. For backward | |
3296 | // compatibility, we check any explicitly set DFS object. If it | |
3297 | // is a default symbols object for its locale, we change the | |
3298 | // currency object to one for that locale. If it is custom, | |
3299 | // we set the currency to null. | |
3300 | UErrorCode ec = U_ZERO_ERROR; | |
374ca955 A |
3301 | const UChar* c = NULL; |
3302 | const char* loc = fSymbols->getLocale().getName(); | |
729e4ab9 | 3303 | UChar intlCurrencySymbol[4]; |
374ca955 A |
3304 | ucurr_forLocale(loc, intlCurrencySymbol, 4, &ec); |
3305 | UnicodeString currencySymbol; | |
3306 | ||
3307 | uprv_getStaticCurrencyName(intlCurrencySymbol, loc, currencySymbol, ec); | |
3308 | if (U_SUCCESS(ec) | |
3309 | && getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) == currencySymbol | |
4388f060 | 3310 | && getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == UnicodeString(intlCurrencySymbol)) |
374ca955 A |
3311 | { |
3312 | // Trap an error in mapping locale to currency. If we can't | |
3313 | // map, then don't fail and set the currency to "". | |
3314 | c = intlCurrencySymbol; | |
b75a7d8f | 3315 | } |
374ca955 | 3316 | ec = U_ZERO_ERROR; // reset local error code! |
729e4ab9 | 3317 | setCurrencyInternally(c, ec); |
51004dcb A |
3318 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3319 | handleChanged(); | |
3320 | #endif | |
b75a7d8f A |
3321 | } |
3322 | ||
3323 | ||
3324 | //------------------------------------------------------------------------------ | |
3325 | // Gets the positive prefix of the number pattern. | |
729e4ab9 | 3326 | |
b75a7d8f A |
3327 | UnicodeString& |
3328 | DecimalFormat::getPositivePrefix(UnicodeString& result) const | |
3329 | { | |
3330 | result = fPositivePrefix; | |
3331 | return result; | |
3332 | } | |
729e4ab9 | 3333 | |
b75a7d8f A |
3334 | //------------------------------------------------------------------------------ |
3335 | // Sets the positive prefix of the number pattern. | |
729e4ab9 | 3336 | |
b75a7d8f A |
3337 | void |
3338 | DecimalFormat::setPositivePrefix(const UnicodeString& newValue) | |
3339 | { | |
3340 | fPositivePrefix = newValue; | |
3341 | delete fPosPrefixPattern; | |
3342 | fPosPrefixPattern = 0; | |
51004dcb A |
3343 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3344 | handleChanged(); | |
3345 | #endif | |
b75a7d8f A |
3346 | } |
3347 | ||
3348 | //------------------------------------------------------------------------------ | |
3349 | // Gets the negative prefix of the number pattern. | |
3350 | ||
3351 | UnicodeString& | |
3352 | DecimalFormat::getNegativePrefix(UnicodeString& result) const | |
3353 | { | |
3354 | result = fNegativePrefix; | |
3355 | return result; | |
3356 | } | |
3357 | ||
3358 | //------------------------------------------------------------------------------ | |
3359 | // Gets the negative prefix of the number pattern. | |
3360 | ||
3361 | void | |
3362 | DecimalFormat::setNegativePrefix(const UnicodeString& newValue) | |
3363 | { | |
3364 | fNegativePrefix = newValue; | |
3365 | delete fNegPrefixPattern; | |
3366 | fNegPrefixPattern = 0; | |
51004dcb A |
3367 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3368 | handleChanged(); | |
3369 | #endif | |
b75a7d8f A |
3370 | } |
3371 | ||
3372 | //------------------------------------------------------------------------------ | |
3373 | // Gets the positive suffix of the number pattern. | |
3374 | ||
3375 | UnicodeString& | |
3376 | DecimalFormat::getPositiveSuffix(UnicodeString& result) const | |
3377 | { | |
3378 | result = fPositiveSuffix; | |
3379 | return result; | |
3380 | } | |
3381 | ||
3382 | //------------------------------------------------------------------------------ | |
3383 | // Sets the positive suffix of the number pattern. | |
3384 | ||
3385 | void | |
3386 | DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) | |
3387 | { | |
3388 | fPositiveSuffix = newValue; | |
3389 | delete fPosSuffixPattern; | |
3390 | fPosSuffixPattern = 0; | |
51004dcb A |
3391 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3392 | handleChanged(); | |
3393 | #endif | |
b75a7d8f A |
3394 | } |
3395 | ||
3396 | //------------------------------------------------------------------------------ | |
3397 | // Gets the negative suffix of the number pattern. | |
3398 | ||
3399 | UnicodeString& | |
3400 | DecimalFormat::getNegativeSuffix(UnicodeString& result) const | |
3401 | { | |
3402 | result = fNegativeSuffix; | |
3403 | return result; | |
3404 | } | |
3405 | ||
3406 | //------------------------------------------------------------------------------ | |
3407 | // Sets the negative suffix of the number pattern. | |
3408 | ||
3409 | void | |
3410 | DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) | |
3411 | { | |
3412 | fNegativeSuffix = newValue; | |
3413 | delete fNegSuffixPattern; | |
3414 | fNegSuffixPattern = 0; | |
51004dcb A |
3415 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3416 | handleChanged(); | |
3417 | #endif | |
b75a7d8f A |
3418 | } |
3419 | ||
3420 | //------------------------------------------------------------------------------ | |
3421 | // Gets the multiplier of the number pattern. | |
729e4ab9 A |
3422 | // Multipliers are stored as decimal numbers (DigitLists) because that |
3423 | // is the most convenient for muliplying or dividing the numbers to be formatted. | |
3424 | // A NULL multiplier implies one, and the scaling operations are skipped. | |
b75a7d8f | 3425 | |
729e4ab9 A |
3426 | int32_t |
3427 | DecimalFormat::getMultiplier() const | |
b75a7d8f | 3428 | { |
729e4ab9 A |
3429 | if (fMultiplier == NULL) { |
3430 | return 1; | |
3431 | } else { | |
3432 | return fMultiplier->getLong(); | |
3433 | } | |
b75a7d8f A |
3434 | } |
3435 | ||
3436 | //------------------------------------------------------------------------------ | |
3437 | // Sets the multiplier of the number pattern. | |
3438 | void | |
3439 | DecimalFormat::setMultiplier(int32_t newValue) | |
3440 | { | |
46f4442e A |
3441 | // if (newValue == 0) { |
3442 | // throw new IllegalArgumentException("Bad multiplier: " + newValue); | |
3443 | // } | |
729e4ab9 A |
3444 | if (newValue == 0) { |
3445 | newValue = 1; // one being the benign default value for a multiplier. | |
3446 | } | |
3447 | if (newValue == 1) { | |
3448 | delete fMultiplier; | |
3449 | fMultiplier = NULL; | |
3450 | } else { | |
3451 | if (fMultiplier == NULL) { | |
3452 | fMultiplier = new DigitList; | |
3453 | } | |
3454 | if (fMultiplier != NULL) { | |
3455 | fMultiplier->set(newValue); | |
3456 | } | |
46f4442e | 3457 | } |
51004dcb A |
3458 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3459 | handleChanged(); | |
3460 | #endif | |
b75a7d8f A |
3461 | } |
3462 | ||
3463 | /** | |
3464 | * Get the rounding increment. | |
3465 | * @return A positive rounding increment, or 0.0 if rounding | |
3466 | * is not in effect. | |
3467 | * @see #setRoundingIncrement | |
3468 | * @see #getRoundingMode | |
3469 | * @see #setRoundingMode | |
3470 | */ | |
374ca955 | 3471 | double DecimalFormat::getRoundingIncrement() const { |
729e4ab9 A |
3472 | if (fRoundingIncrement == NULL) { |
3473 | return 0.0; | |
3474 | } else { | |
3475 | return fRoundingIncrement->getDouble(); | |
3476 | } | |
b75a7d8f A |
3477 | } |
3478 | ||
3479 | /** | |
3480 | * Set the rounding increment. This method also controls whether | |
3481 | * rounding is enabled. | |
3482 | * @param newValue A positive rounding increment, or 0.0 to disable rounding. | |
3483 | * Negative increments are equivalent to 0.0. | |
3484 | * @see #getRoundingIncrement | |
3485 | * @see #getRoundingMode | |
3486 | * @see #setRoundingMode | |
3487 | */ | |
3488 | void DecimalFormat::setRoundingIncrement(double newValue) { | |
3489 | if (newValue > 0.0) { | |
3490 | if (fRoundingIncrement == NULL) { | |
3491 | fRoundingIncrement = new DigitList(); | |
3492 | } | |
46f4442e | 3493 | if (fRoundingIncrement != NULL) { |
729e4ab9 | 3494 | fRoundingIncrement->set(newValue); |
46f4442e A |
3495 | return; |
3496 | } | |
729e4ab9 | 3497 | } |
46f4442e A |
3498 | // These statements are executed if newValue is less than 0.0 |
3499 | // or fRoundingIncrement could not be created. | |
3500 | delete fRoundingIncrement; | |
3501 | fRoundingIncrement = NULL; | |
51004dcb A |
3502 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3503 | handleChanged(); | |
3504 | #endif | |
b75a7d8f A |
3505 | } |
3506 | ||
3507 | /** | |
3508 | * Get the rounding mode. | |
3509 | * @return A rounding mode | |
3510 | * @see #setRoundingIncrement | |
3511 | * @see #getRoundingIncrement | |
3512 | * @see #setRoundingMode | |
3513 | */ | |
374ca955 | 3514 | DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() const { |
b75a7d8f A |
3515 | return fRoundingMode; |
3516 | } | |
3517 | ||
3518 | /** | |
3519 | * Set the rounding mode. This has no effect unless the rounding | |
3520 | * increment is greater than zero. | |
3521 | * @param roundingMode A rounding mode | |
3522 | * @see #setRoundingIncrement | |
3523 | * @see #getRoundingIncrement | |
3524 | * @see #getRoundingMode | |
3525 | */ | |
3526 | void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { | |
3527 | fRoundingMode = roundingMode; | |
51004dcb A |
3528 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3529 | handleChanged(); | |
3530 | #endif | |
b75a7d8f A |
3531 | } |
3532 | ||
3533 | /** | |
3534 | * Get the width to which the output of <code>format()</code> is padded. | |
3535 | * @return the format width, or zero if no padding is in effect | |
3536 | * @see #setFormatWidth | |
3537 | * @see #getPadCharacter | |
3538 | * @see #setPadCharacter | |
3539 | * @see #getPadPosition | |
3540 | * @see #setPadPosition | |
3541 | */ | |
374ca955 | 3542 | int32_t DecimalFormat::getFormatWidth() const { |
b75a7d8f A |
3543 | return fFormatWidth; |
3544 | } | |
3545 | ||
3546 | /** | |
3547 | * Set the width to which the output of <code>format()</code> is padded. | |
3548 | * This method also controls whether padding is enabled. | |
3549 | * @param width the width to which to pad the result of | |
3550 | * <code>format()</code>, or zero to disable padding. A negative | |
3551 | * width is equivalent to 0. | |
3552 | * @see #getFormatWidth | |
3553 | * @see #getPadCharacter | |
3554 | * @see #setPadCharacter | |
3555 | * @see #getPadPosition | |
3556 | * @see #setPadPosition | |
3557 | */ | |
3558 | void DecimalFormat::setFormatWidth(int32_t width) { | |
3559 | fFormatWidth = (width > 0) ? width : 0; | |
51004dcb A |
3560 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3561 | handleChanged(); | |
3562 | #endif | |
b75a7d8f A |
3563 | } |
3564 | ||
374ca955 | 3565 | UnicodeString DecimalFormat::getPadCharacterString() const { |
4388f060 | 3566 | return UnicodeString(fPad); |
b75a7d8f A |
3567 | } |
3568 | ||
b75a7d8f A |
3569 | void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { |
3570 | if (padChar.length() > 0) { | |
3571 | fPad = padChar.char32At(0); | |
3572 | } | |
3573 | else { | |
3574 | fPad = kDefaultPad; | |
3575 | } | |
51004dcb A |
3576 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3577 | handleChanged(); | |
3578 | #endif | |
b75a7d8f A |
3579 | } |
3580 | ||
3581 | /** | |
3582 | * Get the position at which padding will take place. This is the location | |
3583 | * at which padding will be inserted if the result of <code>format()</code> | |
3584 | * is shorter than the format width. | |
3585 | * @return the pad position, one of <code>kPadBeforePrefix</code>, | |
3586 | * <code>kPadAfterPrefix</code>, <code>kPadBeforeSuffix</code>, or | |
3587 | * <code>kPadAfterSuffix</code>. | |
3588 | * @see #setFormatWidth | |
3589 | * @see #getFormatWidth | |
3590 | * @see #setPadCharacter | |
3591 | * @see #getPadCharacter | |
3592 | * @see #setPadPosition | |
3593 | * @see #kPadBeforePrefix | |
3594 | * @see #kPadAfterPrefix | |
3595 | * @see #kPadBeforeSuffix | |
3596 | * @see #kPadAfterSuffix | |
3597 | */ | |
374ca955 | 3598 | DecimalFormat::EPadPosition DecimalFormat::getPadPosition() const { |
b75a7d8f A |
3599 | return fPadPosition; |
3600 | } | |
3601 | ||
3602 | /** | |
3603 | * <strong><font face=helvetica color=red>NEW</font></strong> | |
3604 | * Set the position at which padding will take place. This is the location | |
3605 | * at which padding will be inserted if the result of <code>format()</code> | |
3606 | * is shorter than the format width. This has no effect unless padding is | |
3607 | * enabled. | |
3608 | * @param padPos the pad position, one of <code>kPadBeforePrefix</code>, | |
3609 | * <code>kPadAfterPrefix</code>, <code>kPadBeforeSuffix</code>, or | |
3610 | * <code>kPadAfterSuffix</code>. | |
3611 | * @see #setFormatWidth | |
3612 | * @see #getFormatWidth | |
3613 | * @see #setPadCharacter | |
3614 | * @see #getPadCharacter | |
3615 | * @see #getPadPosition | |
3616 | * @see #kPadBeforePrefix | |
3617 | * @see #kPadAfterPrefix | |
3618 | * @see #kPadBeforeSuffix | |
3619 | * @see #kPadAfterSuffix | |
3620 | */ | |
3621 | void DecimalFormat::setPadPosition(EPadPosition padPos) { | |
3622 | fPadPosition = padPos; | |
51004dcb A |
3623 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3624 | handleChanged(); | |
3625 | #endif | |
b75a7d8f A |
3626 | } |
3627 | ||
3628 | /** | |
3629 | * Return whether or not scientific notation is used. | |
3630 | * @return TRUE if this object formats and parses scientific notation | |
3631 | * @see #setScientificNotation | |
3632 | * @see #getMinimumExponentDigits | |
3633 | * @see #setMinimumExponentDigits | |
3634 | * @see #isExponentSignAlwaysShown | |
3635 | * @see #setExponentSignAlwaysShown | |
3636 | */ | |
3637 | UBool DecimalFormat::isScientificNotation() { | |
3638 | return fUseExponentialNotation; | |
3639 | } | |
3640 | ||
3641 | /** | |
3642 | * Set whether or not scientific notation is used. | |
3643 | * @param useScientific TRUE if this object formats and parses scientific | |
3644 | * notation | |
3645 | * @see #isScientificNotation | |
3646 | * @see #getMinimumExponentDigits | |
3647 | * @see #setMinimumExponentDigits | |
3648 | * @see #isExponentSignAlwaysShown | |
3649 | * @see #setExponentSignAlwaysShown | |
3650 | */ | |
3651 | void DecimalFormat::setScientificNotation(UBool useScientific) { | |
3652 | fUseExponentialNotation = useScientific; | |
51004dcb A |
3653 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3654 | handleChanged(); | |
3655 | #endif | |
b75a7d8f A |
3656 | } |
3657 | ||
3658 | /** | |
3659 | * Return the minimum exponent digits that will be shown. | |
3660 | * @return the minimum exponent digits that will be shown | |
3661 | * @see #setScientificNotation | |
3662 | * @see #isScientificNotation | |
3663 | * @see #setMinimumExponentDigits | |
3664 | * @see #isExponentSignAlwaysShown | |
3665 | * @see #setExponentSignAlwaysShown | |
3666 | */ | |
374ca955 | 3667 | int8_t DecimalFormat::getMinimumExponentDigits() const { |
b75a7d8f A |
3668 | return fMinExponentDigits; |
3669 | } | |
3670 | ||
3671 | /** | |
3672 | * Set the minimum exponent digits that will be shown. This has no | |
3673 | * effect unless scientific notation is in use. | |
3674 | * @param minExpDig a value >= 1 indicating the fewest exponent digits | |
3675 | * that will be shown. Values less than 1 will be treated as 1. | |
3676 | * @see #setScientificNotation | |
3677 | * @see #isScientificNotation | |
3678 | * @see #getMinimumExponentDigits | |
3679 | * @see #isExponentSignAlwaysShown | |
3680 | * @see #setExponentSignAlwaysShown | |
3681 | */ | |
3682 | void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { | |
3683 | fMinExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); | |
51004dcb A |
3684 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3685 | handleChanged(); | |
3686 | #endif | |
b75a7d8f A |
3687 | } |
3688 | ||
3689 | /** | |
3690 | * Return whether the exponent sign is always shown. | |
3691 | * @return TRUE if the exponent is always prefixed with either the | |
3692 | * localized minus sign or the localized plus sign, false if only negative | |
3693 | * exponents are prefixed with the localized minus sign. | |
3694 | * @see #setScientificNotation | |
3695 | * @see #isScientificNotation | |
3696 | * @see #setMinimumExponentDigits | |
3697 | * @see #getMinimumExponentDigits | |
3698 | * @see #setExponentSignAlwaysShown | |
3699 | */ | |
3700 | UBool DecimalFormat::isExponentSignAlwaysShown() { | |
3701 | return fExponentSignAlwaysShown; | |
3702 | } | |
3703 | ||
3704 | /** | |
3705 | * Set whether the exponent sign is always shown. This has no effect | |
3706 | * unless scientific notation is in use. | |
3707 | * @param expSignAlways TRUE if the exponent is always prefixed with either | |
3708 | * the localized minus sign or the localized plus sign, false if only | |
3709 | * negative exponents are prefixed with the localized minus sign. | |
3710 | * @see #setScientificNotation | |
3711 | * @see #isScientificNotation | |
3712 | * @see #setMinimumExponentDigits | |
3713 | * @see #getMinimumExponentDigits | |
3714 | * @see #isExponentSignAlwaysShown | |
3715 | */ | |
3716 | void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { | |
3717 | fExponentSignAlwaysShown = expSignAlways; | |
51004dcb A |
3718 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3719 | handleChanged(); | |
3720 | #endif | |
b75a7d8f A |
3721 | } |
3722 | ||
3723 | //------------------------------------------------------------------------------ | |
3724 | // Gets the grouping size of the number pattern. For example, thousand or 10 | |
3725 | // thousand groupings. | |
729e4ab9 | 3726 | |
b75a7d8f A |
3727 | int32_t |
3728 | DecimalFormat::getGroupingSize() const | |
3729 | { | |
3730 | return fGroupingSize; | |
3731 | } | |
729e4ab9 | 3732 | |
b75a7d8f A |
3733 | //------------------------------------------------------------------------------ |
3734 | // Gets the grouping size of the number pattern. | |
729e4ab9 | 3735 | |
b75a7d8f A |
3736 | void |
3737 | DecimalFormat::setGroupingSize(int32_t newValue) | |
3738 | { | |
3739 | fGroupingSize = newValue; | |
51004dcb A |
3740 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3741 | handleChanged(); | |
3742 | #endif | |
b75a7d8f A |
3743 | } |
3744 | ||
3745 | //------------------------------------------------------------------------------ | |
3746 | ||
3747 | int32_t | |
3748 | DecimalFormat::getSecondaryGroupingSize() const | |
3749 | { | |
3750 | return fGroupingSize2; | |
3751 | } | |
3752 | ||
3753 | //------------------------------------------------------------------------------ | |
3754 | ||
3755 | void | |
3756 | DecimalFormat::setSecondaryGroupingSize(int32_t newValue) | |
3757 | { | |
3758 | fGroupingSize2 = newValue; | |
51004dcb A |
3759 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3760 | handleChanged(); | |
3761 | #endif | |
b75a7d8f A |
3762 | } |
3763 | ||
3764 | //------------------------------------------------------------------------------ | |
3765 | // Checks if to show the decimal separator. | |
3766 | ||
3767 | UBool | |
3768 | DecimalFormat::isDecimalSeparatorAlwaysShown() const | |
3769 | { | |
3770 | return fDecimalSeparatorAlwaysShown; | |
3771 | } | |
3772 | ||
3773 | //------------------------------------------------------------------------------ | |
3774 | // Sets to always show the decimal separator. | |
3775 | ||
3776 | void | |
3777 | DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) | |
3778 | { | |
3779 | fDecimalSeparatorAlwaysShown = newValue; | |
51004dcb A |
3780 | #if UCONFIG_FORMAT_FASTPATHS_49 |
3781 | handleChanged(); | |
3782 | #endif | |
b75a7d8f A |
3783 | } |
3784 | ||
3785 | //------------------------------------------------------------------------------ | |
3786 | // Emits the pattern of this DecimalFormat instance. | |
3787 | ||
3788 | UnicodeString& | |
3789 | DecimalFormat::toPattern(UnicodeString& result) const | |
3790 | { | |
3791 | return toPattern(result, FALSE); | |
3792 | } | |
3793 | ||
3794 | //------------------------------------------------------------------------------ | |
3795 | // Emits the localized pattern this DecimalFormat instance. | |
3796 | ||
3797 | UnicodeString& | |
3798 | DecimalFormat::toLocalizedPattern(UnicodeString& result) const | |
3799 | { | |
3800 | return toPattern(result, TRUE); | |
3801 | } | |
3802 | ||
3803 | //------------------------------------------------------------------------------ | |
3804 | /** | |
3805 | * Expand the affix pattern strings into the expanded affix strings. If any | |
3806 | * affix pattern string is null, do not expand it. This method should be | |
3807 | * called any time the symbols or the affix patterns change in order to keep | |
3808 | * the expanded affix strings up to date. | |
729e4ab9 A |
3809 | * This method also will be called before formatting if format currency |
3810 | * plural names, since the plural name is not a static one, it is | |
3811 | * based on the currency plural count, the affix will be known only | |
3812 | * after the currency plural count is know. | |
3813 | * In which case, the parameter | |
3814 | * 'pluralCount' will be a non-null currency plural count. | |
3815 | * In all other cases, the 'pluralCount' is null, which means it is not needed. | |
b75a7d8f | 3816 | */ |
729e4ab9 A |
3817 | void DecimalFormat::expandAffixes(const UnicodeString* pluralCount) { |
3818 | FieldPositionHandler none; | |
b75a7d8f | 3819 | if (fPosPrefixPattern != 0) { |
729e4ab9 | 3820 | expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, none, FALSE, pluralCount); |
b75a7d8f A |
3821 | } |
3822 | if (fPosSuffixPattern != 0) { | |
729e4ab9 | 3823 | expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, none, FALSE, pluralCount); |
b75a7d8f A |
3824 | } |
3825 | if (fNegPrefixPattern != 0) { | |
729e4ab9 | 3826 | expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, none, FALSE, pluralCount); |
b75a7d8f A |
3827 | } |
3828 | if (fNegSuffixPattern != 0) { | |
729e4ab9 | 3829 | expandAffix(*fNegSuffixPattern, fNegativeSuffix, 0, none, FALSE, pluralCount); |
b75a7d8f A |
3830 | } |
3831 | #ifdef FMT_DEBUG | |
3832 | UnicodeString s; | |
51004dcb A |
3833 | s.append(UnicodeString("[")) |
3834 | .append(DEREFSTR(fPosPrefixPattern)).append((UnicodeString)"|").append(DEREFSTR(fPosSuffixPattern)) | |
3835 | .append((UnicodeString)";") .append(DEREFSTR(fNegPrefixPattern)).append((UnicodeString)"|").append(DEREFSTR(fNegSuffixPattern)) | |
3836 | .append((UnicodeString)"]->[") | |
3837 | .append(fPositivePrefix).append((UnicodeString)"|").append(fPositiveSuffix) | |
3838 | .append((UnicodeString)";") .append(fNegativePrefix).append((UnicodeString)"|").append(fNegativeSuffix) | |
3839 | .append((UnicodeString)"]\n"); | |
b75a7d8f A |
3840 | debugout(s); |
3841 | #endif | |
3842 | } | |
3843 | ||
3844 | /** | |
3845 | * Expand an affix pattern into an affix string. All characters in the | |
3846 | * pattern are literal unless prefixed by kQuote. The following characters | |
3847 | * after kQuote are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, | |
3848 | * PATTERN_MINUS, and kCurrencySign. If kCurrencySign is doubled (kQuote + | |
3849 | * kCurrencySign + kCurrencySign), it is interpreted as an international | |
729e4ab9 A |
3850 | * currency sign. If CURRENCY_SIGN is tripled, it is interpreted as |
3851 | * currency plural long names, such as "US Dollars". | |
3852 | * Any other character after a kQuote represents itself. | |
b75a7d8f A |
3853 | * kQuote must be followed by another character; kQuote may not occur by |
3854 | * itself at the end of the pattern. | |
3855 | * | |
3856 | * This method is used in two distinct ways. First, it is used to expand | |
3857 | * the stored affix patterns into actual affixes. For this usage, doFormat | |
3858 | * must be false. Second, it is used to expand the stored affix patterns | |
3859 | * given a specific number (doFormat == true), for those rare cases in | |
3860 | * which a currency format references a ChoiceFormat (e.g., en_IN display | |
3861 | * name for INR). The number itself is taken from digitList. | |
3862 | * | |
3863 | * When used in the first way, this method has a side effect: It sets | |
3864 | * currencyChoice to a ChoiceFormat object, if the currency's display name | |
3865 | * in this locale is a ChoiceFormat pattern (very rare). It only does this | |
3866 | * if currencyChoice is null to start with. | |
3867 | * | |
3868 | * @param pattern the non-null, fPossibly empty pattern | |
3869 | * @param affix string to receive the expanded equivalent of pattern. | |
3870 | * Previous contents are deleted. | |
3871 | * @param doFormat if false, then the pattern will be expanded, and if a | |
3872 | * currency symbol is encountered that expands to a ChoiceFormat, the | |
3873 | * currencyChoice member variable will be initialized if it is null. If | |
3874 | * doFormat is true, then it is assumed that the currencyChoice has been | |
3875 | * created, and it will be used to format the value in digitList. | |
729e4ab9 A |
3876 | * @param pluralCount the plural count. It is only used for currency |
3877 | * plural format. In which case, it is the plural | |
3878 | * count of the currency amount. For example, | |
3879 | * in en_US, it is the singular "one", or the plural | |
3880 | * "other". For all other cases, it is null, and | |
3881 | * is not being used. | |
b75a7d8f A |
3882 | */ |
3883 | void DecimalFormat::expandAffix(const UnicodeString& pattern, | |
3884 | UnicodeString& affix, | |
3885 | double number, | |
729e4ab9 A |
3886 | FieldPositionHandler& handler, |
3887 | UBool doFormat, | |
3888 | const UnicodeString* pluralCount) const { | |
b75a7d8f A |
3889 | affix.remove(); |
3890 | for (int i=0; i<pattern.length(); ) { | |
3891 | UChar32 c = pattern.char32At(i); | |
3892 | i += U16_LENGTH(c); | |
3893 | if (c == kQuote) { | |
3894 | c = pattern.char32At(i); | |
3895 | i += U16_LENGTH(c); | |
729e4ab9 | 3896 | int beginIdx = affix.length(); |
b75a7d8f A |
3897 | switch (c) { |
3898 | case kCurrencySign: { | |
3899 | // As of ICU 2.2 we use the currency object, and | |
3900 | // ignore the currency symbols in the DFS, unless | |
3901 | // we have a null currency object. This occurs if | |
3902 | // resurrecting a pre-2.2 object or if the user | |
3903 | // sets a custom DFS. | |
3904 | UBool intl = i<pattern.length() && | |
3905 | pattern.char32At(i) == kCurrencySign; | |
729e4ab9 | 3906 | UBool plural = FALSE; |
b75a7d8f A |
3907 | if (intl) { |
3908 | ++i; | |
729e4ab9 A |
3909 | plural = i<pattern.length() && |
3910 | pattern.char32At(i) == kCurrencySign; | |
3911 | if (plural) { | |
3912 | intl = FALSE; | |
3913 | ++i; | |
3914 | } | |
b75a7d8f A |
3915 | } |
3916 | const UChar* currencyUChars = getCurrency(); | |
3917 | if (currencyUChars[0] != 0) { | |
3918 | UErrorCode ec = U_ZERO_ERROR; | |
729e4ab9 A |
3919 | if (plural && pluralCount != NULL) { |
3920 | // plural name is only needed when pluralCount != null, | |
3921 | // which means when formatting currency plural names. | |
3922 | // For other cases, pluralCount == null, | |
3923 | // and plural names are not needed. | |
3924 | int32_t len; | |
4388f060 A |
3925 | CharString pluralCountChar; |
3926 | pluralCountChar.appendInvariantChars(*pluralCount, ec); | |
729e4ab9 A |
3927 | UBool isChoiceFormat; |
3928 | const UChar* s = ucurr_getPluralName(currencyUChars, | |
3929 | fSymbols != NULL ? fSymbols->getLocale().getName() : | |
3930 | Locale::getDefault().getName(), &isChoiceFormat, | |
4388f060 | 3931 | pluralCountChar.data(), &len, &ec); |
729e4ab9 A |
3932 | affix += UnicodeString(s, len); |
3933 | handler.addAttribute(kCurrencyField, beginIdx, affix.length()); | |
3934 | } else if(intl) { | |
4388f060 | 3935 | affix.append(currencyUChars, -1); |
729e4ab9 | 3936 | handler.addAttribute(kCurrencyField, beginIdx, affix.length()); |
b75a7d8f A |
3937 | } else { |
3938 | int32_t len; | |
3939 | UBool isChoiceFormat; | |
46f4442e | 3940 | // If fSymbols is NULL, use default locale |
729e4ab9 A |
3941 | const UChar* s = ucurr_getName(currencyUChars, |
3942 | fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(), | |
3943 | UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec); | |
b75a7d8f A |
3944 | if (isChoiceFormat) { |
3945 | // Two modes here: If doFormat is false, we set up | |
3946 | // currencyChoice. If doFormat is true, we use the | |
3947 | // previously created currencyChoice to format the | |
3948 | // value in digitList. | |
3949 | if (!doFormat) { | |
3950 | // If the currency is handled by a ChoiceFormat, | |
3951 | // then we're not going to use the expanded | |
3952 | // patterns. Instantiate the ChoiceFormat and | |
3953 | // return. | |
3954 | if (fCurrencyChoice == NULL) { | |
3955 | // TODO Replace double-check with proper thread-safe code | |
4388f060 | 3956 | ChoiceFormat* fmt = new ChoiceFormat(UnicodeString(s), ec); |
b75a7d8f A |
3957 | if (U_SUCCESS(ec)) { |
3958 | umtx_lock(NULL); | |
3959 | if (fCurrencyChoice == NULL) { | |
3960 | // Cast away const | |
3961 | ((DecimalFormat*)this)->fCurrencyChoice = fmt; | |
3962 | fmt = NULL; | |
3963 | } | |
3964 | umtx_unlock(NULL); | |
3965 | delete fmt; | |
3966 | } | |
3967 | } | |
3968 | // We could almost return null or "" here, since the | |
3969 | // expanded affixes are almost not used at all | |
3970 | // in this situation. However, one method -- | |
3971 | // toPattern() -- still does use the expanded | |
3972 | // affixes, in order to set up a padding | |
3973 | // pattern. We use the CURRENCY_SIGN as a | |
3974 | // placeholder. | |
3975 | affix.append(kCurrencySign); | |
3976 | } else { | |
3977 | if (fCurrencyChoice != NULL) { | |
3978 | FieldPosition pos(0); // ignored | |
3979 | if (number < 0) { | |
3980 | number = -number; | |
3981 | } | |
3982 | fCurrencyChoice->format(number, affix, pos); | |
3983 | } else { | |
3984 | // We only arrive here if the currency choice | |
3985 | // format in the locale data is INVALID. | |
4388f060 | 3986 | affix.append(currencyUChars, -1); |
729e4ab9 | 3987 | handler.addAttribute(kCurrencyField, beginIdx, affix.length()); |
b75a7d8f A |
3988 | } |
3989 | } | |
3990 | continue; | |
3991 | } | |
3992 | affix += UnicodeString(s, len); | |
729e4ab9 | 3993 | handler.addAttribute(kCurrencyField, beginIdx, affix.length()); |
b75a7d8f A |
3994 | } |
3995 | } else { | |
3996 | if(intl) { | |
3997 | affix += getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); | |
3998 | } else { | |
3999 | affix += getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); | |
4000 | } | |
729e4ab9 | 4001 | handler.addAttribute(kCurrencyField, beginIdx, affix.length()); |
b75a7d8f A |
4002 | } |
4003 | break; | |
4004 | } | |
4005 | case kPatternPercent: | |
4006 | affix += getConstSymbol(DecimalFormatSymbols::kPercentSymbol); | |
729e4ab9 | 4007 | handler.addAttribute(kPercentField, beginIdx, affix.length()); |
b75a7d8f A |
4008 | break; |
4009 | case kPatternPerMill: | |
4010 | affix += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); | |
729e4ab9 | 4011 | handler.addAttribute(kPermillField, beginIdx, affix.length()); |
b75a7d8f A |
4012 | break; |
4013 | case kPatternPlus: | |
4014 | affix += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); | |
729e4ab9 | 4015 | handler.addAttribute(kSignField, beginIdx, affix.length()); |
b75a7d8f A |
4016 | break; |
4017 | case kPatternMinus: | |
4018 | affix += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); | |
729e4ab9 | 4019 | handler.addAttribute(kSignField, beginIdx, affix.length()); |
b75a7d8f A |
4020 | break; |
4021 | default: | |
4022 | affix.append(c); | |
4023 | break; | |
4024 | } | |
4025 | } | |
4026 | else { | |
4027 | affix.append(c); | |
4028 | } | |
4029 | } | |
4030 | } | |
4031 | ||
4032 | /** | |
4033 | * Append an affix to the given StringBuffer. | |
4034 | * @param buf buffer to append to | |
4035 | * @param isNegative | |
4036 | * @param isPrefix | |
4037 | */ | |
4038 | int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number, | |
729e4ab9 | 4039 | FieldPositionHandler& handler, |
b75a7d8f | 4040 | UBool isNegative, UBool isPrefix) const { |
729e4ab9 A |
4041 | // plural format precedes choice format |
4042 | if (fCurrencyChoice != 0 && | |
4043 | fCurrencySignCount != fgCurrencySignCountInPluralFormat) { | |
46f4442e | 4044 | const UnicodeString* affixPat; |
b75a7d8f A |
4045 | if (isPrefix) { |
4046 | affixPat = isNegative ? fNegPrefixPattern : fPosPrefixPattern; | |
4047 | } else { | |
4048 | affixPat = isNegative ? fNegSuffixPattern : fPosSuffixPattern; | |
4049 | } | |
46f4442e A |
4050 | if (affixPat) { |
4051 | UnicodeString affixBuf; | |
729e4ab9 | 4052 | expandAffix(*affixPat, affixBuf, number, handler, TRUE, NULL); |
46f4442e A |
4053 | buf.append(affixBuf); |
4054 | return affixBuf.length(); | |
4055 | } | |
4056 | // else someone called a function that reset the pattern. | |
b75a7d8f | 4057 | } |
729e4ab9 | 4058 | |
46f4442e | 4059 | const UnicodeString* affix; |
729e4ab9 A |
4060 | if (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { |
4061 | UnicodeString pluralCount = fCurrencyPluralInfo->getPluralRules()->select(number); | |
4062 | AffixesForCurrency* oneSet; | |
4388f060 | 4063 | if (fStyle == UNUM_CURRENCY_PLURAL) { |
729e4ab9 A |
4064 | oneSet = (AffixesForCurrency*)fPluralAffixesForCurrency->get(pluralCount); |
4065 | } else { | |
4066 | oneSet = (AffixesForCurrency*)fAffixesForCurrency->get(pluralCount); | |
4067 | } | |
4068 | if (isPrefix) { | |
4069 | affix = isNegative ? &oneSet->negPrefixForCurrency : | |
4070 | &oneSet->posPrefixForCurrency; | |
4071 | } else { | |
4072 | affix = isNegative ? &oneSet->negSuffixForCurrency : | |
4073 | &oneSet->posSuffixForCurrency; | |
4074 | } | |
b75a7d8f | 4075 | } else { |
729e4ab9 A |
4076 | if (isPrefix) { |
4077 | affix = isNegative ? &fNegativePrefix : &fPositivePrefix; | |
4078 | } else { | |
4079 | affix = isNegative ? &fNegativeSuffix : &fPositiveSuffix; | |
4080 | } | |
b75a7d8f | 4081 | } |
729e4ab9 A |
4082 | |
4083 | int32_t begin = (int) buf.length(); | |
4084 | ||
b75a7d8f | 4085 | buf.append(*affix); |
729e4ab9 A |
4086 | |
4087 | if (handler.isRecording()) { | |
4088 | int32_t offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol)); | |
4089 | if (offset > -1) { | |
4090 | UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); | |
4091 | handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length()); | |
4092 | } | |
4093 | ||
4094 | offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); | |
4095 | if (offset > -1) { | |
4096 | UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); | |
4097 | handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length()); | |
4098 | } | |
4099 | ||
4100 | offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)); | |
4101 | if (offset > -1) { | |
4102 | UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); | |
4103 | handler.addAttribute(kSignField, begin + offset, begin + offset + aff.length()); | |
4104 | } | |
4105 | ||
4106 | offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)); | |
4107 | if (offset > -1) { | |
4108 | UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPercentSymbol); | |
4109 | handler.addAttribute(kPercentField, begin + offset, begin + offset + aff.length()); | |
4110 | } | |
4111 | ||
4112 | offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)); | |
4113 | if (offset > -1) { | |
4114 | UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); | |
4115 | handler.addAttribute(kPermillField, begin + offset, begin + offset + aff.length()); | |
4116 | } | |
4117 | } | |
b75a7d8f A |
4118 | return affix->length(); |
4119 | } | |
4120 | ||
4121 | /** | |
4122 | * Appends an affix pattern to the given StringBuffer, quoting special | |
4123 | * characters as needed. Uses the internal affix pattern, if that exists, | |
4124 | * or the literal affix, if the internal affix pattern is null. The | |
4125 | * appended string will generate the same affix pattern (or literal affix) | |
4126 | * when passed to toPattern(). | |
4127 | * | |
4128 | * @param appendTo the affix string is appended to this | |
4129 | * @param affixPattern a pattern such as fPosPrefixPattern; may be null | |
4130 | * @param expAffix a corresponding expanded affix, such as fPositivePrefix. | |
4131 | * Ignored unless affixPattern is null. If affixPattern is null, then | |
4132 | * expAffix is appended as a literal affix. | |
4133 | * @param localized true if the appended pattern should contain localized | |
4134 | * pattern characters; otherwise, non-localized pattern chars are appended | |
4135 | */ | |
4136 | void DecimalFormat::appendAffixPattern(UnicodeString& appendTo, | |
4137 | const UnicodeString* affixPattern, | |
4138 | const UnicodeString& expAffix, | |
4139 | UBool localized) const { | |
4140 | if (affixPattern == 0) { | |
4141 | appendAffixPattern(appendTo, expAffix, localized); | |
4142 | } else { | |
4143 | int i; | |
4144 | for (int pos=0; pos<affixPattern->length(); pos=i) { | |
4145 | i = affixPattern->indexOf(kQuote, pos); | |
4146 | if (i < 0) { | |
4147 | UnicodeString s; | |
4148 | affixPattern->extractBetween(pos, affixPattern->length(), s); | |
4149 | appendAffixPattern(appendTo, s, localized); | |
4150 | break; | |
4151 | } | |
4152 | if (i > pos) { | |
4153 | UnicodeString s; | |
4154 | affixPattern->extractBetween(pos, i, s); | |
4155 | appendAffixPattern(appendTo, s, localized); | |
4156 | } | |
4157 | UChar32 c = affixPattern->char32At(++i); | |
4158 | ++i; | |
4159 | if (c == kQuote) { | |
4160 | appendTo.append(c).append(c); | |
4161 | // Fall through and append another kQuote below | |
4162 | } else if (c == kCurrencySign && | |
4163 | i<affixPattern->length() && | |
4164 | affixPattern->char32At(i) == kCurrencySign) { | |
4165 | ++i; | |
4166 | appendTo.append(c).append(c); | |
4167 | } else if (localized) { | |
4168 | switch (c) { | |
4169 | case kPatternPercent: | |
4170 | appendTo += getConstSymbol(DecimalFormatSymbols::kPercentSymbol); | |
4171 | break; | |
4172 | case kPatternPerMill: | |
4173 | appendTo += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); | |
4174 | break; | |
4175 | case kPatternPlus: | |
4176 | appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); | |
4177 | break; | |
4178 | case kPatternMinus: | |
4179 | appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); | |
4180 | break; | |
4181 | default: | |
4182 | appendTo.append(c); | |
4183 | } | |
4184 | } else { | |
4185 | appendTo.append(c); | |
4186 | } | |
4187 | } | |
4188 | } | |
4189 | } | |
4190 | ||
4191 | /** | |
4192 | * Append an affix to the given StringBuffer, using quotes if | |
4193 | * there are special characters. Single quotes themselves must be | |
4194 | * escaped in either case. | |
4195 | */ | |
4196 | void | |
4197 | DecimalFormat::appendAffixPattern(UnicodeString& appendTo, | |
4198 | const UnicodeString& affix, | |
4199 | UBool localized) const { | |
4200 | UBool needQuote; | |
4201 | if(localized) { | |
4202 | needQuote = affix.indexOf(getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol)) >= 0 | |
4203 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)) >= 0 | |
4204 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)) >= 0 | |
4205 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)) >= 0 | |
4206 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)) >= 0 | |
4207 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)) >= 0 | |
4208 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)) >= 0 | |
4209 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol)) >= 0 | |
4210 | || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)) >= 0 | |
4211 | || affix.indexOf(kCurrencySign) >= 0; | |
4212 | } | |
4213 | else { | |
4214 | needQuote = affix.indexOf(kPatternZeroDigit) >= 0 | |
4215 | || affix.indexOf(kPatternGroupingSeparator) >= 0 | |
4216 | || affix.indexOf(kPatternDecimalSeparator) >= 0 | |
4217 | || affix.indexOf(kPatternPercent) >= 0 | |
4218 | || affix.indexOf(kPatternPerMill) >= 0 | |
4219 | || affix.indexOf(kPatternDigit) >= 0 | |
4220 | || affix.indexOf(kPatternSeparator) >= 0 | |
4221 | || affix.indexOf(kPatternExponent) >= 0 | |
4222 | || affix.indexOf(kPatternPlus) >= 0 | |
4223 | || affix.indexOf(kPatternMinus) >= 0 | |
4224 | || affix.indexOf(kCurrencySign) >= 0; | |
4225 | } | |
4226 | if (needQuote) | |
4227 | appendTo += (UChar)0x0027 /*'\''*/; | |
4228 | if (affix.indexOf((UChar)0x0027 /*'\''*/) < 0) | |
4229 | appendTo += affix; | |
4230 | else { | |
4231 | for (int32_t j = 0; j < affix.length(); ) { | |
4232 | UChar32 c = affix.char32At(j); | |
4233 | j += U16_LENGTH(c); | |
4234 | appendTo += c; | |
4235 | if (c == 0x0027 /*'\''*/) | |
4236 | appendTo += c; | |
4237 | } | |
4238 | } | |
4239 | if (needQuote) | |
4240 | appendTo += (UChar)0x0027 /*'\''*/; | |
4241 | } | |
4242 | ||
4243 | //------------------------------------------------------------------------------ | |
4244 | ||
b75a7d8f A |
4245 | UnicodeString& |
4246 | DecimalFormat::toPattern(UnicodeString& result, UBool localized) const | |
4247 | { | |
4388f060 | 4248 | if (fStyle == UNUM_CURRENCY_PLURAL) { |
729e4ab9 A |
4249 | // the prefix or suffix pattern might not be defined yet, |
4250 | // so they can not be synthesized, | |
4251 | // instead, get them directly. | |
4252 | // but it might not be the actual pattern used in formatting. | |
4253 | // the actual pattern used in formatting depends on the | |
4254 | // formatted number's plural count. | |
4255 | result = fFormatPattern; | |
4256 | return result; | |
4257 | } | |
b75a7d8f | 4258 | result.remove(); |
374ca955 | 4259 | UChar32 zero, sigDigit = kPatternSignificantDigit; |
b75a7d8f A |
4260 | UnicodeString digit, group; |
4261 | int32_t i; | |
4262 | int32_t roundingDecimalPos = 0; // Pos of decimal in roundingDigits | |
4263 | UnicodeString roundingDigits; | |
4264 | int32_t padPos = (fFormatWidth > 0) ? fPadPosition : -1; | |
4265 | UnicodeString padSpec; | |
374ca955 | 4266 | UBool useSigDig = areSignificantDigitsUsed(); |
b75a7d8f A |
4267 | |
4268 | if (localized) { | |
4269 | digit.append(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)); | |
4270 | group.append(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); | |
4271 | zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); | |
374ca955 A |
4272 | if (useSigDig) { |
4273 | sigDigit = getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); | |
4274 | } | |
b75a7d8f A |
4275 | } |
4276 | else { | |
4277 | digit.append((UChar)kPatternDigit); | |
4278 | group.append((UChar)kPatternGroupingSeparator); | |
4279 | zero = (UChar32)kPatternZeroDigit; | |
4280 | } | |
4281 | if (fFormatWidth > 0) { | |
4282 | if (localized) { | |
4283 | padSpec.append(getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); | |
4284 | } | |
4285 | else { | |
4286 | padSpec.append((UChar)kPatternPadEscape); | |
4287 | } | |
4288 | padSpec.append(fPad); | |
4289 | } | |
4290 | if (fRoundingIncrement != NULL) { | |
729e4ab9 A |
4291 | for(i=0; i<fRoundingIncrement->getCount(); ++i) { |
4292 | roundingDigits.append(zero+(fRoundingIncrement->getDigitValue(i))); // Convert to Unicode digit | |
b75a7d8f | 4293 | } |
729e4ab9 | 4294 | roundingDecimalPos = fRoundingIncrement->getDecimalAt(); |
b75a7d8f A |
4295 | } |
4296 | for (int32_t part=0; part<2; ++part) { | |
4297 | if (padPos == kPadBeforePrefix) { | |
4298 | result.append(padSpec); | |
4299 | } | |
4300 | appendAffixPattern(result, | |
4301 | (part==0 ? fPosPrefixPattern : fNegPrefixPattern), | |
4302 | (part==0 ? fPositivePrefix : fNegativePrefix), | |
4303 | localized); | |
4304 | if (padPos == kPadAfterPrefix && ! padSpec.isEmpty()) { | |
4305 | result.append(padSpec); | |
4306 | } | |
4307 | int32_t sub0Start = result.length(); | |
374ca955 | 4308 | int32_t g = isGroupingUsed() ? _max(0, fGroupingSize) : 0; |
b75a7d8f A |
4309 | if (g > 0 && fGroupingSize2 > 0 && fGroupingSize2 != fGroupingSize) { |
4310 | g += fGroupingSize2; | |
4311 | } | |
374ca955 A |
4312 | int32_t maxDig = 0, minDig = 0, maxSigDig = 0; |
4313 | if (useSigDig) { | |
4314 | minDig = getMinimumSignificantDigits(); | |
4315 | maxDig = maxSigDig = getMaximumSignificantDigits(); | |
4316 | } else { | |
4317 | minDig = getMinimumIntegerDigits(); | |
4318 | maxDig = getMaximumIntegerDigits(); | |
4319 | } | |
4320 | if (fUseExponentialNotation) { | |
4321 | if (maxDig > kMaxScientificIntegerDigits) { | |
4322 | maxDig = 1; | |
4323 | } | |
4324 | } else if (useSigDig) { | |
4325 | maxDig = _max(maxDig, g+1); | |
4326 | } else { | |
4327 | maxDig = _max(_max(g, getMinimumIntegerDigits()), | |
4328 | roundingDecimalPos) + 1; | |
4329 | } | |
4330 | for (i = maxDig; i > 0; --i) { | |
4331 | if (!fUseExponentialNotation && i<maxDig && | |
b75a7d8f A |
4332 | isGroupingPosition(i)) { |
4333 | result.append(group); | |
4334 | } | |
374ca955 A |
4335 | if (useSigDig) { |
4336 | // #@,@### (maxSigDig == 5, minSigDig == 2) | |
4337 | // 65 4321 (1-based pos, count from the right) | |
4338 | // Use # if pos > maxSigDig or 1 <= pos <= (maxSigDig - minSigDig) | |
4339 | // Use @ if (maxSigDig - minSigDig) < pos <= maxSigDig | |
4340 | if (maxSigDig >= i && i > (maxSigDig - minDig)) { | |
4341 | result.append(sigDigit); | |
4342 | } else { | |
4343 | result.append(digit); | |
4344 | } | |
4345 | } else { | |
4346 | if (! roundingDigits.isEmpty()) { | |
4347 | int32_t pos = roundingDecimalPos - i; | |
4348 | if (pos >= 0 && pos < roundingDigits.length()) { | |
4349 | result.append((UChar) (roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); | |
4350 | continue; | |
4351 | } | |
4352 | } | |
4353 | if (i<=minDig) { | |
4354 | result.append(zero); | |
4355 | } else { | |
4356 | result.append(digit); | |
b75a7d8f | 4357 | } |
b75a7d8f A |
4358 | } |
4359 | } | |
374ca955 A |
4360 | if (!useSigDig) { |
4361 | if (getMaximumFractionDigits() > 0 || fDecimalSeparatorAlwaysShown) { | |
4362 | if (localized) { | |
4363 | result += getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); | |
4364 | } | |
4365 | else { | |
4366 | result.append((UChar)kPatternDecimalSeparator); | |
4367 | } | |
b75a7d8f | 4368 | } |
374ca955 A |
4369 | int32_t pos = roundingDecimalPos; |
4370 | for (i = 0; i < getMaximumFractionDigits(); ++i) { | |
4371 | if (! roundingDigits.isEmpty() && pos < roundingDigits.length()) { | |
4372 | if (pos < 0) { | |
4373 | result.append(zero); | |
4374 | } | |
4375 | else { | |
4376 | result.append((UChar)(roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); | |
4377 | } | |
4378 | ++pos; | |
4379 | continue; | |
4380 | } | |
4381 | if (i<getMinimumFractionDigits()) { | |
b75a7d8f A |
4382 | result.append(zero); |
4383 | } | |
4384 | else { | |
374ca955 | 4385 | result.append(digit); |
b75a7d8f | 4386 | } |
b75a7d8f A |
4387 | } |
4388 | } | |
4389 | if (fUseExponentialNotation) { | |
4390 | if (localized) { | |
4391 | result += getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); | |
4392 | } | |
4393 | else { | |
4394 | result.append((UChar)kPatternExponent); | |
4395 | } | |
4396 | if (fExponentSignAlwaysShown) { | |
4397 | if (localized) { | |
4398 | result += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); | |
4399 | } | |
4400 | else { | |
4401 | result.append((UChar)kPatternPlus); | |
4402 | } | |
4403 | } | |
4404 | for (i=0; i<fMinExponentDigits; ++i) { | |
4405 | result.append(zero); | |
4406 | } | |
4407 | } | |
4408 | if (! padSpec.isEmpty() && !fUseExponentialNotation) { | |
4409 | int32_t add = fFormatWidth - result.length() + sub0Start | |
4410 | - ((part == 0) | |
4411 | ? fPositivePrefix.length() + fPositiveSuffix.length() | |
4412 | : fNegativePrefix.length() + fNegativeSuffix.length()); | |
4413 | while (add > 0) { | |
4414 | result.insert(sub0Start, digit); | |
374ca955 | 4415 | ++maxDig; |
b75a7d8f A |
4416 | --add; |
4417 | // Only add a grouping separator if we have at least | |
4418 | // 2 additional characters to be added, so we don't | |
4419 | // end up with ",###". | |
374ca955 | 4420 | if (add>1 && isGroupingPosition(maxDig)) { |
b75a7d8f | 4421 | result.insert(sub0Start, group); |
729e4ab9 | 4422 | --add; |
b75a7d8f A |
4423 | } |
4424 | } | |
4425 | } | |
4426 | if (fPadPosition == kPadBeforeSuffix && ! padSpec.isEmpty()) { | |
4427 | result.append(padSpec); | |
4428 | } | |
4429 | if (part == 0) { | |
4430 | appendAffixPattern(result, fPosSuffixPattern, fPositiveSuffix, localized); | |
4431 | if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { | |
4432 | result.append(padSpec); | |
4433 | } | |
4434 | UBool isDefault = FALSE; | |
4435 | if ((fNegSuffixPattern == fPosSuffixPattern && // both null | |
4436 | fNegativeSuffix == fPositiveSuffix) | |
4437 | || (fNegSuffixPattern != 0 && fPosSuffixPattern != 0 && | |
4438 | *fNegSuffixPattern == *fPosSuffixPattern)) | |
4439 | { | |
4440 | if (fNegPrefixPattern != NULL && fPosPrefixPattern != NULL) | |
4441 | { | |
4442 | int32_t length = fPosPrefixPattern->length(); | |
4443 | isDefault = fNegPrefixPattern->length() == (length+2) && | |
4444 | (*fNegPrefixPattern)[(int32_t)0] == kQuote && | |
4445 | (*fNegPrefixPattern)[(int32_t)1] == kPatternMinus && | |
4446 | fNegPrefixPattern->compare(2, length, *fPosPrefixPattern, 0, length) == 0; | |
4447 | } | |
4448 | if (!isDefault && | |
4449 | fNegPrefixPattern == NULL && fPosPrefixPattern == NULL) | |
4450 | { | |
4451 | int32_t length = fPositivePrefix.length(); | |
4452 | isDefault = fNegativePrefix.length() == (length+1) && | |
4453 | fNegativePrefix.compare(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)) == 0 && | |
4454 | fNegativePrefix.compare(1, length, fPositivePrefix, 0, length) == 0; | |
4455 | } | |
4456 | } | |
4457 | if (isDefault) { | |
4458 | break; // Don't output default negative subpattern | |
4459 | } else { | |
4460 | if (localized) { | |
4461 | result += getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); | |
4462 | } | |
4463 | else { | |
4464 | result.append((UChar)kPatternSeparator); | |
4465 | } | |
4466 | } | |
4467 | } else { | |
4468 | appendAffixPattern(result, fNegSuffixPattern, fNegativeSuffix, localized); | |
4469 | if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { | |
4470 | result.append(padSpec); | |
4471 | } | |
4472 | } | |
4473 | } | |
4474 | ||
4475 | return result; | |
4476 | } | |
4477 | ||
4478 | //------------------------------------------------------------------------------ | |
4479 | ||
4480 | void | |
4481 | DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) | |
4482 | { | |
4483 | UParseError parseError; | |
4484 | applyPattern(pattern, FALSE, parseError, status); | |
4485 | } | |
4486 | ||
4487 | //------------------------------------------------------------------------------ | |
4488 | ||
4489 | void | |
4490 | DecimalFormat::applyPattern(const UnicodeString& pattern, | |
729e4ab9 | 4491 | UParseError& parseError, |
b75a7d8f A |
4492 | UErrorCode& status) |
4493 | { | |
4494 | applyPattern(pattern, FALSE, parseError, status); | |
4495 | } | |
4496 | //------------------------------------------------------------------------------ | |
4497 | ||
4498 | void | |
4499 | DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) | |
4500 | { | |
4501 | UParseError parseError; | |
4502 | applyPattern(pattern, TRUE,parseError,status); | |
4503 | } | |
4504 | ||
4505 | //------------------------------------------------------------------------------ | |
4506 | ||
4507 | void | |
4508 | DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, | |
4509 | UParseError& parseError, | |
4510 | UErrorCode& status) | |
4511 | { | |
4512 | applyPattern(pattern, TRUE,parseError,status); | |
4513 | } | |
4514 | ||
4515 | //------------------------------------------------------------------------------ | |
4516 | ||
4517 | void | |
729e4ab9 A |
4518 | DecimalFormat::applyPatternWithoutExpandAffix(const UnicodeString& pattern, |
4519 | UBool localized, | |
4520 | UParseError& parseError, | |
4521 | UErrorCode& status) | |
b75a7d8f A |
4522 | { |
4523 | if (U_FAILURE(status)) | |
4524 | { | |
4525 | return; | |
4526 | } | |
4527 | // Clear error struct | |
4528 | parseError.offset = -1; | |
4529 | parseError.preContext[0] = parseError.postContext[0] = (UChar)0; | |
4530 | ||
4531 | // Set the significant pattern symbols | |
374ca955 A |
4532 | UChar32 zeroDigit = kPatternZeroDigit; // '0' |
4533 | UChar32 sigDigit = kPatternSignificantDigit; // '@' | |
b75a7d8f A |
4534 | UnicodeString groupingSeparator ((UChar)kPatternGroupingSeparator); |
4535 | UnicodeString decimalSeparator ((UChar)kPatternDecimalSeparator); | |
4536 | UnicodeString percent ((UChar)kPatternPercent); | |
4537 | UnicodeString perMill ((UChar)kPatternPerMill); | |
374ca955 | 4538 | UnicodeString digit ((UChar)kPatternDigit); // '#' |
b75a7d8f A |
4539 | UnicodeString separator ((UChar)kPatternSeparator); |
4540 | UnicodeString exponent ((UChar)kPatternExponent); | |
4541 | UnicodeString plus ((UChar)kPatternPlus); | |
4542 | UnicodeString minus ((UChar)kPatternMinus); | |
4543 | UnicodeString padEscape ((UChar)kPatternPadEscape); | |
4544 | // Substitute with the localized symbols if necessary | |
4545 | if (localized) { | |
4546 | zeroDigit = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); | |
374ca955 | 4547 | sigDigit = getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); |
b75a7d8f A |
4548 | groupingSeparator. remove().append(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); |
4549 | decimalSeparator. remove().append(getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); | |
4550 | percent. remove().append(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)); | |
4551 | perMill. remove().append(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)); | |
4552 | digit. remove().append(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)); | |
4553 | separator. remove().append(getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)); | |
4554 | exponent. remove().append(getConstSymbol(DecimalFormatSymbols::kExponentialSymbol)); | |
4555 | plus. remove().append(getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol)); | |
4556 | minus. remove().append(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)); | |
4557 | padEscape. remove().append(getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); | |
4558 | } | |
4559 | UChar nineDigit = (UChar)(zeroDigit + 9); | |
4560 | int32_t digitLen = digit.length(); | |
4561 | int32_t groupSepLen = groupingSeparator.length(); | |
4562 | int32_t decimalSepLen = decimalSeparator.length(); | |
4563 | ||
4564 | int32_t pos = 0; | |
4565 | int32_t patLen = pattern.length(); | |
4566 | // Part 0 is the positive pattern. Part 1, if present, is the negative | |
4567 | // pattern. | |
4568 | for (int32_t part=0; part<2 && pos<patLen; ++part) { | |
4569 | // The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix, | |
4570 | // 2=suffix, 3=prefix in quote, 4=suffix in quote. Subpart 0 is | |
4571 | // between the prefix and suffix, and consists of pattern | |
4572 | // characters. In the prefix and suffix, percent, perMill, and | |
4573 | // currency symbols are recognized and translated. | |
4574 | int32_t subpart = 1, sub0Start = 0, sub0Limit = 0, sub2Limit = 0; | |
4575 | ||
4576 | // It's important that we don't change any fields of this object | |
4577 | // prematurely. We set the following variables for the multiplier, | |
4578 | // grouping, etc., and then only change the actual object fields if | |
4579 | // everything parses correctly. This also lets us register | |
4580 | // the data from part 0 and ignore the part 1, except for the | |
4581 | // prefix and suffix. | |
4582 | UnicodeString prefix; | |
4583 | UnicodeString suffix; | |
4584 | int32_t decimalPos = -1; | |
4585 | int32_t multiplier = 1; | |
374ca955 | 4586 | int32_t digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0, sigDigitCount = 0; |
b75a7d8f A |
4587 | int8_t groupingCount = -1; |
4588 | int8_t groupingCount2 = -1; | |
4589 | int32_t padPos = -1; | |
374ca955 | 4590 | UChar32 padChar = 0; |
b75a7d8f A |
4591 | int32_t roundingPos = -1; |
4592 | DigitList roundingInc; | |
4593 | int8_t expDigits = -1; | |
4594 | UBool expSignAlways = FALSE; | |
729e4ab9 | 4595 | |
b75a7d8f A |
4596 | // The affix is either the prefix or the suffix. |
4597 | UnicodeString* affix = &prefix; | |
729e4ab9 | 4598 | |
b75a7d8f A |
4599 | int32_t start = pos; |
4600 | UBool isPartDone = FALSE; | |
4601 | UChar32 ch; | |
4602 | ||
374ca955 | 4603 | for (; !isPartDone && pos < patLen; ) { |
b75a7d8f A |
4604 | // Todo: account for surrogate pairs |
4605 | ch = pattern.char32At(pos); | |
4606 | switch (subpart) { | |
4607 | case 0: // Pattern proper subpart (between prefix & suffix) | |
4608 | // Process the digits, decimal, and grouping characters. We | |
4609 | // record five pieces of information. We expect the digits | |
4610 | // to occur in the pattern ####00.00####, and we record the | |
4611 | // number of left digits, zero (central) digits, and right | |
4612 | // digits. The position of the last grouping character is | |
4613 | // recorded (should be somewhere within the first two blocks | |
4614 | // of characters), as is the position of the decimal point, | |
4615 | // if any (should be in the zero digits). If there is no | |
4616 | // decimal point, then there should be no right digits. | |
4617 | if (pattern.compare(pos, digitLen, digit) == 0) { | |
374ca955 | 4618 | if (zeroDigitCount > 0 || sigDigitCount > 0) { |
b75a7d8f A |
4619 | ++digitRightCount; |
4620 | } else { | |
4621 | ++digitLeftCount; | |
4622 | } | |
4623 | if (groupingCount >= 0 && decimalPos < 0) { | |
4624 | ++groupingCount; | |
4625 | } | |
4626 | pos += digitLen; | |
374ca955 A |
4627 | } else if ((ch >= zeroDigit && ch <= nineDigit) || |
4628 | ch == sigDigit) { | |
b75a7d8f A |
4629 | if (digitRightCount > 0) { |
4630 | // Unexpected '0' | |
4631 | debug("Unexpected '0'") | |
4632 | status = U_UNEXPECTED_TOKEN; | |
4633 | syntaxError(pattern,pos,parseError); | |
4634 | return; | |
4635 | } | |
374ca955 A |
4636 | if (ch == sigDigit) { |
4637 | ++sigDigitCount; | |
4638 | } else { | |
374ca955 A |
4639 | if (ch != zeroDigit && roundingPos < 0) { |
4640 | roundingPos = digitLeftCount + zeroDigitCount; | |
4641 | } | |
4642 | if (roundingPos >= 0) { | |
4643 | roundingInc.append((char)(ch - zeroDigit + '0')); | |
4644 | } | |
4388f060 | 4645 | ++zeroDigitCount; |
374ca955 | 4646 | } |
b75a7d8f A |
4647 | if (groupingCount >= 0 && decimalPos < 0) { |
4648 | ++groupingCount; | |
4649 | } | |
374ca955 | 4650 | pos += U16_LENGTH(ch); |
b75a7d8f A |
4651 | } else if (pattern.compare(pos, groupSepLen, groupingSeparator) == 0) { |
4652 | if (decimalPos >= 0) { | |
4653 | // Grouping separator after decimal | |
4654 | debug("Grouping separator after decimal") | |
4655 | status = U_UNEXPECTED_TOKEN; | |
4656 | syntaxError(pattern,pos,parseError); | |
4657 | return; | |
4658 | } | |
4659 | groupingCount2 = groupingCount; | |
4660 | groupingCount = 0; | |
4661 | pos += groupSepLen; | |
4662 | } else if (pattern.compare(pos, decimalSepLen, decimalSeparator) == 0) { | |
4663 | if (decimalPos >= 0) { | |
4664 | // Multiple decimal separators | |
4665 | debug("Multiple decimal separators") | |
4666 | status = U_MULTIPLE_DECIMAL_SEPARATORS; | |
4667 | syntaxError(pattern,pos,parseError); | |
4668 | return; | |
4669 | } | |
4670 | // Intentionally incorporate the digitRightCount, | |
4671 | // even though it is illegal for this to be > 0 | |
4672 | // at this point. We check pattern syntax below. | |
4673 | decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; | |
4674 | pos += decimalSepLen; | |
4675 | } else { | |
4676 | if (pattern.compare(pos, exponent.length(), exponent) == 0) { | |
4677 | if (expDigits >= 0) { | |
4678 | // Multiple exponential symbols | |
4679 | debug("Multiple exponential symbols") | |
4680 | status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; | |
4681 | syntaxError(pattern,pos,parseError); | |
4682 | return; | |
4683 | } | |
4684 | if (groupingCount >= 0) { | |
4685 | // Grouping separator in exponential pattern | |
4686 | debug("Grouping separator in exponential pattern") | |
4687 | status = U_MALFORMED_EXPONENTIAL_PATTERN; | |
4688 | syntaxError(pattern,pos,parseError); | |
4689 | return; | |
4690 | } | |
374ca955 | 4691 | pos += exponent.length(); |
b75a7d8f | 4692 | // Check for positive prefix |
374ca955 A |
4693 | if (pos < patLen |
4694 | && pattern.compare(pos, plus.length(), plus) == 0) { | |
b75a7d8f A |
4695 | expSignAlways = TRUE; |
4696 | pos += plus.length(); | |
4697 | } | |
4698 | // Use lookahead to parse out the exponential part of the | |
4699 | // pattern, then jump into suffix subpart. | |
4700 | expDigits = 0; | |
374ca955 A |
4701 | while (pos < patLen && |
4702 | pattern.char32At(pos) == zeroDigit) { | |
b75a7d8f | 4703 | ++expDigits; |
374ca955 | 4704 | pos += U16_LENGTH(zeroDigit); |
b75a7d8f A |
4705 | } |
4706 | ||
374ca955 A |
4707 | // 1. Require at least one mantissa pattern digit |
4708 | // 2. Disallow "#+ @" in mantissa | |
4709 | // 3. Require at least one exponent pattern digit | |
4710 | if (((digitLeftCount + zeroDigitCount) < 1 && | |
4711 | (sigDigitCount + digitRightCount) < 1) || | |
4712 | (sigDigitCount > 0 && digitLeftCount > 0) || | |
b75a7d8f A |
4713 | expDigits < 1) { |
4714 | // Malformed exponential pattern | |
4715 | debug("Malformed exponential pattern") | |
4716 | status = U_MALFORMED_EXPONENTIAL_PATTERN; | |
4717 | syntaxError(pattern,pos,parseError); | |
4718 | return; | |
4719 | } | |
4720 | } | |
4721 | // Transition to suffix subpart | |
4722 | subpart = 2; // suffix subpart | |
4723 | affix = &suffix; | |
4724 | sub0Limit = pos; | |
4725 | continue; | |
4726 | } | |
4727 | break; | |
4728 | case 1: // Prefix subpart | |
4729 | case 2: // Suffix subpart | |
4730 | // Process the prefix / suffix characters | |
4731 | // Process unquoted characters seen in prefix or suffix | |
4732 | // subpart. | |
374ca955 A |
4733 | |
4734 | // Several syntax characters implicitly begins the | |
4735 | // next subpart if we are in the prefix; otherwise | |
4736 | // they are illegal if unquoted. | |
4737 | if (!pattern.compare(pos, digitLen, digit) || | |
4738 | !pattern.compare(pos, groupSepLen, groupingSeparator) || | |
4739 | !pattern.compare(pos, decimalSepLen, decimalSeparator) || | |
4740 | (ch >= zeroDigit && ch <= nineDigit) || | |
4741 | ch == sigDigit) { | |
b75a7d8f A |
4742 | if (subpart == 1) { // prefix subpart |
4743 | subpart = 0; // pattern proper subpart | |
4744 | sub0Start = pos; // Reprocess this character | |
4745 | continue; | |
374ca955 A |
4746 | } else { |
4747 | status = U_UNQUOTED_SPECIAL; | |
4748 | syntaxError(pattern,pos,parseError); | |
4749 | return; | |
b75a7d8f | 4750 | } |
b75a7d8f | 4751 | } else if (ch == kCurrencySign) { |
374ca955 | 4752 | affix->append(kQuote); // Encode currency |
b75a7d8f A |
4753 | // Use lookahead to determine if the currency sign is |
4754 | // doubled or not. | |
374ca955 A |
4755 | U_ASSERT(U16_LENGTH(kCurrencySign) == 1); |
4756 | if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { | |
b75a7d8f A |
4757 | affix->append(kCurrencySign); |
4758 | ++pos; // Skip over the doubled character | |
729e4ab9 A |
4759 | if ((pos+1) < pattern.length() && |
4760 | pattern[pos+1] == kCurrencySign) { | |
4761 | affix->append(kCurrencySign); | |
4762 | ++pos; // Skip over the doubled character | |
4763 | fCurrencySignCount = fgCurrencySignCountInPluralFormat; | |
4764 | } else { | |
4765 | fCurrencySignCount = fgCurrencySignCountInISOFormat; | |
4766 | } | |
4767 | } else { | |
4768 | fCurrencySignCount = fgCurrencySignCountInSymbolFormat; | |
b75a7d8f | 4769 | } |
b75a7d8f A |
4770 | // Fall through to append(ch) |
4771 | } else if (ch == kQuote) { | |
4772 | // A quote outside quotes indicates either the opening | |
4773 | // quote or two quotes, which is a quote literal. That is, | |
4774 | // we have the first quote in 'do' or o''clock. | |
374ca955 | 4775 | U_ASSERT(U16_LENGTH(kQuote) == 1); |
b75a7d8f A |
4776 | ++pos; |
4777 | if (pos < pattern.length() && pattern[pos] == kQuote) { | |
4778 | affix->append(kQuote); // Encode quote | |
b75a7d8f A |
4779 | // Fall through to append(ch) |
4780 | } else { | |
4781 | subpart += 2; // open quote | |
4782 | continue; | |
4783 | } | |
4784 | } else if (pattern.compare(pos, separator.length(), separator) == 0) { | |
4785 | // Don't allow separators in the prefix, and don't allow | |
4786 | // separators in the second pattern (part == 1). | |
4787 | if (subpart == 1 || part == 1) { | |
4788 | // Unexpected separator | |
4789 | debug("Unexpected separator") | |
4790 | status = U_UNEXPECTED_TOKEN; | |
4791 | syntaxError(pattern,pos,parseError); | |
4792 | return; | |
4793 | } | |
4794 | sub2Limit = pos; | |
4795 | isPartDone = TRUE; // Go to next part | |
4796 | pos += separator.length(); | |
4797 | break; | |
4798 | } else if (pattern.compare(pos, percent.length(), percent) == 0) { | |
4799 | // Next handle characters which are appended directly. | |
4800 | if (multiplier != 1) { | |
4801 | // Too many percent/perMill characters | |
4802 | debug("Too many percent characters") | |
4803 | status = U_MULTIPLE_PERCENT_SYMBOLS; | |
4804 | syntaxError(pattern,pos,parseError); | |
4805 | return; | |
4806 | } | |
4807 | affix->append(kQuote); // Encode percent/perMill | |
374ca955 | 4808 | affix->append(kPatternPercent); // Use unlocalized pattern char |
b75a7d8f | 4809 | multiplier = 100; |
b75a7d8f | 4810 | pos += percent.length(); |
374ca955 | 4811 | break; |
b75a7d8f A |
4812 | } else if (pattern.compare(pos, perMill.length(), perMill) == 0) { |
4813 | // Next handle characters which are appended directly. | |
4814 | if (multiplier != 1) { | |
4815 | // Too many percent/perMill characters | |
4816 | debug("Too many perMill characters") | |
4817 | status = U_MULTIPLE_PERMILL_SYMBOLS; | |
4818 | syntaxError(pattern,pos,parseError); | |
4819 | return; | |
4820 | } | |
4821 | affix->append(kQuote); // Encode percent/perMill | |
374ca955 | 4822 | affix->append(kPatternPerMill); // Use unlocalized pattern char |
b75a7d8f | 4823 | multiplier = 1000; |
b75a7d8f | 4824 | pos += perMill.length(); |
374ca955 | 4825 | break; |
b75a7d8f A |
4826 | } else if (pattern.compare(pos, padEscape.length(), padEscape) == 0) { |
4827 | if (padPos >= 0 || // Multiple pad specifiers | |
4828 | (pos+1) == pattern.length()) { // Nothing after padEscape | |
4829 | debug("Multiple pad specifiers") | |
4830 | status = U_MULTIPLE_PAD_SPECIFIERS; | |
4831 | syntaxError(pattern,pos,parseError); | |
4832 | return; | |
4833 | } | |
4834 | padPos = pos; | |
4835 | pos += padEscape.length(); | |
4836 | padChar = pattern.char32At(pos); | |
4837 | pos += U16_LENGTH(padChar); | |
374ca955 | 4838 | break; |
b75a7d8f A |
4839 | } else if (pattern.compare(pos, minus.length(), minus) == 0) { |
4840 | affix->append(kQuote); // Encode minus | |
374ca955 | 4841 | affix->append(kPatternMinus); |
b75a7d8f | 4842 | pos += minus.length(); |
374ca955 | 4843 | break; |
b75a7d8f A |
4844 | } else if (pattern.compare(pos, plus.length(), plus) == 0) { |
4845 | affix->append(kQuote); // Encode plus | |
374ca955 | 4846 | affix->append(kPatternPlus); |
b75a7d8f | 4847 | pos += plus.length(); |
374ca955 | 4848 | break; |
b75a7d8f A |
4849 | } |
4850 | // Unquoted, non-special characters fall through to here, as | |
4851 | // well as other code which needs to append something to the | |
4852 | // affix. | |
4853 | affix->append(ch); | |
374ca955 | 4854 | pos += U16_LENGTH(ch); |
b75a7d8f A |
4855 | break; |
4856 | case 3: // Prefix subpart, in quote | |
4857 | case 4: // Suffix subpart, in quote | |
4858 | // A quote within quotes indicates either the closing | |
4859 | // quote or two quotes, which is a quote literal. That is, | |
4860 | // we have the second quote in 'do' or 'don''t'. | |
b75a7d8f | 4861 | if (ch == kQuote) { |
374ca955 | 4862 | ++pos; |
b75a7d8f | 4863 | if (pos < pattern.length() && pattern[pos] == kQuote) { |
b75a7d8f A |
4864 | affix->append(kQuote); // Encode quote |
4865 | // Fall through to append(ch) | |
4866 | } else { | |
4867 | subpart -= 2; // close quote | |
4868 | continue; | |
4869 | } | |
4870 | } | |
4871 | affix->append(ch); | |
374ca955 | 4872 | pos += U16_LENGTH(ch); |
b75a7d8f A |
4873 | break; |
4874 | } | |
4875 | } | |
4876 | ||
4877 | if (sub0Limit == 0) { | |
4878 | sub0Limit = pattern.length(); | |
4879 | } | |
4880 | ||
4881 | if (sub2Limit == 0) { | |
4882 | sub2Limit = pattern.length(); | |
4883 | } | |
4884 | ||
4885 | /* Handle patterns with no '0' pattern character. These patterns | |
4886 | * are legal, but must be recodified to make sense. "##.###" -> | |
4887 | * "#0.###". ".###" -> ".0##". | |
4888 | * | |
4889 | * We allow patterns of the form "####" to produce a zeroDigitCount | |
4890 | * of zero (got that?); although this seems like it might make it | |
4891 | * possible for format() to produce empty strings, format() checks | |
4892 | * for this condition and outputs a zero digit in this situation. | |
4893 | * Having a zeroDigitCount of zero yields a minimum integer digits | |
4894 | * of zero, which allows proper round-trip patterns. We don't want | |
4895 | * "#" to become "#0" when toPattern() is called (even though that's | |
4896 | * what it really is, semantically). | |
4897 | */ | |
374ca955 A |
4898 | if (zeroDigitCount == 0 && sigDigitCount == 0 && |
4899 | digitLeftCount > 0 && decimalPos >= 0) { | |
b75a7d8f A |
4900 | // Handle "###.###" and "###." and ".###" |
4901 | int n = decimalPos; | |
4902 | if (n == 0) | |
4903 | ++n; // Handle ".###" | |
4904 | digitRightCount = digitLeftCount - n; | |
4905 | digitLeftCount = n - 1; | |
4906 | zeroDigitCount = 1; | |
4907 | } | |
4908 | ||
4909 | // Do syntax checking on the digits, decimal points, and quotes. | |
374ca955 | 4910 | if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) || |
b75a7d8f | 4911 | (decimalPos >= 0 && |
374ca955 A |
4912 | (sigDigitCount > 0 || |
4913 | decimalPos < digitLeftCount || | |
b75a7d8f A |
4914 | decimalPos > (digitLeftCount + zeroDigitCount))) || |
4915 | groupingCount == 0 || groupingCount2 == 0 || | |
374ca955 | 4916 | (sigDigitCount > 0 && zeroDigitCount > 0) || |
b75a7d8f A |
4917 | subpart > 2) |
4918 | { // subpart > 2 == unmatched quote | |
4919 | debug("Syntax error") | |
4920 | status = U_PATTERN_SYNTAX_ERROR; | |
4921 | syntaxError(pattern,pos,parseError); | |
4922 | return; | |
4923 | } | |
4924 | ||
4925 | // Make sure pad is at legal position before or after affix. | |
4926 | if (padPos >= 0) { | |
4927 | if (padPos == start) { | |
4928 | padPos = kPadBeforePrefix; | |
4929 | } else if (padPos+2 == sub0Start) { | |
4930 | padPos = kPadAfterPrefix; | |
4931 | } else if (padPos == sub0Limit) { | |
4932 | padPos = kPadBeforeSuffix; | |
4933 | } else if (padPos+2 == sub2Limit) { | |
4934 | padPos = kPadAfterSuffix; | |
4935 | } else { | |
4936 | // Illegal pad position | |
4937 | debug("Illegal pad position") | |
4938 | status = U_ILLEGAL_PAD_POSITION; | |
4939 | syntaxError(pattern,pos,parseError); | |
4940 | return; | |
4941 | } | |
4942 | } | |
4943 | ||
4944 | if (part == 0) { | |
4945 | delete fPosPrefixPattern; | |
4946 | delete fPosSuffixPattern; | |
4947 | delete fNegPrefixPattern; | |
4948 | delete fNegSuffixPattern; | |
4949 | fPosPrefixPattern = new UnicodeString(prefix); | |
4950 | /* test for NULL */ | |
4951 | if (fPosPrefixPattern == 0) { | |
4952 | status = U_MEMORY_ALLOCATION_ERROR; | |
4953 | return; | |
4954 | } | |
4955 | fPosSuffixPattern = new UnicodeString(suffix); | |
4956 | /* test for NULL */ | |
4957 | if (fPosSuffixPattern == 0) { | |
4958 | status = U_MEMORY_ALLOCATION_ERROR; | |
4959 | delete fPosPrefixPattern; | |
4960 | return; | |
4961 | } | |
4962 | fNegPrefixPattern = 0; | |
4963 | fNegSuffixPattern = 0; | |
4964 | ||
4965 | fUseExponentialNotation = (expDigits >= 0); | |
4966 | if (fUseExponentialNotation) { | |
4967 | fMinExponentDigits = expDigits; | |
4968 | } | |
4969 | fExponentSignAlwaysShown = expSignAlways; | |
374ca955 | 4970 | int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; |
b75a7d8f A |
4971 | // The effectiveDecimalPos is the position the decimal is at or |
4972 | // would be at if there is no decimal. Note that if | |
4973 | // decimalPos<0, then digitTotalCount == digitLeftCount + | |
4974 | // zeroDigitCount. | |
374ca955 A |
4975 | int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; |
4976 | UBool isSigDig = (sigDigitCount > 0); | |
4977 | setSignificantDigitsUsed(isSigDig); | |
4978 | if (isSigDig) { | |
4979 | setMinimumSignificantDigits(sigDigitCount); | |
4980 | setMaximumSignificantDigits(sigDigitCount + digitRightCount); | |
4981 | } else { | |
4982 | int32_t minInt = effectiveDecimalPos - digitLeftCount; | |
4983 | setMinimumIntegerDigits(minInt); | |
4984 | setMaximumIntegerDigits(fUseExponentialNotation | |
b75a7d8f A |
4985 | ? digitLeftCount + getMinimumIntegerDigits() |
4986 | : kDoubleIntegerDigits); | |
374ca955 | 4987 | setMaximumFractionDigits(decimalPos >= 0 |
b75a7d8f | 4988 | ? (digitTotalCount - decimalPos) : 0); |
374ca955 | 4989 | setMinimumFractionDigits(decimalPos >= 0 |
b75a7d8f | 4990 | ? (digitLeftCount + zeroDigitCount - decimalPos) : 0); |
374ca955 | 4991 | } |
b75a7d8f A |
4992 | setGroupingUsed(groupingCount > 0); |
4993 | fGroupingSize = (groupingCount > 0) ? groupingCount : 0; | |
4994 | fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) | |
4995 | ? groupingCount2 : 0; | |
729e4ab9 | 4996 | setMultiplier(multiplier); |
b75a7d8f A |
4997 | setDecimalSeparatorAlwaysShown(decimalPos == 0 |
4998 | || decimalPos == digitTotalCount); | |
4999 | if (padPos >= 0) { | |
5000 | fPadPosition = (EPadPosition) padPos; | |
5001 | // To compute the format width, first set up sub0Limit - | |
5002 | // sub0Start. Add in prefix/suffix length later. | |
5003 | ||
5004 | // fFormatWidth = prefix.length() + suffix.length() + | |
5005 | // sub0Limit - sub0Start; | |
5006 | fFormatWidth = sub0Limit - sub0Start; | |
5007 | fPad = padChar; | |
5008 | } else { | |
5009 | fFormatWidth = 0; | |
5010 | } | |
5011 | if (roundingPos >= 0) { | |
729e4ab9 | 5012 | roundingInc.setDecimalAt(effectiveDecimalPos - roundingPos); |
b75a7d8f A |
5013 | if (fRoundingIncrement != NULL) { |
5014 | *fRoundingIncrement = roundingInc; | |
5015 | } else { | |
5016 | fRoundingIncrement = new DigitList(roundingInc); | |
5017 | /* test for NULL */ | |
729e4ab9 | 5018 | if (fRoundingIncrement == NULL) { |
b75a7d8f A |
5019 | status = U_MEMORY_ALLOCATION_ERROR; |
5020 | delete fPosPrefixPattern; | |
5021 | delete fPosSuffixPattern; | |
5022 | return; | |
5023 | } | |
5024 | } | |
b75a7d8f A |
5025 | fRoundingMode = kRoundHalfEven; |
5026 | } else { | |
5027 | setRoundingIncrement(0.0); | |
5028 | } | |
5029 | } else { | |
5030 | fNegPrefixPattern = new UnicodeString(prefix); | |
5031 | /* test for NULL */ | |
5032 | if (fNegPrefixPattern == 0) { | |
5033 | status = U_MEMORY_ALLOCATION_ERROR; | |
5034 | return; | |
5035 | } | |
5036 | fNegSuffixPattern = new UnicodeString(suffix); | |
5037 | /* test for NULL */ | |
5038 | if (fNegSuffixPattern == 0) { | |
5039 | delete fNegPrefixPattern; | |
5040 | status = U_MEMORY_ALLOCATION_ERROR; | |
5041 | return; | |
5042 | } | |
5043 | } | |
5044 | } | |
5045 | ||
5046 | if (pattern.length() == 0) { | |
5047 | delete fNegPrefixPattern; | |
5048 | delete fNegSuffixPattern; | |
5049 | fNegPrefixPattern = NULL; | |
5050 | fNegSuffixPattern = NULL; | |
5051 | if (fPosPrefixPattern != NULL) { | |
5052 | fPosPrefixPattern->remove(); | |
5053 | } else { | |
5054 | fPosPrefixPattern = new UnicodeString(); | |
5055 | /* test for NULL */ | |
5056 | if (fPosPrefixPattern == 0) { | |
5057 | status = U_MEMORY_ALLOCATION_ERROR; | |
5058 | return; | |
5059 | } | |
5060 | } | |
5061 | if (fPosSuffixPattern != NULL) { | |
5062 | fPosSuffixPattern->remove(); | |
5063 | } else { | |
5064 | fPosSuffixPattern = new UnicodeString(); | |
5065 | /* test for NULL */ | |
5066 | if (fPosSuffixPattern == 0) { | |
5067 | delete fPosPrefixPattern; | |
5068 | status = U_MEMORY_ALLOCATION_ERROR; | |
5069 | return; | |
5070 | } | |
5071 | } | |
5072 | ||
5073 | setMinimumIntegerDigits(0); | |
5074 | setMaximumIntegerDigits(kDoubleIntegerDigits); | |
5075 | setMinimumFractionDigits(0); | |
5076 | setMaximumFractionDigits(kDoubleFractionDigits); | |
5077 | ||
5078 | fUseExponentialNotation = FALSE; | |
729e4ab9 | 5079 | fCurrencySignCount = 0; |
b75a7d8f A |
5080 | setGroupingUsed(FALSE); |
5081 | fGroupingSize = 0; | |
5082 | fGroupingSize2 = 0; | |
729e4ab9 | 5083 | setMultiplier(1); |
b75a7d8f A |
5084 | setDecimalSeparatorAlwaysShown(FALSE); |
5085 | fFormatWidth = 0; | |
5086 | setRoundingIncrement(0.0); | |
5087 | } | |
5088 | ||
5089 | // If there was no negative pattern, or if the negative pattern is | |
5090 | // identical to the positive pattern, then prepend the minus sign to the | |
5091 | // positive pattern to form the negative pattern. | |
5092 | if (fNegPrefixPattern == NULL || | |
5093 | (*fNegPrefixPattern == *fPosPrefixPattern | |
5094 | && *fNegSuffixPattern == *fPosSuffixPattern)) { | |
51004dcb | 5095 | _copy_ptr(&fNegSuffixPattern, fPosSuffixPattern); |
b75a7d8f A |
5096 | if (fNegPrefixPattern == NULL) { |
5097 | fNegPrefixPattern = new UnicodeString(); | |
5098 | /* test for NULL */ | |
5099 | if (fNegPrefixPattern == 0) { | |
5100 | status = U_MEMORY_ALLOCATION_ERROR; | |
5101 | return; | |
5102 | } | |
5103 | } else { | |
5104 | fNegPrefixPattern->remove(); | |
5105 | } | |
5106 | fNegPrefixPattern->append(kQuote).append(kPatternMinus) | |
5107 | .append(*fPosPrefixPattern); | |
5108 | } | |
5109 | #ifdef FMT_DEBUG | |
5110 | UnicodeString s; | |
51004dcb | 5111 | s.append((UnicodeString)"\"").append(pattern).append((UnicodeString)"\"->"); |
b75a7d8f A |
5112 | debugout(s); |
5113 | #endif | |
729e4ab9 A |
5114 | |
5115 | // save the pattern | |
5116 | fFormatPattern = pattern; | |
5117 | } | |
5118 | ||
5119 | ||
5120 | void | |
5121 | DecimalFormat::expandAffixAdjustWidth(const UnicodeString* pluralCount) { | |
5122 | expandAffixes(pluralCount); | |
b75a7d8f A |
5123 | if (fFormatWidth > 0) { |
5124 | // Finish computing format width (see above) | |
729e4ab9 A |
5125 | // TODO: how to handle fFormatWidth, |
5126 | // need to save in f(Plural)AffixesForCurrecy? | |
5127 | fFormatWidth += fPositivePrefix.length() + fPositiveSuffix.length(); | |
5128 | } | |
5129 | } | |
5130 | ||
5131 | ||
5132 | void | |
5133 | DecimalFormat::applyPattern(const UnicodeString& pattern, | |
5134 | UBool localized, | |
5135 | UParseError& parseError, | |
5136 | UErrorCode& status) | |
5137 | { | |
5138 | // do the following re-set first. since they change private data by | |
5139 | // apply pattern again. | |
5140 | if (pattern.indexOf(kCurrencySign) != -1) { | |
5141 | if (fCurrencyPluralInfo == NULL) { | |
5142 | // initialize currencyPluralInfo if needed | |
5143 | fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); | |
5144 | } | |
5145 | if (fAffixPatternsForCurrency == NULL) { | |
5146 | setupCurrencyAffixPatterns(status); | |
5147 | } | |
4388f060 | 5148 | if (pattern.indexOf(fgTripleCurrencySign, 3, 0) != -1) { |
729e4ab9 A |
5149 | // only setup the affixes of the current pattern. |
5150 | setupCurrencyAffixes(pattern, TRUE, FALSE, status); | |
5151 | } | |
b75a7d8f | 5152 | } |
729e4ab9 A |
5153 | applyPatternWithoutExpandAffix(pattern, localized, parseError, status); |
5154 | expandAffixAdjustWidth(NULL); | |
51004dcb A |
5155 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5156 | handleChanged(); | |
5157 | #endif | |
729e4ab9 A |
5158 | } |
5159 | ||
5160 | ||
5161 | void | |
5162 | DecimalFormat::applyPatternInternally(const UnicodeString& pluralCount, | |
5163 | const UnicodeString& pattern, | |
5164 | UBool localized, | |
5165 | UParseError& parseError, | |
5166 | UErrorCode& status) { | |
5167 | applyPatternWithoutExpandAffix(pattern, localized, parseError, status); | |
5168 | expandAffixAdjustWidth(&pluralCount); | |
51004dcb A |
5169 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5170 | handleChanged(); | |
5171 | #endif | |
b75a7d8f A |
5172 | } |
5173 | ||
729e4ab9 | 5174 | |
b75a7d8f A |
5175 | /** |
5176 | * Sets the maximum number of digits allowed in the integer portion of a | |
5177 | * number. This override limits the integer digit count to 309. | |
5178 | * @see NumberFormat#setMaximumIntegerDigits | |
5179 | */ | |
5180 | void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { | |
374ca955 | 5181 | NumberFormat::setMaximumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); |
51004dcb A |
5182 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5183 | handleChanged(); | |
5184 | #endif | |
b75a7d8f A |
5185 | } |
5186 | ||
5187 | /** | |
5188 | * Sets the minimum number of digits allowed in the integer portion of a | |
5189 | * number. This override limits the integer digit count to 309. | |
5190 | * @see NumberFormat#setMinimumIntegerDigits | |
5191 | */ | |
5192 | void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { | |
374ca955 | 5193 | NumberFormat::setMinimumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); |
51004dcb A |
5194 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5195 | handleChanged(); | |
5196 | #endif | |
b75a7d8f A |
5197 | } |
5198 | ||
5199 | /** | |
5200 | * Sets the maximum number of digits allowed in the fraction portion of a | |
5201 | * number. This override limits the fraction digit count to 340. | |
5202 | * @see NumberFormat#setMaximumFractionDigits | |
5203 | */ | |
5204 | void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { | |
374ca955 | 5205 | NumberFormat::setMaximumFractionDigits(_min(newValue, kDoubleFractionDigits)); |
51004dcb A |
5206 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5207 | handleChanged(); | |
5208 | #endif | |
b75a7d8f A |
5209 | } |
5210 | ||
5211 | /** | |
5212 | * Sets the minimum number of digits allowed in the fraction portion of a | |
5213 | * number. This override limits the fraction digit count to 340. | |
5214 | * @see NumberFormat#setMinimumFractionDigits | |
5215 | */ | |
5216 | void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { | |
374ca955 | 5217 | NumberFormat::setMinimumFractionDigits(_min(newValue, kDoubleFractionDigits)); |
51004dcb A |
5218 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5219 | handleChanged(); | |
5220 | #endif | |
b75a7d8f A |
5221 | } |
5222 | ||
374ca955 A |
5223 | int32_t DecimalFormat::getMinimumSignificantDigits() const { |
5224 | return fMinSignificantDigits; | |
5225 | } | |
5226 | ||
5227 | int32_t DecimalFormat::getMaximumSignificantDigits() const { | |
5228 | return fMaxSignificantDigits; | |
5229 | } | |
5230 | ||
5231 | void DecimalFormat::setMinimumSignificantDigits(int32_t min) { | |
5232 | if (min < 1) { | |
729e4ab9 | 5233 | min = 1; |
374ca955 A |
5234 | } |
5235 | // pin max sig dig to >= min | |
5236 | int32_t max = _max(fMaxSignificantDigits, min); | |
5237 | fMinSignificantDigits = min; | |
5238 | fMaxSignificantDigits = max; | |
51004dcb A |
5239 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5240 | handleChanged(); | |
5241 | #endif | |
374ca955 A |
5242 | } |
5243 | ||
5244 | void DecimalFormat::setMaximumSignificantDigits(int32_t max) { | |
5245 | if (max < 1) { | |
5246 | max = 1; | |
5247 | } | |
5248 | // pin min sig dig to 1..max | |
5249 | U_ASSERT(fMinSignificantDigits >= 1); | |
5250 | int32_t min = _min(fMinSignificantDigits, max); | |
5251 | fMinSignificantDigits = min; | |
5252 | fMaxSignificantDigits = max; | |
51004dcb A |
5253 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5254 | handleChanged(); | |
5255 | #endif | |
374ca955 A |
5256 | } |
5257 | ||
5258 | UBool DecimalFormat::areSignificantDigitsUsed() const { | |
5259 | return fUseSignificantDigits; | |
5260 | } | |
5261 | ||
5262 | void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { | |
5263 | fUseSignificantDigits = useSignificantDigits; | |
51004dcb A |
5264 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5265 | handleChanged(); | |
5266 | #endif | |
374ca955 A |
5267 | } |
5268 | ||
729e4ab9 A |
5269 | void DecimalFormat::setCurrencyInternally(const UChar* theCurrency, |
5270 | UErrorCode& ec) { | |
b75a7d8f A |
5271 | // If we are a currency format, then modify our affixes to |
5272 | // encode the currency symbol for the given currency in our | |
5273 | // locale, and adjust the decimal digits and rounding for the | |
5274 | // given currency. | |
5275 | ||
374ca955 A |
5276 | // Note: The code is ordered so that this object is *not changed* |
5277 | // until we are sure we are going to succeed. | |
729e4ab9 | 5278 | |
374ca955 A |
5279 | // NULL or empty currency is *legal* and indicates no currency. |
5280 | UBool isCurr = (theCurrency && *theCurrency); | |
5281 | ||
5282 | double rounding = 0.0; | |
5283 | int32_t frac = 0; | |
729e4ab9 | 5284 | if (fCurrencySignCount > fgCurrencySignCountZero && isCurr) { |
374ca955 A |
5285 | rounding = ucurr_getRoundingIncrement(theCurrency, &ec); |
5286 | frac = ucurr_getDefaultFractionDigits(theCurrency, &ec); | |
5287 | } | |
729e4ab9 | 5288 | |
374ca955 A |
5289 | NumberFormat::setCurrency(theCurrency, ec); |
5290 | if (U_FAILURE(ec)) return; | |
b75a7d8f | 5291 | |
729e4ab9 | 5292 | if (fCurrencySignCount > fgCurrencySignCountZero) { |
374ca955 A |
5293 | // NULL or empty currency is *legal* and indicates no currency. |
5294 | if (isCurr) { | |
5295 | setRoundingIncrement(rounding); | |
5296 | setMinimumFractionDigits(frac); | |
5297 | setMaximumFractionDigits(frac); | |
b75a7d8f | 5298 | } |
729e4ab9 A |
5299 | expandAffixes(NULL); |
5300 | } | |
51004dcb A |
5301 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5302 | handleChanged(); | |
5303 | #endif | |
729e4ab9 A |
5304 | } |
5305 | ||
5306 | void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { | |
5307 | // set the currency before compute affixes to get the right currency names | |
5308 | NumberFormat::setCurrency(theCurrency, ec); | |
4388f060 | 5309 | if (fFormatPattern.indexOf(fgTripleCurrencySign, 3, 0) != -1) { |
729e4ab9 A |
5310 | UnicodeString savedPtn = fFormatPattern; |
5311 | setupCurrencyAffixes(fFormatPattern, TRUE, TRUE, ec); | |
5312 | UParseError parseErr; | |
5313 | applyPattern(savedPtn, FALSE, parseErr, ec); | |
b75a7d8f | 5314 | } |
729e4ab9 A |
5315 | // set the currency after apply pattern to get the correct rounding/fraction |
5316 | setCurrencyInternally(theCurrency, ec); | |
51004dcb A |
5317 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5318 | handleChanged(); | |
5319 | #endif | |
b75a7d8f A |
5320 | } |
5321 | ||
374ca955 A |
5322 | // Deprecated variant with no UErrorCode parameter |
5323 | void DecimalFormat::setCurrency(const UChar* theCurrency) { | |
5324 | UErrorCode ec = U_ZERO_ERROR; | |
5325 | setCurrency(theCurrency, ec); | |
51004dcb A |
5326 | #if UCONFIG_FORMAT_FASTPATHS_49 |
5327 | handleChanged(); | |
5328 | #endif | |
374ca955 A |
5329 | } |
5330 | ||
46f4442e A |
5331 | void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { |
5332 | if (fSymbols == NULL) { | |
5333 | ec = U_MEMORY_ALLOCATION_ERROR; | |
5334 | return; | |
5335 | } | |
5336 | ec = U_ZERO_ERROR; | |
374ca955 A |
5337 | const UChar* c = getCurrency(); |
5338 | if (*c == 0) { | |
5339 | const UnicodeString &intl = | |
5340 | fSymbols->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); | |
5341 | c = intl.getBuffer(); // ok for intl to go out of scope | |
5342 | } | |
5343 | u_strncpy(result, c, 3); | |
5344 | result[3] = 0; | |
5345 | } | |
5346 | ||
5347 | /** | |
5348 | * Return the number of fraction digits to display, or the total | |
5349 | * number of digits for significant digit formats and exponential | |
5350 | * formats. | |
5351 | */ | |
5352 | int32_t | |
729e4ab9 | 5353 | DecimalFormat::precision() const { |
374ca955 A |
5354 | if (areSignificantDigitsUsed()) { |
5355 | return getMaximumSignificantDigits(); | |
5356 | } else if (fUseExponentialNotation) { | |
5357 | return getMinimumIntegerDigits() + getMaximumFractionDigits(); | |
5358 | } else { | |
729e4ab9 A |
5359 | return getMaximumFractionDigits(); |
5360 | } | |
5361 | } | |
5362 | ||
5363 | ||
5364 | // TODO: template algorithm | |
5365 | Hashtable* | |
5366 | DecimalFormat::initHashForAffix(UErrorCode& status) { | |
5367 | if ( U_FAILURE(status) ) { | |
5368 | return NULL; | |
5369 | } | |
5370 | Hashtable* hTable; | |
5371 | if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { | |
5372 | status = U_MEMORY_ALLOCATION_ERROR; | |
5373 | return NULL; | |
5374 | } | |
5375 | if ( U_FAILURE(status) ) { | |
5376 | delete hTable; | |
5377 | return NULL; | |
5378 | } | |
5379 | hTable->setValueComparator(decimfmtAffixValueComparator); | |
5380 | return hTable; | |
5381 | } | |
5382 | ||
5383 | Hashtable* | |
5384 | DecimalFormat::initHashForAffixPattern(UErrorCode& status) { | |
5385 | if ( U_FAILURE(status) ) { | |
5386 | return NULL; | |
5387 | } | |
5388 | Hashtable* hTable; | |
5389 | if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { | |
5390 | status = U_MEMORY_ALLOCATION_ERROR; | |
5391 | return NULL; | |
5392 | } | |
5393 | if ( U_FAILURE(status) ) { | |
5394 | delete hTable; | |
5395 | return NULL; | |
5396 | } | |
5397 | hTable->setValueComparator(decimfmtAffixPatternValueComparator); | |
5398 | return hTable; | |
5399 | } | |
5400 | ||
5401 | void | |
5402 | DecimalFormat::deleteHashForAffix(Hashtable*& table) | |
5403 | { | |
5404 | if ( table == NULL ) { | |
5405 | return; | |
5406 | } | |
5407 | int32_t pos = -1; | |
5408 | const UHashElement* element = NULL; | |
5409 | while ( (element = table->nextElement(pos)) != NULL ) { | |
729e4ab9 A |
5410 | const UHashTok valueTok = element->value; |
5411 | const AffixesForCurrency* value = (AffixesForCurrency*)valueTok.pointer; | |
5412 | delete value; | |
5413 | } | |
5414 | delete table; | |
5415 | table = NULL; | |
5416 | } | |
5417 | ||
5418 | ||
5419 | ||
5420 | void | |
5421 | DecimalFormat::deleteHashForAffixPattern() | |
5422 | { | |
5423 | if ( fAffixPatternsForCurrency == NULL ) { | |
5424 | return; | |
5425 | } | |
5426 | int32_t pos = -1; | |
5427 | const UHashElement* element = NULL; | |
5428 | while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { | |
729e4ab9 A |
5429 | const UHashTok valueTok = element->value; |
5430 | const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; | |
5431 | delete value; | |
5432 | } | |
5433 | delete fAffixPatternsForCurrency; | |
5434 | fAffixPatternsForCurrency = NULL; | |
5435 | } | |
5436 | ||
5437 | ||
5438 | void | |
5439 | DecimalFormat::copyHashForAffixPattern(const Hashtable* source, | |
5440 | Hashtable* target, | |
5441 | UErrorCode& status) { | |
5442 | if ( U_FAILURE(status) ) { | |
5443 | return; | |
5444 | } | |
5445 | int32_t pos = -1; | |
5446 | const UHashElement* element = NULL; | |
5447 | if ( source ) { | |
5448 | while ( (element = source->nextElement(pos)) != NULL ) { | |
5449 | const UHashTok keyTok = element->key; | |
5450 | const UnicodeString* key = (UnicodeString*)keyTok.pointer; | |
5451 | const UHashTok valueTok = element->value; | |
5452 | const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; | |
5453 | AffixPatternsForCurrency* copy = new AffixPatternsForCurrency( | |
5454 | value->negPrefixPatternForCurrency, | |
5455 | value->negSuffixPatternForCurrency, | |
5456 | value->posPrefixPatternForCurrency, | |
5457 | value->posSuffixPatternForCurrency, | |
5458 | value->patternType); | |
5459 | target->put(UnicodeString(*key), copy, status); | |
5460 | if ( U_FAILURE(status) ) { | |
5461 | return; | |
5462 | } | |
5463 | } | |
5464 | } | |
5465 | } | |
5466 | ||
51004dcb A |
5467 | DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr, |
5468 | int32_t newValue, | |
5469 | UErrorCode &status) { | |
5470 | if(U_FAILURE(status)) return *this; | |
5471 | ||
5472 | switch(attr) { | |
5473 | case UNUM_LENIENT_PARSE: | |
5474 | setLenient(newValue!=0); | |
5475 | break; | |
5476 | ||
5477 | case UNUM_PARSE_INT_ONLY: | |
5478 | setParseIntegerOnly(newValue!=0); | |
5479 | break; | |
5480 | ||
5481 | case UNUM_GROUPING_USED: | |
5482 | setGroupingUsed(newValue!=0); | |
5483 | break; | |
5484 | ||
5485 | case UNUM_DECIMAL_ALWAYS_SHOWN: | |
5486 | setDecimalSeparatorAlwaysShown(newValue!=0); | |
5487 | break; | |
5488 | ||
5489 | case UNUM_MAX_INTEGER_DIGITS: | |
5490 | setMaximumIntegerDigits(newValue); | |
5491 | break; | |
5492 | ||
5493 | case UNUM_MIN_INTEGER_DIGITS: | |
5494 | setMinimumIntegerDigits(newValue); | |
5495 | break; | |
5496 | ||
5497 | case UNUM_INTEGER_DIGITS: | |
5498 | setMinimumIntegerDigits(newValue); | |
5499 | setMaximumIntegerDigits(newValue); | |
5500 | break; | |
5501 | ||
5502 | case UNUM_MAX_FRACTION_DIGITS: | |
5503 | setMaximumFractionDigits(newValue); | |
5504 | break; | |
5505 | ||
5506 | case UNUM_MIN_FRACTION_DIGITS: | |
5507 | setMinimumFractionDigits(newValue); | |
5508 | break; | |
5509 | ||
5510 | case UNUM_FRACTION_DIGITS: | |
5511 | setMinimumFractionDigits(newValue); | |
5512 | setMaximumFractionDigits(newValue); | |
5513 | break; | |
5514 | ||
5515 | case UNUM_SIGNIFICANT_DIGITS_USED: | |
5516 | setSignificantDigitsUsed(newValue!=0); | |
5517 | break; | |
5518 | ||
5519 | case UNUM_MAX_SIGNIFICANT_DIGITS: | |
5520 | setMaximumSignificantDigits(newValue); | |
5521 | break; | |
5522 | ||
5523 | case UNUM_MIN_SIGNIFICANT_DIGITS: | |
5524 | setMinimumSignificantDigits(newValue); | |
5525 | break; | |
5526 | ||
5527 | case UNUM_MULTIPLIER: | |
5528 | setMultiplier(newValue); | |
5529 | break; | |
5530 | ||
5531 | case UNUM_GROUPING_SIZE: | |
5532 | setGroupingSize(newValue); | |
5533 | break; | |
5534 | ||
5535 | case UNUM_ROUNDING_MODE: | |
5536 | setRoundingMode((DecimalFormat::ERoundingMode)newValue); | |
5537 | break; | |
5538 | ||
5539 | case UNUM_FORMAT_WIDTH: | |
5540 | setFormatWidth(newValue); | |
5541 | break; | |
5542 | ||
5543 | case UNUM_PADDING_POSITION: | |
5544 | /** The position at which padding will take place. */ | |
5545 | setPadPosition((DecimalFormat::EPadPosition)newValue); | |
5546 | break; | |
5547 | ||
5548 | case UNUM_SECONDARY_GROUPING_SIZE: | |
5549 | setSecondaryGroupingSize(newValue); | |
5550 | break; | |
5551 | ||
5552 | #if UCONFIG_HAVE_PARSEALLINPUT | |
5553 | case UNUM_PARSE_ALL_INPUT: | |
5554 | setParseAllInput((UNumberFormatAttributeValue)newValue); | |
5555 | break; | |
5556 | #endif | |
5557 | ||
5558 | /* These are stored in fBoolFlags */ | |
5559 | case UNUM_PARSE_NO_EXPONENT: | |
5560 | case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: | |
5561 | if(!fBoolFlags.isValidValue(newValue)) { | |
5562 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
5563 | } else { | |
5564 | fBoolFlags.set(attr, newValue); | |
5565 | } | |
5566 | break; | |
5567 | ||
5568 | case UNUM_SCALE: | |
5569 | fScale = newValue; | |
5570 | break; | |
5571 | ||
5572 | default: | |
5573 | status = U_UNSUPPORTED_ERROR; | |
5574 | break; | |
5575 | } | |
5576 | return *this; | |
5577 | } | |
5578 | ||
5579 | int32_t DecimalFormat::getAttribute( UNumberFormatAttribute attr, | |
5580 | UErrorCode &status ) const { | |
5581 | if(U_FAILURE(status)) return -1; | |
5582 | switch(attr) { | |
5583 | case UNUM_LENIENT_PARSE: | |
5584 | return isLenient(); | |
5585 | ||
5586 | case UNUM_PARSE_INT_ONLY: | |
5587 | return isParseIntegerOnly(); | |
5588 | ||
5589 | case UNUM_GROUPING_USED: | |
5590 | return isGroupingUsed(); | |
5591 | ||
5592 | case UNUM_DECIMAL_ALWAYS_SHOWN: | |
5593 | return isDecimalSeparatorAlwaysShown(); | |
5594 | ||
5595 | case UNUM_MAX_INTEGER_DIGITS: | |
5596 | return getMaximumIntegerDigits(); | |
5597 | ||
5598 | case UNUM_MIN_INTEGER_DIGITS: | |
5599 | return getMinimumIntegerDigits(); | |
5600 | ||
5601 | case UNUM_INTEGER_DIGITS: | |
5602 | // TBD: what should this return? | |
5603 | return getMinimumIntegerDigits(); | |
5604 | ||
5605 | case UNUM_MAX_FRACTION_DIGITS: | |
5606 | return getMaximumFractionDigits(); | |
5607 | ||
5608 | case UNUM_MIN_FRACTION_DIGITS: | |
5609 | return getMinimumFractionDigits(); | |
5610 | ||
5611 | case UNUM_FRACTION_DIGITS: | |
5612 | // TBD: what should this return? | |
5613 | return getMinimumFractionDigits(); | |
5614 | ||
5615 | case UNUM_SIGNIFICANT_DIGITS_USED: | |
5616 | return areSignificantDigitsUsed(); | |
5617 | ||
5618 | case UNUM_MAX_SIGNIFICANT_DIGITS: | |
5619 | return getMaximumSignificantDigits(); | |
5620 | ||
5621 | case UNUM_MIN_SIGNIFICANT_DIGITS: | |
5622 | return getMinimumSignificantDigits(); | |
5623 | ||
5624 | case UNUM_MULTIPLIER: | |
5625 | return getMultiplier(); | |
5626 | ||
5627 | case UNUM_GROUPING_SIZE: | |
5628 | return getGroupingSize(); | |
5629 | ||
5630 | case UNUM_ROUNDING_MODE: | |
5631 | return getRoundingMode(); | |
5632 | ||
5633 | case UNUM_FORMAT_WIDTH: | |
5634 | return getFormatWidth(); | |
5635 | ||
5636 | case UNUM_PADDING_POSITION: | |
5637 | return getPadPosition(); | |
5638 | ||
5639 | case UNUM_SECONDARY_GROUPING_SIZE: | |
5640 | return getSecondaryGroupingSize(); | |
5641 | ||
5642 | /* These are stored in fBoolFlags */ | |
5643 | case UNUM_PARSE_NO_EXPONENT: | |
5644 | case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: | |
5645 | return fBoolFlags.get(attr); | |
5646 | ||
5647 | case UNUM_SCALE: | |
5648 | return fScale; | |
5649 | ||
5650 | default: | |
5651 | status = U_UNSUPPORTED_ERROR; | |
5652 | break; | |
5653 | } | |
729e4ab9 | 5654 | |
51004dcb A |
5655 | return -1; /* undefined */ |
5656 | } | |
5657 | ||
5658 | #if UCONFIG_HAVE_PARSEALLINPUT | |
5659 | void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { | |
5660 | fParseAllInput = value; | |
5661 | #if UCONFIG_FORMAT_FASTPATHS_49 | |
5662 | handleChanged(); | |
5663 | #endif | |
5664 | } | |
5665 | #endif | |
729e4ab9 A |
5666 | |
5667 | void | |
5668 | DecimalFormat::copyHashForAffix(const Hashtable* source, | |
5669 | Hashtable* target, | |
5670 | UErrorCode& status) { | |
5671 | if ( U_FAILURE(status) ) { | |
5672 | return; | |
5673 | } | |
5674 | int32_t pos = -1; | |
5675 | const UHashElement* element = NULL; | |
5676 | if ( source ) { | |
5677 | while ( (element = source->nextElement(pos)) != NULL ) { | |
5678 | const UHashTok keyTok = element->key; | |
5679 | const UnicodeString* key = (UnicodeString*)keyTok.pointer; | |
5680 | ||
5681 | const UHashTok valueTok = element->value; | |
5682 | const AffixesForCurrency* value = (AffixesForCurrency*)valueTok.pointer; | |
5683 | AffixesForCurrency* copy = new AffixesForCurrency( | |
5684 | value->negPrefixForCurrency, | |
5685 | value->negSuffixForCurrency, | |
5686 | value->posPrefixForCurrency, | |
5687 | value->posSuffixForCurrency); | |
5688 | target->put(UnicodeString(*key), copy, status); | |
5689 | if ( U_FAILURE(status) ) { | |
5690 | return; | |
5691 | } | |
5692 | } | |
374ca955 A |
5693 | } |
5694 | } | |
5695 | ||
b75a7d8f A |
5696 | U_NAMESPACE_END |
5697 | ||
5698 | #endif /* #if !UCONFIG_NO_FORMATTING */ | |
5699 | ||
5700 | //eof |