/* ******************************************************************************* * Copyright (C) 1997-2001, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * * File DECIMFMT.CPP * * Modification History: * * Date Name Description * 02/19/97 aliu Converted from java. * 03/20/97 clhuang Implemented with new APIs. * 03/31/97 aliu Moved isLONG_MIN to DigitList, and fixed it. * 04/3/97 aliu Rewrote parsing and formatting completely, and * cleaned up and debugged. Actually works now. * Implemented NAN and INF handling, for both parsing * and formatting. Extensive testing & debugging. * 04/10/97 aliu Modified to compile on AIX. * 04/16/97 aliu Rewrote to use DigitList, which has been resurrected. * Changed DigitCount to int per code review. * 07/09/97 helena Made ParsePosition into a class. * 08/26/97 aliu Extensive changes to applyPattern; completely * rewritten from the Java. * 09/09/97 aliu Ported over support for exponential formats. * 07/20/98 stephen JDK 1.2 sync up. * Various instances of '0' replaced with 'NULL' * Check for grouping size in subFormat() * Brought subParse() in line with Java 1.2 * Added method appendAffix() * 08/24/1998 srl Removed Mutex calls. This is not a thread safe class! * 02/22/99 stephen Removed character literals for EBCDIC safety * 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes * 06/28/99 stephen Fixed bugs in toPattern(). * 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, * fPadPosition ******************************************************************************** */ #include "unicode/decimfmt.h" #include "digitlst.h" #include "unicode/dcfmtsym.h" #include "unicode/resbund.h" #include "unicode/unicode.h" #include "cmemory.h" U_NAMESPACE_BEGIN //#define FMT_DEBUG #ifdef FMT_DEBUG #include static void debugout(UnicodeString s) { char buf[2000]; s.extract((UTextOffset) 0, s.length(), buf); printf("%s", buf); } #define debug(x) printf("%s", x); #else #define debugout(x) #define debug(x) #endif // ***************************************************************************** // class DecimalFormat // ***************************************************************************** const char DecimalFormat::fgClassID = 0; // Value is irrelevant // Constants for characters used in programmatic (unlocalized) patterns. const UChar DecimalFormat::kPatternZeroDigit = 0x0030 /*'0'*/; const UChar DecimalFormat::kPatternGroupingSeparator = 0x002C /*','*/; const UChar DecimalFormat::kPatternDecimalSeparator = 0x002E /*'.'*/; const UChar DecimalFormat::kPatternPerMill = 0x2030; const UChar DecimalFormat::kPatternPercent = 0x0025 /*'%'*/; const UChar DecimalFormat::kPatternDigit = 0x0023 /*'#'*/; const UChar DecimalFormat::kPatternSeparator = 0x003B /*';'*/; const UChar DecimalFormat::kPatternExponent = 0x0045 /*'E'*/; const UChar DecimalFormat::kPatternPlus = 0x002B /*'+'*/; const UChar DecimalFormat::kPatternMinus = 0x002D /*'-'*/; const UChar DecimalFormat::kPatternPadEscape = 0x002A /*'*'*/; const UChar DecimalFormat::kCurrencySign = 0x00A4; const UChar DecimalFormat::kQuote = 0x0027 /*'\''*/; //const int8_t DecimalFormat::fgMaxDigit = 9; const int32_t DecimalFormat::kDoubleIntegerDigits = 309; const int32_t DecimalFormat::kDoubleFractionDigits = 340; /** * These are the tags we expect to see in normal resource bundle files associated * with a locale. */ const char DecimalFormat::fgNumberPatterns[]="NumberPatterns"; //------------------------------------------------------------------------------ // Constructs a DecimalFormat instance in the default locale. DecimalFormat::DecimalFormat(UErrorCode& status) : NumberFormat(), fPosPrefixPattern(0), fPosSuffixPattern(0), fNegPrefixPattern(0), fNegSuffixPattern(0), fSymbols(0) { UParseError parseError; construct(status, parseError); } //------------------------------------------------------------------------------ // Constructs a DecimalFormat instance with the specified number format // pattern in the default locale. DecimalFormat::DecimalFormat(const UnicodeString& pattern, UErrorCode& status) : NumberFormat(), fPosPrefixPattern(0), fPosSuffixPattern(0), fNegPrefixPattern(0), fNegSuffixPattern(0), fSymbols(0) { UParseError parseError; construct(status, parseError, &pattern); } //------------------------------------------------------------------------------ // Constructs a DecimalFormat instance with the specified number format // pattern and the number format symbols in the default locale. The // created instance owns the symbols. DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) : NumberFormat(), fPosPrefixPattern(0), fPosSuffixPattern(0), fNegPrefixPattern(0), fNegSuffixPattern(0), fSymbols(0) { UParseError parseError; if (symbolsToAdopt == NULL) status = U_ILLEGAL_ARGUMENT_ERROR; construct(status, parseError, &pattern, symbolsToAdopt); } DecimalFormat::DecimalFormat( const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, UParseError& parseErr, UErrorCode& status) : NumberFormat(), fPosPrefixPattern(0), fPosSuffixPattern(0), fNegPrefixPattern(0), fNegSuffixPattern(0), fSymbols(0) { if (symbolsToAdopt == NULL) status = U_ILLEGAL_ARGUMENT_ERROR; construct(status,parseErr, &pattern, symbolsToAdopt); } //------------------------------------------------------------------------------ // Constructs a DecimalFormat instance with the specified number format // pattern and the number format symbols in the default locale. The // created instance owns the clone of the symbols. DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols, UErrorCode& status) : NumberFormat(), fPosPrefixPattern(0), fPosSuffixPattern(0), fNegPrefixPattern(0), fNegSuffixPattern(0), fSymbols(0) { UParseError parseError; construct(status, parseError, &pattern, new DecimalFormatSymbols(symbols)); } //------------------------------------------------------------------------------ // Constructs a DecimalFormat instance with the specified number format // pattern and the number format symbols in the desired locale. The // created instance owns the symbols. void DecimalFormat::construct(UErrorCode& status, UParseError& parseErr, const UnicodeString* pattern, DecimalFormatSymbols* symbolsToAdopt, const Locale& locale) { fSymbols = symbolsToAdopt; // Do this BEFORE aborting on status failure!!! // fDigitList = new DigitList(); // Do this BEFORE aborting on status failure!!! fRoundingIncrement = NULL; fRoundingDouble = 0.0; fRoundingMode = kRoundHalfEven; fPad = kPatternPadEscape; fPadPosition = kPadBeforePrefix; if (U_FAILURE(status)) return; fPosPrefixPattern = fPosSuffixPattern = NULL; fNegPrefixPattern = fNegSuffixPattern = NULL; fMultiplier = 1; fGroupingSize = 3; fGroupingSize2 = 0; fDecimalSeparatorAlwaysShown = FALSE; fIsCurrencyFormat = FALSE; fUseExponentialNotation = FALSE; fMinExponentDigits = 0; if (fSymbols == NULL) { fSymbols = new DecimalFormatSymbols(locale, status); } UnicodeString str; // Uses the default locale's number format pattern if there isn't // one specified. if (pattern == NULL) { ResourceBundle resource((char *)0, Locale::getDefault(), status); str = resource.get(fgNumberPatterns, status).getStringEx((int32_t)0, status); pattern = &str; } if (U_FAILURE(status)) { return; } applyPattern(*pattern, FALSE /*not localized*/,parseErr, status); } //------------------------------------------------------------------------------ DecimalFormat::~DecimalFormat() { // delete fDigitList; delete fPosPrefixPattern; delete fPosSuffixPattern; delete fNegPrefixPattern; delete fNegSuffixPattern; delete fSymbols; delete fRoundingIncrement; } //------------------------------------------------------------------------------ // copy constructor DecimalFormat::DecimalFormat(const DecimalFormat &source) : NumberFormat(source), // fDigitList(NULL), fPosPrefixPattern(NULL), fPosSuffixPattern(NULL), fNegPrefixPattern(NULL), fNegSuffixPattern(NULL), fSymbols(NULL), fRoundingIncrement(NULL) { *this = source; } //------------------------------------------------------------------------------ // assignment operator // Note that fDigitList is not considered a significant part of the // DecimalFormat because it's used as a buffer to process the numbers. static void _copy_us_ptr(UnicodeString** pdest, const UnicodeString* source) { if (source == NULL) { delete *pdest; *pdest = NULL; } else if (*pdest == NULL) { *pdest = new UnicodeString(*source); } else { **pdest = *source; } } DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { if(this != &rhs) { NumberFormat::operator=(rhs); fPositivePrefix = rhs.fPositivePrefix; fPositiveSuffix = rhs.fPositiveSuffix; fNegativePrefix = rhs.fNegativePrefix; fNegativeSuffix = rhs.fNegativeSuffix; _copy_us_ptr(&fPosPrefixPattern, rhs.fPosPrefixPattern); _copy_us_ptr(&fPosSuffixPattern, rhs.fPosSuffixPattern); _copy_us_ptr(&fNegPrefixPattern, rhs.fNegPrefixPattern); _copy_us_ptr(&fNegSuffixPattern, rhs.fNegSuffixPattern); if(rhs.fRoundingIncrement == NULL) { delete fRoundingIncrement; fRoundingIncrement = NULL; } else if(fRoundingIncrement == NULL) { fRoundingIncrement = new DigitList(*rhs.fRoundingIncrement); } else { *fRoundingIncrement = *rhs.fRoundingIncrement; } fRoundingDouble = rhs.fRoundingDouble; fMultiplier = rhs.fMultiplier; fGroupingSize = rhs.fGroupingSize; fGroupingSize2 = rhs.fGroupingSize2; fDecimalSeparatorAlwaysShown = rhs.fDecimalSeparatorAlwaysShown; if(fSymbols == NULL) fSymbols = new DecimalFormatSymbols(*rhs.fSymbols); else *fSymbols = *rhs.fSymbols; fUseExponentialNotation = rhs.fUseExponentialNotation; /*Bertrand A. D. Update 98.03.17*/ fIsCurrencyFormat = rhs.fIsCurrencyFormat; /*end of Update*/ fMinExponentDigits = rhs.fMinExponentDigits; // if (fDigitList == NULL) // fDigitList = new DigitList(); /* sfb 990629 */ fFormatWidth = rhs.fFormatWidth; fPad = rhs.fPad; fPadPosition = rhs.fPadPosition; /* end sfb */ } return *this; } //------------------------------------------------------------------------------ UBool DecimalFormat::operator==(const Format& that) const { if (this == &that) return TRUE; if (getDynamicClassID() != that.getDynamicClassID()) return FALSE; const DecimalFormat* other = (DecimalFormat*)&that; #ifdef FMT_DEBUG // This code makes it easy to determine why two format objects that should // be equal aren't. UBool first = TRUE; if (!NumberFormat::operator==(that)) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("NumberFormat::!="); } if (!((fPosPrefixPattern == other->fPosPrefixPattern && // both null fPositivePrefix == other->fPositivePrefix) || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && *fPosPrefixPattern == *other->fPosPrefixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Pos Prefix !="); } if (!((fPosSuffixPattern == other->fPosSuffixPattern && // both null fPositiveSuffix == other->fPositiveSuffix) || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && *fPosSuffixPattern == *other->fPosSuffixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Pos Suffix !="); } if (!((fNegPrefixPattern == other->fNegPrefixPattern && // both null fNegativePrefix == other->fNegativePrefix) || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && *fNegPrefixPattern == *other->fNegPrefixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Neg Prefix "); if (fNegPrefixPattern == NULL) { debug("NULL("); debugout(fNegativePrefix); debug(")"); } else { debugout(*fNegPrefixPattern); } debug(" != "); if (other->fNegPrefixPattern == NULL) { debug("NULL("); debugout(other->fNegativePrefix); debug(")"); } else { debugout(*other->fNegPrefixPattern); } } if (!((fNegSuffixPattern == other->fNegSuffixPattern && // both null fNegativeSuffix == other->fNegativeSuffix) || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && *fNegSuffixPattern == *other->fNegSuffixPattern))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Neg Suffix "); if (fNegSuffixPattern == NULL) { debug("NULL("); debugout(fNegativeSuffix); debug(")"); } else { debugout(*fNegSuffixPattern); } debug(" != "); if (other->fNegSuffixPattern == NULL) { debug("NULL("); debugout(other->fNegativeSuffix); debug(")"); } else { debugout(*other->fNegSuffixPattern); } } if (!((fRoundingIncrement == other->fRoundingIncrement) // both null || (fRoundingIncrement != NULL && other->fRoundingIncrement != NULL && *fRoundingIncrement == *other->fRoundingIncrement))) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Rounding Increment !="); } if (fMultiplier != other->fMultiplier) { if (first) { printf("[ "); first = FALSE; } printf("Multiplier %ld != %ld", fMultiplier, other->fMultiplier); } if (fGroupingSize != other->fGroupingSize) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } printf("Grouping Size %ld != %ld", fGroupingSize, other->fGroupingSize); } if (fGroupingSize2 != other->fGroupingSize2) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } printf("Secondary Grouping Size %ld != %ld", fGroupingSize2, other->fGroupingSize2); } if (fDecimalSeparatorAlwaysShown != other->fDecimalSeparatorAlwaysShown) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } printf("Dec Sep Always %d != %d", fDecimalSeparatorAlwaysShown, other->fDecimalSeparatorAlwaysShown); } if (fUseExponentialNotation != other->fUseExponentialNotation) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Use Exp !="); } if (!(!fUseExponentialNotation || fMinExponentDigits != other->fMinExponentDigits)) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Exp Digits !="); } if (*fSymbols != *(other->fSymbols)) { if (first) { printf("[ "); first = FALSE; } else { printf(", "); } debug("Symbols !="); } if (!first) { printf(" ]"); } #endif return (NumberFormat::operator==(that) && ((fPosPrefixPattern == other->fPosPrefixPattern && // both null fPositivePrefix == other->fPositivePrefix) || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && *fPosPrefixPattern == *other->fPosPrefixPattern)) && ((fPosSuffixPattern == other->fPosSuffixPattern && // both null fPositiveSuffix == other->fPositiveSuffix) || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && *fPosSuffixPattern == *other->fPosSuffixPattern)) && ((fNegPrefixPattern == other->fNegPrefixPattern && // both null fNegativePrefix == other->fNegativePrefix) || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && *fNegPrefixPattern == *other->fNegPrefixPattern)) && ((fNegSuffixPattern == other->fNegSuffixPattern && // both null fNegativeSuffix == other->fNegativeSuffix) || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && *fNegSuffixPattern == *other->fNegSuffixPattern)) && ((fRoundingIncrement == other->fRoundingIncrement) // both null || (fRoundingIncrement != NULL && other->fRoundingIncrement != NULL && *fRoundingIncrement == *other->fRoundingIncrement)) && fMultiplier == other->fMultiplier && fGroupingSize == other->fGroupingSize && fGroupingSize2 == other->fGroupingSize2 && fDecimalSeparatorAlwaysShown == other->fDecimalSeparatorAlwaysShown && fUseExponentialNotation == other->fUseExponentialNotation && (!fUseExponentialNotation || fMinExponentDigits == other->fMinExponentDigits) && *fSymbols == *(other->fSymbols)); } //------------------------------------------------------------------------------ Format* DecimalFormat::clone() const { return new DecimalFormat(*this); } //------------------------------------------------------------------------------ UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& result, FieldPosition& fieldPosition) const { DigitList digits; // Clears field positions. fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); // If we are to do rounding, we need to move into the BigDecimal // domain in order to do divide/multiply correctly. // || // In general, long values always represent real finite numbers, so // we don't have to check for +/- Infinity or NaN. However, there // is one case we have to be careful of: The multiplier can push // a number near MIN_VALUE or MAX_VALUE outside the legal range. We // check for this before multiplying, and if it happens we use doubles // instead, trading off accuracy for range. if (fRoundingIncrement != NULL || (fMultiplier != 0 && (number > (INT32_MAX / fMultiplier) || number < (INT32_MIN / fMultiplier)))) { digits.set(((double)number) * fMultiplier, fUseExponentialNotation ? getMinimumIntegerDigits() + getMaximumFractionDigits() : 0, !fUseExponentialNotation); } else { digits.set(number * fMultiplier, fUseExponentialNotation ? getMinimumIntegerDigits() + getMaximumFractionDigits() : 0); } return subformat(result, fieldPosition, digits, TRUE); } //------------------------------------------------------------------------------ UnicodeString& DecimalFormat::format( double number, UnicodeString& result, FieldPosition& fieldPosition) const { // Clears field positions. fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); // Special case for NaN, sets the begin and end index to be the // the string length of localized name of NaN. if (uprv_isNaN(number)) { if (fieldPosition.getField() == NumberFormat::kIntegerField) fieldPosition.setBeginIndex(result.length()); result += fSymbols->getSymbol(DecimalFormatSymbols::kNaNSymbol); if (fieldPosition.getField() == NumberFormat::kIntegerField) fieldPosition.setEndIndex(result.length()); addPadding(result, fieldPosition, FALSE, FALSE /*ignored*/); return result; } /* Detecting whether a double is negative is easy with the exception of * the value -0.0. This is a double which has a zero mantissa (and * exponent), but a negative sign bit. It is semantically distinct from * a zero with a positive sign bit, and this distinction is important * to certain kinds of computations. However, it's a little tricky to * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) == * -Infinity. Proper detection of -0.0 is needed to deal with the * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98. */ UBool isNegative = uprv_isNegative(number); // Do this BEFORE checking to see if value is infinite! Sets the // begin and end index to be length of the string composed of // localized name of Infinite and the positive/negative localized // signs. number *= fMultiplier; // Apply rounding after multiplier if (fRoundingIncrement != NULL) { if (isNegative) // For rounding in the correct direction number = -number; number = fRoundingDouble * round(number / fRoundingDouble, fRoundingMode, isNegative); if (isNegative) number = -number; } // Special case for INFINITE, if (uprv_isInfinite(number)) { result += (isNegative ? fNegativePrefix : fPositivePrefix); if (fieldPosition.getField() == NumberFormat::kIntegerField) fieldPosition.setBeginIndex(result.length()); result += fSymbols->getSymbol(DecimalFormatSymbols::kInfinitySymbol); if (fieldPosition.getField() == NumberFormat::kIntegerField) fieldPosition.setEndIndex(result.length()); result += (isNegative ? fNegativeSuffix : fPositiveSuffix); addPadding(result, fieldPosition, TRUE, isNegative); return result; } DigitList digits; // This detects negativity too. digits.set(number, fUseExponentialNotation ? getMinimumIntegerDigits() + getMaximumFractionDigits() : getMaximumFractionDigits(), !fUseExponentialNotation); return subformat(result, fieldPosition, digits, FALSE); } /** * Round a double value to the nearest integer according to the * given mode. * @param a the absolute value of the number to be rounded * @param mode a BigDecimal rounding mode * @param isNegative true if the number to be rounded is negative * @return the absolute value of the rounded result */ double DecimalFormat::round(double a, ERoundingMode mode, UBool isNegative) { switch (mode) { case kRoundCeiling: return isNegative ? uprv_floor(a) : uprv_ceil(a); case kRoundFloor: return isNegative ? uprv_ceil(a) : uprv_floor(a); case kRoundDown: return uprv_floor(a); case kRoundUp: return uprv_ceil(a); case kRoundHalfEven: { double f = uprv_floor(a); if ((a - f) != 0.5) { return uprv_floor(a + 0.5); } double g = f / 2.0; return (g == uprv_floor(g)) ? f : (f + 1.0); } case kRoundHalfDown: return ((a - uprv_floor(a)) <= 0.5) ? uprv_floor(a) : uprv_ceil(a); case kRoundHalfUp: return ((a - uprv_floor(a)) < 0.5) ? uprv_floor(a) : uprv_ceil(a); } return 1.0; } UnicodeString& DecimalFormat::format( const Formattable& obj, UnicodeString& result, FieldPosition& fieldPosition, UErrorCode& status) const { return NumberFormat::format(obj, result, fieldPosition, status); } /** * Return true if a grouping separator belongs at the given * position, based on whether grouping is in use and the values of * the primary and secondary grouping interval. * @param pos the number of integer digits to the right of * the current position. Zero indicates the position after the * rightmost integer digit. * @return true if a grouping character belongs at the current * position. */ UBool DecimalFormat::isGroupingPosition(int32_t pos) const { UBool result = FALSE; if (isGroupingUsed() && (pos > 0) && (fGroupingSize > 0)) { if ((fGroupingSize2 > 0) && (pos > fGroupingSize)) { result = ((pos - fGroupingSize) % fGroupingSize2) == 0; } else { result = pos % fGroupingSize == 0; } } return result; } //------------------------------------------------------------------------------ /** * Complete the formatting of a finite number. On entry, the fDigitList must * be filled in with the correct digits. */ UnicodeString& DecimalFormat::subformat(UnicodeString& result, FieldPosition& fieldPosition, DigitList& digits, UBool isInteger) const { // Gets the localized zero Unicode character. UChar32 zero = fSymbols->getSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); int32_t zeroDelta = zero - '0'; // '0' is the DigitList representation of zero UnicodeString grouping(fSymbols->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); UnicodeString decimal(fIsCurrencyFormat ? fSymbols->getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol) : fSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); int32_t maxIntDig = getMaximumIntegerDigits(); int32_t minIntDig = getMinimumIntegerDigits(); /* Per bug 4147706, DecimalFormat must respect the sign of numbers which * format as zero. This allows sensible computations and preserves * relations such as signum(1/x) = signum(x), where x is +Infinity or * -Infinity. Prior to this fix, we always formatted zero values as if * they were positive. Liu 7/6/98. */ if (digits.isZero()) { digits.fDecimalAt = digits.fCount = 0; // Normalize } // Appends the prefix. result += (digits.fIsPositive ? fPositivePrefix : fNegativePrefix); if (fUseExponentialNotation) { // Record field information for caller. if (fieldPosition.getField() == NumberFormat::kIntegerField) { fieldPosition.setBeginIndex(result.length()); fieldPosition.setEndIndex(-1); } else if (fieldPosition.getField() == NumberFormat::kFractionField) { fieldPosition.setBeginIndex(-1); } // Minimum integer digits are handled in exponential format by // adjusting the exponent. For example, 0.01234 with 3 minimum // integer digits is "123.4E-4". // Maximum integer digits are interpreted as indicating the // repeating range. This is useful for engineering notation, in // which the exponent is restricted to a multiple of 3. For // example, 0.01234 with 3 maximum integer digits is "12.34e-3". // If maximum integer digits are defined and are larger than // minimum integer digits, then minimum integer digits are // ignored. int32_t exponent = digits.fDecimalAt; if (maxIntDig > 1 && maxIntDig != minIntDig) { // A exponent increment is defined; adjust to it. exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1; exponent *= maxIntDig; } else { // No exponent increment is defined; use minimum integer digits. // If none is specified, as in "#E0", generate 1 integer digit. exponent -= (minIntDig > 0 || getMinimumFractionDigits() > 0) ? minIntDig : 1; } // We now output a minimum number of digits, and more if there // are more digits, up to the maximum number of digits. We // place the decimal point after the "integer" digits, which // are the first (decimalAt - exponent) digits. int32_t minimumDigits = minIntDig + getMinimumFractionDigits(); // The number of integer digits is handled specially if the number // is zero, since then there may be no digits. int32_t integerDigits = digits.isZero() ? minIntDig : digits.fDecimalAt - exponent; int32_t totalDigits = digits.fCount; if (minimumDigits > totalDigits) totalDigits = minimumDigits; if (integerDigits > totalDigits) totalDigits = integerDigits; // totalDigits records total number of digits needs to be processed int32_t i; for (i=0; igetSymbol(DecimalFormatSymbols::kExponentialSymbol); // For zero values, we force the exponent to zero. We // must do this here, and not earlier, because the value // is used to determine integer digit count above. if (digits.isZero()) exponent = 0; if (exponent < 0) { result += fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol); } else if (fExponentSignAlwaysShown) { result += fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol); } DigitList expDigits; expDigits.set(exponent); for (i=expDigits.fDecimalAt; i 0 && count < digits.fDecimalAt) count = digits.fDecimalAt; // Handle the case where getMaximumIntegerDigits() is smaller // than the real number of integer digits. If this is so, we // output the least significant max integer digits. For example, // the value 1997 printed with 2 max integer digits is just "97". if (count > maxIntDig) { count = maxIntDig; digitIndex = digits.fDecimalAt - count; } int32_t sizeBeforeIntegerPart = result.length(); int32_t i; for (i=count-1; i>=0; --i) { if (i < digits.fDecimalAt && digitIndex < digits.fCount) { // Output a real digit result += ((UChar32)(digits.fDigits[digitIndex++] + zeroDelta)); } else { // Output a leading zero result += (zero); } // Output grouping separator if necessary. if (isGroupingPosition(i)) { result.append(grouping); } } // Record field information for caller. if (fieldPosition.getField() == NumberFormat::kIntegerField) fieldPosition.setEndIndex(result.length()); // Determine whether or not there are any printable fractional // digits. If we've used up the digits we know there aren't. UBool fractionPresent = (getMinimumFractionDigits() > 0) || (!isInteger && digitIndex < digits.fCount); // If there is no fraction present, and we haven't printed any // integer digits, then print a zero. Otherwise we won't print // _any_ digits, and we won't be able to parse this string. if (!fractionPresent && result.length() == sizeBeforeIntegerPart) result += (zero); // Output the decimal separator if we always do so. if (fDecimalSeparatorAlwaysShown || fractionPresent) result += (decimal); // Record field information for caller. if (fieldPosition.getField() == NumberFormat::kFractionField) fieldPosition.setBeginIndex(result.length()); int32_t maxFracDigits = getMaximumFractionDigits(); int32_t negDecimalAt = -digits.fDecimalAt; for (i=0; i < maxFracDigits; ++i) { if (!isInteger && digitIndex < digits.fCount) { if (i >= negDecimalAt) { // Output a digit result += ((UChar32)(digits.fDigits[digitIndex++] + zeroDelta)); } else { // Output leading fractional zeros. These are zeros that come after // the decimal but before any significant digits. These are only // output if abs(number being formatted) < 1.0. result += zero; } } else { // Here is where we escape from the loop. We escape if we've output // the maximum fraction digits (specified in the for expression above). // We also stop when we've output the minimum digits and either: // we have an integer, so there is no fractional stuff to display, // or we're out of significant digits. if (i >= getMinimumFractionDigits()) break; // No precision is left. result += zero; } } // Record field information for caller. if (fieldPosition.getField() == NumberFormat::kFractionField) fieldPosition.setEndIndex(result.length()); } result += (digits.fIsPositive ? fPositiveSuffix : fNegativeSuffix); addPadding(result, fieldPosition, TRUE, !digits.fIsPositive); return result; } /** * Inserts the character fPad as needed to expand result to fFormatWidth. * @param result the string to be padded * @param hasAffixes if true, padding is positioned appropriately before or * after affixes. If false, then isNegative is ignored, and there are only * two effective pad positions: kPadBeforePrefix/kPadAfterPrefix and * kPadBeforeSuffix/kPadAfterSuffix. * @param isNegative must be true if result contains a formatted negative * number, and false otherwise. Ignored if hasAffixes is false. */ void DecimalFormat::addPadding(UnicodeString& result, FieldPosition& fieldPosition, UBool hasAffixes, UBool isNegative) const { if (fFormatWidth > 0) { int32_t len = fFormatWidth - result.length(); if (len > 0) { UnicodeString padding; for (int32_t i=0; i 0) { i = parsePosition.getIndex(); while (i < text.length() && !text.compare(i, padLen, fPad, 0, padLen)) { i += padLen; } parsePosition.setIndex(i); } // special case NaN // If the text is composed of the representation of NaN, returns NaN.length UnicodeString nan(fSymbols->getSymbol(DecimalFormatSymbols::kNaNSymbol)); int32_t nanLen = (text.compare(parsePosition.getIndex(), nan.length(), nan) ? 0 : nan.length()); if (nanLen) { parsePosition.setIndex(parsePosition.getIndex() + nanLen); result.setDouble(uprv_getNaN()); return; } // status is used to record whether a number is infinite. UBool status[fgStatusLength]; DigitList digits; if (!subparse(text, parsePosition, digits, status)) { parsePosition.setIndex(backup); return; } if (fFormatWidth < 0) { i = parsePosition.getIndex(); while (i < text.length() && !text.compare(i, padLen, fPad, 0, padLen)) { i += padLen; } parsePosition.setIndex(i); } // Handle infinity if (status[fgStatusInfinite]) { double inf = uprv_getInfinity(); result.setDouble(digits.fIsPositive ? inf : -inf); return; } // Do as much of the multiplier conversion as possible without // losing accuracy. int32_t mult = fMultiplier; // Don't modify this.multiplier while (mult % 10 == 0) { mult /= 10; --digits.fDecimalAt; } // Handle integral values. We want to return the most // parsimonious type that will accommodate all of the result's // precision. We therefore only return a long if the result fits // entirely within a long (taking into account the multiplier) -- // otherwise we fall through and return a double. When more // numeric types are supported by Formattable (e.g., 64-bit // integers, bignums) we will extend this logic to include them. if (digits.fitsIntoLong(isParseIntegerOnly())) { int32_t n = digits.getLong(); if (n % mult == 0) { result.setLong(n / mult); return; } else { // else handle the remainder result.setDouble(((double)n) / mult); return; } } else { // Handle non-integral or very large values // Dividing by one is okay and not that costly. result.setDouble(digits.getDouble() / mult); return; } } /* This is an old implimentation that was preparing for 64-bit numbers in ICU. It is very slow, and 64-bit numbers are not ANSI-C compatible. This code is here if we change our minds. */ /** * Parse the given text into a number. The text is parsed beginning at * parsePosition, until an unparseable character is seen. * @param text The string to parse. * @param parsePosition The position at which to being parsing. Upon * return, the first unparseable character. * @param digits The DigitList to set to the parsed value. * @param isExponent If true, parse an exponent. This means no * infinite values and integer only. By default it's really false. * @param status Upon return contains boolean status flags indicating * whether the value was infinite and whether it was positive. */ UBool DecimalFormat::subparse(const UnicodeString& text, ParsePosition& parsePosition, DigitList& digits, UBool* status) const { UTextOffset position = parsePosition.getIndex(); UTextOffset oldStart = position; // check for positivePrefix; take longest UBool gotPositive = text.compare(position,fPositivePrefix.length(),fPositivePrefix,0, fPositivePrefix.length()) == 0; UBool gotNegative = text.compare(position,fNegativePrefix.length(),fNegativePrefix,0, fNegativePrefix.length()) == 0; // If the number is positive and negative at the same time, // 1. the number is positive if the positive prefix is longer // 2. the number is negative if the negative prefix is longer if (gotPositive && gotNegative) { if (fPositivePrefix.length() > fNegativePrefix.length()) gotNegative = FALSE; else if (fPositivePrefix.length() < fNegativePrefix.length()) gotPositive = FALSE; } if(gotPositive) position += fPositivePrefix.length(); else if(gotNegative) position += fNegativePrefix.length(); else { parsePosition.setErrorIndex(position); return FALSE; } // process digits or Inf, find decimal position UnicodeString inf(fSymbols->getSymbol(DecimalFormatSymbols::kInfinitySymbol)); int32_t infLen = (text.compare(position, inf.length(), inf) ? 0 : inf.length()); position += infLen; // infLen is non-zero when it does equal to infinity status[fgStatusInfinite] = (UBool)infLen; if (!infLen) { // We now have a string of digits, possibly with grouping symbols, // and decimal points. We want to process these into a DigitList. // We don't want to put a bunch of leading zeros into the DigitList // though, so we keep track of the location of the decimal point, // put only significant digits into the DigitList, and adjust the // exponent as needed. digits.fDecimalAt = digits.fCount = 0; UChar32 zero = fSymbols->getSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); UnicodeString decimal(fIsCurrencyFormat ? fSymbols->getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol) : fSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); UnicodeString grouping(fSymbols->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); UnicodeString exponentChar(fSymbols->getSymbol(DecimalFormatSymbols::kExponentialSymbol)); UBool sawDecimal = FALSE; UBool sawDigit = FALSE; int32_t backup = -1; UChar32 ch; int32_t digit; int32_t textLength = text.length(); // One less pointer to follow int32_t groupingLen = grouping.length(); int32_t decimalLen = decimal.length(); // We have to track digitCount ourselves, because digits.fCount will // pin when the maximum allowable digits is reached. int32_t digitCount = 0; for (; position < textLength; position += 1 + UTF_NEED_MULTIPLE_UCHAR(ch)) { ch = text.char32At(position); /* We recognize all digit ranges, not only the Latin digit range * '0'..'9'. We do so by using the Character.digit() method, * which converts a valid Unicode digit to the range 0..9. * * The character 'ch' may be a digit. If so, place its value * from 0 to 9 in 'digit'. First try using the locale digit, * which may or MAY NOT be a standard Unicode digit range. If * this fails, try using the standard Unicode digit ranges by * calling Character.digit(). If this also fails, digit will * have a value outside the range 0..9. */ digit = ch - zero; if (digit < 0 || digit > 9) { digit = u_charDigitValue(ch); } if (digit > 0 && digit <= 9) { // Cancel out backup setting (see grouping handler below) backup = -1; sawDigit = TRUE; // output a regular non-zero digit. ++digitCount; digits.append((char)(digit + '0')); } else if (digit == 0) { // Cancel out backup setting (see grouping handler below) backup = -1; sawDigit = TRUE; // Check for leading zeros if (digits.fCount != 0) { // output a regular zero digit. ++digitCount; digits.append((char)(digit + '0')); } else if (sawDecimal) { // If we have seen the decimal, but no significant digits yet, // then we account for leading zeros by decrementing the // digits.fDecimalAt into negative values. --digits.fDecimalAt; } // else ignore leading zeros in integer part of number. } else if (!text.compare(position, groupingLen, grouping) && isGroupingUsed()) { // Ignore grouping characters, if we are using them, but require // that they be followed by a digit. Otherwise we backup and // reprocess them. backup = position; } else if (!text.compare(position, decimalLen, decimal) && !isParseIntegerOnly() && !sawDecimal) { // If we're only parsing integers, or if we ALREADY saw the // decimal, then don't parse this one. digits.fDecimalAt = digitCount; // Not digits.fCount! sawDecimal = TRUE; } else if (!text.compare(position, fSymbols->getSymbol(DecimalFormatSymbols::kExponentialSymbol).length(), fSymbols->getSymbol(DecimalFormatSymbols::kExponentialSymbol))) // error code is set below if !sawDigit { // Parse sign, if present int32_t pos = position + 1; // position + exponentSep.length(); DigitList exponentDigits; if (pos < textLength) { if (!text.compare(pos, fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol).length(), fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol))) { ++pos; } else if (!text.compare(pos, fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol).length(), fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol))) { ++pos; exponentDigits.fIsPositive = FALSE; } } while (pos < textLength) { ch = text[(UTextOffset)pos]; digit = ch - zero; if (digit < 0 || digit > 9) { digit = u_charDigitValue(ch); } if (0 <= digit && digit <= 9) { ++pos; exponentDigits.append((char)(digit + '0')); } else { break; } } if (exponentDigits.fCount > 0) { exponentDigits.fDecimalAt = exponentDigits.fCount; digits.fDecimalAt += exponentDigits.getLong(); position = pos; // Advance past the exponent } break; // Whether we fail or succeed, we exit this loop } else break; } if (backup != -1) { position = backup; } // If there was no decimal point we have an integer if (!sawDecimal) { digits.fDecimalAt += digitCount; // Not digits.fCount! } // If none of the text string was recognized. For example, parse // "x" with pattern "#0.00" (return index and error index both 0) // parse "$" with pattern "$#0.00". (return index 0 and error index // 1). if (!sawDigit && digitCount == 0) { parsePosition.setIndex(oldStart); parsePosition.setErrorIndex(oldStart); return FALSE; } } // check for positiveSuffix if (gotPositive && fPositiveSuffix.length() > 0) { gotPositive = text.compare(position,fPositiveSuffix.length(),fPositiveSuffix,0, fPositiveSuffix.length()) == 0; } if (gotNegative && fNegativeSuffix.length() > 0) { gotNegative = text.compare(position,fNegativeSuffix.length(),fNegativeSuffix,0, fNegativeSuffix.length()) == 0; } // if both match, take longest if (gotPositive && gotNegative) { if (fPositiveSuffix.length() > fNegativeSuffix.length()) { gotNegative = FALSE; } else if (fPositiveSuffix.length() < fNegativeSuffix.length()) { gotPositive = FALSE; } else { gotPositive = TRUE; // Make them equal to each other. gotNegative = TRUE; } } // fail if neither or both if (gotPositive == gotNegative) { parsePosition.setErrorIndex(position); return FALSE; } parsePosition.setIndex(position + (gotPositive ? fPositiveSuffix.length() : fNegativeSuffix.length())); // mark success! digits.fIsPositive = gotPositive; if(parsePosition.getIndex() == oldStart) { parsePosition.setErrorIndex(position); return FALSE; } return TRUE; } //------------------------------------------------------------------------------ // Gets the pointer to the localized decimal format symbols const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols() const { return fSymbols; } //------------------------------------------------------------------------------ // De-owning the current localized symbols and adopt the new symbols. void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) { if (fSymbols != NULL) delete fSymbols; fSymbols = symbolsToAdopt; } //------------------------------------------------------------------------------ // Setting the symbols is equlivalent to adopting a newly created localized // symbols. void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); expandAffixes(); } //------------------------------------------------------------------------------ // Gets the positive prefix of the number pattern. UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const { result = fPositivePrefix; return result; } //------------------------------------------------------------------------------ // Sets the positive prefix of the number pattern. void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) { fPositivePrefix = newValue; delete fPosPrefixPattern; fPosPrefixPattern = 0; } //------------------------------------------------------------------------------ // Gets the negative prefix of the number pattern. UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const { result = fNegativePrefix; return result; } //------------------------------------------------------------------------------ // Gets the negative prefix of the number pattern. void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) { fNegativePrefix = newValue; delete fNegPrefixPattern; fNegPrefixPattern = 0; } //------------------------------------------------------------------------------ // Gets the positive suffix of the number pattern. UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const { result = fPositiveSuffix; return result; } //------------------------------------------------------------------------------ // Sets the positive suffix of the number pattern. void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) { fPositiveSuffix = newValue; delete fPosSuffixPattern; fPosSuffixPattern = 0; } //------------------------------------------------------------------------------ // Gets the negative suffix of the number pattern. UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const { result = fNegativeSuffix; return result; } //------------------------------------------------------------------------------ // Sets the negative suffix of the number pattern. void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) { fNegativeSuffix = newValue; delete fNegSuffixPattern; fNegSuffixPattern = 0; } //------------------------------------------------------------------------------ // Gets the multiplier of the number pattern. int32_t DecimalFormat::getMultiplier() const { return fMultiplier; } //------------------------------------------------------------------------------ // Sets the multiplier of the number pattern. void DecimalFormat::setMultiplier(int32_t newValue) { // This shouldn't be set to 0. // Due to compatibility with ICU4J we cannot set an error code and refuse 0. // So the rest of the code should ignore fMultiplier when it's 0. [grhoten] fMultiplier = newValue; } /** * Get the rounding increment. * @return A positive rounding increment, or 0.0 if rounding * is not in effect. * @see #setRoundingIncrement * @see #getRoundingMode * @see #setRoundingMode */ double DecimalFormat::getRoundingIncrement() { return fRoundingDouble; } /** * Set the rounding increment. This method also controls whether * rounding is enabled. * @param newValue A positive rounding increment, or 0.0 to disable rounding. * Negative increments are equivalent to 0.0. * @see #getRoundingIncrement * @see #getRoundingMode * @see #setRoundingMode */ void DecimalFormat::setRoundingIncrement(double newValue) { if (newValue > 0.0) { if (fRoundingIncrement == NULL) { fRoundingIncrement = new DigitList(); } fRoundingIncrement->set((int32_t)newValue); fRoundingDouble = newValue; } else { delete fRoundingIncrement; fRoundingIncrement = NULL; fRoundingDouble = 0.0; } } /** * Get the rounding mode. * @return A rounding mode * @see #setRoundingIncrement * @see #getRoundingIncrement * @see #setRoundingMode */ DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() { return fRoundingMode; } /** * Set the rounding mode. This has no effect unless the rounding * increment is greater than zero. * @param roundingMode A rounding mode * @see #setRoundingIncrement * @see #getRoundingIncrement * @see #getRoundingMode */ void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { fRoundingMode = roundingMode; } /** * Get the width to which the output of format() is padded. * @return the format width, or zero if no padding is in effect * @see #setFormatWidth * @see #getPadCharacter * @see #setPadCharacter * @see #getPadPosition * @see #setPadPosition */ int32_t DecimalFormat::getFormatWidth() { return fFormatWidth; } /** * Set the width to which the output of format() is padded. * This method also controls whether padding is enabled. * @param width the width to which to pad the result of * format(), or zero to disable padding. A negative * width is equivalent to 0. * @see #getFormatWidth * @see #getPadCharacter * @see #setPadCharacter * @see #getPadPosition * @see #setPadPosition */ void DecimalFormat::setFormatWidth(int32_t width) { fFormatWidth = (width > 0) ? width : 0; } /** * Get the character used to pad to the format width. The default is ' '. * @return the pad character * @see #setFormatWidth * @see #getFormatWidth * @see #setPadCharacter * @see #getPadPosition * @see #setPadPosition */ UnicodeString DecimalFormat::getPadCharacterString() { return fPad; } /** * Set the character used to pad to the format width. This has no effect * unless padding is enabled. * @param padChar the pad character * @see #setFormatWidth * @see #getFormatWidth * @see #getPadCharacter * @see #getPadPosition * @see #setPadPosition */ void DecimalFormat::setPadCharacter(UnicodeString padChar) { if (padChar.length() > 0) { fPad = padChar; } else { fPad = kPatternPadEscape; } } /** * Get the position at which padding will take place. This is the location * at which padding will be inserted if the result of format() * is shorter than the format width. * @return the pad position, one of kPadBeforePrefix, * kPadAfterPrefix, kPadBeforeSuffix, or * kPadAfterSuffix. * @see #setFormatWidth * @see #getFormatWidth * @see #setPadCharacter * @see #getPadCharacter * @see #setPadPosition * @see #kPadBeforePrefix * @see #kPadAfterPrefix * @see #kPadBeforeSuffix * @see #kPadAfterSuffix */ DecimalFormat::EPadPosition DecimalFormat::getPadPosition() { return fPadPosition; } /** * NEW * Set the position at which padding will take place. This is the location * at which padding will be inserted if the result of format() * is shorter than the format width. This has no effect unless padding is * enabled. * @param padPos the pad position, one of kPadBeforePrefix, * kPadAfterPrefix, kPadBeforeSuffix, or * kPadAfterSuffix. * @see #setFormatWidth * @see #getFormatWidth * @see #setPadCharacter * @see #getPadCharacter * @see #getPadPosition * @see #kPadBeforePrefix * @see #kPadAfterPrefix * @see #kPadBeforeSuffix * @see #kPadAfterSuffix */ void DecimalFormat::setPadPosition(EPadPosition padPos) { fPadPosition = padPos; } /** * Return whether or not scientific notation is used. * @return TRUE if this object formats and parses scientific notation * @see #setScientificNotation * @see #getMinimumExponentDigits * @see #setMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ UBool DecimalFormat::isScientificNotation() { return fUseExponentialNotation; } /** * Set whether or not scientific notation is used. * @param useScientific TRUE if this object formats and parses scientific * notation * @see #isScientificNotation * @see #getMinimumExponentDigits * @see #setMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ void DecimalFormat::setScientificNotation(UBool useScientific) { fUseExponentialNotation = useScientific; if (fUseExponentialNotation && fMinExponentDigits < 1) { fMinExponentDigits = 1; } } /** * Return the minimum exponent digits that will be shown. * @return the minimum exponent digits that will be shown * @see #setScientificNotation * @see #isScientificNotation * @see #setMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ int8_t DecimalFormat::getMinimumExponentDigits() { return fMinExponentDigits; } /** * Set the minimum exponent digits that will be shown. This has no * effect unless scientific notation is in use. * @param minExpDig a value >= 1 indicating the fewest exponent digits * that will be shown. Values less than 1 will be treated as 1. * @see #setScientificNotation * @see #isScientificNotation * @see #getMinimumExponentDigits * @see #isExponentSignAlwaysShown * @see #setExponentSignAlwaysShown */ void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { fMinExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); } /** * Return whether the exponent sign is always shown. * @return TRUE if the exponent is always prefixed with either the * localized minus sign or the localized plus sign, false if only negative * exponents are prefixed with the localized minus sign. * @see #setScientificNotation * @see #isScientificNotation * @see #setMinimumExponentDigits * @see #getMinimumExponentDigits * @see #setExponentSignAlwaysShown */ UBool DecimalFormat::isExponentSignAlwaysShown() { return fExponentSignAlwaysShown; } /** * Set whether the exponent sign is always shown. This has no effect * unless scientific notation is in use. * @param expSignAlways TRUE if the exponent is always prefixed with either * the localized minus sign or the localized plus sign, false if only * negative exponents are prefixed with the localized minus sign. * @see #setScientificNotation * @see #isScientificNotation * @see #setMinimumExponentDigits * @see #getMinimumExponentDigits * @see #isExponentSignAlwaysShown */ void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { fExponentSignAlwaysShown = expSignAlways; } //------------------------------------------------------------------------------ // Gets the grouping size of the number pattern. For example, thousand or 10 // thousand groupings. int32_t DecimalFormat::getGroupingSize() const { return fGroupingSize; } //------------------------------------------------------------------------------ // Gets the grouping size of the number pattern. void DecimalFormat::setGroupingSize(int32_t newValue) { fGroupingSize = newValue; } //------------------------------------------------------------------------------ int32_t DecimalFormat::getSecondaryGroupingSize() const { return fGroupingSize2; } //------------------------------------------------------------------------------ void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) { fGroupingSize2 = newValue; } //------------------------------------------------------------------------------ // Checks if to show the decimal separator. UBool DecimalFormat::isDecimalSeparatorAlwaysShown() const { return fDecimalSeparatorAlwaysShown; } //------------------------------------------------------------------------------ // Sets to always show the decimal separator. void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) { fDecimalSeparatorAlwaysShown = newValue; } //------------------------------------------------------------------------------ // Emits the pattern of this DecimalFormat instance. UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { return toPattern(result, FALSE); } //------------------------------------------------------------------------------ // Emits the localized pattern this DecimalFormat instance. UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const { return toPattern(result, TRUE); } //------------------------------------------------------------------------------ /** * Expand the affix pattern strings into the expanded affix strings. If any * affix pattern string is null, do not expand it. This method should be * called any time the symbols or the affix patterns change in order to keep * the expanded affix strings up to date. */ void DecimalFormat::expandAffixes(void) { if (fPosPrefixPattern != 0) { expandAffix(*fPosPrefixPattern, fPositivePrefix); } if (fPosSuffixPattern != 0) { expandAffix(*fPosSuffixPattern, fPositiveSuffix); } if (fNegPrefixPattern != 0) { expandAffix(*fNegPrefixPattern, fNegativePrefix); } if (fNegSuffixPattern != 0) { expandAffix(*fNegSuffixPattern, fNegativeSuffix); } #ifdef FMT_DEBUG UnicodeString s; s.append("[") .append(*fPosPrefixPattern).append("|").append(*fPosSuffixPattern) .append(";") .append(*fNegPrefixPattern).append("|").append(*fNegSuffixPattern) .append("]->[") .append(fPositivePrefix).append("|").append(fPositiveSuffix) .append(";") .append(fNegativePrefix).append("|").append(fNegativeSuffix) .append("]\n"); debugout(s); #endif } /** * Expand an affix pattern into an affix string. All characters in the * pattern are literal unless prefixed by kQuote. The following characters * after kQuote are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, * PATTERN_MINUS, and kCurrencySign. If kCurrencySign is doubled (kQuote + * kCurrencySign + kCurrencySign), it is interpreted as an international * currency sign. Any other character after a kQuote represents itself. * kQuote must be followed by another character; kQuote may not occur by * itself at the end of the pattern. * * @param pattern the non-null, fPossibly empty pattern * @param affix string to receive the expanded equivalent of pattern */ void DecimalFormat::expandAffix(const UnicodeString& pattern, UnicodeString& affix) const { affix.remove(); for (int i=0; igetSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); } else { affix += fSymbols->getSymbol(DecimalFormatSymbols::kCurrencySymbol); } } continue; case kPatternPercent: affix.append(fSymbols->getSymbol(DecimalFormatSymbols::kPercentSymbol)); break; case kPatternPerMill: affix.append(fSymbols->getSymbol(DecimalFormatSymbols::kPerMillSymbol)); break; case kPatternPlus: affix.append(fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol)); break; case kPatternMinus: affix.append(fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol)); break; default: affix.append(c); break; } } else { affix.append(c); } } } /** * Appends an affix pattern to the given StringBuffer, quoting special * characters as needed. Uses the internal affix pattern, if that exists, * or the literal affix, if the internal affix pattern is null. The * appended string will generate the same affix pattern (or literal affix) * when passed to toPattern(). * * @param buffer the affix string is appended to this * @param affixPattern a pattern such as fPosPrefixPattern; may be null * @param expAffix a corresponding expanded affix, such as fPositivePrefix. * Ignored unless affixPattern is null. If affixPattern is null, then * expAffix is appended as a literal affix. * @param localized true if the appended pattern should contain localized * pattern characters; otherwise, non-localized pattern chars are appended */ void DecimalFormat::appendAffix(UnicodeString& buffer, const UnicodeString* affixPattern, const UnicodeString& expAffix, UBool localized) const { if (affixPattern == 0) { appendAffix(buffer, expAffix, localized); } else { int i; for (int pos=0; poslength(); pos=i) { i = affixPattern->indexOf(kQuote, pos); if (i < 0) { UnicodeString s; affixPattern->extractBetween(pos, affixPattern->length(), s); appendAffix(buffer, s, localized); break; } if (i > pos) { UnicodeString s; affixPattern->extractBetween(pos, i, s); appendAffix(buffer, s, localized); } UChar32 c = affixPattern->char32At(++i); ++i; if (c == kQuote) { buffer.append(c).append(c); // Fall through and append another kQuote below } else if (c == kCurrencySign && ilength() && affixPattern->char32At(i) == kCurrencySign) { ++i; buffer.append(c).append(c); } else if (localized) { switch (c) { case kPatternPercent: buffer.append(fSymbols->getSymbol(DecimalFormatSymbols::kPercentSymbol)); break; case kPatternPerMill: buffer.append(fSymbols->getSymbol(DecimalFormatSymbols::kPerMillSymbol)); break; case kPatternPlus: buffer.append(fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol)); break; case kPatternMinus: buffer.append(fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol)); break; default: buffer.append(c); } } else { buffer.append(c); } } } } /** * Append an affix to the given StringBuffer, using quotes if * there are special characters. Single quotes themselves must be * escaped in either case. */ void DecimalFormat::appendAffix( UnicodeString& buffer, const UnicodeString& affix, UBool localized) const { UBool needQuote; if(localized) { needQuote = affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kZeroDigitSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kPercentSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kPerMillSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kDigitSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol)) >= 0 || affix.indexOf(fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol)) >= 0 || affix.indexOf(kCurrencySign) >= 0; } else { needQuote = affix.indexOf(kPatternZeroDigit) >= 0 || affix.indexOf(kPatternGroupingSeparator) >= 0 || affix.indexOf(kPatternDecimalSeparator) >= 0 || affix.indexOf(kPatternPercent) >= 0 || affix.indexOf(kPatternPerMill) >= 0 || affix.indexOf(kPatternDigit) >= 0 || affix.indexOf(kPatternSeparator) >= 0 || affix.indexOf(kPatternExponent) >= 0 || affix.indexOf(kPatternPlus) >= 0 || affix.indexOf(kPatternMinus) >= 0 || affix.indexOf(kCurrencySign) >= 0; } if (needQuote) buffer += (UChar)0x0027 /*'\''*/; if (affix.indexOf((UChar)0x0027 /*'\''*/) < 0) buffer += affix; else { for (int32_t j = 0; j < affix.length(); ++j) { UChar32 c = affix.char32At(j); buffer += c; if (c == 0x0027 /*'\''*/) buffer += c; j = j + UTF_NEED_MULTIPLE_UCHAR(c); } } if (needQuote) buffer += (UChar)0x0027 /*'\''*/; } //------------------------------------------------------------------------------ /* Tell the VC++ compiler not to spew out the warnings about integral size conversion */ /* #ifdef _WIN32 #pragma warning( disable : 4761 ) #endif */ UnicodeString& DecimalFormat::toPattern(UnicodeString& result, UBool localized) const { result.remove(); UChar32 zero; UnicodeString digit; UnicodeString group; int32_t i; int32_t roundingDecimalPos = 0; // Pos of decimal in roundingDigits UnicodeString roundingDigits; int32_t padPos = (fFormatWidth > 0) ? fPadPosition : -1; UnicodeString padSpec; if (localized) { digit = fSymbols->getSymbol(DecimalFormatSymbols::kDigitSymbol); group = fSymbols->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); zero = fSymbols->getSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); } else { digit.append((UChar)kPatternDigit); group.append((UChar)kPatternGroupingSeparator); zero = (UChar32)kPatternZeroDigit; } if (fFormatWidth > 0) { if (localized) { padSpec.append(fSymbols->getSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); } else { padSpec.append((UChar)kPatternPadEscape); } padSpec.append(fPad); } if (fRoundingIncrement != NULL) { for(i=0; ifCount; ++i) { roundingDigits.append((UChar)fRoundingIncrement->fDigits[i]); } roundingDecimalPos = fRoundingIncrement->fDecimalAt; } for (int32_t part=0; part<2; ++part) { if (padPos == kPadBeforePrefix) { result.append(padSpec); } appendAffix(result, (part==0 ? fPosPrefixPattern : fNegPrefixPattern), (part==0 ? fPositivePrefix : fNegativePrefix), localized); if (padPos == kPadAfterPrefix && ! padSpec.isEmpty()) { result.append(padSpec); } int32_t sub0Start = result.length(); int32_t g = isGroupingUsed() ? uprv_max(0, fGroupingSize) : 0; if (g > 0 && fGroupingSize2 > 0 && fGroupingSize2 != fGroupingSize) { g += fGroupingSize2; } int32_t maxIntDig = fUseExponentialNotation ? getMaximumIntegerDigits() : (uprv_max(uprv_max(g, getMinimumIntegerDigits()), roundingDecimalPos) + 1); for (i = maxIntDig; i > 0; --i) { if (!fUseExponentialNotation && i= 0 && pos < roundingDigits.length()) { result.append((UChar) (roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); continue; } } if (i<=getMinimumIntegerDigits()) { result.append(zero); } else { result.append(digit); } } if (getMaximumFractionDigits() > 0 || fDecimalSeparatorAlwaysShown) { if (localized) { result.append(fSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); } else { result.append((UChar)kPatternDecimalSeparator); } } int32_t pos = roundingDecimalPos; for (i = 0; i < getMaximumFractionDigits(); ++i) { if (! roundingDigits.isEmpty() && pos < roundingDigits.length()) { if (pos < 0) { result.append(zero); } else { result.append((UChar)(roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); } ++pos; continue; } if (igetSymbol(DecimalFormatSymbols::kExponentialSymbol)); } else { result.append((UChar)kPatternExponent); } if (fExponentSignAlwaysShown) { if (localized) { result.append(fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol)); } else { result.append((UChar)kPatternPlus); } } for (i=0; i 0) { result.insert(sub0Start, digit); ++maxIntDig; --add; // Only add a grouping separator if we have at least // 2 additional characters to be added, so we don't // end up with ",###". if (add>1 && isGroupingPosition(maxIntDig)) { result.insert(sub0Start, group); --add; } } } if (fPadPosition == kPadBeforeSuffix && ! padSpec.isEmpty()) { result.append(padSpec); } if (part == 0) { appendAffix(result, fPosSuffixPattern, fPositiveSuffix, localized); if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { result.append(padSpec); } UBool isDefault = FALSE; if ((fNegSuffixPattern == fPosSuffixPattern && // both null fNegativeSuffix == fPositiveSuffix) || (fNegSuffixPattern != 0 && fPosSuffixPattern != 0 && *fNegSuffixPattern == *fPosSuffixPattern)) { if (fNegPrefixPattern != NULL && fPosPrefixPattern != NULL) { int32_t length = fPosPrefixPattern->length(); isDefault = fNegPrefixPattern->length() == (length+2) && (*fNegPrefixPattern)[(UTextOffset)0] == kQuote && (*fNegPrefixPattern)[(UTextOffset)1] == kPatternMinus && fNegPrefixPattern->compare(2, length, *fPosPrefixPattern, 0, length) == 0; } if (!isDefault && fNegPrefixPattern == NULL && fPosPrefixPattern == NULL) { int32_t length = fPositivePrefix.length(); isDefault = fNegativePrefix.length() == (length+1) && fNegativePrefix.compare(fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol)) == 0 && fNegativePrefix.compare(1, length, fPositivePrefix, 0, length) == 0; } } if (isDefault) { break; // Don't output default negative subpattern } else { if (localized) { result.append(fSymbols->getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)); } else { result.append((UChar)kPatternSeparator); } } } else { appendAffix(result, fNegSuffixPattern, fNegativeSuffix, localized); if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { result.append(padSpec); } } } return result; } //------------------------------------------------------------------------------ void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { UParseError parseError; applyPattern(pattern, FALSE, parseError, status); } //------------------------------------------------------------------------------ void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError& parseError, UErrorCode& status) { applyPattern(pattern, FALSE, parseError, status); } //------------------------------------------------------------------------------ void DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) { UParseError parseError; applyPattern(pattern, TRUE,parseError,status); } //------------------------------------------------------------------------------ void DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UParseError& parseError, UErrorCode& status) { applyPattern(pattern, TRUE,parseError,status); } //------------------------------------------------------------------------------ void DecimalFormat::applyPattern(const UnicodeString& pattern, UBool localized, UParseError& parseError, UErrorCode& status) { if (U_FAILURE(status)) { return; } // Clear error struct parseError.offset = 0; parseError.preContext[0] = parseError.postContext[0] = (UChar)0; // Set the significant pattern symbols UChar32 zeroDigit = kPatternZeroDigit; UnicodeString groupingSeparator ((UChar)kPatternGroupingSeparator); UnicodeString decimalSeparator ((UChar)kPatternDecimalSeparator); UnicodeString percent ((UChar)kPatternPercent); UnicodeString perMill ((UChar)kPatternPerMill); UnicodeString digit ((UChar)kPatternDigit); UnicodeString separator ((UChar)kPatternSeparator); UnicodeString exponent ((UChar)kPatternExponent); UnicodeString plus ((UChar)kPatternPlus); UnicodeString minus ((UChar)kPatternMinus); UnicodeString padEscape ((UChar)kPatternPadEscape); // Substitute with the localized symbols if necessary if (localized) { zeroDigit = fSymbols->getSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); groupingSeparator = fSymbols->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); decimalSeparator = fSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); percent = fSymbols->getSymbol(DecimalFormatSymbols::kPercentSymbol); perMill = fSymbols->getSymbol(DecimalFormatSymbols::kPerMillSymbol); digit = fSymbols->getSymbol(DecimalFormatSymbols::kDigitSymbol); separator = fSymbols->getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); exponent = fSymbols->getSymbol(DecimalFormatSymbols::kExponentialSymbol); plus = fSymbols->getSymbol(DecimalFormatSymbols::kPlusSignSymbol); minus = fSymbols->getSymbol(DecimalFormatSymbols::kMinusSignSymbol); padEscape = fSymbols->getSymbol(DecimalFormatSymbols::kPadEscapeSymbol); } UChar nineDigit = (UChar)(zeroDigit + 9); int32_t digitLen = digit.length(); int32_t groupSepLen = groupingSeparator.length(); int32_t decimalSepLen = decimalSeparator.length(); UTextOffset pos = 0; int32_t patLen = pattern.length(); // Part 0 is the positive pattern. Part 1, if present, is the negative // pattern. for (int32_t part=0; part<2 && pos 0) { ++digitRightCount; } else { ++digitLeftCount; } if (groupingCount >= 0 && decimalPos < 0) { ++groupingCount; } pos += digitLen; } else if (ch >= zeroDigit && ch <= nineDigit) { if (digitRightCount > 0) { // Unexpected '0' debug("Unexpected '0'") status = U_UNEXPECTED_TOKEN; syntaxError(pattern,pos,parseError); return; } ++zeroDigitCount; if (groupingCount >= 0 && decimalPos < 0) { ++groupingCount; } if (ch != zeroDigit && roundingPos < 0) { roundingPos = digitLeftCount + zeroDigitCount; } if (roundingPos >= 0) { roundingInc.append((char)(ch - zeroDigit + '0')); } pos++; } else if (pattern.compare(pos, groupSepLen, groupingSeparator) == 0) { if (decimalPos >= 0) { // Grouping separator after decimal debug("Grouping separator after decimal") status = U_UNEXPECTED_TOKEN; syntaxError(pattern,pos,parseError); return; } groupingCount2 = groupingCount; groupingCount = 0; pos += groupSepLen; } else if (pattern.compare(pos, decimalSepLen, decimalSeparator) == 0) { if (decimalPos >= 0) { // Multiple decimal separators debug("Multiple decimal separators") status = U_MULTIPLE_DECIMAL_SEPERATORS; syntaxError(pattern,pos,parseError); return; } // Intentionally incorporate the digitRightCount, // even though it is illegal for this to be > 0 // at this point. We check pattern syntax below. decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; pos += decimalSepLen; } else { if (pattern.compare(pos, exponent.length(), exponent) == 0) { if (expDigits >= 0) { // Multiple exponential symbols debug("Multiple exponential symbols") status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; syntaxError(pattern,pos,parseError); return; } if (groupingCount >= 0) { // Grouping separator in exponential pattern debug("Grouping separator in exponential pattern") status = U_MALFORMED_EXPONENTIAL_PATTERN; syntaxError(pattern,pos,parseError); return; } // Check for positive prefix if ((pos+1) < patLen && pattern.compare((UTextOffset) (pos+1), plus.length(), plus) == 0) { expSignAlways = TRUE; pos += plus.length(); } // Use lookahead to parse out the exponential part of the // pattern, then jump into suffix subpart. expDigits = 0; pos += exponent.length() - 1; while (++pos < patLen && pattern[(UTextOffset) pos] == zeroDigit) { ++expDigits; } if ((digitLeftCount + zeroDigitCount) < 1 || expDigits < 1) { // Malformed exponential pattern debug("Malformed exponential pattern") status = U_MALFORMED_EXPONENTIAL_PATTERN; syntaxError(pattern,pos,parseError); return; } } // Transition to suffix subpart subpart = 2; // suffix subpart affix = &suffix; sub0Limit = pos; continue; } break; case 1: // Prefix subpart case 2: // Suffix subpart // Process the prefix / suffix characters // Process unquoted characters seen in prefix or suffix // subpart. if (pattern.compare(pos, digitLen, digit) == 0) { // Any of these characters implicitly begins the // next subpart if we are in the prefix if (subpart == 1) { // prefix subpart subpart = 0; // pattern proper subpart sub0Start = pos; // Reprocess this character continue; } pos += digitLen; // Fall through to append(ch) } else if (pattern.compare(pos, groupSepLen, groupingSeparator) == 0) { // Any of these characters implicitly begins the // next subpart if we are in the prefix if (subpart == 1) { // prefix subpart subpart = 0; // pattern proper subpart sub0Start = pos; // Reprocess this character continue; } pos += groupSepLen; // Fall through to append(ch) } else if (pattern.compare(pos, decimalSepLen, decimalSeparator) == 0) { // Any of these characters implicitly begins the // next subpart if we are in the prefix if (subpart == 1) { // prefix subpart subpart = 0; // pattern proper subpart sub0Start = pos; // Reprocess this character continue; } pos += decimalSepLen; // Fall through to append(ch) } else if (ch >= zeroDigit && ch <= nineDigit) { // Any of these characters implicitly begins the // next subpart if we are in the prefix if (subpart == 1) { // prefix subpart subpart = 0; // pattern proper subpart sub0Start = pos; // Reprocess this character continue; } pos++; // Fall through to append(ch) } else if (ch == kCurrencySign) { // Use lookahead to determine if the currency sign is // doubled or not. pos++; affix->append(kQuote); // Encode currency if (pos < pattern.length() && pattern[pos] == kCurrencySign) { affix->append(kCurrencySign); ++pos; // Skip over the doubled character } isCurrency = TRUE; // Fall through to append(ch) } else if (ch == kQuote) { // A quote outside quotes indicates either the opening // quote or two quotes, which is a quote literal. That is, // we have the first quote in 'do' or o''clock. ++pos; if (pos < pattern.length() && pattern[pos] == kQuote) { affix->append(kQuote); // Encode quote ++pos; // Fall through to append(ch) } else { subpart += 2; // open quote continue; } } else if (pattern.compare(pos, separator.length(), separator) == 0) { // Don't allow separators in the prefix, and don't allow // separators in the second pattern (part == 1). if (subpart == 1 || part == 1) { // Unexpected separator debug("Unexpected separator") status = U_UNEXPECTED_TOKEN; syntaxError(pattern,pos,parseError); return; } sub2Limit = pos; isPartDone = TRUE; // Go to next part pos += separator.length(); break; } else if (pattern.compare(pos, percent.length(), percent) == 0) { // Next handle characters which are appended directly. if (multiplier != 1) { // Too many percent/perMill characters debug("Too many percent characters") status = U_MULTIPLE_PERCENT_SYMBOLS; syntaxError(pattern,pos,parseError); return; } affix->append(kQuote); // Encode percent/perMill multiplier = 100; ch = kPatternPercent; // Use unlocalized pattern char pos += percent.length(); // Fall through to append(ch) } else if (pattern.compare(pos, perMill.length(), perMill) == 0) { // Next handle characters which are appended directly. if (multiplier != 1) { // Too many percent/perMill characters debug("Too many perMill characters") status = U_MULTIPLE_PERMILL_SYMBOLS; syntaxError(pattern,pos,parseError); return; } affix->append(kQuote); // Encode percent/perMill multiplier = 1000; ch = kPatternPerMill; // Use unlocalized pattern char pos += perMill.length(); // Fall through to append(ch) } else if (pattern.compare(pos, padEscape.length(), padEscape) == 0) { if (padPos >= 0 || // Multiple pad specifiers (pos+1) == pattern.length()) { // Nothing after padEscape debug("Multiple pad specifiers") status = U_MULTIPLE_PAD_SPECIFIERS; syntaxError(pattern,pos,parseError); return; } padPos = pos; padChar = pattern.char32At(++pos); pos += 1 + UTF_NEED_MULTIPLE_UCHAR(pattern.char32At(pos)); // pos += padEscape.length(); continue; } else if (pattern.compare(pos, minus.length(), minus) == 0) { affix->append(kQuote); // Encode minus ch = kPatternMinus; pos += minus.length(); // Fall through to append(ch) } else if (pattern.compare(pos, plus.length(), plus) == 0) { affix->append(kQuote); // Encode plus ch = kPatternPlus; pos += plus.length(); // Fall through to append(ch) } else { pos++; } // Unquoted, non-special characters fall through to here, as // well as other code which needs to append something to the // affix. affix->append(ch); break; case 3: // Prefix subpart, in quote case 4: // Suffix subpart, in quote // A quote within quotes indicates either the closing // quote or two quotes, which is a quote literal. That is, // we have the second quote in 'do' or 'don''t'. pos++; if (ch == kQuote) { if (pos < pattern.length() && pattern[pos] == kQuote) { ++pos; affix->append(kQuote); // Encode quote // Fall through to append(ch) } else { subpart -= 2; // close quote continue; } } affix->append(ch); break; } } if (sub0Limit == 0) { sub0Limit = pattern.length(); } if (sub2Limit == 0) { sub2Limit = pattern.length(); } /* Handle patterns with no '0' pattern character. These patterns * are legal, but must be recodified to make sense. "##.###" -> * "#0.###". ".###" -> ".0##". * * We allow patterns of the form "####" to produce a zeroDigitCount * of zero (got that?); although this seems like it might make it * possible for format() to produce empty strings, format() checks * for this condition and outputs a zero digit in this situation. * Having a zeroDigitCount of zero yields a minimum integer digits * of zero, which allows proper round-trip patterns. We don't want * "#" to become "#0" when toPattern() is called (even though that's * what it really is, semantically). */ if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { // Handle "###.###" and "###." and ".###" int n = decimalPos; if (n == 0) ++n; // Handle ".###" digitRightCount = digitLeftCount - n; digitLeftCount = n - 1; zeroDigitCount = 1; } // Do syntax checking on the digits, decimal points, and quotes. if ((decimalPos < 0 && digitRightCount > 0) || (decimalPos >= 0 && (decimalPos < digitLeftCount || decimalPos > (digitLeftCount + zeroDigitCount))) || groupingCount == 0 || groupingCount2 == 0 || subpart > 2) { // subpart > 2 == unmatched quote debug("Syntax error") status = U_PATTERN_SYNTAX_ERROR; syntaxError(pattern,pos,parseError); return; } // Make sure pad is at legal position before or after affix. if (padPos >= 0) { if (padPos == start) { padPos = kPadBeforePrefix; } else if (padPos+2 == sub0Start) { padPos = kPadAfterPrefix; } else if (padPos == sub0Limit) { padPos = kPadBeforeSuffix; } else if (padPos+2 == sub2Limit) { padPos = kPadAfterSuffix; } else { // Illegal pad position debug("Illegal pad position") status = U_ILLEGAL_PAD_POSITION; syntaxError(pattern,pos,parseError); return; } } if (part == 0) { delete fPosPrefixPattern; delete fPosSuffixPattern; delete fNegPrefixPattern; delete fNegSuffixPattern; fPosPrefixPattern = new UnicodeString(prefix); fPosSuffixPattern = new UnicodeString(suffix); fNegPrefixPattern = 0; fNegSuffixPattern = 0; fUseExponentialNotation = (expDigits >= 0); if (fUseExponentialNotation) { fMinExponentDigits = expDigits; fExponentSignAlwaysShown = expSignAlways; } fIsCurrencyFormat = isCurrency; int digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; // The effectiveDecimalPos is the position the decimal is at or // would be at if there is no decimal. Note that if // decimalPos<0, then digitTotalCount == digitLeftCount + // zeroDigitCount. int effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; setMinimumIntegerDigits(effectiveDecimalPos - digitLeftCount); setMaximumIntegerDigits(fUseExponentialNotation ? digitLeftCount + getMinimumIntegerDigits() : kDoubleIntegerDigits); setMaximumFractionDigits(decimalPos >= 0 ? (digitTotalCount - decimalPos) : 0); setMinimumFractionDigits(decimalPos >= 0 ? (digitLeftCount + zeroDigitCount - decimalPos) : 0); setGroupingUsed(groupingCount > 0); fGroupingSize = (groupingCount > 0) ? groupingCount : 0; fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) ? groupingCount2 : 0; fMultiplier = multiplier; setDecimalSeparatorAlwaysShown(decimalPos == 0 || decimalPos == digitTotalCount); if (padPos >= 0) { fPadPosition = (EPadPosition) padPos; // To compute the format width, first set up sub0Limit - // sub0Start. Add in prefix/suffix length later. // fFormatWidth = prefix.length() + suffix.length() + // sub0Limit - sub0Start; fFormatWidth = sub0Limit - sub0Start; fPad = padChar; } else { fFormatWidth = 0; } if (roundingPos >= 0) { roundingInc.fDecimalAt = effectiveDecimalPos - roundingPos; if (fRoundingIncrement != NULL) { *fRoundingIncrement = roundingInc; } else { fRoundingIncrement = new DigitList(roundingInc); } fRoundingDouble = fRoundingIncrement->getDouble(); fRoundingMode = kRoundHalfEven; } else { setRoundingIncrement(0.0); } } else { fNegPrefixPattern = new UnicodeString(prefix); fNegSuffixPattern = new UnicodeString(suffix); } } if (pattern.length() == 0) { delete fNegPrefixPattern; delete fNegSuffixPattern; fNegPrefixPattern = NULL; fNegSuffixPattern = NULL; if (fPosPrefixPattern != NULL) { fPosPrefixPattern->remove(); } else { fPosPrefixPattern = new UnicodeString(); } if (fPosSuffixPattern != NULL) { fPosSuffixPattern->remove(); } else { fPosSuffixPattern = new UnicodeString(); } setMinimumIntegerDigits(0); setMaximumIntegerDigits(kDoubleIntegerDigits); setMinimumFractionDigits(0); setMaximumFractionDigits(kDoubleFractionDigits); fUseExponentialNotation = FALSE; fIsCurrencyFormat = FALSE; setGroupingUsed(FALSE); fGroupingSize = 0; fGroupingSize2 = 0; fMultiplier = 1; setDecimalSeparatorAlwaysShown(FALSE); fFormatWidth = 0; setRoundingIncrement(0.0); } // If there was no negative pattern, or if the negative pattern is // identical to the positive pattern, then prepend the minus sign to the // positive pattern to form the negative pattern. if (fNegPrefixPattern == NULL || (*fNegPrefixPattern == *fPosPrefixPattern && *fNegSuffixPattern == *fPosSuffixPattern)) { _copy_us_ptr(&fNegSuffixPattern, fPosSuffixPattern); if (fNegPrefixPattern == NULL) { fNegPrefixPattern = new UnicodeString(); } else { fNegPrefixPattern->remove(); } fNegPrefixPattern->append(kQuote).append(kPatternMinus) .append(*fPosPrefixPattern); } #ifdef FMT_DEBUG UnicodeString s; s.append("\"").append(pattern).append("\"->"); debugout(s); #endif expandAffixes(); if (fFormatWidth > 0) { // Finish computing format width (see above) fFormatWidth += fPositivePrefix.length() + fPositiveSuffix.length(); } } /** * Sets the maximum number of digits allowed in the integer portion of a * number. This override limits the integer digit count to 309. * @see NumberFormat#setMaximumIntegerDigits */ void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { NumberFormat::setMaximumIntegerDigits(uprv_min(newValue, kDoubleIntegerDigits)); } /** * Sets the minimum number of digits allowed in the integer portion of a * number. This override limits the integer digit count to 309. * @see NumberFormat#setMinimumIntegerDigits */ void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { NumberFormat::setMinimumIntegerDigits(uprv_min(newValue, kDoubleIntegerDigits)); } /** * Sets the maximum number of digits allowed in the fraction portion of a * number. This override limits the fraction digit count to 340. * @see NumberFormat#setMaximumFractionDigits */ void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { NumberFormat::setMaximumFractionDigits(uprv_min(newValue, kDoubleFractionDigits)); } /** * Sets the minimum number of digits allowed in the fraction portion of a * number. This override limits the fraction digit count to 340. * @see NumberFormat#setMinimumFractionDigits */ void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { NumberFormat::setMinimumFractionDigits(uprv_min(newValue, kDoubleFractionDigits)); } U_NAMESPACE_END //eof