tec.units.ri.internal.format.l10n.DecimalFormat Maven / Gradle / Ivy
Show all versions of unit-ri Show documentation
/* * Units of Measurement Reference Implementation * Copyright (c) 2005-2015, Jean-Marie Dautelle, Werner Keil, V2COM. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions * and the following disclaimer in the documentation and/or other materials provided with the distribution. * * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved * * The original version of this source code and documentation is copyrighted * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These * materials are provided under terms of a License Agreement between Taligent * and Sun. This technology is protected by multiple US and International * patents. This notice and attribution to Taligent may not be removed. * Taligent is a registered trademark of Taligent, Inc. * */ package tec.units.ri.internal.format.l10n; import java.util.Vector; /** *
in most locales) is used as the negative subpattern. * That is,DecimalFormat
is a concrete subclass of *NumberFormat
that formats decimal numbers. It has a variety of * features designed to make it possible to parse and format numbers in any * locale, including support for Western, Arabic, and Indic digits. It also * supports different kinds of numbers, including integers (123), fixed-point * numbers (123.4), scientific notation (1.23E4), percentages (12%), and * currency amounts ($123). All of these can be localized. * ** To obtain a
NumberFormat
for a specific locale, including the * default locale, call one ofNumberFormat
's factory methods, such * asgetInstance()
. In general, do not call the *DecimalFormat
constructors directly, since the *NumberFormat
factory methods may return subclasses other than *DecimalFormat
. If you need to customize the format object, do * something like this: * ** ** ** NumberFormat f = NumberFormat.getInstance(loc); * if (f instanceof DecimalFormat) { * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true); * } ** ** A
DecimalFormat
comprises a pattern and a set of * symbols. The pattern may be set directly using *applyPattern()
, or indirectly using the API methods. The symbols * are stored in aDecimalFormatSymbols
object. When using the *NumberFormat
factory methods, the pattern and symbols are read * from localizedResourceBundle
s. * *Patterns
* *DecimalFormat
patterns have the following syntax:* ** ** Pattern: * PositivePattern * PositivePattern ; NegativePattern * PositivePattern: * Prefixopt Number Suffixopt * NegativePattern: * Prefixopt Number Suffixopt * Prefix: * any Unicode characters except \uFFFE, \uFFFF, and special characters * Suffix: * any Unicode characters except \uFFFE, \uFFFF, and special characters * Number: * Integer Exponentopt * Integer . Fraction Exponentopt * Integer: * MinimumInteger * # * # Integer * # , Integer * MinimumInteger: * 0 * 0 MinimumInteger * 0 , MinimumInteger * Fraction: * MinimumFractionopt OptionalFractionopt * MinimumFraction: * 0 MinimumFractionopt * OptionalFraction: * # OptionalFractionopt * Exponent: * E MinimumExponent * MinimumExponent: * 0 MinimumExponentopt ** ** A
DecimalFormat
pattern contains a positive and negative * subpattern, for example,"#,##0.00;(#,##0.00)"
. Each subpattern * has a prefix, numeric part, and suffix. The negative subpattern is optional; * if absent, then the positive subpattern prefixed with the localized minus * sign (code>'-'"0.00"
alone is equivalent to"0.00;-0.00"
* . If there is an explicit negative subpattern, it serves only to specify the * negative prefix and suffix; the number of digits, minimal digits, and other * characteristics are all the same as the positive pattern. That means that *"#,##0.0#;(#)"
produces precisely the same behavior as *"#,##0.0#;(#,##0.0#)"
. * ** The prefixes, suffixes, and various symbols used for infinity, digits, * thousands separators, decimal separators, etc. may be set to arbitrary * values, and they will appear properly during formatting. However, care must * be taken that the symbols and strings do not conflict, or parsing will be * unreliable. For example, either the positive and negative prefixes or the * suffixes must be distinct for
DecimalFormat.parse()
to be able * to distinguish positive from negative values. (If they are identical, then *DecimalFormat
will behave as if no negative subpattern was * specified.) Another example is that the decimal separator and thousands * separator should be distinct characters, or parsing will be impossible. * ** The grouping separator is commonly used for thousands, but in some countries * it separates ten-thousands. The grouping size is a constant number of digits * between the grouping characters, such as 3 for 100,000,000 or 4 for * 1,0000,0000. If you supply a pattern with multiple grouping characters, the * interval between the last one and the end of the integer is the one that is * used. So
"#,##,###,####"
=="######,####"
== *"##,####,####"
. * *Special Pattern Characters
* ** Many characters in a pattern are taken literally; they are matched during * parsing and output unchanged during formatting. Special characters, on the * other hand, stand for other characters, strings, or classes of characters. * They must be quoted, unless noted otherwise, if they are to appear in the * prefix or suffix as literals. * *
* The characters listed here are used in non-localized patterns. Localized * patterns use the corresponding characters taken from this formatter's *
DecimalFormatSymbols
object instead, and these characters lose * their special status. Two exceptions are the currency sign and quote, which * are not localized. * *** **
** Symbol * Location * Localized? * Meaning * * 0
*Number * Yes * Digit * * #
*Number * Yes * Digit, zero shows as absent * * .
*Number * Yes * Decimal separator or monetary decimal separator * * -
*Number * Yes * Minus sign * * ,
*Number * Yes * Grouping separator * * E
*Number * Yes * Separates mantissa and exponent in scientific notation. * Need not be quoted in prefix or suffix. * * ;
*Subpattern boundary * Yes * Separates positive and negative subpatterns * * %
*Prefix or suffix * Yes * Multiply by 100 and show as percentage * * \u2030
*Prefix or suffix * Yes * Multiply by 1000 and show as per mille * * ¤
(\u00A4
) *Prefix or suffix * No * Currency sign, replaced by currency symbol. If doubled, replaced by * international currency symbol. If present in a pattern, the monetary decimal * separator is used instead of the decimal separator. * * '
*Prefix or suffix * No * Used to quote special characters in a prefix or suffix, for example, * "'#'#"
formats 123 to"#123"
. To create a single * quote itself, use two in a row:"# o''clock"
. *Scientific Notation
* ** Numbers in scientific notation are expressed as the product of a mantissa and * a power of ten, for example, 1234 can be expressed as 1.234 x 10^3. The * mantissa is often in the range 1.0 <= x < 10.0, but it need not be. *
DecimalFormat
can be instructed to format and parse scientific * notation only via a pattern; there is currently no factory method * that creates a scientific notation format. In a pattern, the exponent * character immediately followed by one or more digit characters indicates * scientific notation. Example:"0.###E0"
formats the number 1234 * as"1.234E3"
. * *
-
*
- The number of digit characters after the exponent character gives the
* minimum exponent digit count. There is no maximum. Negative exponents are
* formatted using the localized minus sign, not the prefix and suffix
* from the pattern. This allows patterns such as
"0.###E0 m/s"
. * * - The minimum and maximum number of integer digits are interpreted
* together:
*
*
-
*
- If the maximum number of integer digits is greater than their minimum
* number and greater than 1, it forces the exponent to be a multiple of the
* maximum number of integer digits, and the minimum number of integer digits to
* be interpreted as 1. The most common use of this is to generate
* engineering notation, in which the exponent is a multiple of three,
* e.g.,
"##0.#####E0"
. Using this pattern, the number 12345 * formats to"12.345E3"
, and 123456 formats to *"123.456E3"
. * * - Otherwise, the minimum number of integer digits is achieved by adjusting
* the exponent. Example: 0.00123 formatted with
"00.###E0"
yields *"12.3E-4"
. *
- If the maximum number of integer digits is greater than their minimum
* number and greater than 1, it forces the exponent to be a multiple of the
* maximum number of integer digits, and the minimum number of integer digits to
* be interpreted as 1. The most common use of this is to generate
* engineering notation, in which the exponent is a multiple of three,
* e.g.,
- The number of significant digits in the mantissa is the sum of the
* minimum integer and maximum fraction digits, and is
* unaffected by the maximum integer digits. For example, 12345 formatted with
*
"##0.##E0"
is"12.3E3"
. To show all digits, set the * significant digits count to zero. The number of significant digits does not * affect parsing. * * - Exponential patterns may not contain grouping separators. *
Rounding
* *DecimalFormat
uses half-even rounding (see
* {@link java.math.BigDecimal#ROUND_HALF_EVEN ROUND_HALF_EVEN}) for formatting.
*
* Digits
* * For formatting,DecimalFormat
uses the ten consecutive
* characters starting with the localized zero digit defined in the
* DecimalFormatSymbols
object as digits. For parsing, these digits
* as well as all Unicode decimal digits, as defined by {@link Character#digit
* Character.digit}, are recognized.
*
* Special Values
* *
* NaN
is formatted as a single character, typically
* \uFFFD
. This character is determined by the
* DecimalFormatSymbols
object. This is the only value for which
* the prefixes and suffixes are not used.
*
*
* Infinity is formatted as a single character, typically
* \u221E
, with the positive or negative prefixes and suffixes
* applied. The infinity character is determined by the
* DecimalFormatSymbols
object.
*
*
* Negative zero ("-0"
) parses to Double(-0.0)
, unless
* isParseIntegerOnly()
is true, in which case it parses to
* Long(0)
.
*
*
Synchronization
* ** Decimal formats are generally not synchronized. It is recommended to create * separate format instances for each thread. If multiple threads access a * format concurrently, it must be synchronized externally. * *
Example
* ** ** * @see Java * Tutorial * @see NumberFormat * @see DecimalFormatSymbols * @see ParsePosition * @author Mark Davis * @author Alan Liu * @author Werner Keil */ public class DecimalFormat extends NumberFormat { private String mPattern = "#,##0.###"; /** * Creates a DecimalFormat using the default pattern and symbols for the * default locale. This is a convenient way to obtain a DecimalFormat when * internationalization is not the main concern. ** // Print out a number using the localized number, integer, currency, * // and percent format for each locale * Locale[] locales = NumberFormat.getAvailableLocales(); * double myNumber = -1234.56; * NumberFormat form; * for (int j=0; j<4; ++j) { * System.out.println("FORMAT"); * for (int i = 0; i < locales.length; ++i) { * if (locales[i].getCountry().length() == 0) { * continue; // Skip language-only locales * } * System.out.print(locales[i].getDisplayName()); * switch (j) { * case 0: * form = NumberFormat.getInstance(locales[i]); break; * case 1: * form = NumberFormat.getIntegerInstance(locales[i]); break; * case 2: * form = NumberFormat.getCurrencyInstance(locales[i]); break; * default: * form = NumberFormat.getPercentInstance(locales[i]); break; * } * if (form instanceof DecimalFormat) { * System.out.print(": " + ((DecimalFormat) form).toPattern()); * } * System.out.print(" -> " + form.format(myNumber)); * try { * System.out.println(" -> " + form.parse(form.format(myNumber))); * } catch (ParseException e) {} * } * } ** *
* To obtain standard formats for a given locale, use the factory methods on * NumberFormat such as getNumberInstance. These factories will return the * most appropriate sub-class of NumberFormat for a given locale. * * @see NumberFormat#getInstance * @see NumberFormat#getNumberInstance * @see NumberFormat#getCurrencyInstance * @see NumberFormat#getPercentInstance */ public DecimalFormat() { // Always applyPattern after the symbols are set this.symbols = new DecimalFormatSymbols(); applyPattern(mPattern, false); } /** * Creates a DecimalFormat using the given pattern and the symbols for the * default locale. This is a convenient way to obtain a DecimalFormat when * internationalization is not the main concern. *
* To obtain standard formats for a given locale, use the factory methods on
* NumberFormat such as getNumberInstance. These factories will return the
* most appropriate sub-class of NumberFormat for a given locale.
*
* @param pattern
* A non-localized pattern string.
* @exception NullPointerException
* if pattern
is null
* @exception IllegalArgumentException
* if the given pattern is invalid.
* @see NumberFormat#getInstance
* @see NumberFormat#getNumberInstance
* @see NumberFormat#getCurrencyInstance
* @see NumberFormat#getPercentInstance
*/
public DecimalFormat(String pattern) {
// Always applyPattern after the symbols are set
this.symbols = new DecimalFormatSymbols();
applyPattern(pattern, false);
}
/**
* Creates a DecimalFormat using the given pattern and symbols. Use this
* constructor when you need to completely customize the behavior of the
* format.
*
* To obtain standard formats for a given locale, use the factory methods on
* NumberFormat such as getInstance or getCurrencyInstance. If you need only
* minor adjustments to a standard format, you can modify the format
* returned by a NumberFormat factory method.
*
* @param pattern
* a non-localized pattern string
* @param symbols
* the set of symbols to be used
* @exception NullPointerException
* if any of the given arguments is null
* @exception IllegalArgumentException
* if the given pattern is invalid
* @see NumberFormat#getInstance
* @see NumberFormat#getNumberInstance
* @see NumberFormat#getCurrencyInstance
* @see NumberFormat#getPercentInstance
* @see DecimalFormatSymbols
*/
DecimalFormat(String pattern, DecimalFormatSymbols symbols) {
// Always applyPattern after the symbols are set
this.symbols = (DecimalFormatSymbols) symbols.clone();
applyPattern(pattern, false);
}
// Overrides
/**
* Formats a double to produce a string.
*
* @param number
* The double to format
* @param result
* where the text is to be appended
* @param fieldPosition
* On input: an alignment field, if desired. On output: the
* offsets of the alignment field.
* @return The formatted number string
* @see FieldPosition
*/
public StringBuffer format(double number, StringBuffer result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, result, fieldPosition.getFieldDelegate());
}
/**
* Formats a double to produce a string.
*
* @param number
* The double to format
* @param result
* where the text is to be appended
* @param delegate
* notified of locations of sub fields
* @return The formatted number string
*/
private StringBuffer format(double number, StringBuffer result,
FieldDelegate delegate) {
if (Double.isNaN(number)) {
int iFieldStart = result.length();
result.append(symbols.getNaN());
delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
iFieldStart, result.length(), result);
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.
*/
boolean isNegative = (number < 0.0)
|| (number == 0.0 && 1 / number < 0.0);
if (isNegative)
number = -number;
// Do this BEFORE checking to see if value is infinite!
if (multiplier != 1)
number *= multiplier;
if (Double.isInfinite(number)) {
if (isNegative) {
append(result, negativePrefix, delegate,
getNegativePrefixFieldPositions(), Field.SIGN);
} else {
append(result, positivePrefix, delegate,
getPositivePrefixFieldPositions(), Field.SIGN);
}
int iFieldStart = result.length();
result.append(symbols.getInfinity());
delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
iFieldStart, result.length(), result);
if (isNegative) {
append(result, negativeSuffix, delegate,
getNegativeSuffixFieldPositions(), Field.SIGN);
} else {
append(result, positiveSuffix, delegate,
getPositiveSuffixFieldPositions(), Field.SIGN);
}
return result;
}
// At this point we are guaranteed a nonnegative finite
// number.
synchronized (digitList) {
digitList.set(number,
useExponentialNotation ? getMaximumIntegerDigits()
+ getMaximumFractionDigits()
: getMaximumFractionDigits(),
!useExponentialNotation);
return subformat(result, delegate, isNegative, false);
}
}
/**
* Format a long to produce a string.
*
* @param number
* The long to format
* @param result
* where the text is to be appended
* @param fieldPosition
* On input: an alignment field, if desired. On output: the
* offsets of the alignment field.
* @return The formatted number string
* @see FieldPosition
*/
public StringBuffer format(long number, StringBuffer result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, result, fieldPosition.getFieldDelegate());
}
/**
* Format a long to produce a string.
*
* @param number
* The long to format
* @param result
* where the text is to be appended
* @param delegate
* notified of locations of sub fields
* @return The formatted number string
* @see FieldPosition
*/
private StringBuffer format(long number, StringBuffer result,
FieldDelegate delegate) {
boolean isNegative = (number < 0);
if (isNegative)
number = -number;
// 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 (multiplier != 1 && multiplier != 0) {
boolean useDouble = false;
if (number < 0) // This can only happen if number == Long.MIN_VALUE
{
long cutoff = Long.MIN_VALUE / multiplier;
useDouble = (number < cutoff);
} else {
long cutoff = Long.MAX_VALUE / multiplier;
useDouble = (number > cutoff);
}
if (useDouble) {
double dnumber = (double) (isNegative ? -number : number);
return format(dnumber, result, delegate);
}
}
number *= multiplier;
synchronized (digitList) {
digitList.set(number,
useExponentialNotation ? getMaximumIntegerDigits()
+ getMaximumFractionDigits() : 0);
return subformat(result, delegate, isNegative, true);
}
}
/**
* Formats an Object producing an AttributedCharacterIterator
.
* You can use the returned AttributedCharacterIterator
to
* build the resulting String, as well as to determine information about the
* resulting String.
*
* Each attribute key of the AttributedCharacterIterator will be of type
* NumberFormat.Field
, with the attribute value being the same
* as the attribute key.
*
* @exception NullPointerException
* if obj is null.
* @exception IllegalArgumentException
* when the Format cannot format the given object.
* @param obj
* The object to format
* @return AttributedCharacterIterator describing the formatted value.
*/
AttributedCharacterIterator formatToCharacterIterator(Object obj) {
CharacterIteratorFieldDelegate delegate = new CharacterIteratorFieldDelegate();
StringBuffer sb = new StringBuffer();
if (obj instanceof Long) {
format(((Long) obj).longValue(), sb, delegate);
} else if (obj == null) {
throw new NullPointerException(
"formatToCharacterIterator must be passed non-null object");
} else if (obj instanceof Double) {
format(((Double) obj).doubleValue(), sb, delegate);
} else {
throw new IllegalArgumentException(
"Cannot format given Object as a Number");
}
return delegate.getIterator(sb.toString());
}
/**
* Complete the formatting of a finite number. On entry, the digitList must
* be filled in with the correct digits.
*/
private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
boolean isNegative, boolean isInteger) {
// NOTE: This isn't required anymore because DigitList takes care of
// this.
//
// // The negative of the exponent represents the number of leading
// // zeros between the decimal and the first non-zero digit, for
// // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this
// // is more than the maximum fraction digits, then we have an
// underflow
// // for the printed representation. We recognize this here and set
// // the DigitList representation to zero in this situation.
//
// if (-digitList.decimalAt >= getMaximumFractionDigits())
// {
// digitList.count = 0;
// }
char zero = symbols.getZeroDigit();
int zeroDelta = zero - '0'; // '0' is the DigitList representation of
// zero
char grouping = symbols.getGroupingSeparator();
char decimal = isCurrencyFormat ? symbols.getMonetaryDecimalSeparator()
: symbols.getDecimalSeparator();
/*
* 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 (digitList.isZero()) {
digitList.decimalAt = 0; // Normalize
}
int fieldStart = result.length();
if (isNegative) {
append(result, negativePrefix, delegate,
getNegativePrefixFieldPositions(), Field.SIGN);
} else {
append(result, positivePrefix, delegate,
getPositivePrefixFieldPositions(), Field.SIGN);
}
if (useExponentialNotation) {
int iFieldStart = result.length();
int iFieldEnd = -1;
int fFieldStart = -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 > 1 and are larger than
// minimum integer digits, then minimum integer digits are
// ignored.
int exponent = digitList.decimalAt;
int repeat = getMaximumIntegerDigits();
int minimumIntegerDigits = getMinimumIntegerDigits();
if (repeat > 1 && repeat > minimumIntegerDigits) {
// A repeating range is defined; adjust to it as follows.
// If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
// -3,-4,-5=>-6, etc. This takes into account that the
// exponent we have here is off by one from what we expect;
// it is for the format 0.MMMMMx10^n.
if (exponent >= 1) {
exponent = ((exponent - 1) / repeat) * repeat;
} else {
// integer division rounds towards 0
exponent = ((exponent - repeat) / repeat) * repeat;
}
minimumIntegerDigits = 1;
} else {
// No repeating range is defined; use minimum integer digits.
exponent -= minimumIntegerDigits;
}
// 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.
int minimumDigits = getMinimumIntegerDigits()
+ getMinimumFractionDigits();
// The number of integer digits is handled specially if the number
// is zero, since then there may be no digits.
int integerDigits = digitList.isZero() ? minimumIntegerDigits
: digitList.decimalAt - exponent;
if (minimumDigits < integerDigits) {
minimumDigits = integerDigits;
}
int totalDigits = digitList.count;
if (minimumDigits > totalDigits)
totalDigits = minimumDigits;
boolean addedDecimalSeparator = false;
for (int i = 0; i < totalDigits; ++i) {
if (i == integerDigits) {
// Record field information for caller.
iFieldEnd = result.length();
result.append(decimal);
addedDecimalSeparator = true;
// Record field information for caller.
fFieldStart = result.length();
}
result.append((i < digitList.count) ? (char) (digitList.digits[i] + zeroDelta)
: zero);
}
// Record field information
if (iFieldEnd == -1) {
iFieldEnd = result.length();
}
delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
iFieldStart, iFieldEnd, result);
if (addedDecimalSeparator) {
delegate.formatted(Field.DECIMAL_SEPARATOR,
Field.DECIMAL_SEPARATOR, iFieldEnd, fFieldStart, result);
}
if (fFieldStart == -1) {
fFieldStart = result.length();
}
delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
fFieldStart, result.length(), result);
// The exponent is output using the pattern-specified minimum
// exponent digits. There is no maximum limit to the exponent
// digits, since truncating the exponent would result in an
// unacceptable inaccuracy.
fieldStart = result.length();
result.append(symbols.getExponentialSymbol());
delegate.formatted(Field.EXPONENT_SYMBOL, Field.EXPONENT_SYMBOL,
fieldStart, result.length(), result);
// 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 (digitList.isZero())
exponent = 0;
boolean negativeExponent = exponent < 0;
if (negativeExponent) {
exponent = -exponent;
append(result, negativePrefix, delegate,
getNegativePrefixFieldPositions(), Field.EXPONENT_SIGN);
} else {
append(result, positivePrefix, delegate,
getPositivePrefixFieldPositions(), Field.EXPONENT_SIGN);
}
digitList.set(exponent);
int eFieldStart = result.length();
for (int i = digitList.decimalAt; i < minExponentDigits; ++i)
result.append(zero);
for (int i = 0; i < digitList.decimalAt; ++i) {
result.append((i < digitList.count) ? (char) (digitList.digits[i] + zeroDelta)
: zero);
}
delegate.formatted(Field.EXPONENT, Field.EXPONENT, eFieldStart,
result.length(), result);
fieldStart = result.length();
if (negativeExponent) {
append(result, negativeSuffix, delegate,
getNegativeSuffixFieldPositions(), Field.EXPONENT_SIGN);
} else {
append(result, positiveSuffix, delegate,
getPositiveSuffixFieldPositions(), Field.EXPONENT_SIGN);
}
} else {
int iFieldStart = result.length();
// Output the integer portion. Here 'count' is the total
// number of integer digits we will display, including both
// leading zeros required to satisfy getMinimumIntegerDigits,
// and actual digits present in the number.
int count = getMinimumIntegerDigits();
int digitIndex = 0; // Index into digitList.fDigits[]
if (digitList.decimalAt > 0 && count < digitList.decimalAt)
count = digitList.decimalAt;
// 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 > getMaximumIntegerDigits()) {
count = getMaximumIntegerDigits();
digitIndex = digitList.decimalAt - count;
}
int sizeBeforeIntegerPart = result.length();
for (int i = count - 1; i >= 0; --i) {
if (i < digitList.decimalAt && digitIndex < digitList.count) {
// Output a real digit
result.append((char) (digitList.digits[digitIndex++] + zeroDelta));
} else {
// Output a leading zero
result.append(zero);
}
// Output grouping separator if necessary. Don't output a
// grouping separator if i==0 though; that's at the end of
// the integer part.
if (isGroupingUsed() && i > 0 && (groupingSize != 0)
&& (i % groupingSize == 0)) {
int gStart = result.length();
result.append(grouping);
delegate.formatted(Field.GROUPING_SEPARATOR,
Field.GROUPING_SEPARATOR, gStart, result.length(),
result);
}
}
// Determine whether or not there are any printable fractional
// digits. If we've used up the digits we know there aren't.
boolean fractionPresent = (getMinimumFractionDigits() > 0)
|| (!isInteger && digitIndex < digitList.count);
// 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.append(zero);
}
delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
iFieldStart, result.length(), result);
// Output the decimal separator if we always do so.
int sStart = result.length();
if (decimalSeparatorAlwaysShown || fractionPresent)
result.append(decimal);
if (sStart != result.length()) {
delegate.formatted(Field.DECIMAL_SEPARATOR,
Field.DECIMAL_SEPARATOR, sStart, result.length(),
result);
}
int fFieldStart = result.length();
for (int i = 0; i < getMaximumFractionDigits(); ++i) {
// 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()
&& (isInteger || digitIndex >= digitList.count))
break;
// 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.
if (-1 - i > (digitList.decimalAt - 1)) {
result.append(zero);
continue;
}
// Output a digit, if we have any precision left, or a
// zero if we don't. We don't want to output noise digits.
if (!isInteger && digitIndex < digitList.count) {
result.append((char) (digitList.digits[digitIndex++] + zeroDelta));
} else {
result.append(zero);
}
}
// Record field information for caller.
delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
fFieldStart, result.length(), result);
}
if (isNegative) {
append(result, negativeSuffix, delegate,
getNegativeSuffixFieldPositions(), Field.SIGN);
} else {
append(result, positiveSuffix, delegate,
getPositiveSuffixFieldPositions(), Field.SIGN);
}
return result;
}
/**
* Appends the String string
to result
.
* delegate
is notified of all the FieldPosition
s
* in positions
.
*
* If one of the FieldPosition
s in positions
* identifies a SIGN
attribute, it is mapped to
* signAttribute
. This is used to map the SIGN
* attribute to the EXPONENT
attribute as necessary.
*
* This is used by subformat
to add the prefix/suffix.
*/
private void append(StringBuffer result, String string,
FieldDelegate delegate, FieldPosition[] positions,
Format.Field signAttribute) {
int start = result.length();
if (string.length() > 0) {
result.append(string);
for (int counter = 0, max = positions.length; counter < max; counter++) {
FieldPosition fp = positions[counter];
Format.Field attribute = fp.getFieldAttribute();
if (attribute == Field.SIGN) {
attribute = signAttribute;
}
delegate.formatted(attribute, attribute,
start + fp.getBeginIndex(), start + fp.getEndIndex(),
result);
}
}
}
/**
* Parses text from a string to produce a Number
.
*
* The method attempts to parse text starting at the index given by
* pos
. If parsing succeeds, then the index of pos
* is updated to the index after the last character used (parsing does not
* necessarily use all characters up to the end of the string), and the
* parsed number is returned. The updated pos
can be used to
* indicate the starting point for the next call to this method. If an error
* occurs, then the index of pos
is not changed, the error
* index of pos
is set to the index of the character where the
* error occurred, and null is returned.
*
* The most economical subclass that can represent the number given by the
* string is chosen. Most integer values are returned as Long
* objects, no matter how they are written: "17"
and
* "17.000"
both parse to Long(17)
. Values that
* cannot fit into a Long
are returned as Double
s.
* This includes values with a fractional part, infinite values,
* NaN
, and the value -0.0. DecimalFormat
does
* not decide whether to return a Double
or a
* Long
based on the presence of a decimal separator in the
* source string. Doing so would prevent integers that overflow the mantissa
* of a double, such as "10,000,000,000,000,000.00"
, from being
* parsed accurately. Currently, the only classes that parse
* returns are Long
and Double
, but callers should
* not rely on this. Callers may use the Number
methods
* doubleValue
, longValue
, etc., to obtain the
* type they want.
*
* DecimalFormat
parses all Unicode characters that represent
* decimal digits, as defined by Character.digit()
. In
* addition, DecimalFormat
also recognizes as digits the ten
* consecutive characters starting with the localized zero digit defined in
* the DecimalFormatSymbols
object.
*
* @param text
* the string to be parsed
* @param pos
* A ParsePosition
object with index and error index
* information as described above.
* @return the parsed value, or null
if the parse fails
* @exception NullPointerException
* if text
or pos
is null.
*/
// public Number parse(String text, ParsePosition pos) {
// // special case NaN
// if (text.regionMatches(pos.index, symbols.getNaN(), 0, symbols
// .getNaN().length())) {
// pos.index = pos.index + symbols.getNaN().length();
// return new Double(Double.NaN);
// }
//
// boolean[] status = new boolean[STATUS_LENGTH];
//
// if (!subparse(text, pos, digitList, false, status))
// return null;
//
// double doubleResult = 0.0;
// long longResult = 0;
// boolean gotDouble = true;
//
// // Finally, have DigitList parse the digits into a value.
// if (status[STATUS_INFINITE]) {
// doubleResult = Double.POSITIVE_INFINITY;
// } else if (digitList.fitsIntoLong(status[STATUS_POSITIVE],
// isParseIntegerOnly())) {
// gotDouble = false;
// longResult = digitList.getLong();
// } else
// doubleResult = digitList.getDouble();
//
// // Divide by multiplier. We have to be careful here not to do unneeded
// // conversions between double and long.
// if (multiplier != 1) {
// if (gotDouble)
// doubleResult /= multiplier;
// else {
// // Avoid converting to double if we can
// if (longResult % multiplier == 0) {
// longResult /= multiplier;
// } else {
// doubleResult = ((double) longResult) / multiplier;
// if (doubleResult < 0)
// doubleResult = -doubleResult;
// gotDouble = true;
// }
// }
// }
//
// if (!status[STATUS_POSITIVE]) {
// doubleResult = -doubleResult;
// // If longResult was Long.MIN_VALUE or a divisor of it (if
// // multiplier != 1) then don't negate it.
// if (longResult > 0) {
// longResult = -longResult;
// }
// }
//
// // At this point, if we divided the result by the multiplier, the result
// may
// // fit into a long. We check for this case and return a long if possible.
// // We must do this AFTER applying the negative (if appropriate) in order
// to
// // handle the case of LONG_MIN; otherwise, if we do this with a positive
// value
// // -LONG_MIN, the double is > 0, but the long is < 0. This is a
// C++-specific
// // situation. We also must retain a double in the case of -0.0, which
// will
// // compare as == to a long 0 cast to a double (bug 4162852).
// if (multiplier != 1 && gotDouble) {
// longResult = (long) doubleResult;
// gotDouble = (doubleResult != (double) longResult)
// || (doubleResult == 0.0 && !status[STATUS_POSITIVE] &&
// !isParseIntegerOnly());
// }
//
// return gotDouble ? (Number) new Double(doubleResult)
// : (Number) new Long(longResult);
// }
private static final int STATUS_INFINITE = 0;
private static final int STATUS_POSITIVE = 1;
private static final int STATUS_LENGTH = 2;
/**
* 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.
* @param status
* Upon return contains boolean status flags indicating whether
* the value was infinite and whether it was positive.
*/
private final boolean subparse(String text, ParsePosition parsePosition,
DigitList digits, boolean isExponent, boolean status[]) {
int position = parsePosition.index;
int oldStart = parsePosition.index;
int backup;
// check for positivePrefix; take longest
// TODO move to StrUtils
String str1 = text.substring(position,
position + positivePrefix.length());
String str2 = positivePrefix.substring(0, 0 + positivePrefix.length());
boolean gotPositive = str1.equalsIgnoreCase(str2);
str1 = text.substring(position, position + negativePrefix.length());
str2 = positivePrefix.substring(0, 0 + negativePrefix.length());
boolean gotNegative = str1.equalsIgnoreCase(str2);
if (gotPositive && gotNegative) {
if (positivePrefix.length() > negativePrefix.length())
gotNegative = false;
else if (positivePrefix.length() < negativePrefix.length())
gotPositive = false;
}
if (gotPositive) {
position += positivePrefix.length();
} else if (gotNegative) {
position += negativePrefix.length();
} else {
parsePosition.errorIndex = position;
return false;
}
// process digits or Inf, find decimal position
status[STATUS_INFINITE] = false;
str1 = text.substring(position, position
+ symbols.getInfinity().length());
str2 = positivePrefix.substring(0, 0 + symbols.getInfinity().length());
boolean gotInfinity = str1.equalsIgnoreCase(str2);
if (!isExponent && gotInfinity) {
position += symbols.getInfinity().length();
status[STATUS_INFINITE] = true;
} else {
// 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.decimalAt = digits.count = 0;
char zero = symbols.getZeroDigit();
char decimal = isCurrencyFormat ? symbols
.getMonetaryDecimalSeparator() : symbols
.getDecimalSeparator();
char grouping = symbols.getGroupingSeparator();
char exponentChar = symbols.getExponentialSymbol();
boolean sawDecimal = false;
boolean sawExponent = false;
boolean sawDigit = false;
int exponent = 0; // Set to the exponent value, if any
// We have to track digitCount ourselves, because digits.count will
// pin when the maximum allowable digits is reached.
int digitCount = 0;
backup = -1;
for (; position < text.length(); ++position) {
char ch = text.charAt(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.
*/
int digit = ch - zero;
if (digit < 0 || digit > 9)
digit = Character.digit(ch, 10);
if (digit == 0) {
// Cancel out backup setting (see grouping handler below)
backup = -1; // Do this BEFORE continue statement below!!!
sawDigit = true;
// Handle leading zeros
if (digits.count == 0) {
// Ignore leading zeros in integer part of number.
if (!sawDecimal)
continue;
// If we have seen the decimal, but no significant
// digits yet,
// then we account for leading zeros by decrementing the
// digits.decimalAt into negative values.
--digits.decimalAt;
} else {
++digitCount;
digits.append((char) (digit + '0'));
}
} else if (digit > 0 && digit <= 9) // [sic] digit==0 handled
// above
{
sawDigit = true;
++digitCount;
digits.append((char) (digit + '0'));
// Cancel out backup setting (see grouping handler below)
backup = -1;
} else if (!isExponent && ch == decimal) {
// If we're only parsing integers, or if we ALREADY saw the
// decimal, then don't parse this one.
if (isParseIntegerOnly() || sawDecimal)
break;
digits.decimalAt = digitCount; // Not digits.count!
sawDecimal = true;
} else if (!isExponent && ch == grouping && isGroupingUsed()) {
if (sawDecimal) {
break;
}
// 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 (!isExponent && ch == exponentChar && !sawExponent) {
// Process the exponent by recursively calling this method.
ParsePosition pos = new ParsePosition(position + 1);
boolean[] stat = new boolean[STATUS_LENGTH];
DigitList exponentDigits = new DigitList();
if (subparse(text, pos, exponentDigits, true, stat)
&& exponentDigits.fitsIntoLong(
stat[STATUS_POSITIVE], true)) {
position = pos.index; // Advance past the exponent
exponent = (int) exponentDigits.getLong();
if (!stat[STATUS_POSITIVE])
exponent = -exponent;
sawExponent = true;
}
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.decimalAt = digitCount; // Not digits.count!
// Adjust for exponent, if any
digits.decimalAt += exponent;
// 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.index = oldStart;
parsePosition.errorIndex = oldStart;
return false;
}
}
// check for positiveSuffix
if (gotPositive) {
str1 = text.substring(position, position + positivePrefix.length());
str2 = positivePrefix.substring(0, 0 + positivePrefix.length());
gotPositive = str1.equalsIgnoreCase(str2);
}
if (gotNegative) {
str1 = text.substring(position, position + negativeSuffix.length());
str2 = positivePrefix.substring(0, 0 + negativeSuffix.length());
gotPositive = str1.equalsIgnoreCase(str2);
}
// if both match, take longest
if (gotPositive && gotNegative) {
if (positiveSuffix.length() > negativeSuffix.length())
gotNegative = false;
else if (positiveSuffix.length() < negativeSuffix.length())
gotPositive = false;
}
// fail if neither or both
if (gotPositive == gotNegative) {
parsePosition.errorIndex = position;
return false;
}
parsePosition.index = position
+ (gotPositive ? positiveSuffix.length() : negativeSuffix
.length()); // mark success!
status[STATUS_POSITIVE] = gotPositive;
if (parsePosition.index == oldStart) {
parsePosition.errorIndex = position;
return false;
}
return true;
}
/**
* Returns the decimal format symbols, which is generally not changed by the
* programmer or user.
*
* @return desired DecimalFormatSymbols
* @see DecimalFormatSymbols
*/
public DecimalFormatSymbols getDecimalFormatSymbols() {
try {
// don't allow multiple references
return (DecimalFormatSymbols) symbols.clone();
} catch (Exception foo) {
return null; // should never happen
}
}
/**
* Sets the decimal format symbols, which is generally not changed by the
* programmer or user.
*
* @param newSymbols
* desired DecimalFormatSymbols
* @see DecimalFormatSymbols
*/
public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
try {
// don't allow multiple references
symbols = (DecimalFormatSymbols) newSymbols.clone();
expandAffixes();
} catch (Exception foo) {
// should never happen
}
}
/**
* Get the positive prefix.
*
* Examples: +123, $123, sFr123 */ public String getPositivePrefix() { return positivePrefix; } /** * Set the positive prefix. *
* Examples: +123, $123, sFr123
*/
public void setPositivePrefix(String newValue) {
positivePrefix = newValue;
posPrefixPattern = null;
positivePrefixFieldPositions = null;
}
/**
* Returns the FieldPositions of the fields in the prefix used for positive
* numbers. This is not used if the user has explicitly set a positive
* prefix via setPositivePrefix
. This is lazily created.
*
* @return FieldPositions in positive prefix
*/
private FieldPosition[] getPositivePrefixFieldPositions() {
if (positivePrefixFieldPositions == null) {
if (posPrefixPattern != null) {
positivePrefixFieldPositions = expandAffix(posPrefixPattern);
} else {
positivePrefixFieldPositions = EmptyFieldPositionArray;
}
}
return positivePrefixFieldPositions;
}
/**
* Get the negative prefix.
*
* Examples: -123, ($123) (with negative suffix), sFr-123 */ public String getNegativePrefix() { return negativePrefix; } /** * Set the negative prefix. *
* Examples: -123, ($123) (with negative suffix), sFr-123
*/
public void setNegativePrefix(String newValue) {
negativePrefix = newValue;
negPrefixPattern = null;
}
/**
* Returns the FieldPositions of the fields in the prefix used for negative
* numbers. This is not used if the user has explicitly set a negative
* prefix via setNegativePrefix
. This is lazily created.
*
* @return FieldPositions in positive prefix
*/
private FieldPosition[] getNegativePrefixFieldPositions() {
if (negativePrefixFieldPositions == null) {
if (negPrefixPattern != null) {
negativePrefixFieldPositions = expandAffix(negPrefixPattern);
} else {
negativePrefixFieldPositions = EmptyFieldPositionArray;
}
}
return negativePrefixFieldPositions;
}
/**
* Get the positive suffix.
*
* Example: 123% */ public String getPositiveSuffix() { return positiveSuffix; } /** * Set the positive suffix. *
* Example: 123%
*/
public void setPositiveSuffix(String newValue) {
positiveSuffix = newValue;
posSuffixPattern = null;
}
/**
* Returns the FieldPositions of the fields in the suffix used for positive
* numbers. This is not used if the user has explicitly set a positive
* suffix via setPositiveSuffix
. This is lazily created.
*
* @return FieldPositions in positive prefix
*/
private FieldPosition[] getPositiveSuffixFieldPositions() {
if (positiveSuffixFieldPositions == null) {
if (posSuffixPattern != null) {
positiveSuffixFieldPositions = expandAffix(posSuffixPattern);
} else {
positiveSuffixFieldPositions = EmptyFieldPositionArray;
}
}
return positiveSuffixFieldPositions;
}
/**
* Get the negative suffix.
*
* Examples: -123%, ($123) (with positive suffixes) */ public String getNegativeSuffix() { return negativeSuffix; } /** * Set the positive suffix. *
* Examples: 123%
*/
public void setNegativeSuffix(String newValue) {
negativeSuffix = newValue;
negSuffixPattern = null;
}
/**
* Returns the FieldPositions of the fields in the suffix used for negative
* numbers. This is not used if the user has explicitly set a negative
* suffix via setNegativeSuffix
. This is lazily created.
*
* @return FieldPositions in positive prefix
*/
private FieldPosition[] getNegativeSuffixFieldPositions() {
if (negativeSuffixFieldPositions == null) {
if (negSuffixPattern != null) {
negativeSuffixFieldPositions = expandAffix(negSuffixPattern);
} else {
negativeSuffixFieldPositions = EmptyFieldPositionArray;
}
}
return negativeSuffixFieldPositions;
}
/**
* Get the multiplier for use in percent, permill, etc. For a percentage,
* set the suffixes to have "%" and the multiplier to be 100. (For Arabic,
* use arabic percent symbol). For a permill, set the suffixes to have
* "\u2031" and the multiplier to be 1000.
*
* Examples: with 100, 1.23 -> "123", and "123" -> 1.23 */ public int getMultiplier() { return multiplier; } /** * Set the multiplier for use in percent, permill, etc. For a percentage, * set the suffixes to have "%" and the multiplier to be 100. (For Arabic, * use arabic percent symbol). For a permill, set the suffixes to have * "\u2031" and the multiplier to be 1000. *
* Examples: with 100, 1.23 -> "123", and "123" -> 1.23 */ public void setMultiplier(int newValue) { multiplier = newValue; } /** * Return the grouping size. Grouping size is the number of digits between * grouping separators in the integer portion of a number. For example, in * the number "123,456.78", the grouping size is 3. * * @see #setGroupingSize * @see NumberFormat#isGroupingUsed * @see DecimalFormatSymbols#getGroupingSeparator */ int getGroupingSize() { return groupingSize; } /** * Set the grouping size. Grouping size is the number of digits between * grouping separators in the integer portion of a number. For example, in * the number "123,456.78", the grouping size is 3. * * @see #getGroupingSize * @see NumberFormat#setGroupingUsed * @see DecimalFormatSymbols#setGroupingSeparator */ public void setGroupingSize(int newValue) { groupingSize = (byte) newValue; } /** * Allows you to get the behavior of the decimal separator with integers. * (The decimal separator will always appear with decimals.) *
* Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 */ public boolean isDecimalSeparatorAlwaysShown() { return decimalSeparatorAlwaysShown; } /** * Allows you to set the behavior of the decimal separator with integers. * (The decimal separator will always appear with decimals.) *
* Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 */ public void setDecimalSeparatorAlwaysShown(boolean newValue) { decimalSeparatorAlwaysShown = newValue; } /** * Overrides equals */ public boolean equals(Object obj) { if (obj == null) return false; if (!super.equals(obj)) return false; // super does class check DecimalFormat other = (DecimalFormat) obj; return ((posPrefixPattern == other.posPrefixPattern && positivePrefix .equals(other.positivePrefix)) || (posPrefixPattern != null && posPrefixPattern .equals(other.posPrefixPattern))) && ((posSuffixPattern == other.posSuffixPattern && positiveSuffix .equals(other.positiveSuffix)) || (posSuffixPattern != null && posSuffixPattern .equals(other.posSuffixPattern))) && ((negPrefixPattern == other.negPrefixPattern && negativePrefix .equals(other.negativePrefix)) || (negPrefixPattern != null && negPrefixPattern .equals(other.negPrefixPattern))) && ((negSuffixPattern == other.negSuffixPattern && negativeSuffix .equals(other.negativeSuffix)) || (negSuffixPattern != null && negSuffixPattern .equals(other.negSuffixPattern))) && multiplier == other.multiplier && groupingSize == other.groupingSize && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown && useExponentialNotation == other.useExponentialNotation && (!useExponentialNotation || minExponentDigits == other.minExponentDigits) && symbols.equals(other.symbols); } /** * Overrides hashCode */ public int hashCode() { return super.hashCode() * 37 + positivePrefix.hashCode(); // just enough fields for a reasonable distribution } /** * Synthesizes a pattern string that represents the current state of this * Format object. * * @see #applyPattern */ public String toPattern() { return toPattern(false); } /** * Synthesizes a localized pattern string that represents the current state * of this Format object. * * @see #applyPattern */ String toLocalizedPattern() { return toPattern(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. */ private void expandAffixes() { // Reuse one StringBuffer for better performance final StringBuffer buffer = new StringBuffer(); if (posPrefixPattern != null) { positivePrefix = expandAffix(posPrefixPattern, buffer); positivePrefixFieldPositions = null; } if (posSuffixPattern != null) { positiveSuffix = expandAffix(posSuffixPattern, buffer); positiveSuffixFieldPositions = null; } if (negPrefixPattern != null) { negativePrefix = expandAffix(negPrefixPattern, buffer); negativePrefixFieldPositions = null; } if (negSuffixPattern != null) { negativeSuffix = expandAffix(negSuffixPattern, buffer); negativeSuffixFieldPositions = null; } } /** * Expand an affix pattern into an affix string. All characters in the * pattern are literal unless prefixed by QUOTE. The following characters * after QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, * PATTERN_MINUS, and CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE + * CURRENCY_SIGN + CURRENCY_SIGN), it is interpreted as an ISO 4217 currency * code. Any other character after a QUOTE represents itself. QUOTE must be * followed by another character; QUOTE may not occur by itself at the end * of the pattern. * * @param pattern * the non-null, possibly empty pattern * @param buffer * a scratch StringBuffer; its contents will be lost * @return the expanded equivalent of pattern */ private String expandAffix(String pattern, StringBuffer buffer) { buffer.setLength(0); for (int i = 0; i < pattern.length();) { char c = pattern.charAt(i++); if (c == QUOTE) { c = pattern.charAt(i++); switch (c) { case CURRENCY_SIGN: buffer.append(symbols.getCurrencySymbol()); continue; case PATTERN_PERCENT: c = symbols.getPercent(); break; case PATTERN_PER_MILLE: c = symbols.getPerMill(); break; case PATTERN_MINUS: c = symbols.getMinusSign(); break; } } buffer.append(c); } return buffer.toString(); } /** * Expand an affix pattern into an array of FieldPositions describing how * the pattern would be expanded. All characters in the pattern are literal * unless prefixed by QUOTE. The following characters after QUOTE are * recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, PATTERN_MINUS, and * CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE + CURRENCY_SIGN + * CURRENCY_SIGN), it is interpreted as an ISO 4217 currency code. Any other * character after a QUOTE represents itself. QUOTE must be followed by * another character; QUOTE may not occur by itself at the end of the * pattern. * * @param pattern * the non-null, possibly empty pattern * @return FieldPosition array of the resulting fields. */ private FieldPosition[] expandAffix(String pattern) { Vector positions = null; int stringIndex = 0; for (int i = 0; i < pattern.length();) { char c = pattern.charAt(i++); if (c == QUOTE) { int field = -1; Format.Field fieldID = null; c = pattern.charAt(i++); switch (c) { case CURRENCY_SIGN: String string = symbols.getCurrencySymbol(); if (string.length() > 0) { if (positions == null) { positions = new Vector(2); } FieldPosition fp = new FieldPosition(Field.CURRENCY); fp.setBeginIndex(stringIndex); fp.setEndIndex(stringIndex + string.length()); positions.addElement(fp); stringIndex += string.length(); } continue; case PATTERN_PERCENT: c = symbols.getPercent(); field = -1; fieldID = Field.PERCENT; break; case PATTERN_PER_MILLE: c = symbols.getPerMill(); field = -1; fieldID = Field.PERMILLE; break; case PATTERN_MINUS: c = symbols.getMinusSign(); field = -1; fieldID = Field.SIGN; break; } if (fieldID != null) { if (positions == null) { positions = new Vector(2); } FieldPosition fp = new FieldPosition(fieldID, field); fp.setBeginIndex(stringIndex); fp.setEndIndex(stringIndex + 1); positions.addElement(fp); } } stringIndex++; } if (positions != null) { EmptyFieldPositionArray = new FieldPosition[positions.size()]; positions.copyInto(EmptyFieldPositionArray); } return EmptyFieldPositionArray; } /** * 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 posPrefixPattern; may be null * @param expAffix * a corresponding expanded affix, such as positivePrefix. * 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 */ private void appendAffix(StringBuffer buffer, String affixPattern, String expAffix, boolean localized) { if (affixPattern == null) { appendAffix(buffer, expAffix, localized); } else { int i; for (int pos = 0; pos < affixPattern.length(); pos = i) { i = affixPattern.indexOf(QUOTE, pos); if (i < 0) { appendAffix(buffer, affixPattern.substring(pos), localized); break; } if (i > pos) { appendAffix(buffer, affixPattern.substring(pos, i), localized); } char c = affixPattern.charAt(++i); ++i; if (c == QUOTE) { buffer.append(c); // Fall through and append another QUOTE below } else if (c == CURRENCY_SIGN && i < affixPattern.length() && affixPattern.charAt(i) == CURRENCY_SIGN) { ++i; buffer.append(c); // Fall through and append another CURRENCY_SIGN below } else if (localized) { switch (c) { case PATTERN_PERCENT: c = symbols.getPercent(); break; case PATTERN_PER_MILLE: c = symbols.getPerMill(); break; case PATTERN_MINUS: c = symbols.getMinusSign(); break; } } 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. */ private void appendAffix(StringBuffer buffer, String affix, boolean localized) { boolean needQuote; if (localized) { needQuote = affix.indexOf(symbols.getZeroDigit()) >= 0 || affix.indexOf(symbols.getGroupingSeparator()) >= 0 || affix.indexOf(symbols.getDecimalSeparator()) >= 0 || affix.indexOf(symbols.getPercent()) >= 0 || affix.indexOf(symbols.getPerMill()) >= 0 || affix.indexOf(symbols.getDigit()) >= 0 || affix.indexOf(symbols.getPatternSeparator()) >= 0 || affix.indexOf(symbols.getMinusSign()) >= 0 || affix.indexOf(CURRENCY_SIGN) >= 0; } else { needQuote = affix.indexOf(PATTERN_ZERO_DIGIT) >= 0 || affix.indexOf(PATTERN_GROUPING_SEPARATOR) >= 0 || affix.indexOf(PATTERN_DECIMAL_SEPARATOR) >= 0 || affix.indexOf(PATTERN_PERCENT) >= 0 || affix.indexOf(PATTERN_PER_MILLE) >= 0 || affix.indexOf(PATTERN_DIGIT) >= 0 || affix.indexOf(PATTERN_SEPARATOR) >= 0 || affix.indexOf(PATTERN_MINUS) >= 0 || affix.indexOf(CURRENCY_SIGN) >= 0; } if (needQuote) buffer.append('\''); if (affix.indexOf('\'') < 0) buffer.append(affix); else { for (int j = 0; j < affix.length(); ++j) { char c = affix.charAt(j); buffer.append(c); if (c == '\'') buffer.append(c); } } if (needQuote) buffer.append('\''); } /** * Does the real work of generating a pattern. */ private String toPattern(boolean localized) { StringBuffer result = new StringBuffer(); for (int j = 1; j >= 0; --j) { if (j == 1) appendAffix(result, posPrefixPattern, positivePrefix, localized); else appendAffix(result, negPrefixPattern, negativePrefix, localized); int i; int digitCount = useExponentialNotation ? getMaximumIntegerDigits() : Math.max(groupingSize, getMinimumIntegerDigits()) + 1; for (i = digitCount; i > 0; --i) { if (i != digitCount && isGroupingUsed() && groupingSize != 0 && i % groupingSize == 0) { result.append(localized ? symbols.getGroupingSeparator() : PATTERN_GROUPING_SEPARATOR); } result.append(i <= getMinimumIntegerDigits() ? (localized ? symbols .getZeroDigit() : PATTERN_ZERO_DIGIT) : (localized ? symbols.getDigit() : PATTERN_DIGIT)); } if (getMaximumFractionDigits() > 0 || decimalSeparatorAlwaysShown) result.append(localized ? symbols.getDecimalSeparator() : PATTERN_DECIMAL_SEPARATOR); for (i = 0; i < getMaximumFractionDigits(); ++i) { if (i < getMinimumFractionDigits()) { result.append(localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT); } else { result.append(localized ? symbols.getDigit() : PATTERN_DIGIT); } } if (useExponentialNotation) { result.append(localized ? symbols.getExponentialSymbol() : PATTERN_EXPONENT); for (i = 0; i < minExponentDigits; ++i) result.append(localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT); } if (j == 1) { appendAffix(result, posSuffixPattern, positiveSuffix, localized); if ((negSuffixPattern == posSuffixPattern && // n == p == null negativeSuffix.equals(positiveSuffix)) || (negSuffixPattern != null && negSuffixPattern .equals(posSuffixPattern))) { if ((negPrefixPattern != null && posPrefixPattern != null && negPrefixPattern .equals("'-" + posPrefixPattern)) || (negPrefixPattern == posPrefixPattern && // n == // p == // null negativePrefix.equals(symbols.getMinusSign() + positivePrefix))) break; } result.append(localized ? symbols.getPatternSeparator() : PATTERN_SEPARATOR); } else appendAffix(result, negSuffixPattern, negativeSuffix, localized); } return result.toString(); } /** * Apply the given pattern to this Format object. A pattern is a short-hand * specification for the various formatting properties. These properties can * also be changed individually through the various setter methods. *
* There is no limit to integer digits are set by this routine, since that * is the typical end-user desire; use setMaximumInteger if you want to set * a real value. For negative numbers, use a second pattern, separated by a * semicolon *
* Example "#,#00.0#"
-> 1,234.56
*
* This means a minimum of 2 integer digits, 1 fraction digit, and a maximum * of 2 fraction digits. *
* Example: "#,#00.0#;(#,#00.0#)"
for negatives in parentheses.
*
* In negative patterns, the minimum and maximum counts are ignored; these
* are presumed to be set in the positive pattern.
*
* @exception NullPointerException
* if pattern
is null
* @exception IllegalArgumentException
* if the given pattern is invalid.
*/
public void applyPattern(String pattern) {
applyPattern(pattern, false);
}
/**
* Apply the given pattern to this Format object. The pattern is assumed to
* be in a localized notation. A pattern is a short-hand specification for
* the various formatting properties. These properties can also be changed
* individually through the various setter methods.
*
* There is no limit to integer digits are set by this routine, since that * is the typical end-user desire; use setMaximumInteger if you want to set * a real value. For negative numbers, use a second pattern, separated by a * semicolon *
* Example "#,#00.0#"
-> 1,234.56
*
* This means a minimum of 2 integer digits, 1 fraction digit, and a maximum * of 2 fraction digits. *
* Example: "#,#00.0#;(#,#00.0#)"
for negatives in parentheses.
*
* In negative patterns, the minimum and maximum counts are ignored; these
* are presumed to be set in the positive pattern.
*
* @exception NullPointerException
* if pattern
is null
* @exception IllegalArgumentException
* if the given pattern is invalid.
*/
void applyLocalizedPattern(String pattern) {
applyPattern(pattern, true);
}
/**
* Does the real work of applying a pattern.
*/
private void applyPattern(String pattern, boolean localized) {
char zeroDigit = PATTERN_ZERO_DIGIT;
char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
char decimalSeparator = PATTERN_DECIMAL_SEPARATOR;
char percent = PATTERN_PERCENT;
char perMill = PATTERN_PER_MILLE;
char digit = PATTERN_DIGIT;
char separator = PATTERN_SEPARATOR;
char exponent = PATTERN_EXPONENT;
char minus = PATTERN_MINUS;
if (localized) {
zeroDigit = symbols.getZeroDigit();
groupingSeparator = symbols.getGroupingSeparator();
decimalSeparator = symbols.getDecimalSeparator();
percent = symbols.getPercent();
perMill = symbols.getPerMill();
digit = symbols.getDigit();
separator = symbols.getPatternSeparator();
exponent = symbols.getExponentialSymbol();
minus = symbols.getMinusSign();
}
boolean gotNegative = false;
decimalSeparatorAlwaysShown = false;
isCurrencyFormat = false;
useExponentialNotation = false;
// Two variables are used to record the subrange of the pattern
// occupied by phase 1. This is used during the processing of the
// second pattern (the one representing negative numbers) to ensure
// that no deviation exists in phase 1 between the two patterns.
int phaseOneStart = 0;
int phaseOneLength = 0;
/**
* Back-out comment : HShih boolean phaseTwo = false;
*/
int start = 0;
for (int j = 1; j >= 0 && start < pattern.length(); --j) {
boolean inQuote = false;
StringBuffer prefix = new StringBuffer();
StringBuffer suffix = new StringBuffer();
int decimalPos = -1;
int multiplier = 1;
int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0;
byte groupingCount = -1;
// The phase ranges from 0 to 2. Phase 0 is the prefix. Phase 1 is
// the section of the pattern with digits, decimal separator,
// grouping characters. Phase 2 is the suffix. In phases 0 and 2,
// percent, permille, and currency symbols are recognized and
// translated. The separation of the characters into phases is
// strictly enforced; if phase 1 characters are to appear in the
// suffix, for example, they must be quoted.
int phase = 0;
// The affix is either the prefix or the suffix.
StringBuffer affix = prefix;
for (int pos = start; pos < pattern.length(); ++pos) {
char ch = pattern.charAt(pos);
switch (phase) {
case 0:
case 2:
// Process the prefix / suffix characters
if (inQuote) {
// 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'.
if (ch == QUOTE) {
if ((pos + 1) < pattern.length()
&& pattern.charAt(pos + 1) == QUOTE) {
++pos;
affix.append("''"); // 'don''t'
} else {
inQuote = false; // 'do'
}
continue;
}
} else {
// Process unquoted characters seen in prefix or suffix
// phase.
if (ch == digit || ch == zeroDigit
|| ch == groupingSeparator
|| ch == decimalSeparator) {
// Any of these characters implicitly begins the
// next
// phase. If we are in phase 2, there is no next
// phase,
// so these characters are illegal.
/**
* 1.2 Back-out comment : HShih Can't throw
* exception here. if (phase == 2) throw new
* IllegalArgumentException
* ("Unquoted special character '" + ch +
* "' in pattern \"" + pattern + '"');
*/
phase = 1;
if (j == 1)
phaseOneStart = pos;
--pos; // Reprocess this character
continue;
} else if (ch == CURRENCY_SIGN) {
// Use lookahead to determine if the currency sign
// is
// doubled or not.
boolean doubled = (pos + 1) < pattern.length()
&& pattern.charAt(pos + 1) == CURRENCY_SIGN;
if (doubled)
++pos; // Skip over the doubled character
isCurrencyFormat = true;
affix.append(doubled ? "'\u00A4\u00A4" : "'\u00A4");
continue;
} else if (ch == QUOTE) {
// 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.
if (ch == QUOTE) {
if ((pos + 1) < pattern.length()
&& pattern.charAt(pos + 1) == QUOTE) {
++pos;
affix.append("''"); // o''clock
} else {
inQuote = true; // 'do'
}
continue;
}
} else if (ch == separator) {
// Don't allow separators before we see digit
// characters of phase
// 1, and don't allow separators in the second
// pattern (j == 0).
if (phase == 0 || j == 0)
throw new IllegalArgumentException(
"Unquoted special character '" + ch
+ "' in pattern \"" + pattern
+ '"');
start = pos + 1;
pos = pattern.length();
continue;
}
// Next handle characters which are appended directly.
else if (ch == percent) {
if (multiplier != 1)
throw new IllegalArgumentException(
"Too many percent/permille characters in pattern \""
+ pattern + '"');
multiplier = 100;
affix.append("'%");
continue;
} else if (ch == perMill) {
if (multiplier != 1)
throw new IllegalArgumentException(
"Too many percent/permille characters in pattern \""
+ pattern + '"');
multiplier = 1000;
affix.append("'\u2030");
continue;
} else if (ch == minus) {
affix.append("'-");
continue;
}
}
// Note that if we are within quotes, or if this is an
// unquoted,
// non-special character, then we usually fall through to
// here.
affix.append(ch);
break;
case 1:
// Phase one must be identical in the two sub-patterns. We
// enforce this by doing a direct comparison. While
// processing the first sub-pattern, we just record its
// length. While processing the second, we compare
// characters.
if (j == 1)
++phaseOneLength;
else {
/**
* 1.2 Back-out comment : HShih if (ch !=
* pattern.charAt(phaseOneStart++)) throw new
* IllegalArgumentException("Subpattern mismatch in \""
* + pattern + '"'); phaseTwo = true;
*/
if (--phaseOneLength == 0) {
phase = 2;
affix = suffix;
}
continue;
}
// Process the digits, decimal, and grouping characters. We
// record five pieces of information. We expect the digits
// to occur in the pattern ####0000.####, and we record the
// number of left digits, zero (central) digits, and right
// digits. The position of the last grouping character is
// recorded (should be somewhere within the first two blocks
// of characters), as is the position of the decimal point,
// if any (should be in the zero digits). If there is no
// decimal point, then there should be no right digits.
if (ch == digit) {
if (zeroDigitCount > 0)
++digitRightCount;
else
++digitLeftCount;
if (groupingCount >= 0 && decimalPos < 0)
++groupingCount;
} else if (ch == zeroDigit) {
if (digitRightCount > 0)
throw new IllegalArgumentException(
"Unexpected '0' in pattern \"" + pattern
+ '"');
++zeroDigitCount;
if (groupingCount >= 0 && decimalPos < 0)
++groupingCount;
} else if (ch == groupingSeparator) {
groupingCount = 0;
} else if (ch == decimalSeparator) {
if (decimalPos >= 0)
throw new IllegalArgumentException(
"Multiple decimal separators in pattern \""
+ pattern + '"');
decimalPos = digitLeftCount + zeroDigitCount
+ digitRightCount;
} else if (ch == exponent) {
if (useExponentialNotation)
throw new IllegalArgumentException(
"Multiple exponential "
+ "symbols in pattern \"" + pattern
+ '"');
useExponentialNotation = true;
minExponentDigits = 0;
// Use lookahead to parse out the exponential part of
// the
// pattern, then jump into phase 2.
while (++pos < pattern.length()
&& pattern.charAt(pos) == zeroDigit) {
++minExponentDigits;
++phaseOneLength;
}
if ((digitLeftCount + zeroDigitCount) < 1
|| minExponentDigits < 1)
throw new IllegalArgumentException(
"Malformed exponential " + "pattern \""
+ pattern + '"');
// Transition to phase 2
phase = 2;
affix = suffix;
--pos;
continue;
} else {
phase = 2;
affix = suffix;
--pos;
--phaseOneLength;
continue;
}
break;
}
}
/**
* 1.2 Back-out comment : HShih if (phaseTwo && phaseOneLength > 0)
* throw new IllegalArgumentException("Subpattern mismatch in \"" +
* pattern + '"');
*/
// Handle patterns with no '0' pattern character. These patterns
// are legal, but must be interpreted. "##.###" -> "#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. That is, 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.
if ((decimalPos < 0 && digitRightCount > 0)
|| (decimalPos >= 0 && (decimalPos < digitLeftCount || decimalPos > (digitLeftCount + zeroDigitCount)))
|| groupingCount == 0 || inQuote)
throw new IllegalArgumentException("Malformed pattern \""
+ pattern + '"');
if (j == 1) {
posPrefixPattern = prefix.toString();
posSuffixPattern = suffix.toString();
negPrefixPattern = posPrefixPattern; // assume these for now
negSuffixPattern = posSuffixPattern;
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(useExponentialNotation ? digitLeftCount
+ getMinimumIntegerDigits() : DOUBLE_INTEGER_DIGITS);
setMaximumFractionDigits(decimalPos >= 0 ? (digitTotalCount - decimalPos)
: 0);
setMinimumFractionDigits(decimalPos >= 0 ? (digitLeftCount
+ zeroDigitCount - decimalPos) : 0);
setGroupingUsed(groupingCount > 0);
this.groupingSize = (groupingCount > 0) ? groupingCount : 0;
this.multiplier = multiplier;
setDecimalSeparatorAlwaysShown(decimalPos == 0
|| decimalPos == digitTotalCount);
} else {
negPrefixPattern = prefix.toString();
negSuffixPattern = suffix.toString();
gotNegative = true;
}
}
if (pattern.length() == 0) {
posPrefixPattern = posSuffixPattern = "";
setMinimumIntegerDigits(0);
setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
setMinimumFractionDigits(0);
setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
}
// 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 (!gotNegative
|| (negPrefixPattern.equals(posPrefixPattern) && negSuffixPattern
.equals(posSuffixPattern))) {
negSuffixPattern = posSuffixPattern;
negPrefixPattern = "'-" + posPrefixPattern;
}
expandAffixes();
}
/**
* 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
*/
public void setMaximumIntegerDigits(int newValue) {
super.setMaximumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS));
}
/**
* 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
*/
public void setMinimumIntegerDigits(int newValue) {
super.setMinimumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS));
}
/**
* 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
*/
public void setMaximumFractionDigits(int newValue) {
super.setMaximumFractionDigits(Math.min(newValue,
DOUBLE_FRACTION_DIGITS));
}
/**
* 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
*/
public void setMinimumFractionDigits(int newValue) {
super.setMinimumFractionDigits(Math.min(newValue,
DOUBLE_FRACTION_DIGITS));
}
// ----------------------------------------------------------------------
// INSTANCE VARIABLES
// ----------------------------------------------------------------------
private transient DigitList digitList = new DigitList();
/**
* The symbol used as a prefix when formatting positive numbers, e.g. "+".
*
* @serial
* @see #getPositivePrefix
*/
private String positivePrefix = "";
/**
* The symbol used as a suffix when formatting positive numbers. This is
* often an empty string.
*
* @serial
* @see #getPositiveSuffix
*/
private String positiveSuffix = "";
/**
* The symbol used as a prefix when formatting negative numbers, e.g. "-".
*
* @serial
* @see #getNegativePrefix
*/
private String negativePrefix = "-";
/**
* The symbol used as a suffix when formatting negative numbers. This is
* often an empty string.
*
* @serial
* @see #getNegativeSuffix
*/
private String negativeSuffix = "";
/**
* The prefix pattern for non-negative numbers. This variable corresponds to
* positivePrefix
.
*
*
* This pattern is expanded by the method expandAffix()
to
* positivePrefix
to update the latter to reflect changes in
* symbols
. If this variable is null
then
* positivePrefix
is taken as a literal value that does not
* change when symbols
changes. This variable is always
* null
for DecimalFormat
objects older than
* stream version 2 restored from stream.
*
* @serial
* @since 1.3
*/
private String posPrefixPattern;
/**
* The suffix pattern for non-negative numbers. This variable corresponds to
* positiveSuffix
. This variable is analogous to
* posPrefixPattern
; see that variable for further
* documentation.
*
* @serial
* @since 1.3
*/
private String posSuffixPattern;
/**
* The prefix pattern for negative numbers. This variable corresponds to
* negativePrefix
. This variable is analogous to
* posPrefixPattern
; see that variable for further
* documentation.
*
* @serial
* @since 1.3
*/
private String negPrefixPattern;
/**
* The suffix pattern for negative numbers. This variable corresponds to
* negativeSuffix
. This variable is analogous to
* posPrefixPattern
; see that variable for further
* documentation.
*
* @serial
* @since 1.3
*/
private String negSuffixPattern;
/**
* The multiplier for use in percent, permill, etc.
*
* @serial
* @see #getMultiplier
*/
private int multiplier = 1;
/**
* The number of digits between grouping separators in the integer portion
* of a number. Must be greater than 0 if
* NumberFormat.groupingUsed
is true.
*
* @serial
* @see #getGroupingSize
* @see NumberFormat#isGroupingUsed
*/
private byte groupingSize = 3; // invariant, > 0 if useThousands
/**
* If true, forces the decimal separator to always appear in a formatted
* number, even if the fractional part of the number is zero.
*
* @serial
* @see #isDecimalSeparatorAlwaysShown
*/
private boolean decimalSeparatorAlwaysShown = false;
/**
* True if this object represents a currency format. This determines whether
* the monetary decimal separator is used instead of the normal one.
*/
private transient boolean isCurrencyFormat = false;
/**
* The DecimalFormatSymbols
object used by this format. It
* contains the symbols used to format numbers, e.g. the grouping separator,
* decimal separator, and so on.
*
* @serial
* @see #setDecimalFormatSymbols
* @see DecimalFormatSymbols
*/
private DecimalFormatSymbols symbols = null; // LIU new
// DecimalFormatSymbols();
/**
* True to force the use of exponential (i.e. scientific) notation when
* formatting numbers.
*
* @serial
* @since 1.2
*/
private boolean useExponentialNotation; // Newly persistent in the Java 2
// platform
/**
* FieldPositions describing the positive prefix String. This is lazily
* created. Use getPositivePrefixFieldPositions
when needed.
*/
private transient FieldPosition[] positivePrefixFieldPositions;
/**
* FieldPositions describing the positive suffix String. This is lazily
* created. Use getPositiveSuffixFieldPositions
when needed.
*/
private transient FieldPosition[] positiveSuffixFieldPositions;
/**
* FieldPositions describing the negative prefix String. This is lazily
* created. Use getNegativePrefixFieldPositions
when needed.
*/
private transient FieldPosition[] negativePrefixFieldPositions;
/**
* FieldPositions describing the negative suffix String. This is lazily
* created. Use getNegativeSuffixFieldPositions
when needed.
*/
private transient FieldPosition[] negativeSuffixFieldPositions;
/**
* The minimum number of digits used to display the exponent when a number
* is formatted in exponential notation. This field is ignored if
* useExponentialNotation
is not true.
*
* @serial
* @since 1.2
*/
private byte minExponentDigits; // Newly persistent in the Java 2 platform
// ----------------------------------------------------------------------
static final int currentSerialVersion = 2;
/**
* The internal serial version which says which version was written.
* Possible values are:
*
-
*
- 0 (default): versions before the Java 2 platform v1.2 *
- 1: version for 1.2, which includes the two new fields
*
useExponentialNotation
andminExponentDigits
. * - 2: version for 1.3 and later, which adds four new fields:
*
posPrefixPattern
,posSuffixPattern
, *negPrefixPattern
, andnegSuffixPattern
. *