diff -r 000000000000 -r 588d5bf7a560 rt/emul/compact/src/main/java/java/text/DecimalFormat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/main/java/java/text/DecimalFormat.java Thu Oct 03 15:40:35 2013 +0200 @@ -0,0 +1,3278 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (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 java.text; + +import java.io.InvalidObjectException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Currency; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import sun.util.resources.LocaleData; + +/** + * 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 of NumberFormat's factory methods, such + * as getInstance(). 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 a DecimalFormatSymbols object. When using + * the NumberFormat factory methods, the pattern and symbols are + * read from localized ResourceBundles. + * + *

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 ('-' in most locales) is used as the + * negative subpattern. That is, "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 value + *
¤ (\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". + * + *

+ * + *

Rounding

+ * + * DecimalFormat provides rounding modes defined in + * {@link java.math.RoundingMode} for formatting. By default, it uses + * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}. + * + *

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 string, which typically has a single character + * \uFFFD. This string 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 string, which typically has a single character + * \u221E, with the positive or negative prefixes and suffixes + * applied. The infinity string is determined by the + * DecimalFormatSymbols object. + * + *

Negative zero ("-0") parses to + *

+ * + *

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

+ * + *
+ * // 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) {}
+ *     }
+ * }
+ * 
+ * + * @see Java Tutorial + * @see NumberFormat + * @see DecimalFormatSymbols + * @see ParsePosition + * @author Mark Davis + * @author Alan Liu + */ +public class DecimalFormat extends NumberFormat { + + /** + * 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. + *

+ * 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 java.text.NumberFormat#getInstance + * @see java.text.NumberFormat#getNumberInstance + * @see java.text.NumberFormat#getCurrencyInstance + * @see java.text.NumberFormat#getPercentInstance + */ + public DecimalFormat() { + Locale def = Locale.getDefault(Locale.Category.FORMAT); + // try to get the pattern from the cache + String pattern = cachedLocaleData.get(def); + if (pattern == null) { /* cache miss */ + // Get the pattern for the default locale. + ResourceBundle rb = LocaleData.getNumberFormatData(def); + String[] all = rb.getStringArray("NumberPatterns"); + pattern = all[0]; + /* update cache */ + cachedLocaleData.putIfAbsent(def, pattern); + } + + // Always applyPattern after the symbols are set + this.symbols = new DecimalFormatSymbols(def); + applyPattern(pattern, 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 java.text.NumberFormat#getInstance + * @see java.text.NumberFormat#getNumberInstance + * @see java.text.NumberFormat#getCurrencyInstance + * @see java.text.NumberFormat#getPercentInstance + */ + public DecimalFormat(String pattern) { + // Always applyPattern after the symbols are set + this.symbols = new DecimalFormatSymbols(Locale.getDefault(Locale.Category.FORMAT)); + 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 java.text.NumberFormat#getInstance + * @see java.text.NumberFormat#getNumberInstance + * @see java.text.NumberFormat#getCurrencyInstance + * @see java.text.NumberFormat#getPercentInstance + * @see java.text.DecimalFormatSymbols + */ + public DecimalFormat (String pattern, DecimalFormatSymbols symbols) { + // Always applyPattern after the symbols are set + this.symbols = (DecimalFormatSymbols)symbols.clone(); + applyPattern(pattern, false); + } + + + // 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}. + *

+ * This implementation uses the maximum precision permitted. + * @param number the number to format + * @param toAppendTo the StringBuffer 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 toAppendTo + * @exception IllegalArgumentException if number is + * null or not an instance of Number. + * @exception NullPointerException if toAppendTo or + * pos is null + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @see java.text.FieldPosition + */ + public final StringBuffer format(Object number, + StringBuffer toAppendTo, + FieldPosition pos) { + if (number instanceof Long || number instanceof Integer || + number instanceof Short || number instanceof Byte || + number instanceof AtomicInteger || + number instanceof AtomicLong || + (number instanceof BigInteger && + ((BigInteger)number).bitLength () < 64)) { + return format(((Number)number).longValue(), toAppendTo, pos); + } else if (number instanceof BigDecimal) { + return format((BigDecimal)number, toAppendTo, pos); + } else if (number instanceof BigInteger) { + return format((BigInteger)number, toAppendTo, pos); + } else if (number instanceof Number) { + return format(((Number)number).doubleValue(), toAppendTo, pos); + } 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 fieldPosition On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @return The formatted number string + * @see java.text.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 + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @return The formatted number string + */ + private StringBuffer format(double number, StringBuffer result, + FieldDelegate delegate) { + if (Double.isNaN(number) || + (Double.isInfinite(number) && multiplier == 0)) { + 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)) ^ (multiplier < 0); + + 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; + } + + 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(isNegative, number, useExponentialNotation ? + maxIntDigits + maxFraDigits : maxFraDigits, + !useExponentialNotation); + return subformat(result, delegate, 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 fieldPosition On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @return The formatted number string + * @see java.text.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 + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @see java.text.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 + // 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, delegate, 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(isNegative, number, + useExponentialNotation ? maxIntDigits + maxFraDigits : 0); + + return subformat(result, delegate, 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 fieldPosition On input: an alignment field, if desired. + * On output: the offsets of the alignment field. + * @return The formatted number string + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @see java.text.FieldPosition + */ + private StringBuffer format(BigDecimal number, StringBuffer result, + FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + return format(number, result, fieldPosition.getFieldDelegate()); + } + + /** + * 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 + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @return The formatted number string + */ + private StringBuffer format(BigDecimal number, StringBuffer result, + FieldDelegate delegate) { + 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(isNegative, number, useExponentialNotation ? + ((maximumDigits < 0) ? Integer.MAX_VALUE : maximumDigits) : + maxFraDigits, !useExponentialNotation); + + return subformat(result, delegate, 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 + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @see java.text.FieldPosition + */ + private StringBuffer format(BigInteger number, StringBuffer result, + FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + + return format(number, result, fieldPosition.getFieldDelegate(), 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 + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @see java.text.FieldPosition + */ + private StringBuffer format(BigInteger number, StringBuffer result, + FieldDelegate delegate, boolean formatLong) { + 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(isNegative, number, + useExponentialNotation ? maximumDigits : 0); + + return subformat(result, delegate, isNegative, true, + maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); + } + } + + /** + * 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. + * @exception ArithmeticException if rounding is needed with rounding + * mode being set to RoundingMode.UNNECESSARY + * @param obj The object to format + * @return AttributedCharacterIterator describing the formatted value. + * @since 1.4 + */ + public AttributedCharacterIterator formatToCharacterIterator(Object obj) { + CharacterIteratorFieldDelegate delegate = + new CharacterIteratorFieldDelegate(); + StringBuffer sb = new StringBuffer(); + + if (obj instanceof Double || obj instanceof Float) { + format(((Number)obj).doubleValue(), sb, delegate); + } else if (obj instanceof Long || obj instanceof Integer || + obj instanceof Short || obj instanceof Byte || + obj instanceof AtomicInteger || obj instanceof AtomicLong) { + format(((Number)obj).longValue(), sb, delegate); + } else if (obj instanceof BigDecimal) { + format((BigDecimal)obj, sb, delegate); + } else if (obj instanceof BigInteger) { + format((BigInteger)obj, sb, delegate, false); + } else if (obj == null) { + throw new NullPointerException( + "formatToCharacterIterator must be passed non-null object"); + } 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, + int maxIntDigits, int minIntDigits, + int maxFraDigits, int minFraDigits) { + // 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 + } + + 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 = maxIntDigits; + int minimumIntegerDigits = minIntDigits; + 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 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; + } + + 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 = (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 && 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 < 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); + } + } + + // 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 + * FieldPositions in positions. + *

+ * If one of the FieldPositions 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 subclass returned depends on the value of {@link #isParseBigDecimal} + * as well as on the string being parsed. + *

+ *

+ * 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, 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(), roundingMode); + } + } + + 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 != (double)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.index; + int oldStart = parsePosition.index; + 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.errorIndex = 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(); + String exponentString = symbols.getExponentSeparator(); + 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 && text.regionMatches(position, exponentString, 0, exponentString.length()) + && !sawExponent) { + // Process the exponent by recursively calling this method. + ParsePosition pos = new ParsePosition(position + exponentString.length()); + 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.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 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.errorIndex = position; + return false; + } + + parsePosition.index = position + + (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success! + } else { + parsePosition.index = position; + } + + status[STATUS_POSITIVE] = gotPositive; + if (parsePosition.index == oldStart) { + parsePosition.errorIndex = position; + return false; + } + return true; + } + + /** + * Returns a copy of the decimal format symbols, which is generally not + * changed by the programmer or user. + * @return a copy of the desired DecimalFormatSymbols + * @see java.text.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 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. + *

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 negative 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; + } + + /** + * Gets the multiplier for use in percent, per mille, and similar + * formats. + * + * @see #setMultiplier(int) + */ + 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'. + * + *

Example: with multiplier 100, 1.23 is formatted as "123", and + * "123" is parsed into 1.23. + * + * @see #getMultiplier + */ + 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 + */ + 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. + *
+ * 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 + */ + 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; + } + + /** + * Returns whether the {@link #parse(java.lang.String, java.text.ParsePosition)} + * method returns BigDecimal. The default value is false. + * @see #setParseBigDecimal + * @since 1.5 + */ + public boolean isParseBigDecimal() { + return parseBigDecimal; + } + + /** + * Sets whether the {@link #parse(java.lang.String, java.text.ParsePosition)} + * method returns BigDecimal. + * @see #isParseBigDecimal + * @since 1.5 + */ + public void setParseBigDecimal(boolean newValue) { + parseBigDecimal = newValue; + } + + /** + * Standard override; no change in semantics. + */ + public Object clone() { + try { + DecimalFormat other = (DecimalFormat) super.clone(); + other.symbols = (DecimalFormatSymbols) symbols.clone(); + other.digitList = (DigitList) digitList.clone(); + return other; + } catch (Exception e) { + throw new InternalError(); + } + } + + /** + * 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 + && parseBigDecimal == other.parseBigDecimal + && useExponentialNotation == other.useExponentialNotation + && (!useExponentialNotation || + minExponentDigits == other.minExponentDigits) + && maximumIntegerDigits == other.maximumIntegerDigits + && minimumIntegerDigits == other.minimumIntegerDigits + && maximumFractionDigits == other.maximumFractionDigits + && minimumFractionDigits == other.minimumFractionDigits + && roundingMode == other.roundingMode + && 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 + */ + 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 0) { + if (positions == null) { + positions = new ArrayList(2); + } + FieldPosition fp = new FieldPosition(Field.CURRENCY); + fp.setBeginIndex(stringIndex); + fp.setEndIndex(stringIndex + string.length()); + positions.add(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 ArrayList(2); + } + FieldPosition fp = new FieldPosition(fieldID, field); + fp.setBeginIndex(stringIndex); + fp.setEndIndex(stringIndex + 1); + positions.add(fp); + } + } + stringIndex++; + } + if (positions != null) { + return (FieldPosition[])positions.toArray(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 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= 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= 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.getExponentSeparator() : + PATTERN_EXPONENT); + for (i=0; i + * There is no limit to integer digits 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 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 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; + String 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.getExponentSeparator(); + 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 (pattern.regionMatches(pos, exponent, 0, exponent.length())){ + 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. + pos = pos+exponent.length(); + while (pos < pattern.length() && + pattern.charAt(pos) == zeroDigit) { + ++minExponentDigits; + ++phaseOneLength; + ++pos; + } + + 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 BigInteger and + * BigDecimal objects, the lower of newValue and + * 309 is used. Negative input values are replaced with 0. + * @see NumberFormat#setMaximumIntegerDigits + */ + 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 BigInteger and + * BigDecimal objects, the lower of newValue and + * 309 is used. Negative input values are replaced with 0. + * @see NumberFormat#setMinimumIntegerDigits + */ + 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 BigInteger and + * BigDecimal objects, the lower of newValue and + * 340 is used. Negative input values are replaced with 0. + * @see NumberFormat#setMaximumFractionDigits + */ + 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 BigInteger and + * BigDecimal objects, the lower of newValue and + * 340 is used. Negative input values are replaced with 0. + * @see NumberFormat#setMinimumFractionDigits + */ + 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 BigInteger and + * BigDecimal objects, the lower of the return value and + * 309 is used. + * @see #setMaximumIntegerDigits + */ + public int getMaximumIntegerDigits() { + return maximumIntegerDigits; + } + + /** + * Gets the minimum number of digits allowed in the integer portion of a + * number. + * For formatting numbers other than BigInteger and + * BigDecimal objects, the lower of the return value and + * 309 is used. + * @see #setMinimumIntegerDigits + */ + public int getMinimumIntegerDigits() { + return minimumIntegerDigits; + } + + /** + * Gets the maximum number of digits allowed in the fraction portion of a + * number. + * For formatting numbers other than BigInteger and + * BigDecimal objects, the lower of the return value and + * 340 is used. + * @see #setMaximumFractionDigits + */ + public int getMaximumFractionDigits() { + return maximumFractionDigits; + } + + /** + * Gets the minimum number of digits allowed in the fraction portion of a + * number. + * For formatting numbers other than BigInteger and + * BigDecimal objects, the lower of the return value and + * 340 is used. + * @see #setMinimumFractionDigits + */ + 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 null + * @since 1.4 + */ + public Currency getCurrency() { + return symbols.getCurrency(); + } + + /** + * Sets the currency used by this number format when formatting + * currency values. This does not update the minimum or maximum + * number of fraction digits used by the number format. + * The currency is set by calling + * {@link DecimalFormatSymbols#setCurrency DecimalFormatSymbols.setCurrency} + * on this number format's symbols. + * + * @param currency the new currency to be used by this decimal format + * @exception NullPointerException if currency is null + * @since 1.4 + */ + public void setCurrency(Currency currency) { + if (currency != symbols.getCurrency()) { + symbols.setCurrency(currency); + if (isCurrencyFormat) { + expandAffixes(); + } + } + } + + /** + * Gets the {@link java.math.RoundingMode} used in this DecimalFormat. + * + * @return The RoundingMode used for this DecimalFormat. + * @see #setRoundingMode(RoundingMode) + * @since 1.6 + */ + public RoundingMode getRoundingMode() { + return roundingMode; + } + + /** + * Sets the {@link java.math.RoundingMode} used in this DecimalFormat. + * + * @param roundingMode The RoundingMode to be used + * @see #getRoundingMode() + * @exception NullPointerException if roundingMode is null. + * @since 1.6 + */ + public void setRoundingMode(RoundingMode roundingMode) { + if (roundingMode == null) { + throw new NullPointerException(); + } + + this.roundingMode = roundingMode; + digitList.setRoundingMode(roundingMode); + } + + /** + * 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: + *

    + *
  1. + * Verify that the superclass's digit count fields correctly reflect + * the limits imposed on formatting numbers other than + * BigInteger and BigDecimal objects. These + * limits are stored in the superclass for serialization compatibility + * with older versions, while the limits for BigInteger and + * BigDecimal objects are kept in this class. + * If, in the superclass, the minimum or maximum integer digit count is + * larger than DOUBLE_INTEGER_DIGITS or if the minimum or + * maximum fraction digit count is larger than + * DOUBLE_FRACTION_DIGITS, then the stream data is invalid + * and this method throws an InvalidObjectException. + *
  2. + * If serialVersionOnStream is less than 4, initialize + * roundingMode to {@link java.math.RoundingMode#HALF_EVEN + * RoundingMode.HALF_EVEN}. This field is new with version 4. + *
  3. + * If serialVersionOnStream 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. + *
  4. + * If serialVersionOnStream is less than 1, indicating that + * the stream was written by JDK 1.1, initialize + * useExponentialNotation + * to false, since it was not present in JDK 1.1. + *
  5. + * Set serialVersionOnStream to the maximum allowed value so + * that default serialization will work properly if this object is streamed + * out again. + *
+ * + *

Stream versions older than 2 will not have the affix pattern variables + * posPrefixPattern etc. As a result, they will be initialized + * to null, 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(); + digitList = new DigitList(); + + if (serialVersionOnStream < 4) { + setRoundingMode(RoundingMode.HALF_EVEN); + } + // 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; + } + + //---------------------------------------------------------------------- + // 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, 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 + * NumberFormat.groupingUsed 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 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 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 v.1.2 + + /** + * 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 v.1.2 + + /** + * The maximum number of digits allowed in the integer portion of a + * BigInteger or BigDecimal number. + * maximumIntegerDigits must be greater than or equal to + * minimumIntegerDigits. + * + * @serial + * @see #getMaximumIntegerDigits + * @since 1.5 + */ + private int maximumIntegerDigits = super.getMaximumIntegerDigits(); + + /** + * The minimum number of digits allowed in the integer portion of a + * BigInteger or BigDecimal number. + * minimumIntegerDigits must be less than or equal to + * maximumIntegerDigits. + * + * @serial + * @see #getMinimumIntegerDigits + * @since 1.5 + */ + private int minimumIntegerDigits = super.getMinimumIntegerDigits(); + + /** + * The maximum number of digits allowed in the fractional portion of a + * BigInteger or BigDecimal number. + * maximumFractionDigits must be greater than or equal to + * minimumFractionDigits. + * + * @serial + * @see #getMaximumFractionDigits + * @since 1.5 + */ + private int maximumFractionDigits = super.getMaximumFractionDigits(); + + /** + * The minimum number of digits allowed in the fractional portion of a + * BigInteger or BigDecimal number. + * minimumFractionDigits must be less than or equal to + * maximumFractionDigits. + * + * @serial + * @see #getMinimumFractionDigits + * @since 1.5 + */ + private int minimumFractionDigits = super.getMinimumFractionDigits(); + + /** + * The {@link java.math.RoundingMode} used in this DecimalFormat. + * + * @serial + * @since 1.6 + */ + private RoundingMode roundingMode = RoundingMode.HALF_EVEN; + + //---------------------------------------------------------------------- + + static final int currentSerialVersion = 4; + + /** + * The internal serial version which says which version was written. + * Possible values are: + *

+ * @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 String 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; + + /** + * Cache to hold the NumberPattern of a Locale. + */ + private static final ConcurrentMap cachedLocaleData + = new ConcurrentHashMap(3); +}