package ecologylab.generic.text; /* * @(#)DecimalFormat.java 1.79 04/06/28 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ /* * (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. * */ import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.text.DecimalFormat; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; import java.util.Currency; /** * <code>EfficientDecimalFormat</code> is like the * {@link java.text.DecimalFormat} class, as described below, except that it: * * 1.) will format into an Appendable, to improve efficiency * * 2.) emits NaN for not-a-number, rather than ? (as described below). * * 3.) DOES NOT USE LOCALES. Due to an undocumented change between the 1.5 and * 1.6 JREs there is currently no way to support them. Instead, this class (and * ecologylab.generic.text.DecimalFormatSymbols) will simply use the standard * characters for the US). * * <code>DecimalFormat</code> is a concrete subclass of * <code>NumberFormat</code> 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. * * <p> * To obtain a <code>NumberFormat</code> for a specific locale, including the * default locale, call one of <code>NumberFormat</code>'s factory methods, * such as <code>getInstance()</code>. In general, do not call the * <code>DecimalFormat</code> constructors directly, since the * <code>NumberFormat</code> factory methods may return subclasses other than * <code>DecimalFormat</code>. If you need to customize the format object, do * something like this: * * <blockquote> * * <pre> * NumberFormat f = NumberFormat.getInstance(loc); * if (f instanceof DecimalFormat) * { * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true); * } * </pre> * * </blockquote> * * <p> * A <code>DecimalFormat</code> comprises a <em>pattern</em> and a set of * <em>symbols</em>. The pattern may be set directly using * <code>applyPattern()</code>, or indirectly using the API methods. The * symbols are stored in a <code>DecimalFormatSymbols</code> object. When * using the <code>NumberFormat</code> factory methods, the pattern and * symbols are read from localized <code>ResourceBundle</code>s. * * <h4>Patterns</h4> * * <code>DecimalFormat</code> patterns have the following syntax: <blockquote> * * <pre> * <i>Pattern:</i> * <i>PositivePattern</i> * <i>PositivePattern</i> ; <i>NegativePattern</i> * <i>PositivePattern:</i> * <i>Prefix<sub>opt</sub></i> <i>Number</i> <i>Suffix<sub>opt</sub></i> * <i>NegativePattern:</i> * <i>Prefix<sub>opt</sub></i> <i>Number</i> <i>Suffix<sub>opt</sub></i> * <i>Prefix:</i> * any Unicode characters except \uFFFE, \uFFFF, and special characters * <i>Suffix:</i> * any Unicode characters except \uFFFE, \uFFFF, and special characters * <i>Number:</i> * <i>Integer</i> <i>Exponent<sub>opt</sub></i> * <i>Integer</i> . <i>Fraction</i> <i>Exponent<sub>opt</sub></i> * <i>Integer:</i> * <i>MinimumInteger</i> * # * # <i>Integer</i> * # , <i>Integer</i> * <i>MinimumInteger:</i> * 0 * 0 <i>MinimumInteger</i> * 0 , <i>MinimumInteger</i> * <i>Fraction:</i> * <i>MinimumFraction<sub>opt</sub></i> <i>OptionalFraction<sub>opt</sub></i> * <i>MinimumFraction:</i> * 0 <i>MinimumFraction<sub>opt</sub></i> * <i>OptionalFraction:</i> * # <i>OptionalFraction<sub>opt</sub></i> * <i>Exponent:</i> * E <i>MinimumExponent</i> * <i>MinimumExponent:</i> * 0 <i>MinimumExponent<sub>opt</sub></i> * </pre> * * </blockquote> * * <p> * A <code>DecimalFormat</code> pattern contains a positive and negative * subpattern, for example, <code>"#,##0.00;(#,##0.00)"</code>. 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>'-'</code> in most locales) is used as the negative * subpattern. That is, <code>"0.00"</code> alone is equivalent to * <code>"0.00;-0.00"</code>. 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 <code>"#,##0.0#;(#)"</code> produces precisely the * same behavior as <code>"#,##0.0#;(#,##0.0#)"</code>. * * <p> * 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 <code>DecimalFormat.parse()</code> to be able * to distinguish positive from negative values. (If they are identical, then * <code>DecimalFormat</code> 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. * * <p> * 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 <code>"#,##,###,####"</code> == <code>"######,####"</code> == * <code>"##,####,####"</code>. * * <h4>Special Pattern Characters</h4> * * <p> * 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. * * <p> * The characters listed here are used in non-localized patterns. Localized * patterns use the corresponding characters taken from this formatter's * <code>DecimalFormatSymbols</code> object instead, and these characters lose * their special status. Two exceptions are the currency sign and quote, which * are not localized. * * <blockquote> <table border=0 cellspacing=3 cellpadding=0 summary="Chart * showing symbol, location, localized, and meaning."> * <tr bgcolor="#ccccff"> * <th align=left>Symbol * <th align=left>Location * <th align=left>Localized? * <th align=left>Meaning * <tr valign=top> * <td><code>0</code> * <td>Number * <td>Yes * <td>Digit * <tr valign=top bgcolor="#eeeeff"> * <td><code>#</code> * <td>Number * <td>Yes * <td>Digit, zero shows as absent * <tr valign=top> * <td><code>.</code> * <td>Number * <td>Yes * <td>Decimal separator or monetary decimal separator * <tr valign=top bgcolor="#eeeeff"> * <td><code>-</code> * <td>Number * <td>Yes * <td>Minus sign * <tr valign=top> * <td><code>,</code> * <td>Number * <td>Yes * <td>Grouping separator * <tr valign=top bgcolor="#eeeeff"> * <td><code>E</code> * <td>Number * <td>Yes * <td>Separates mantissa and exponent in scientific notation. * <em>Need not be quoted in prefix or suffix.</em> * <tr valign=top> * <td><code>;</code> * <td>Subpattern boundary * <td>Yes * <td>Separates positive and negative subpatterns * <tr valign=top bgcolor="#eeeeff"> * <td><code>%</code> * <td>Prefix or suffix * <td>Yes * <td>Multiply by 100 and show as percentage * <tr valign=top> * <td><code>\u2030</code> * <td>Prefix or suffix * <td>Yes * <td>Multiply by 1000 and show as per mille value * <tr valign=top bgcolor="#eeeeff"> * <td><code>¤</code> (<code>\u00A4</code>) * <td>Prefix or suffix * <td>No * <td>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. * <tr valign=top> * <td><code>'</code> * <td>Prefix or suffix * <td>No * <td>Used to quote special characters in a prefix or suffix, for example, * <code>"'#'#"</code> formats 123 to <code>"#123"</code>. To create a * single quote itself, use two in a row: <code>"# o''clock"</code>. </table> * </blockquote> * * <h4>Scientific Notation</h4> * * <p> * 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. * <code>DecimalFormat</code> can be instructed to format and parse scientific * notation <em>only via a pattern</em>; 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: <code>"0.###E0"</code> formats the number * 1234 as <code>"1.234E3"</code>. * * <ul> * <li>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, <em>not</em> the prefix and * suffix from the pattern. This allows patterns such as * <code>"0.###E0 m/s"</code>. * * <li>The minimum and maximum number of integer digits are interpreted * together: * * <ul> * <li>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 * <em>engineering notation</em>, in which the exponent is a multiple of * three, e.g., <code>"##0.#####E0"</code>. Using this pattern, the number * 12345 formats to <code>"12.345E3"</code>, and 123456 formats to * <code>"123.456E3"</code>. * * <li>Otherwise, the minimum number of integer digits is achieved by adjusting * the exponent. Example: 0.00123 formatted with <code>"00.###E0"</code> * yields <code>"12.3E-4"</code>. * </ul> * * <li>The number of significant digits in the mantissa is the sum of the * <em>minimum integer</em> and <em>maximum fraction</em> digits, and is * unaffected by the maximum integer digits. For example, 12345 formatted with * <code>"##0.##E0"</code> is <code>"12.3E3"</code>. To show all digits, * set the significant digits count to zero. The number of significant digits * does not affect parsing. * * <li>Exponential patterns may not contain grouping separators. * </ul> * * <h4>Rounding</h4> * * <code>DecimalFormat</code> uses half-even rounding (see * {@link java.math.BigDecimal#ROUND_HALF_EVEN ROUND_HALF_EVEN}) for * formatting. * * <h4>Digits</h4> * * For formatting, <code>DecimalFormat</code> uses the ten consecutive * characters starting with the localized zero digit defined in the * <code>DecimalFormatSymbols</code> object as digits. For parsing, these * digits as well as all Unicode decimal digits, as defined by * {@link Character#digit Character.digit}, are recognized. * * <h4>Special Values</h4> * * <p> * <code>NaN</code> is formatted as a single character, typically * <code>\uFFFD</code>. This character is determined by the * <code>DecimalFormatSymbols</code> object. This is the only value for which * the prefixes and suffixes are not used. * * <p> * Infinity is formatted as a single character, typically * <code>\u221E</code>, with the positive or negative prefixes and * suffixes applied. The infinity character is determined by the * <code>DecimalFormatSymbols</code> object. * * <p> * Negative zero (<code>"-0"</code>) parses to * <ul> * <li><code>BigDecimal(0)</code> if <code>isParseBigDecimal()</code> is * true, * <li><code>Long(0)</code> if <code>isParseBigDecimal()</code> is false * and <code>isParseIntegerOnly()</code> is true, * <li><code>Double(-0.0)</code> if both <code>isParseBigDecimal()</code> * and <code>isParseIntegerOnly()</code> are false. * </ul> * * <h4><a name="synchronization">Synchronization</a></h4> * * <p> * 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. * * <h4>Example</h4> * * <blockquote> * * <pre> * <strong>// Print out a number using the localized number, integer, currency, * // and percent format for each locale</strong> * 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) {} * } * } * </pre> * * </blockquote> * * @see <a * href="http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html">Java * Tutorial</a> * @see NumberFormat * @see DecimalFormatSymbols * @see ParsePosition * @version 1.79 06/28/04 * @author Mark Davis * @author Alan Liu * @author Zachary O. Toups (toupsz@ecologylab.net) */ public class EfficientDecimalFormat extends DecimalFormat { /** * 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. * <p> * 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 <code>pattern</code> is null * @exception IllegalArgumentException * if the given pattern is invalid. * @see java.text.NumberFormat#getInstance * @see java.text.NumberFormat#getNumberInstance * @see java.text.NumberFormat#getCurrencyInstance * @see java.text.NumberFormat#getPercentInstance */ public EfficientDecimalFormat(String pattern) { // Always applyPattern after the symbols are set this.symbols = new DecimalFormatSymbols(); applyPattern(pattern); } /** * Creates a DecimalFormat using the given pattern and symbols. Use this * constructor when you need to completely customize the behavior of the * format. * <p> * 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 java.text.NumberFormat#getInstance * @see java.text.NumberFormat#getNumberInstance * @see java.text.NumberFormat#getCurrencyInstance * @see java.text.NumberFormat#getPercentInstance * @see java.text.DecimalFormatSymbols */ public EfficientDecimalFormat(String pattern, DecimalFormatSymbols symbols) { // Always applyPattern after the symbols are set this.symbols = (DecimalFormatSymbols) symbols.clone(); this.symbols.setNaN("NaN"); applyPattern(pattern); } // Overrides /** * Formats a number and appends the resulting text to the given string * buffer. The number can be of any subclass of {@link java.lang.Number}. * <p> * This implementation uses the maximum precision permitted. * * @param number * the number to format * @param toAppendTo * the <code>StringBuffer</code> to which the formatted text is * to be appended * @param pos * On input: an alignment field, if desired. On output: the offsets * of the alignment field. * @return the value passed in as <code>toAppendTo</code> * @throws IOException * @exception IllegalArgumentException * if <code>number</code> is null or not an instance of * <code>Number</code>. * @exception NullPointerException * if <code>toAppendTo</code> or <code>pos</code> is null * @see java.text.FieldPosition */ public final Appendable format(Object number, Appendable toAppendTo) throws IOException { if (number instanceof Long || number instanceof Integer || number instanceof Short || number instanceof Byte || (number instanceof BigInteger && ((BigInteger) number) .bitLength() < 64)) { return format(((Number) number).longValue(), toAppendTo); } else if (number instanceof BigDecimal) { return format((BigDecimal) number, toAppendTo); } else if (number instanceof BigInteger) { return format((BigInteger) number, toAppendTo); } else if (number instanceof Number) { return format(((Number) number).doubleValue(), toAppendTo); } else { throw new IllegalArgumentException( "Cannot format given Object as a Number"); } } /** * 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 * @throws IOException */ public Appendable format(double number, Appendable result) throws IOException { if (Double.isNaN(number) || (Double.isInfinite(number) && multiplier == 0)) { result.append(symbols.getNaN()); 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)) ^ (multiplier < 0); if (multiplier != 1) { number *= multiplier; } if (Double.isInfinite(number)) { if (isNegative) { result.append(negativePrefix); } else { result.append(positivePrefix); } result.append(symbols.getInfinity()); if (isNegative) { result.append(negativeSuffix); } else { result.append(positiveSuffix); } return result; } if (isNegative) { number = -number; } // at this point we are guaranteed a nonnegative finite number. assert (number >= 0 && !Double.isInfinite(number)); synchronized (digitList) { int maxIntDigits = super.getMaximumIntegerDigits(); int minIntDigits = super.getMinimumIntegerDigits(); int maxFraDigits = super.getMaximumFractionDigits(); int minFraDigits = super.getMinimumFractionDigits(); digitList.set(number, useExponentialNotation ? maxIntDigits + maxFraDigits : maxFraDigits, !useExponentialNotation); return subformat(result, isNegative, false, maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); } } /** * 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 * @throws IOException * @see java.text.FieldPosition */ public Appendable format(long number, Appendable result) throws IOException { 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 // BigInteger instead. boolean useBigInteger = false; if (number < 0) { // This can only happen if number == Long.MIN_VALUE. if (multiplier != 0) { useBigInteger = true; } } else if (multiplier != 1 && multiplier != 0) { long cutoff = Long.MAX_VALUE / multiplier; if (cutoff < 0) { cutoff = -cutoff; } useBigInteger = (number > cutoff); } if (useBigInteger) { if (isNegative) { number = -number; } BigInteger bigIntegerValue = BigInteger.valueOf(number); return format(bigIntegerValue, result, true); } number *= multiplier; if (number == 0) { isNegative = false; } else { if (multiplier < 0) { number = -number; isNegative = !isNegative; } } synchronized (digitList) { int maxIntDigits = super.getMaximumIntegerDigits(); int minIntDigits = super.getMinimumIntegerDigits(); int maxFraDigits = super.getMaximumFractionDigits(); int minFraDigits = super.getMinimumFractionDigits(); digitList.set(number, useExponentialNotation ? maxIntDigits + maxFraDigits : 0); return subformat(result, isNegative, true, maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); } } /** * Formats a BigDecimal to produce a string. * * @param number * The BigDecimal to format * @param result * where the text is to be appended * @param delegate * notified of locations of sub fields * @return The formatted number string * @throws IOException */ public Appendable format(BigDecimal number, Appendable result) throws IOException { if (multiplier != 1) { number = number.multiply(getBigDecimalMultiplier()); } boolean isNegative = number.signum() == -1; if (isNegative) { number = number.negate(); } synchronized (digitList) { int maxIntDigits = getMaximumIntegerDigits(); int minIntDigits = getMinimumIntegerDigits(); int maxFraDigits = getMaximumFractionDigits(); int minFraDigits = getMinimumFractionDigits(); int maximumDigits = maxIntDigits + maxFraDigits; digitList .set( number, useExponentialNotation ? ((maximumDigits < 0) ? Integer.MAX_VALUE : maximumDigits) : maxFraDigits, !useExponentialNotation); return subformat(result, isNegative, false, maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); } } /** * Format a BigInteger to produce a string. * * @param number * The BigInteger 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 * @throws IOException * @see java.text.FieldPosition */ private Appendable format(BigInteger number, Appendable result) throws IOException { return format(number, result, false); } /** * Format a BigInteger to produce a string. * * @param number * The BigInteger to format * @param result * where the text is to be appended * @param delegate * notified of locations of sub fields * @return The formatted number string * @throws IOException * @see java.text.FieldPosition */ private Appendable format(BigInteger number, Appendable result, boolean formatLong) throws IOException { if (multiplier != 1) { number = number.multiply(getBigIntegerMultiplier()); } boolean isNegative = number.signum() == -1; if (isNegative) { number = number.negate(); } synchronized (digitList) { int maxIntDigits, minIntDigits, maxFraDigits, minFraDigits, maximumDigits; if (formatLong) { maxIntDigits = super.getMaximumIntegerDigits(); minIntDigits = super.getMinimumIntegerDigits(); maxFraDigits = super.getMaximumFractionDigits(); minFraDigits = super.getMinimumFractionDigits(); maximumDigits = maxIntDigits + maxFraDigits; } else { maxIntDigits = getMaximumIntegerDigits(); minIntDigits = getMinimumIntegerDigits(); maxFraDigits = getMaximumFractionDigits(); minFraDigits = getMinimumFractionDigits(); maximumDigits = maxIntDigits + maxFraDigits; if (maximumDigits < 0) { maximumDigits = Integer.MAX_VALUE; } } digitList.set(number, useExponentialNotation ? maximumDigits : 0); return subformat(result, isNegative, true, maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); } } /** * Complete the formatting of a finite number. On entry, the digitList must * be filled in with the correct digits. * * @throws IOException */ private Appendable subformat(Appendable result, boolean isNegative, boolean isInteger, int maxIntDigits, int minIntDigits, int maxFraDigits, int minFraDigits) throws IOException { 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 if (isNegative) result.append(negativePrefix); else result.append(positivePrefix); if (useExponentialNotation) { 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 = maxIntDigits; if (repeat > 1 && repeat > minIntDigits) { // 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 = minIntDigits + minFraDigits; if (minimumDigits < 0) { // overflow? minimumDigits = Integer.MAX_VALUE; } // 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) { result.append(decimal); addedDecimalSeparator = true; } result .append((i < digitList.count) ? (char) (digitList.digits[i] + zeroDelta) : zero); } if (decimalSeparatorAlwaysShown && totalDigits == integerDigits) { result.append(decimal); addedDecimalSeparator = true; } // 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. result.append(symbols.getExponentialSymbol()); // 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; result.append(symbols.getMinusSign()); } digitList.set(exponent); 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); } } else { // 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 = minIntDigits; 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 > maxIntDigits) { count = maxIntDigits; digitIndex = digitList.decimalAt - count; } // indicate whether or not any characters have been appended before the // fraction part boolean actuallyAppended = (count - 1 < 0); 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)) { result.append(grouping); } } // Determine whether or not there are any printable fractional // digits. If we've used up the digits we know there aren't. boolean fractionPresent = (minFraDigits > 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 && actuallyAppended) { result.append(zero); } // Output the decimal separator if we always do so. if (decimalSeparatorAlwaysShown || fractionPresent) { result.append(decimal); } for (int i = 0; i < maxFraDigits; ++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 >= minFraDigits && (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); } } } if (isNegative) result.append(negativeSuffix); else result.append(positiveSuffix); return result; } /** * Parses text from a string to produce a <code>Number</code>. * <p> * The method attempts to parse text starting at the index given by * <code>pos</code>. If parsing succeeds, then the index of * <code>pos</code> 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 <code>pos</code> * can be used to indicate the starting point for the next call to this * method. If an error occurs, then the index of <code>pos</code> is not * changed, the error index of <code>pos</code> is set to the index of the * character where the error occurred, and null is returned. * <p> * The subclass returned depends on the value of {@link #isParseBigDecimal} * as well as on the string being parsed. * <ul> * <li>If <code>isParseBigDecimal()</code> is false (the default), most * integer values are returned as <code>Long</code> objects, no matter how * they are written: <code>"17"</code> and <code>"17.000"</code> both * parse to <code>Long(17)</code>. Values that cannot fit into a * <code>Long</code> are returned as <code>Double</code>s. This includes * values with a fractional part, infinite values, <code>NaN</code>, and * the value -0.0. <code>DecimalFormat</code> does <em>not</em> decide * whether to return a <code>Double</code> or a <code>Long</code> 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 * <code>"-9,223,372,036,854,775,808.00"</code>, from being parsed * accurately. * <p> * Callers may use the <code>Number</code> methods <code>doubleValue</code>, * <code>longValue</code>, etc., to obtain the type they want. * <li>If <code>isParseBigDecimal()</code> is true, values are returned as * <code>BigDecimal</code> objects. The values are the ones constructed by * {@link java.math.BigDecimal#BigDecimal(String)} for corresponding strings * in locale-independent format. The special cases negative and positive * infinity and NaN are returned as <code>Double</code> instances holding * the values of the corresponding <code>Double</code> constants. * </ul> * <p> * <code>DecimalFormat</code> parses all Unicode characters that represent * decimal digits, as defined by <code>Character.digit()</code>. In * addition, <code>DecimalFormat</code> also recognizes as digits the ten * consecutive characters starting with the localized zero digit defined in * the <code>DecimalFormatSymbols</code> object. * * @param text * the string to be parsed * @param pos * A <code>ParsePosition</code> object with index and error index * information as described above. * @return the parsed value, or <code>null</code> if the parse fails * @exception NullPointerException * if <code>text</code> or <code>pos</code> is null. */ @Override public Number parse(String text, ParsePosition pos) { // special case NaN if (text.regionMatches(pos.getIndex(), symbols.getNaN(), 0, symbols .getNaN().length())) { pos.setIndex(pos.getIndex() + symbols.getNaN().length()); return new Double(Double.NaN); } boolean[] status = new boolean[STATUS_LENGTH]; if (!subparse(text, pos, positivePrefix, negativePrefix, digitList, false, status)) { return null; } // special case INFINITY if (status[STATUS_INFINITE]) { if (status[STATUS_POSITIVE] == (multiplier >= 0)) { return new Double(Double.POSITIVE_INFINITY); } else { return new Double(Double.NEGATIVE_INFINITY); } } if (multiplier == 0) { if (digitList.isZero()) { return new Double(Double.NaN); } else if (status[STATUS_POSITIVE]) { return new Double(Double.POSITIVE_INFINITY); } else { return new Double(Double.NEGATIVE_INFINITY); } } if (isParseBigDecimal()) { BigDecimal bigDecimalResult = digitList.getBigDecimal(); if (multiplier != 1) { try { bigDecimalResult = bigDecimalResult .divide(getBigDecimalMultiplier()); } catch (ArithmeticException e) { // non-terminating decimal expansion bigDecimalResult = bigDecimalResult.divide( getBigDecimalMultiplier(), BigDecimal.ROUND_HALF_EVEN); } } if (!status[STATUS_POSITIVE]) { bigDecimalResult = bigDecimalResult.negate(); } return bigDecimalResult; } else { boolean gotDouble = true; boolean gotLongMinimum = false; double doubleResult = 0.0; long longResult = 0; // Finally, have DigitList parse the digits into a value. if (digitList.fitsIntoLong(status[STATUS_POSITIVE], isParseIntegerOnly())) { gotDouble = false; longResult = digitList.getLong(); if (longResult < 0) { // got Long.MIN_VALUE gotLongMinimum = true; } } 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; gotDouble = true; } } } if (!status[STATUS_POSITIVE] && !gotLongMinimum) { doubleResult = -doubleResult; 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. 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 != longResult) || (doubleResult == 0.0 && 1 / doubleResult < 0.0)) && !isParseIntegerOnly(); } return gotDouble ? (Number) new Double(doubleResult) : (Number) new Long(longResult); } } /** * Return a BigInteger multiplier. */ private BigInteger getBigIntegerMultiplier() { if (bigIntegerMultiplier == null) { bigIntegerMultiplier = BigInteger.valueOf(multiplier); } return bigIntegerMultiplier; } private transient BigInteger bigIntegerMultiplier; /** * Return a BigDecimal multiplier. */ private BigDecimal getBigDecimalMultiplier() { if (bigDecimalMultiplier == null) { bigDecimalMultiplier = new BigDecimal(multiplier); } return bigDecimalMultiplier; } private transient BigDecimal bigDecimalMultiplier; 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, String positivePrefix, String negativePrefix, DigitList digits, boolean isExponent, boolean status[]) { int position = parsePosition.getIndex(); int oldStart = parsePosition.getIndex(); int backup; boolean gotPositive, gotNegative; // check for positivePrefix; take longest gotPositive = text.regionMatches(position, positivePrefix, 0, positivePrefix.length()); gotNegative = text.regionMatches(position, negativePrefix, 0, negativePrefix.length()); 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.setErrorIndex(position); return false; } // process digits or Inf, find decimal position status[STATUS_INFINITE] = false; if (!isExponent && text.regionMatches(position, symbols.getInfinity(), 0, symbols .getInfinity().length())) { 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, "", Character.toString(symbols .getMinusSign()), exponentDigits, true, stat) && exponentDigits .fitsIntoLong(stat[STATUS_POSITIVE], true)) { position = pos.getIndex(); // 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.setIndex(oldStart); parsePosition.setErrorIndex(oldStart); return false; } } // check for suffix if (!isExponent) { if (gotPositive) { gotPositive = text.regionMatches(position, positiveSuffix, 0, positiveSuffix.length()); } if (gotNegative) { gotNegative = text.regionMatches(position, negativeSuffix, 0, negativeSuffix.length()); } // 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.setErrorIndex(position); return false; } parsePosition.setIndex(position + (gotPositive ? positiveSuffix.length() : negativeSuffix .length())); // mark success! } else { parsePosition.setIndex(position); } status[STATUS_POSITIVE] = gotPositive; if (parsePosition.getIndex() == oldStart) { parsePosition.setErrorIndex(position); return false; } return true; } /** * Sets the decimal format symbols, which is generally not changed by the * programmer or user. * * @param newSymbols * desired DecimalFormatSymbols * @see java.text.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. * <P> * Examples: +123, $123, sFr123 */ @Override public String getPositivePrefix() { return positivePrefix; } /** * Set the positive prefix. * <P> * Examples: +123, $123, sFr123 */ @Override public void setPositivePrefix(String newValue) { positivePrefix = newValue; posPrefixPattern = null; positivePrefixFieldPositions = null; } /** * Get the negative prefix. * <P> * Examples: -123, ($123) (with negative suffix), sFr-123 */ @Override public String getNegativePrefix() { return negativePrefix; } /** * Set the negative prefix. * <P> * Examples: -123, ($123) (with negative suffix), sFr-123 */ @Override public void setNegativePrefix(String newValue) { negativePrefix = newValue; negPrefixPattern = null; } /** * Get the positive suffix. * <P> * Example: 123% */ @Override public String getPositiveSuffix() { return positiveSuffix; } /** * Set the positive suffix. * <P> * Example: 123% */ @Override public void setPositiveSuffix(String newValue) { positiveSuffix = newValue; posSuffixPattern = null; } /** * Get the negative suffix. * <P> * Examples: -123%, ($123) (with positive suffixes) */ @Override public String getNegativeSuffix() { return negativeSuffix; } /** * Set the negative suffix. * <P> * Examples: 123% */ @Override public void setNegativeSuffix(String newValue) { negativeSuffix = newValue; negSuffixPattern = null; } /** * Gets the multiplier for use in percent, per mille, and similar formats. * * @see #setMultiplier(int) */ @Override public int getMultiplier() { return multiplier; } /** * Sets the multiplier for use in percent, per mille, and similar formats. * For a percent format, set the multiplier to 100 and the suffixes to have * '%' (for Arabic, use the Arabic percent sign). For a per mille format, set * the multiplier to 1000 and the suffixes to have '\u2030'. * * <P> * Example: with multiplier 100, 1.23 is formatted as "123", and "123" is * parsed into 1.23. * * @see #getMultiplier */ @Override public void setMultiplier(int newValue) { multiplier = newValue; bigDecimalMultiplier = null; bigIntegerMultiplier = null; } /** * 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 java.text.NumberFormat#isGroupingUsed * @see java.text.DecimalFormatSymbols#getGroupingSeparator */ @Override public 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. <br> * The value passed in is converted to a byte, which may lose information. * * @see #getGroupingSize * @see java.text.NumberFormat#setGroupingUsed * @see java.text.DecimalFormatSymbols#setGroupingSeparator */ @Override 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.) * <P> * Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 */ @Override 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.) * <P> * Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 */ @Override public void setDecimalSeparatorAlwaysShown(boolean newValue) { decimalSeparatorAlwaysShown = newValue; } /** * Returns whether the * {@link #parse(java.lang.String, java.text.ParsePosition)} method returns * <code>BigDecimal</code>. The default value is false. * * @see #setParseBigDecimal * @since 1.5 */ @Override public boolean isParseBigDecimal() { return parseBigDecimal; } /** * Sets whether the {@link #parse(java.lang.String, java.text.ParsePosition)} * method returns <code>BigDecimal</code>. * * @see #isParseBigDecimal * @since 1.5 */ @Override public void setParseBigDecimal(boolean newValue) { parseBigDecimal = newValue; } /** * Standard override; no change in semantics. */ @Override public Object clone() { try { EfficientDecimalFormat other = (EfficientDecimalFormat) super.clone(); other.symbols = (DecimalFormatSymbols) symbols.clone(); other.digitList = (DigitList) digitList.clone(); return other; } catch (Exception e) { throw new InternalError(); } } /** * Overrides equals */ @Override public boolean equals(Object obj) { if (obj == null) return false; if (!super.equals(obj)) return false; // super does class check EfficientDecimalFormat other = (EfficientDecimalFormat) 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 && parseBigDecimal == other.parseBigDecimal && useExponentialNotation == other.useExponentialNotation && (!useExponentialNotation || minExponentDigits == other.minExponentDigits) && maximumIntegerDigits == other.maximumIntegerDigits && minimumIntegerDigits == other.minimumIntegerDigits && maximumFractionDigits == other.maximumFractionDigits && minimumFractionDigits == other.minimumFractionDigits && symbols.equals(other.symbols); } /** * Overrides hashCode */ @Override 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 */ @Override public String toPattern() { return toPattern(false); } /** * Synthesizes a localized pattern string that represents the current state * of this Format object. * * @see #applyPattern */ @Override public 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 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: if (i < pattern.length() && pattern.charAt(i) == CURRENCY_SIGN) { ++i; buffer.append(symbols.getInternationalCurrencySymbol()); } else { 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(); } /** * 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. 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. * <p> * 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 * <P> * Example <code>"#,#00.0#"</code> -> 1,234.56 * <P> * This means a minimum of 2 integer digits, 1 fraction digit, and a maximum * of 2 fraction digits. * <p> * Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in * parentheses. * <p> * In negative patterns, the minimum and maximum counts are ignored; these * are presumed to be set in the positive pattern. * * @exception NullPointerException * if <code>pattern</code> is null * @exception IllegalArgumentException * if the given pattern is invalid. */ @Override public void applyLocalizedPattern(String pattern) { applyPattern(pattern); } /** * Does the real work of applying a pattern. */ @Override public void applyPattern(String pattern) { 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; 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; 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, per mille, 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) { 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) { // Skip over the doubled character ++pos; } 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/per mille characters in pattern \"" + pattern + '"'); } multiplier = 100; affix.append("'%"); continue; } else if (ch == perMill) { if (multiplier != 1) { throw new IllegalArgumentException( "Too many percent/per mille 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 { 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; } } // 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) { // Handle ".###" ++n; } 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() : MAXIMUM_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(MAXIMUM_INTEGER_DIGITS); setMinimumFractionDigits(0); setMaximumFractionDigits(MAXIMUM_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. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of <code>newValue</code> * and 309 is used. Negative input values are replaced with 0. * * @see NumberFormat#setMaximumIntegerDigits */ @Override public void setMaximumIntegerDigits(int newValue) { maximumIntegerDigits = Math.min(Math.max(0, newValue), MAXIMUM_INTEGER_DIGITS); super .setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ? DOUBLE_INTEGER_DIGITS : maximumIntegerDigits); if (minimumIntegerDigits > maximumIntegerDigits) { minimumIntegerDigits = maximumIntegerDigits; super .setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ? DOUBLE_INTEGER_DIGITS : minimumIntegerDigits); } } /** * Sets the minimum number of digits allowed in the integer portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of <code>newValue</code> * and 309 is used. Negative input values are replaced with 0. * * @see NumberFormat#setMinimumIntegerDigits */ @Override public void setMinimumIntegerDigits(int newValue) { minimumIntegerDigits = Math.min(Math.max(0, newValue), MAXIMUM_INTEGER_DIGITS); super .setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ? DOUBLE_INTEGER_DIGITS : minimumIntegerDigits); if (minimumIntegerDigits > maximumIntegerDigits) { maximumIntegerDigits = minimumIntegerDigits; super .setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ? DOUBLE_INTEGER_DIGITS : maximumIntegerDigits); } } /** * Sets the maximum number of digits allowed in the fraction portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of <code>newValue</code> * and 340 is used. Negative input values are replaced with 0. * * @see NumberFormat#setMaximumFractionDigits */ @Override public void setMaximumFractionDigits(int newValue) { maximumFractionDigits = Math.min(Math.max(0, newValue), MAXIMUM_FRACTION_DIGITS); super .setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ? DOUBLE_FRACTION_DIGITS : maximumFractionDigits); if (minimumFractionDigits > maximumFractionDigits) { minimumFractionDigits = maximumFractionDigits; super .setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ? DOUBLE_FRACTION_DIGITS : minimumFractionDigits); } } /** * Sets the minimum number of digits allowed in the fraction portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of <code>newValue</code> * and 340 is used. Negative input values are replaced with 0. * * @see NumberFormat#setMinimumFractionDigits */ @Override public void setMinimumFractionDigits(int newValue) { minimumFractionDigits = Math.min(Math.max(0, newValue), MAXIMUM_FRACTION_DIGITS); super .setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ? DOUBLE_FRACTION_DIGITS : minimumFractionDigits); if (minimumFractionDigits > maximumFractionDigits) { maximumFractionDigits = minimumFractionDigits; super .setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ? DOUBLE_FRACTION_DIGITS : maximumFractionDigits); } } /** * Gets the maximum number of digits allowed in the integer portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of the return value and 309 * is used. * * @see #setMaximumIntegerDigits */ @Override public int getMaximumIntegerDigits() { return maximumIntegerDigits; } /** * Gets the minimum number of digits allowed in the integer portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of the return value and 309 * is used. * * @see #setMinimumIntegerDigits */ @Override public int getMinimumIntegerDigits() { return minimumIntegerDigits; } /** * Gets the maximum number of digits allowed in the fraction portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of the return value and 340 * is used. * * @see #setMaximumFractionDigits */ @Override public int getMaximumFractionDigits() { return maximumFractionDigits; } /** * Gets the minimum number of digits allowed in the fraction portion of a * number. For formatting numbers other than <code>BigInteger</code> and * <code>BigDecimal</code> objects, the lower of the return value and 340 * is used. * * @see #setMinimumFractionDigits */ @Override public int getMinimumFractionDigits() { return minimumFractionDigits; } /** * Gets the currency used by this decimal format when formatting currency * values. The currency is obtained by calling * {@link DecimalFormatSymbols#getCurrency DecimalFormatSymbols.getCurrency} * on this number format's symbols. * * @return the currency used by this decimal format, or <code>null</code> * @since 1.4 */ @Override public Currency getCurrency() { return symbols.getCurrency(); } /** * Adjusts the minimum and maximum fraction digits to values that are * reasonable for the currency's default fraction digits. */ void adjustForCurrencyDefaultFractionDigits() { Currency currency = symbols.getCurrency(); if (currency == null) { try { currency = Currency.getInstance(symbols .getInternationalCurrencySymbol()); } catch (IllegalArgumentException e) { } } if (currency != null) { int digits = currency.getDefaultFractionDigits(); if (digits != -1) { int oldMinDigits = getMinimumFractionDigits(); // Common patterns are "#.##", "#.00", "#". // Try to adjust all of them in a reasonable way. if (oldMinDigits == getMaximumFractionDigits()) { setMinimumFractionDigits(digits); setMaximumFractionDigits(digits); } else { setMinimumFractionDigits(Math.min(digits, oldMinDigits)); setMaximumFractionDigits(digits); } } } } /** * Reads the default serializable fields from the stream and performs * validations and adjustments for older serialized versions. The validations * and adjustments are: * <ol> * <li> Verify that the superclass's digit count fields correctly reflect the * limits imposed on formatting numbers other than <code>BigInteger</code> * and <code>BigDecimal</code> objects. These limits are stored in the * superclass for serialization compatibility with older versions, while the * limits for <code>BigInteger</code> and <code>BigDecimal</code> objects * are kept in this class. If, in the superclass, the minimum or maximum * integer digit count is larger than <code>DOUBLE_INTEGER_DIGITS</code> or * if the minimum or maximum fraction digit count is larger than * <code>DOUBLE_FRACTION_DIGITS</code>, then the stream data is invalid * and this method throws an <code>InvalidObjectException</code>. * <li> If <code>serialVersionOnStream</code> is less than 3, then call the * setters for the minimum and maximum integer and fraction digits with the * values of the corresponding superclass getters to initialize the fields in * this class. The fields in this class are new with version 3. * <li> If <code>serialVersionOnStream</code> is less than 1, indicating * that the stream was written by JDK 1.1, initialize * <code>useExponentialNotation</code> to false, since it was not present * in JDK 1.1. * <li> Set <code>serialVersionOnStream</code> to the maximum allowed value * so that default serialization will work properly if this object is * streamed out again. * </ol> * * <p> * Stream versions older than 2 will not have the affix pattern variables * <code>posPrefixPattern</code> etc. As a result, they will be initialized * to <code>null</code>, which means the affix strings will be taken as * literal values. This is exactly what we want, since that corresponds to * the pre-version-2 behavior. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); // We only need to check the maximum counts because NumberFormat // .readObject has already ensured that the maximum is greater than the // minimum count. if (super.getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS || super.getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) { throw new InvalidObjectException("Digit count out of range"); } if (serialVersionOnStream < 3) { setMaximumIntegerDigits(super.getMaximumIntegerDigits()); setMinimumIntegerDigits(super.getMinimumIntegerDigits()); setMaximumFractionDigits(super.getMaximumFractionDigits()); setMinimumFractionDigits(super.getMinimumFractionDigits()); } if (serialVersionOnStream < 1) { // Didn't have exponential fields useExponentialNotation = false; } serialVersionOnStream = currentSerialVersion; digitList = new DigitList(); } // ---------------------------------------------------------------------- // 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 * <code>positivePrefix</code>. * * <p> * This pattern is expanded by the method <code>expandAffix()</code> to * <code>positivePrefix</code> to update the latter to reflect changes in * <code>symbols</code>. If this variable is <code>null</code> then * <code>positivePrefix</code> is taken as a literal value that does not * change when <code>symbols</code> changes. This variable is always * <code>null</code> for <code>DecimalFormat</code> 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 * <code>positiveSuffix</code>. This variable is analogous to * <code>posPrefixPattern</code>; see that variable for further * documentation. * * @serial * @since 1.3 */ private String posSuffixPattern; /** * The prefix pattern for negative numbers. This variable corresponds to * <code>negativePrefix</code>. This variable is analogous to * <code>posPrefixPattern</code>; see that variable for further * documentation. * * @serial * @since 1.3 */ private String negPrefixPattern; /** * The suffix pattern for negative numbers. This variable corresponds to * <code>negativeSuffix</code>. This variable is analogous to * <code>posPrefixPattern</code>; see that variable for further * documentation. * * @serial * @since 1.3 */ private String negSuffixPattern; /** * The multiplier for use in percent, per mille, 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 <code>NumberFormat.groupingUsed</code> * is true. * * @serial * @see #getGroupingSize * @see java.text.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; /** * If true, parse returns BigDecimal wherever possible. * * @serial * @see #isParseBigDecimal * @since 1.5 */ private boolean parseBigDecimal = 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 <code>DecimalFormatSymbols</code> 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 java.text.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 <code>getPositivePrefixFieldPositions</code> when needed. */ private transient FieldPosition[] positivePrefixFieldPositions; /** * FieldPositions describing the positive suffix String. This is lazily * created. Use <code>getPositiveSuffixFieldPositions</code> when needed. */ private transient FieldPosition[] positiveSuffixFieldPositions; /** * FieldPositions describing the negative prefix String. This is lazily * created. Use <code>getNegativePrefixFieldPositions</code> when needed. */ private transient FieldPosition[] negativePrefixFieldPositions; /** * FieldPositions describing the negative suffix String. This is lazily * created. Use <code>getNegativeSuffixFieldPositions</code> 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 * <code>useExponentialNotation</code> is not true. * * @serial * @since 1.2 */ private byte minExponentDigits; // Newly // persistent // in // the // Java // 2 // platform /** * The maximum number of digits allowed in the integer portion of a * <code>BigInteger</code> or <code>BigDecimal</code> number. * <code>maximumIntegerDigits</code> must be greater than or equal to * <code>minimumIntegerDigits</code>. * * @serial * @see #getMaximumIntegerDigits * @since 1.5 */ private int maximumIntegerDigits = super .getMaximumIntegerDigits(); /** * The minimum number of digits allowed in the integer portion of a * <code>BigInteger</code> or <code>BigDecimal</code> number. * <code>minimumIntegerDigits</code> must be less than or equal to * <code>maximumIntegerDigits</code>. * * @serial * @see #getMinimumIntegerDigits * @since 1.5 */ private int minimumIntegerDigits = super .getMinimumIntegerDigits(); /** * The maximum number of digits allowed in the fractional portion of a * <code>BigInteger</code> or <code>BigDecimal</code> number. * <code>maximumFractionDigits</code> must be greater than or equal to * <code>minimumFractionDigits</code>. * * @serial * @see #getMaximumFractionDigits * @since 1.5 */ private int maximumFractionDigits = super .getMaximumFractionDigits(); /** * The minimum number of digits allowed in the fractional portion of a * <code>BigInteger</code> or <code>BigDecimal</code> number. * <code>minimumFractionDigits</code> must be less than or equal to * <code>maximumFractionDigits</code>. * * @serial * @see #getMinimumFractionDigits * @since 1.5 */ private int minimumFractionDigits = super .getMinimumFractionDigits(); // ---------------------------------------------------------------------- static final int currentSerialVersion = 3; /** * The internal serial version which says which version was written. Possible * values are: * <ul> * <li><b>0</b> (default): versions before the Java 2 platform v1.2 * <li><b>1</b>: version for 1.2, which includes the two new fields * <code>useExponentialNotation</code> and <code>minExponentDigits</code>. * <li><b>2</b>: version for 1.3 and later, which adds four new fields: * <code>posPrefixPattern</code>, <code>posSuffixPattern</code>, * <code>negPrefixPattern</code>, and <code>negSuffixPattern</code>. * <li><b>3</b>: version for 5 and later, which adds five new fields: * <code>maximumIntegerDigits</code>, <code>minimumIntegerDigits</code>, * <code>maximumFractionDigits</code>, <code>minimumFractionDigits</code>, * and <code>parseBigDecimal</code>. * </ul> * * @since 1.2 * @serial */ private int serialVersionOnStream = currentSerialVersion; // ---------------------------------------------------------------------- // CONSTANTS // ---------------------------------------------------------------------- // Constants for characters used in programmatic (unlocalized) patterns. private static final char PATTERN_ZERO_DIGIT = '0'; private static final char PATTERN_GROUPING_SEPARATOR = ','; private static final char PATTERN_DECIMAL_SEPARATOR = '.'; private static final char PATTERN_PER_MILLE = '\u2030'; private static final char PATTERN_PERCENT = '%'; private static final char PATTERN_DIGIT = '#'; private static final char PATTERN_SEPARATOR = ';'; private static final char PATTERN_EXPONENT = 'E'; private static final char PATTERN_MINUS = '-'; /** * The CURRENCY_SIGN is the standard Unicode symbol for currency. It is used * in patterns and substituted with either the currency symbol, or if it is * doubled, with the international currency symbol. If the CURRENCY_SIGN is * seen in a pattern, then the decimal separator is replaced with the * monetary decimal separator. * * The CURRENCY_SIGN is not localized. */ private static final char CURRENCY_SIGN = '\u00A4'; private static final char QUOTE = '\''; private static FieldPosition[] EmptyFieldPositionArray = new FieldPosition[0]; // Upper limit on integer and fraction digits for a Java double static final int DOUBLE_INTEGER_DIGITS = 309; static final int DOUBLE_FRACTION_DIGITS = 340; // Upper limit on integer and fraction digits for BigDecimal and BigInteger static final int MAXIMUM_INTEGER_DIGITS = Integer.MAX_VALUE; static final int MAXIMUM_FRACTION_DIGITS = Integer.MAX_VALUE; // Proclaim JDK 1.1 serial compatibility. static final long serialVersionUID = 864413376551465018L; }