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