All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tec.units.ri.internal.format.l10n.DecimalFormat Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
/*
 * Units of Measurement Reference Implementation
 * Copyright (c) 2005-2016, Jean-Marie Dautelle, Werner Keil, V2COM.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
 *
 *   The original version of this source code and documentation is copyrighted
 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
 * materials are provided under terms of a License Agreement between Taligent
 * and Sun. This technology is protected by multiple US and International
 * patents. This notice and attribution to Taligent may not be removed.
 *   Taligent is a registered trademark of Taligent, Inc.
 *
 */

package tec.units.ri.internal.format.l10n;

import java.util.Vector;

/**
 * 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 (code>'-' 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 *
¤ (\u00A4) * Prefix or suffix * No * Currency sign, replaced by currency symbol. If doubled, replaced by international currency symbol. If present in a pattern, the monetary * decimal separator is used instead of the decimal separator. *
' * Prefix or suffix * No * Used to quote special characters in a prefix or suffix, for example, "'#'#" formats 123 to "#123". To create a single * quote itself, use two in a row: "# o''clock". *
*
* *

Scientific Notation

* *

* Numbers in scientific notation are expressed as the product of a mantissa and a power of ten, for example, 1234 can be expressed as 1.234 x 10^3. * The mantissa is often in the range 1.0 <= x < 10.0, but it need not be. DecimalFormat can be instructed to format and parse scientific * notation only via a pattern; there is currently no factory method that creates a scientific notation format. In a pattern, the exponent * character immediately followed by one or more digit characters indicates scientific notation. Example: "0.###E0" formats the number * 1234 as "1.234E3". * *

    *
  • The number of digit characters after the exponent character gives the minimum exponent digit count. There is no maximum. Negative exponents are * formatted using the localized minus sign, not the prefix and suffix from the pattern. This allows patterns such as * "0.###E0 m/s". * *
  • The minimum and maximum number of integer digits are interpreted together: * *
      *
    • If the maximum number of integer digits is greater than their minimum number and greater than 1, it forces the exponent to be a multiple of the * maximum number of integer digits, and the minimum number of integer digits to be interpreted as 1. The most common use of this is to generate * engineering notation, in which the exponent is a multiple of three, e.g., "##0.#####E0". Using this pattern, the number 12345 * formats to "12.345E3", and 123456 formats to "123.456E3". * *
    • Otherwise, the minimum number of integer digits is achieved by adjusting the exponent. Example: 0.00123 formatted with "00.###E0" * yields "12.3E-4". *
    * *
  • The number of significant digits in the mantissa is the sum of the minimum integer and maximum fraction digits, and is * unaffected by the maximum integer digits. For example, 12345 formatted with "##0.##E0" is "12.3E3". To show all digits, * set the significant digits count to zero. The number of significant digits does not affect parsing. * *
  • Exponential patterns may not contain grouping separators. *
* *

Rounding

* * DecimalFormat uses half-even rounding (see {@link java.math.BigDecimal#ROUND_HALF_EVEN ROUND_HALF_EVEN}) for formatting. * *

Digits

* * For formatting, DecimalFormat uses the ten consecutive characters starting with the localized zero digit defined in the * DecimalFormatSymbols object as digits. For parsing, these digits as well as all Unicode decimal digits, as defined by * {@link Character#digit Character.digit}, are recognized. * *

Special Values

* *

* NaN is formatted as a single character, typically \uFFFD. This character is determined by the * DecimalFormatSymbols object. This is the only value for which the prefixes and suffixes are not used. * *

* Infinity is formatted as a single character, typically \u221E, with the positive or negative prefixes and suffixes applied. The * infinity character is determined by the DecimalFormatSymbols object. * *

* Negative zero ("-0") parses to Double(-0.0), unless isParseIntegerOnly() is true, in which case it parses to * Long(0). * *

Synchronization

* *

* Decimal formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a * format concurrently, it must be synchronized externally. * *

Example

* *
* *
 * // 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 * @author Werner Keil */ class DecimalFormat extends NumberFormat { private String mPattern = "#,##0.###"; /** * Creates a DecimalFormat using the default pattern and symbols for the default locale. This is a convenient way to obtain a DecimalFormat when * internationalization is not the main concern. *

* To obtain standard formats for a given locale, use the factory methods on NumberFormat such as getNumberInstance. These factories will return the * most appropriate sub-class of NumberFormat for a given locale. * * @see NumberFormat#getInstance * @see NumberFormat#getNumberInstance * @see NumberFormat#getCurrencyInstance * @see NumberFormat#getPercentInstance */ DecimalFormat() { // Always applyPattern after the symbols are set this.symbols = new DecimalFormatSymbols(); applyPattern(mPattern, false); } /** * Creates a DecimalFormat using the given pattern and the symbols for the default locale. This is a convenient way to obtain a DecimalFormat when * internationalization is not the main concern. *

* To obtain standard formats for a given locale, use the factory methods on NumberFormat such as getNumberInstance. These factories will return the * most appropriate sub-class of NumberFormat for a given locale. * * @param pattern * A non-localized pattern string. * @exception NullPointerException * if pattern is null * @exception IllegalArgumentException * if the given pattern is invalid. * @see NumberFormat#getInstance * @see NumberFormat#getNumberInstance * @see NumberFormat#getCurrencyInstance * @see NumberFormat#getPercentInstance */ DecimalFormat(String pattern) { // Always applyPattern after the symbols are set this.symbols = new DecimalFormatSymbols(); applyPattern(pattern, false); } /** * Creates a DecimalFormat using the given pattern and symbols. Use this constructor when you need to completely customize the behavior of the * format. *

* To obtain standard formats for a given locale, use the factory methods on NumberFormat such as getInstance or getCurrencyInstance. If you need * only minor adjustments to a standard format, you can modify the format returned by a NumberFormat factory method. * * @param pattern * a non-localized pattern string * @param symbols * the set of symbols to be used * @exception NullPointerException * if any of the given arguments is null * @exception IllegalArgumentException * if the given pattern is invalid * @see NumberFormat#getInstance * @see NumberFormat#getNumberInstance * @see NumberFormat#getCurrencyInstance * @see NumberFormat#getPercentInstance * @see DecimalFormatSymbols */ DecimalFormat(String pattern, DecimalFormatSymbols symbols) { // Always applyPattern after the symbols are set this.symbols = (DecimalFormatSymbols) symbols.clone(); applyPattern(pattern, false); } // Overrides /** * Formats a double to produce a string. * * @param number * The double to format * @param result * where the text is to be appended * @param fieldPosition * On input: an alignment field, if desired. On output: the offsets of the alignment field. * @return The formatted number string * @see FieldPosition */ public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); return format(number, result, fieldPosition.getFieldDelegate()); } /** * Formats a double to produce a string. * * @param number * The double to format * @param result * where the text is to be appended * @param delegate * notified of locations of sub fields * @return The formatted number string */ private StringBuffer format(double number, StringBuffer result, FieldDelegate delegate) { if (Double.isNaN(number)) { int iFieldStart = result.length(); result.append(symbols.getNaN()); delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER, iFieldStart, result.length(), result); return result; } /* * Detecting whether a double is negative is easy with the exception of * the value -0.0. This is a double which has a zero mantissa (and * exponent), but a negative sign bit. It is semantically distinct from * a zero with a positive sign bit, and this distinction is important to * certain kinds of computations. However, it's a little tricky to * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may ask, * does it behave distinctly from +0.0? Well, 1/(-0.0) == -Infinity. * Proper detection of -0.0 is needed to deal with the issues raised by * bugs 4106658, 4106667, and 4147706. Liu 7/6/98. */ boolean isNegative = (number < 0.0) || (number == 0.0 && 1 / number < 0.0); if (isNegative) number = -number; // Do this BEFORE checking to see if value is infinite! if (multiplier != 1) number *= multiplier; if (Double.isInfinite(number)) { if (isNegative) { append(result, negativePrefix, delegate, getNegativePrefixFieldPositions(), Field.SIGN); } else { append(result, positivePrefix, delegate, getPositivePrefixFieldPositions(), Field.SIGN); } int iFieldStart = result.length(); result.append(symbols.getInfinity()); delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER, iFieldStart, result.length(), result); if (isNegative) { append(result, negativeSuffix, delegate, getNegativeSuffixFieldPositions(), Field.SIGN); } else { append(result, positiveSuffix, delegate, getPositiveSuffixFieldPositions(), Field.SIGN); } return result; } // At this point we are guaranteed a nonnegative finite // number. synchronized (digitList) { digitList.set(number, useExponentialNotation ? getMaximumIntegerDigits() + getMaximumFractionDigits() : getMaximumFractionDigits(), !useExponentialNotation); return subformat(result, delegate, isNegative, false); } } /** * Format a long to produce a string. * * @param number * The long to format * @param result * where the text is to be appended * @param fieldPosition * On input: an alignment field, if desired. On output: the offsets of the alignment field. * @return The formatted number string * @see FieldPosition */ public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); return format(number, result, fieldPosition.getFieldDelegate()); } /** * Format a long to produce a string. * * @param number * The long to format * @param result * where the text is to be appended * @param delegate * notified of locations of sub fields * @return The formatted number string * @see FieldPosition */ private StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) { boolean isNegative = (number < 0); if (isNegative) number = -number; // In general, long values always represent real finite numbers, so // we don't have to check for +/- Infinity or NaN. However, there // is one case we have to be careful of: The multiplier can push // a number near MIN_VALUE or MAX_VALUE outside the legal range. We // check for this before multiplying, and if it happens we use doubles // instead, trading off accuracy for range. if (multiplier != 1 && multiplier != 0) { boolean useDouble = false; if (number < 0) // This can only happen if number == Long.MIN_VALUE { long cutoff = Long.MIN_VALUE / multiplier; useDouble = (number < cutoff); } else { long cutoff = Long.MAX_VALUE / multiplier; useDouble = (number > cutoff); } if (useDouble) { double dnumber = (double) (isNegative ? -number : number); return format(dnumber, result, delegate); } } number *= multiplier; synchronized (digitList) { digitList.set(number, useExponentialNotation ? getMaximumIntegerDigits() + getMaximumFractionDigits() : 0); return subformat(result, delegate, isNegative, true); } } /** * Formats an Object producing an AttributedCharacterIterator. You can use the returned AttributedCharacterIterator to * build the resulting String, as well as to determine information about the resulting String. *

* Each attribute key of the AttributedCharacterIterator will be of type NumberFormat.Field, with the attribute value being the same as * the attribute key. * * @exception NullPointerException * if obj is null. * @exception IllegalArgumentException * when the Format cannot format the given object. * @param obj * The object to format * @return AttributedCharacterIterator describing the formatted value. */ AttributedCharacterIterator formatToCharacterIterator(Object obj) { AttributedCharacterFieldDelegate delegate = new AttributedCharacterFieldDelegate(); StringBuffer sb = new StringBuffer(); if (obj instanceof Long) { format(((Long) obj).longValue(), sb, delegate); } else if (obj == null) { throw new NullPointerException("formatToCharacterIterator must be passed non-null object"); } else if (obj instanceof Double) { format(((Double) obj).doubleValue(), sb, delegate); } else { throw new IllegalArgumentException("Cannot format given Object as a Number"); } return delegate.getIterator(sb.toString()); } /** * Complete the formatting of a finite number. On entry, the digitList must be filled in with the correct digits. */ private StringBuffer subformat(StringBuffer result, FieldDelegate delegate, boolean isNegative, boolean isInteger) { // NOTE: This isn't required anymore because DigitList takes care of // this. // // // The negative of the exponent represents the number of leading // // zeros between the decimal and the first non-zero digit, for // // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this // // is more than the maximum fraction digits, then we have an // underflow // // for the printed representation. We recognize this here and set // // the DigitList representation to zero in this situation. // // if (-digitList.decimalAt >= getMaximumFractionDigits()) // { // digitList.count = 0; // } char zero = symbols.getZeroDigit(); int zeroDelta = zero - '0'; // '0' is the DigitList representation of // zero char grouping = symbols.getGroupingSeparator(); char decimal = isCurrencyFormat ? symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator(); /* * Per bug 4147706, DecimalFormat must respect the sign of numbers which * format as zero. This allows sensible computations and preserves * relations such as signum(1/x) = signum(x), where x is +Infinity or * -Infinity. Prior to this fix, we always formatted zero values as if * they were positive. Liu 7/6/98. */ if (digitList.isZero()) { digitList.decimalAt = 0; // Normalize } int fieldStart = result.length(); if (isNegative) { append(result, negativePrefix, delegate, getNegativePrefixFieldPositions(), Field.SIGN); } else { append(result, positivePrefix, delegate, getPositivePrefixFieldPositions(), Field.SIGN); } if (useExponentialNotation) { int iFieldStart = result.length(); int iFieldEnd = -1; int fFieldStart = -1; // Minimum integer digits are handled in exponential format by // adjusting the exponent. For example, 0.01234 with 3 minimum // integer digits is "123.4E-4". // Maximum integer digits are interpreted as indicating the // repeating range. This is useful for engineering notation, in // which the exponent is restricted to a multiple of 3. For // example, 0.01234 with 3 maximum integer digits is "12.34e-3". // If maximum integer digits are > 1 and are larger than // minimum integer digits, then minimum integer digits are // ignored. int exponent = digitList.decimalAt; int repeat = getMaximumIntegerDigits(); int minimumIntegerDigits = getMinimumIntegerDigits(); if (repeat > 1 && repeat > minimumIntegerDigits) { // A repeating range is defined; adjust to it as follows. // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3; // -3,-4,-5=>-6, etc. This takes into account that the // exponent we have here is off by one from what we expect; // it is for the format 0.MMMMMx10^n. if (exponent >= 1) { exponent = ((exponent - 1) / repeat) * repeat; } else { // integer division rounds towards 0 exponent = ((exponent - repeat) / repeat) * repeat; } minimumIntegerDigits = 1; } else { // No repeating range is defined; use minimum integer digits. exponent -= minimumIntegerDigits; } // We now output a minimum number of digits, and more if there // are more digits, up to the maximum number of digits. We // place the decimal point after the "integer" digits, which // are the first (decimalAt - exponent) digits. int minimumDigits = getMinimumIntegerDigits() + getMinimumFractionDigits(); // The number of integer digits is handled specially if the number // is zero, since then there may be no digits. int integerDigits = digitList.isZero() ? minimumIntegerDigits : digitList.decimalAt - exponent; if (minimumDigits < integerDigits) { minimumDigits = integerDigits; } int totalDigits = digitList.count; if (minimumDigits > totalDigits) totalDigits = minimumDigits; boolean addedDecimalSeparator = false; for (int i = 0; i < totalDigits; ++i) { if (i == integerDigits) { // Record field information for caller. iFieldEnd = result.length(); result.append(decimal); addedDecimalSeparator = true; // Record field information for caller. fFieldStart = result.length(); } result.append((i < digitList.count) ? (char) (digitList.digits[i] + zeroDelta) : zero); } // Record field information if (iFieldEnd == -1) { iFieldEnd = result.length(); } delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER, iFieldStart, iFieldEnd, result); if (addedDecimalSeparator) { delegate.formatted(Field.DECIMAL_SEPARATOR, Field.DECIMAL_SEPARATOR, iFieldEnd, fFieldStart, result); } if (fFieldStart == -1) { fFieldStart = result.length(); } delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION, fFieldStart, result.length(), result); // The exponent is output using the pattern-specified minimum // exponent digits. There is no maximum limit to the exponent // digits, since truncating the exponent would result in an // unacceptable inaccuracy. fieldStart = result.length(); result.append(symbols.getExponentialSymbol()); delegate.formatted(Field.EXPONENT_SYMBOL, Field.EXPONENT_SYMBOL, fieldStart, result.length(), result); // For zero values, we force the exponent to zero. We // must do this here, and not earlier, because the value // is used to determine integer digit count above. if (digitList.isZero()) exponent = 0; boolean negativeExponent = exponent < 0; if (negativeExponent) { exponent = -exponent; append(result, negativePrefix, delegate, getNegativePrefixFieldPositions(), Field.EXPONENT_SIGN); } else { append(result, positivePrefix, delegate, getPositivePrefixFieldPositions(), Field.EXPONENT_SIGN); } digitList.set(exponent); int eFieldStart = result.length(); for (int i = digitList.decimalAt; i < minExponentDigits; ++i) result.append(zero); for (int i = 0; i < digitList.decimalAt; ++i) { result.append((i < digitList.count) ? (char) (digitList.digits[i] + zeroDelta) : zero); } delegate.formatted(Field.EXPONENT, Field.EXPONENT, eFieldStart, result.length(), result); fieldStart = result.length(); if (negativeExponent) { append(result, negativeSuffix, delegate, getNegativeSuffixFieldPositions(), Field.EXPONENT_SIGN); } else { append(result, positiveSuffix, delegate, getPositiveSuffixFieldPositions(), Field.EXPONENT_SIGN); } } else { int iFieldStart = result.length(); // Output the integer portion. Here 'count' is the total // number of integer digits we will display, including both // leading zeros required to satisfy getMinimumIntegerDigits, // and actual digits present in the number. int count = getMinimumIntegerDigits(); int digitIndex = 0; // Index into digitList.fDigits[] if (digitList.decimalAt > 0 && count < digitList.decimalAt) count = digitList.decimalAt; // Handle the case where getMaximumIntegerDigits() is smaller // than the real number of integer digits. If this is so, we // output the least significant max integer digits. For example, // the value 1997 printed with 2 max integer digits is just "97". if (count > getMaximumIntegerDigits()) { count = getMaximumIntegerDigits(); digitIndex = digitList.decimalAt - count; } int sizeBeforeIntegerPart = result.length(); for (int i = count - 1; i >= 0; --i) { if (i < digitList.decimalAt && digitIndex < digitList.count) { // Output a real digit result.append((char) (digitList.digits[digitIndex++] + zeroDelta)); } else { // Output a leading zero result.append(zero); } // Output grouping separator if necessary. Don't output a // grouping separator if i==0 though; that's at the end of // the integer part. if (isGroupingUsed() && i > 0 && (groupingSize != 0) && (i % groupingSize == 0)) { int gStart = result.length(); result.append(grouping); delegate.formatted(Field.GROUPING_SEPARATOR, Field.GROUPING_SEPARATOR, gStart, result.length(), result); } } // Determine whether or not there are any printable fractional // digits. If we've used up the digits we know there aren't. boolean fractionPresent = (getMinimumFractionDigits() > 0) || (!isInteger && digitIndex < digitList.count); // If there is no fraction present, and we haven't printed any // integer digits, then print a zero. Otherwise we won't print // _any_ digits, and we won't be able to parse this string. if (!fractionPresent && result.length() == sizeBeforeIntegerPart) { result.append(zero); } delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER, iFieldStart, result.length(), result); // Output the decimal separator if we always do so. int sStart = result.length(); if (decimalSeparatorAlwaysShown || fractionPresent) result.append(decimal); if (sStart != result.length()) { delegate.formatted(Field.DECIMAL_SEPARATOR, Field.DECIMAL_SEPARATOR, sStart, result.length(), result); } int fFieldStart = result.length(); for (int i = 0; i < getMaximumFractionDigits(); ++i) { // Here is where we escape from the loop. We escape if we've // output // the maximum fraction digits (specified in the for expression // above). // We also stop when we've output the minimum digits and either: // we have an integer, so there is no fractional stuff to // display, // or we're out of significant digits. if (i >= getMinimumFractionDigits() && (isInteger || digitIndex >= digitList.count)) break; // Output leading fractional zeros. These are zeros that come // after // the decimal but before any significant digits. These are only // output if abs(number being formatted) < 1.0. if (-1 - i > (digitList.decimalAt - 1)) { result.append(zero); continue; } // Output a digit, if we have any precision left, or a // zero if we don't. We don't want to output noise digits. if (!isInteger && digitIndex < digitList.count) { result.append((char) (digitList.digits[digitIndex++] + zeroDelta)); } else { result.append(zero); } } // Record field information for caller. delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION, fFieldStart, result.length(), result); } if (isNegative) { append(result, negativeSuffix, delegate, getNegativeSuffixFieldPositions(), Field.SIGN); } else { append(result, positiveSuffix, delegate, getPositiveSuffixFieldPositions(), Field.SIGN); } return result; } /** * Appends the String string to result. delegate is notified of all the 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 most economical subclass that can represent the number given by the string is chosen. Most integer values are returned as Long * objects, no matter how they are written: "17" and "17.000" both parse to Long(17). Values that cannot fit * into a Long are returned as Doubles. This includes values with a fractional part, infinite values, NaN, * and the value -0.0. DecimalFormat does not decide whether to return a Double or a Long based on * the presence of a decimal separator in the source string. Doing so would prevent integers that overflow the mantissa of a double, such as * "10,000,000,000,000,000.00", from being parsed accurately. Currently, the only classes that parse returns are * Long and Double, but callers should not rely on this. Callers may use the Number methods * doubleValue, longValue, etc., to obtain the type they want. *

* DecimalFormat parses all Unicode characters that represent decimal digits, as defined by Character.digit(). In * addition, DecimalFormat also recognizes as digits the ten consecutive characters starting with the localized zero digit defined in * the DecimalFormatSymbols object. * * @param text * the string to be parsed * @param pos * A ParsePosition object with index and error index information as described above. * @return the parsed value, or null if the parse fails * @exception NullPointerException * if text or pos is null. */ // public Number parse(String text, ParsePosition pos) { // // special case NaN // if (text.regionMatches(pos.index, symbols.getNaN(), 0, symbols // .getNaN().length())) { // pos.index = pos.index + symbols.getNaN().length(); // return new Double(Double.NaN); // } // // boolean[] status = new boolean[STATUS_LENGTH]; // // if (!subparse(text, pos, digitList, false, status)) // return null; // // double doubleResult = 0.0; // long longResult = 0; // boolean gotDouble = true; // // // Finally, have DigitList parse the digits into a value. // if (status[STATUS_INFINITE]) { // doubleResult = Double.POSITIVE_INFINITY; // } else if (digitList.fitsIntoLong(status[STATUS_POSITIVE], // isParseIntegerOnly())) { // gotDouble = false; // longResult = digitList.getLong(); // } else // doubleResult = digitList.getDouble(); // // // Divide by multiplier. We have to be careful here not to do unneeded // // conversions between double and long. // if (multiplier != 1) { // if (gotDouble) // doubleResult /= multiplier; // else { // // Avoid converting to double if we can // if (longResult % multiplier == 0) { // longResult /= multiplier; // } else { // doubleResult = ((double) longResult) / multiplier; // if (doubleResult < 0) // doubleResult = -doubleResult; // gotDouble = true; // } // } // } // // if (!status[STATUS_POSITIVE]) { // doubleResult = -doubleResult; // // If longResult was Long.MIN_VALUE or a divisor of it (if // // multiplier != 1) then don't negate it. // if (longResult > 0) { // longResult = -longResult; // } // } // // // At this point, if we divided the result by the multiplier, the result // may // // fit into a long. We check for this case and return a long if possible. // // We must do this AFTER applying the negative (if appropriate) in order // to // // handle the case of LONG_MIN; otherwise, if we do this with a positive // value // // -LONG_MIN, the double is > 0, but the long is < 0. This is a // C++-specific // // situation. We also must retain a double in the case of -0.0, which // will // // compare as == to a long 0 cast to a double (bug 4162852). // if (multiplier != 1 && gotDouble) { // longResult = (long) doubleResult; // gotDouble = (doubleResult != (double) longResult) // || (doubleResult == 0.0 && !status[STATUS_POSITIVE] && // !isParseIntegerOnly()); // } // // return gotDouble ? (Number) new Double(doubleResult) // : (Number) new Long(longResult); // } // private static final int STATUS_INFINITE = 0; // private static final int STATUS_POSITIVE = 1; // private static final int STATUS_LENGTH = 2; /** * Parse the given text into a number. The text is parsed beginning at parsePosition, until an unparseable character is seen. * * @param text * The string to parse. * @param parsePosition * The position at which to being parsing. Upon return, the first unparseable character. * @param digits * The DigitList to set to the parsed value. * @param isExponent * If true, parse an exponent. This means no infinite values and integer only. * @param status * Upon return contains boolean status flags indicating whether the value was infinite and whether it was positive. */ // private final boolean subparse(String text, ParsePosition parsePosition, DigitList digits, boolean isExponent, boolean status[]) { // int position = parsePosition.index; // int oldStart = parsePosition.index; // int backup; // // // check for positivePrefix; take longest // // TODO move to StrUtils // String str1 = text.substring(position, position + positivePrefix.length()); // String str2 = positivePrefix.substring(0, 0 + positivePrefix.length()); // boolean gotPositive = str1.equalsIgnoreCase(str2); // // str1 = text.substring(position, position + negativePrefix.length()); // str2 = positivePrefix.substring(0, 0 + negativePrefix.length()); // boolean gotNegative = str1.equalsIgnoreCase(str2); // // if (gotPositive && gotNegative) { // if (positivePrefix.length() > negativePrefix.length()) // gotNegative = false; // else if (positivePrefix.length() < negativePrefix.length()) // gotPositive = false; // } // if (gotPositive) { // position += positivePrefix.length(); // } else if (gotNegative) { // position += negativePrefix.length(); // } else { // parsePosition.errorIndex = position; // return false; // } // // process digits or Inf, find decimal position // status[STATUS_INFINITE] = false; // str1 = text.substring(position, position + symbols.getInfinity().length()); // str2 = positivePrefix.substring(0, 0 + symbols.getInfinity().length()); // boolean gotInfinity = str1.equalsIgnoreCase(str2); // if (!isExponent && gotInfinity) { // position += symbols.getInfinity().length(); // status[STATUS_INFINITE] = true; // } else { // // We now have a string of digits, possibly with grouping symbols, // // and decimal points. We want to process these into a DigitList. // // We don't want to put a bunch of leading zeros into the DigitList // // though, so we keep track of the location of the decimal point, // // put only significant digits into the DigitList, and adjust the // // exponent as needed. // // digits.decimalAt = digits.count = 0; // char zero = symbols.getZeroDigit(); // char decimal = isCurrencyFormat ? symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator(); // char grouping = symbols.getGroupingSeparator(); // char exponentChar = symbols.getExponentialSymbol(); // boolean sawDecimal = false; // boolean sawExponent = false; // boolean sawDigit = false; // int exponent = 0; // Set to the exponent value, if any // // // We have to track digitCount ourselves, because digits.count will // // pin when the maximum allowable digits is reached. // int digitCount = 0; // // backup = -1; // for (; position < text.length(); ++position) { // char ch = text.charAt(position); // // /* // * We recognize all digit ranges, not only the Latin digit range // * '0'..'9'. We do so by using the Character.digit() method, // * which converts a valid Unicode digit to the range 0..9. // * // * The character 'ch' may be a digit. If so, place its value // * from 0 to 9 in 'digit'. First try using the locale digit, // * which may or MAY NOT be a standard Unicode digit range. If // * this fails, try using the standard Unicode digit ranges by // * calling Character.digit(). If this also fails, digit will // * have a value outside the range 0..9. // */ // int digit = ch - zero; // if (digit < 0 || digit > 9) // digit = Character.digit(ch, 10); // // if (digit == 0) { // // Cancel out backup setting (see grouping handler below) // backup = -1; // Do this BEFORE continue statement below!!! // sawDigit = true; // // // Handle leading zeros // if (digits.count == 0) { // // Ignore leading zeros in integer part of number. // if (!sawDecimal) // continue; // // // If we have seen the decimal, but no significant // // digits yet, // // then we account for leading zeros by decrementing the // // digits.decimalAt into negative values. // --digits.decimalAt; // } else { // ++digitCount; // digits.append((char) (digit + '0')); // } // } else if (digit > 0 && digit <= 9) // [sic] digit==0 handled // // above // { // sawDigit = true; // ++digitCount; // digits.append((char) (digit + '0')); // // // Cancel out backup setting (see grouping handler below) // backup = -1; // } else if (!isExponent && ch == decimal) { // // If we're only parsing integers, or if we ALREADY saw the // // decimal, then don't parse this one. // if (isParseIntegerOnly() || sawDecimal) // break; // digits.decimalAt = digitCount; // Not digits.count! // sawDecimal = true; // } else if (!isExponent && ch == grouping && isGroupingUsed()) { // if (sawDecimal) { // break; // } // // Ignore grouping characters, if we are using them, but // // require // // that they be followed by a digit. Otherwise we backup and // // reprocess them. // backup = position; // } else if (!isExponent && ch == exponentChar && !sawExponent) { // // Process the exponent by recursively calling this method. // ParsePosition pos = new ParsePosition(position + 1); // boolean[] stat = new boolean[STATUS_LENGTH]; // DigitList exponentDigits = new DigitList(); // // if (subparse(text, pos, exponentDigits, true, stat) && exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) { // position = pos.index; // Advance past the exponent // exponent = (int) exponentDigits.getLong(); // if (!stat[STATUS_POSITIVE]) // exponent = -exponent; // sawExponent = true; // } // break; // Whether we fail or succeed, we exit this loop // } else // break; // } // // if (backup != -1) // position = backup; // // // If there was no decimal point we have an integer // if (!sawDecimal) // digits.decimalAt = digitCount; // Not digits.count! // // // Adjust for exponent, if any // digits.decimalAt += exponent; // // // If none of the text string was recognized. For example, parse // // "x" with pattern "#0.00" (return index and error index both 0) // // parse "$" with pattern "$#0.00". (return index 0 and error index // // 1). // if (!sawDigit && digitCount == 0) { // parsePosition.index = oldStart; // parsePosition.errorIndex = oldStart; // return false; // } // } // // // check for positiveSuffix // if (gotPositive) { // str1 = text.substring(position, position + positivePrefix.length()); // str2 = positivePrefix.substring(0, 0 + positivePrefix.length()); // gotPositive = str1.equalsIgnoreCase(str2); // } // if (gotNegative) { // str1 = text.substring(position, position + negativeSuffix.length()); // str2 = positivePrefix.substring(0, 0 + negativeSuffix.length()); // gotPositive = str1.equalsIgnoreCase(str2); // } // // // if both match, take longest // if (gotPositive && gotNegative) { // if (positiveSuffix.length() > negativeSuffix.length()) // gotNegative = false; // else if (positiveSuffix.length() < negativeSuffix.length()) // gotPositive = false; // } // // // fail if neither or both // if (gotPositive == gotNegative) { // parsePosition.errorIndex = position; // return false; // } // // parsePosition.index = position + (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success! // // status[STATUS_POSITIVE] = gotPositive; // if (parsePosition.index == oldStart) { // parsePosition.errorIndex = position; // return false; // } // return true; // } /** * Returns the decimal format symbols, which is generally not changed by the programmer or user. * * @return desired DecimalFormatSymbols * @see DecimalFormatSymbols */ public DecimalFormatSymbols getDecimalFormatSymbols() { try { // don't allow multiple references return (DecimalFormatSymbols) symbols.clone(); } catch (Exception foo) { return null; // should never happen } } /** * Sets the decimal format symbols, which is generally not changed by the programmer or user. * * @param newSymbols * desired DecimalFormatSymbols * @see DecimalFormatSymbols */ public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { try { // don't allow multiple references symbols = (DecimalFormatSymbols) newSymbols.clone(); expandAffixes(); } catch (Exception foo) { // should never happen } } /** * Get the positive prefix. *

* Examples: +123, $123, sFr123 */ public String getPositivePrefix() { return positivePrefix; } /** * Set the positive prefix. *

* Examples: +123, $123, sFr123 */ public void setPositivePrefix(String newValue) { positivePrefix = newValue; posPrefixPattern = null; positivePrefixFieldPositions = null; } /** * Returns the FieldPositions of the fields in the prefix used for positive numbers. This is not used if the user has explicitly set a positive * prefix via setPositivePrefix. This is lazily created. * * @return FieldPositions in positive prefix */ private FieldPosition[] getPositivePrefixFieldPositions() { if (positivePrefixFieldPositions == null) { if (posPrefixPattern != null) { positivePrefixFieldPositions = expandAffix(posPrefixPattern); } else { positivePrefixFieldPositions = EmptyFieldPositionArray; } } return positivePrefixFieldPositions; } /** * Get the negative prefix. *

* Examples: -123, ($123) (with negative suffix), sFr-123 */ public String getNegativePrefix() { return negativePrefix; } /** * Set the negative prefix. *

* Examples: -123, ($123) (with negative suffix), sFr-123 */ public void setNegativePrefix(String newValue) { negativePrefix = newValue; negPrefixPattern = null; } /** * Returns the FieldPositions of the fields in the prefix used for negative numbers. This is not used if the user has explicitly set a negative * prefix via setNegativePrefix. This is lazily created. * * @return FieldPositions in positive prefix */ private FieldPosition[] getNegativePrefixFieldPositions() { if (negativePrefixFieldPositions == null) { if (negPrefixPattern != null) { negativePrefixFieldPositions = expandAffix(negPrefixPattern); } else { negativePrefixFieldPositions = EmptyFieldPositionArray; } } return negativePrefixFieldPositions; } /** * Get the positive suffix. *

* Example: 123% */ public String getPositiveSuffix() { return positiveSuffix; } /** * Set the positive suffix. *

* Example: 123% */ public void setPositiveSuffix(String newValue) { positiveSuffix = newValue; posSuffixPattern = null; } /** * Returns the FieldPositions of the fields in the suffix used for positive numbers. This is not used if the user has explicitly set a positive * suffix via setPositiveSuffix. This is lazily created. * * @return FieldPositions in positive prefix */ private FieldPosition[] getPositiveSuffixFieldPositions() { if (positiveSuffixFieldPositions == null) { if (posSuffixPattern != null) { positiveSuffixFieldPositions = expandAffix(posSuffixPattern); } else { positiveSuffixFieldPositions = EmptyFieldPositionArray; } } return positiveSuffixFieldPositions; } /** * Get the negative suffix. *

* Examples: -123%, ($123) (with positive suffixes) */ public String getNegativeSuffix() { return negativeSuffix; } /** * Set the positive suffix. *

* Examples: 123% */ public void setNegativeSuffix(String newValue) { negativeSuffix = newValue; negSuffixPattern = null; } /** * Returns the FieldPositions of the fields in the suffix used for negative numbers. This is not used if the user has explicitly set a negative * suffix via setNegativeSuffix. This is lazily created. * * @return FieldPositions in positive prefix */ private FieldPosition[] getNegativeSuffixFieldPositions() { if (negativeSuffixFieldPositions == null) { if (negSuffixPattern != null) { negativeSuffixFieldPositions = expandAffix(negSuffixPattern); } else { negativeSuffixFieldPositions = EmptyFieldPositionArray; } } return negativeSuffixFieldPositions; } /** * Get the multiplier for use in percent, permill, etc. For a percentage, set the suffixes to have "%" and the multiplier to be 100. (For Arabic, * use arabic percent symbol). For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000. *

* Examples: with 100, 1.23 -> "123", and "123" -> 1.23 */ public int getMultiplier() { return multiplier; } /** * Set the multiplier for use in percent, permill, etc. For a percentage, set the suffixes to have "%" and the multiplier to be 100. (For Arabic, * use arabic percent symbol). For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000. *

* Examples: with 100, 1.23 -> "123", and "123" -> 1.23 */ public void setMultiplier(int newValue) { multiplier = newValue; } /** * Return the grouping size. Grouping size is the number of digits between grouping separators in the integer portion of a number. For example, in * the number "123,456.78", the grouping size is 3. * * @see #setGroupingSize * @see NumberFormat#isGroupingUsed * @see DecimalFormatSymbols#getGroupingSeparator */ int getGroupingSize() { return groupingSize; } /** * Set the grouping size. Grouping size is the number of digits between grouping separators in the integer portion of a number. For example, in the * number "123,456.78", the grouping size is 3. * * @see #getGroupingSize * @see NumberFormat#setGroupingUsed * @see DecimalFormatSymbols#setGroupingSeparator */ public void setGroupingSize(int newValue) { groupingSize = (byte) newValue; } /** * Allows you to get the behavior of the decimal separator with integers. (The decimal separator will always appear with decimals.) *

* Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 */ public boolean isDecimalSeparatorAlwaysShown() { return decimalSeparatorAlwaysShown; } /** * Allows you to set the behavior of the decimal separator with integers. (The decimal separator will always appear with decimals.) *

* Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 */ public void setDecimalSeparatorAlwaysShown(boolean newValue) { decimalSeparatorAlwaysShown = newValue; } /** * Overrides equals */ public boolean equals(Object obj) { if (obj == null) return false; if (!super.equals(obj)) return false; // super does class check DecimalFormat other = (DecimalFormat) obj; return ((posPrefixPattern == other.posPrefixPattern && positivePrefix.equals(other.positivePrefix)) || (posPrefixPattern != null && posPrefixPattern .equals(other.posPrefixPattern))) && ((posSuffixPattern == other.posSuffixPattern && positiveSuffix.equals(other.positiveSuffix)) || (posSuffixPattern != null && posSuffixPattern .equals(other.posSuffixPattern))) && ((negPrefixPattern == other.negPrefixPattern && negativePrefix.equals(other.negativePrefix)) || (negPrefixPattern != null && negPrefixPattern .equals(other.negPrefixPattern))) && ((negSuffixPattern == other.negSuffixPattern && negativeSuffix.equals(other.negativeSuffix)) || (negSuffixPattern != null && negSuffixPattern .equals(other.negSuffixPattern))) && multiplier == other.multiplier && groupingSize == other.groupingSize && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown && useExponentialNotation == other.useExponentialNotation && (!useExponentialNotation || minExponentDigits == other.minExponentDigits) && symbols.equals(other.symbols); } /** * Overrides hashCode */ public int hashCode() { return super.hashCode() * 37 + positivePrefix.hashCode(); // just enough fields for a reasonable distribution } /** * Synthesizes a pattern string that represents the current state of this Format object. * * @see #applyPattern */ public String toPattern() { return toPattern(false); } /** * Synthesizes a localized pattern string that represents the current state of this Format object. * * @see #applyPattern */ String toLocalizedPattern() { return toPattern(true); } /** * Expand the affix pattern strings into the expanded affix strings. If any affix pattern string is null, do not expand it. This method should be * called any time the symbols or the affix patterns change in order to keep the expanded affix strings up to date. */ private void expandAffixes() { // Reuse one StringBuffer for better performance final StringBuffer buffer = new StringBuffer(); if (posPrefixPattern != null) { positivePrefix = expandAffix(posPrefixPattern, buffer); positivePrefixFieldPositions = null; } if (posSuffixPattern != null) { positiveSuffix = expandAffix(posSuffixPattern, buffer); positiveSuffixFieldPositions = null; } if (negPrefixPattern != null) { negativePrefix = expandAffix(negPrefixPattern, buffer); negativePrefixFieldPositions = null; } if (negSuffixPattern != null) { negativeSuffix = expandAffix(negSuffixPattern, buffer); negativeSuffixFieldPositions = null; } } /** * Expand an affix pattern into an affix string. All characters in the pattern are literal unless prefixed by QUOTE. The following characters after * QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, PATTERN_MINUS, and CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE + CURRENCY_SIGN + * CURRENCY_SIGN), it is interpreted as an ISO 4217 currency code. Any other character after a QUOTE represents itself. QUOTE must be followed by * another character; QUOTE may not occur by itself at the end of the pattern. * * @param pattern * the non-null, possibly empty pattern * @param buffer * a scratch StringBuffer; its contents will be lost * @return the expanded equivalent of pattern */ private String expandAffix(String pattern, StringBuffer buffer) { buffer.setLength(0); for (int i = 0; i < pattern.length();) { char c = pattern.charAt(i++); if (c == QUOTE) { c = pattern.charAt(i++); switch (c) { case CURRENCY_SIGN: buffer.append(symbols.getCurrencySymbol()); continue; case PATTERN_PERCENT: c = symbols.getPercent(); break; case PATTERN_PER_MILLE: c = symbols.getPerMill(); break; case PATTERN_MINUS: c = symbols.getMinusSign(); break; } } buffer.append(c); } return buffer.toString(); } /** * Expand an affix pattern into an array of FieldPositions describing how the pattern would be expanded. All characters in the pattern are literal * unless prefixed by QUOTE. The following characters after QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, PATTERN_MINUS, and * CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE + CURRENCY_SIGN + CURRENCY_SIGN), it is interpreted as an ISO 4217 currency code. Any other * character after a QUOTE represents itself. QUOTE must be followed by another character; QUOTE may not occur by itself at the end of the pattern. * * @param pattern * the non-null, possibly empty pattern * @return FieldPosition array of the resulting fields. */ private FieldPosition[] expandAffix(String pattern) { Vector positions = null; int stringIndex = 0; for (int i = 0; i < pattern.length();) { char c = pattern.charAt(i++); if (c == QUOTE) { int field = -1; Format.Field fieldID = null; c = pattern.charAt(i++); switch (c) { case CURRENCY_SIGN: String string = symbols.getCurrencySymbol(); if (string.length() > 0) { if (positions == null) { positions = new Vector<>(2); } FieldPosition fp = new FieldPosition(Field.CURRENCY); fp.setBeginIndex(stringIndex); fp.setEndIndex(stringIndex + string.length()); positions.addElement(fp); stringIndex += string.length(); } continue; case PATTERN_PERCENT: c = symbols.getPercent(); field = -1; fieldID = Field.PERCENT; break; case PATTERN_PER_MILLE: c = symbols.getPerMill(); field = -1; fieldID = Field.PERMILLE; break; case PATTERN_MINUS: c = symbols.getMinusSign(); field = -1; fieldID = Field.SIGN; break; } if (fieldID != null) { if (positions == null) { positions = new Vector<>(2); } FieldPosition fp = new FieldPosition(fieldID, field); fp.setBeginIndex(stringIndex); fp.setEndIndex(stringIndex + 1); positions.addElement(fp); } } stringIndex++; } if (positions != null) { EmptyFieldPositionArray = new FieldPosition[positions.size()]; positions.copyInto(EmptyFieldPositionArray); } return EmptyFieldPositionArray; } /** * Appends an affix pattern to the given StringBuffer, quoting special characters as needed. Uses the internal affix pattern, if that exists, or the * literal affix, if the internal affix pattern is null. The appended string will generate the same affix pattern (or literal affix) when passed to * toPattern(). * * @param buffer * the affix string is appended to this * @param affixPattern * a pattern such as posPrefixPattern; may be null * @param expAffix * a corresponding expanded affix, such as positivePrefix. Ignored unless affixPattern is null. If affixPattern is null, then expAffix is * appended as a literal affix. * @param localized * true if the appended pattern should contain localized pattern characters; otherwise, non-localized pattern chars are appended */ private void appendAffix(StringBuffer buffer, String affixPattern, String expAffix, boolean localized) { if (affixPattern == null) { appendAffix(buffer, expAffix, localized); } else { int i; for (int pos = 0; pos < affixPattern.length(); pos = i) { i = affixPattern.indexOf(QUOTE, pos); if (i < 0) { appendAffix(buffer, affixPattern.substring(pos), localized); break; } if (i > pos) { appendAffix(buffer, affixPattern.substring(pos, i), localized); } char c = affixPattern.charAt(++i); ++i; if (c == QUOTE) { buffer.append(c); // Fall through and append another QUOTE below } else if (c == CURRENCY_SIGN && i < affixPattern.length() && affixPattern.charAt(i) == CURRENCY_SIGN) { ++i; buffer.append(c); // Fall through and append another CURRENCY_SIGN below } else if (localized) { switch (c) { case PATTERN_PERCENT: c = symbols.getPercent(); break; case PATTERN_PER_MILLE: c = symbols.getPerMill(); break; case PATTERN_MINUS: c = symbols.getMinusSign(); break; } } buffer.append(c); } } } /** * Append an affix to the given StringBuffer, using quotes if there are special characters. Single quotes themselves must be escaped in either case. */ private void appendAffix(StringBuffer buffer, String affix, boolean localized) { boolean needQuote; if (localized) { needQuote = affix.indexOf(symbols.getZeroDigit()) >= 0 || affix.indexOf(symbols.getGroupingSeparator()) >= 0 || affix.indexOf(symbols.getDecimalSeparator()) >= 0 || affix.indexOf(symbols.getPercent()) >= 0 || affix.indexOf(symbols.getPerMill()) >= 0 || affix.indexOf(symbols.getDigit()) >= 0 || affix.indexOf(symbols.getPatternSeparator()) >= 0 || affix.indexOf(symbols.getMinusSign()) >= 0 || affix.indexOf(CURRENCY_SIGN) >= 0; } else { needQuote = affix.indexOf(PATTERN_ZERO_DIGIT) >= 0 || affix.indexOf(PATTERN_GROUPING_SEPARATOR) >= 0 || affix.indexOf(PATTERN_DECIMAL_SEPARATOR) >= 0 || affix.indexOf(PATTERN_PERCENT) >= 0 || affix.indexOf(PATTERN_PER_MILLE) >= 0 || affix.indexOf(PATTERN_DIGIT) >= 0 || affix.indexOf(PATTERN_SEPARATOR) >= 0 || affix.indexOf(PATTERN_MINUS) >= 0 || affix.indexOf(CURRENCY_SIGN) >= 0; } if (needQuote) buffer.append('\''); if (affix.indexOf('\'') < 0) buffer.append(affix); else { for (int j = 0; j < affix.length(); ++j) { char c = affix.charAt(j); buffer.append(c); if (c == '\'') buffer.append(c); } } if (needQuote) buffer.append('\''); } /** * Does the real work of generating a pattern. */ private String toPattern(boolean localized) { StringBuffer result = new StringBuffer(); for (int j = 1; j >= 0; --j) { if (j == 1) appendAffix(result, posPrefixPattern, positivePrefix, localized); else appendAffix(result, negPrefixPattern, negativePrefix, localized); int i; int digitCount = useExponentialNotation ? getMaximumIntegerDigits() : Math.max(groupingSize, getMinimumIntegerDigits()) + 1; for (i = digitCount; i > 0; --i) { if (i != digitCount && isGroupingUsed() && groupingSize != 0 && i % groupingSize == 0) { result.append(localized ? symbols.getGroupingSeparator() : PATTERN_GROUPING_SEPARATOR); } result.append(i <= getMinimumIntegerDigits() ? (localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT) : (localized ? symbols.getDigit() : PATTERN_DIGIT)); } if (getMaximumFractionDigits() > 0 || decimalSeparatorAlwaysShown) result.append(localized ? symbols.getDecimalSeparator() : PATTERN_DECIMAL_SEPARATOR); for (i = 0; i < getMaximumFractionDigits(); ++i) { if (i < getMinimumFractionDigits()) { result.append(localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT); } else { result.append(localized ? symbols.getDigit() : PATTERN_DIGIT); } } if (useExponentialNotation) { result.append(localized ? symbols.getExponentialSymbol() : PATTERN_EXPONENT); for (i = 0; i < minExponentDigits; ++i) result.append(localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT); } if (j == 1) { appendAffix(result, posSuffixPattern, positiveSuffix, localized); if ((negSuffixPattern == posSuffixPattern && // n == p == null negativeSuffix.equals(positiveSuffix)) || (negSuffixPattern != null && negSuffixPattern.equals(posSuffixPattern))) { if ((negPrefixPattern != null && posPrefixPattern != null && negPrefixPattern.equals("'-" + posPrefixPattern)) || (negPrefixPattern == posPrefixPattern && // n == // p == // null negativePrefix.equals(symbols.getMinusSign() + positivePrefix))) break; } result.append(localized ? symbols.getPatternSeparator() : PATTERN_SEPARATOR); } else appendAffix(result, negSuffixPattern, negativeSuffix, localized); } return result.toString(); } /** * Apply the given pattern to this Format object. A pattern is a short-hand specification for the various formatting properties. These properties * can also be changed individually through the various setter methods. *

* There is no limit to integer digits are set by this routine, since that is the typical end-user desire; use setMaximumInteger if you want to set * a real value. For negative numbers, use a second pattern, separated by a semicolon *

* Example "#,#00.0#" -> 1,234.56 *

* This means a minimum of 2 integer digits, 1 fraction digit, and a maximum of 2 fraction digits. *

* Example: "#,#00.0#;(#,#00.0#)" for negatives in parentheses. *

* In negative patterns, the minimum and maximum counts are ignored; these are presumed to be set in the positive pattern. * * @exception NullPointerException * if pattern is null * @exception IllegalArgumentException * if the given pattern is invalid. */ public void applyPattern(String pattern) { applyPattern(pattern, false); } /** * Apply the given pattern to this Format object. The pattern is assumed to be in a localized notation. A pattern is a short-hand specification for * the various formatting properties. These properties can also be changed individually through the various setter methods. *

* There is no limit to integer digits are set by this routine, since that is the typical end-user desire; use setMaximumInteger if you want to set * a real value. For negative numbers, use a second pattern, separated by a semicolon *

* Example "#,#00.0#" -> 1,234.56 *

* This means a minimum of 2 integer digits, 1 fraction digit, and a maximum of 2 fraction digits. *

* Example: "#,#00.0#;(#,#00.0#)" for negatives in parentheses. *

* In negative patterns, the minimum and maximum counts are ignored; these are presumed to be set in the positive pattern. * * @exception NullPointerException * if pattern is null * @exception IllegalArgumentException * if the given pattern is invalid. */ void applyLocalizedPattern(String pattern) { applyPattern(pattern, true); } /** * Does the real work of applying a pattern. */ private void applyPattern(String pattern, boolean localized) { char zeroDigit = PATTERN_ZERO_DIGIT; char groupingSeparator = PATTERN_GROUPING_SEPARATOR; char decimalSeparator = PATTERN_DECIMAL_SEPARATOR; char percent = PATTERN_PERCENT; char perMill = PATTERN_PER_MILLE; char digit = PATTERN_DIGIT; char separator = PATTERN_SEPARATOR; char exponent = PATTERN_EXPONENT; char minus = PATTERN_MINUS; if (localized) { zeroDigit = symbols.getZeroDigit(); groupingSeparator = symbols.getGroupingSeparator(); decimalSeparator = symbols.getDecimalSeparator(); percent = symbols.getPercent(); perMill = symbols.getPerMill(); digit = symbols.getDigit(); separator = symbols.getPatternSeparator(); exponent = symbols.getExponentialSymbol(); minus = symbols.getMinusSign(); } boolean gotNegative = false; decimalSeparatorAlwaysShown = false; isCurrencyFormat = false; useExponentialNotation = false; // Two variables are used to record the subrange of the pattern // occupied by phase 1. This is used during the processing of the // second pattern (the one representing negative numbers) to ensure // that no deviation exists in phase 1 between the two patterns. int phaseOneStart = 0; int phaseOneLength = 0; /** * Back-out comment : HShih boolean phaseTwo = false; */ int start = 0; for (int j = 1; j >= 0 && start < pattern.length(); --j) { boolean inQuote = false; StringBuffer prefix = new StringBuffer(); StringBuffer suffix = new StringBuffer(); int decimalPos = -1; int multiplier = 1; int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0; byte groupingCount = -1; // The phase ranges from 0 to 2. Phase 0 is the prefix. Phase 1 is // the section of the pattern with digits, decimal separator, // grouping characters. Phase 2 is the suffix. In phases 0 and 2, // percent, permille, and currency symbols are recognized and // translated. The separation of the characters into phases is // strictly enforced; if phase 1 characters are to appear in the // suffix, for example, they must be quoted. int phase = 0; // The affix is either the prefix or the suffix. StringBuffer affix = prefix; for (int pos = start; pos < pattern.length(); ++pos) { char ch = pattern.charAt(pos); switch (phase) { case 0: case 2: // Process the prefix / suffix characters if (inQuote) { // A quote within quotes indicates either the // closing // quote or two quotes, which is a quote literal. // That // is, // we have the second quote in 'do' or 'don''t'. if (ch == QUOTE) { if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) { ++pos; affix.append("''"); // 'don''t' } else { inQuote = false; // 'do' } continue; } } else { // Process unquoted characters seen in prefix or // suffix // phase. if (ch == digit || ch == zeroDigit || ch == groupingSeparator || ch == decimalSeparator) { // Any of these characters implicitly begins the // next // phase. If we are in phase 2, there is no next // phase, // so these characters are illegal. /** * 1.2 Back-out comment : HShih Can't throw exception here. if (phase == 2) throw new IllegalArgumentException * ("Unquoted special character '" + ch + "' in pattern \"" + pattern + '"'); */ phase = 1; if (j == 1) phaseOneStart = pos; --pos; // Reprocess this character continue; } else if (ch == CURRENCY_SIGN) { // Use lookahead to determine if the currency // sign // is // doubled or not. boolean doubled = (pos + 1) < pattern.length() && pattern.charAt(pos + 1) == CURRENCY_SIGN; if (doubled) ++pos; // Skip over the doubled character isCurrencyFormat = true; affix.append(doubled ? "'\u00A4\u00A4" : "'\u00A4"); continue; } else if (ch == QUOTE) { // A quote outside quotes indicates either the // opening // quote or two quotes, which is a quote // literal. // That is, // we have the first quote in 'do' or o''clock. if (ch == QUOTE) { if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) { ++pos; affix.append("''"); // o''clock } else { inQuote = true; // 'do' } continue; } } else if (ch == separator) { // Don't allow separators before we see digit // characters of phase // 1, and don't allow separators in the second // pattern (j == 0). if (phase == 0 || j == 0) throw new IllegalArgumentException("Unquoted special character '" + ch + "' in pattern \"" + pattern + '"'); start = pos + 1; pos = pattern.length(); continue; } // Next handle characters which are appended // directly. else if (ch == percent) { if (multiplier != 1) throw new IllegalArgumentException("Too many percent/permille characters in pattern \"" + pattern + '"'); multiplier = 100; affix.append("'%"); continue; } else if (ch == perMill) { if (multiplier != 1) throw new IllegalArgumentException("Too many percent/permille characters in pattern \"" + pattern + '"'); multiplier = 1000; affix.append("'\u2030"); continue; } else if (ch == minus) { affix.append("'-"); continue; } } // Note that if we are within quotes, or if this is an // unquoted, // non-special character, then we usually fall through // to // here. affix.append(ch); break; case 1: // Phase one must be identical in the two sub-patterns. // We // enforce this by doing a direct comparison. While // processing the first sub-pattern, we just record its // length. While processing the second, we compare // characters. if (j == 1) ++phaseOneLength; else { /** * 1.2 Back-out comment : HShih if (ch != pattern.charAt(phaseOneStart++)) throw new IllegalArgumentException * ("Subpattern mismatch in \"" + pattern + '"'); phaseTwo = true; */ if (--phaseOneLength == 0) { phase = 2; affix = suffix; } continue; } // Process the digits, decimal, and grouping characters. // We // record five pieces of information. We expect the // digits // to occur in the pattern ####0000.####, and we record // the // number of left digits, zero (central) digits, and // right // digits. The position of the last grouping character // is // recorded (should be somewhere within the first two // blocks // of characters), as is the position of the decimal // point, // if any (should be in the zero digits). If there is no // decimal point, then there should be no right digits. if (ch == digit) { if (zeroDigitCount > 0) ++digitRightCount; else ++digitLeftCount; if (groupingCount >= 0 && decimalPos < 0) ++groupingCount; } else if (ch == zeroDigit) { if (digitRightCount > 0) throw new IllegalArgumentException("Unexpected '0' in pattern \"" + pattern + '"'); ++zeroDigitCount; if (groupingCount >= 0 && decimalPos < 0) ++groupingCount; } else if (ch == groupingSeparator) { groupingCount = 0; } else if (ch == decimalSeparator) { if (decimalPos >= 0) throw new IllegalArgumentException("Multiple decimal separators in pattern \"" + pattern + '"'); decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; } else if (ch == exponent) { if (useExponentialNotation) throw new IllegalArgumentException("Multiple exponential " + "symbols in pattern \"" + pattern + '"'); useExponentialNotation = true; minExponentDigits = 0; // Use lookahead to parse out the exponential part // of // the // pattern, then jump into phase 2. while (++pos < pattern.length() && pattern.charAt(pos) == zeroDigit) { ++minExponentDigits; ++phaseOneLength; } if ((digitLeftCount + zeroDigitCount) < 1 || minExponentDigits < 1) throw new IllegalArgumentException("Malformed exponential " + "pattern \"" + pattern + '"'); // Transition to phase 2 phase = 2; affix = suffix; --pos; continue; } else { phase = 2; affix = suffix; --pos; --phaseOneLength; continue; } break; } } /** * 1.2 Back-out comment : HShih if (phaseTwo && phaseOneLength > 0) throw new IllegalArgumentException("Subpattern mismatch in \"" + pattern + * '"'); */ // Handle patterns with no '0' pattern character. These patterns // are legal, but must be interpreted. "##.###" -> "#0.###". // ".###" -> ".0##". /* * We allow patterns of the form "####" to produce a zeroDigitCount * of zero (got that?); although this seems like it might make it * possible for format() to produce empty strings, format() checks * for this condition and outputs a zero digit in this situation. * Having a zeroDigitCount of zero yields a minimum integer digits * of zero, which allows proper round-trip patterns. That is, we * don't want "#" to become "#0" when toPattern() is called (even * though that's what it really is, semantically). */ if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { // Handle "###.###" and "###." and ".###" int n = decimalPos; if (n == 0) ++n; // Handle ".###" digitRightCount = digitLeftCount - n; digitLeftCount = n - 1; zeroDigitCount = 1; } // Do syntax checking on the digits. if ((decimalPos < 0 && digitRightCount > 0) || (decimalPos >= 0 && (decimalPos < digitLeftCount || decimalPos > (digitLeftCount + zeroDigitCount))) || groupingCount == 0 || inQuote) throw new IllegalArgumentException("Malformed pattern \"" + pattern + '"'); if (j == 1) { posPrefixPattern = prefix.toString(); posSuffixPattern = suffix.toString(); negPrefixPattern = posPrefixPattern; // assume these for now negSuffixPattern = posSuffixPattern; int digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; /* * The effectiveDecimalPos is the position the decimal is at or * would be at if there is no decimal. Note that if * decimalPos<0, then digitTotalCount == digitLeftCount + * zeroDigitCount. */ int effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; setMinimumIntegerDigits(effectiveDecimalPos - digitLeftCount); setMaximumIntegerDigits(useExponentialNotation ? digitLeftCount + getMinimumIntegerDigits() : DOUBLE_INTEGER_DIGITS); setMaximumFractionDigits(decimalPos >= 0 ? (digitTotalCount - decimalPos) : 0); setMinimumFractionDigits(decimalPos >= 0 ? (digitLeftCount + zeroDigitCount - decimalPos) : 0); setGroupingUsed(groupingCount > 0); this.groupingSize = (groupingCount > 0) ? groupingCount : 0; this.multiplier = multiplier; setDecimalSeparatorAlwaysShown(decimalPos == 0 || decimalPos == digitTotalCount); } else { negPrefixPattern = prefix.toString(); negSuffixPattern = suffix.toString(); gotNegative = true; } } if (pattern.length() == 0) { posPrefixPattern = posSuffixPattern = ""; setMinimumIntegerDigits(0); setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS); setMinimumFractionDigits(0); setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); } // If there was no negative pattern, or if the negative pattern is // identical // to the positive pattern, then prepend the minus sign to the positive // pattern to form the negative pattern. if (!gotNegative || (negPrefixPattern.equals(posPrefixPattern) && negSuffixPattern.equals(posSuffixPattern))) { negSuffixPattern = posSuffixPattern; negPrefixPattern = "'-" + posPrefixPattern; } expandAffixes(); } /** * Sets the maximum number of digits allowed in the integer portion of a number. This override limits the integer digit count to 309. * * @see NumberFormat#setMaximumIntegerDigits */ public void setMaximumIntegerDigits(int newValue) { super.setMaximumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS)); } /** * Sets the minimum number of digits allowed in the integer portion of a number. This override limits the integer digit count to 309. * * @see NumberFormat#setMinimumIntegerDigits */ public void setMinimumIntegerDigits(int newValue) { super.setMinimumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS)); } /** * Sets the maximum number of digits allowed in the fraction portion of a number. This override limits the fraction digit count to 340. * * @see NumberFormat#setMaximumFractionDigits */ public void setMaximumFractionDigits(int newValue) { super.setMaximumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS)); } /** * Sets the minimum number of digits allowed in the fraction portion of a number. This override limits the fraction digit count to 340. * * @see NumberFormat#setMinimumFractionDigits */ public void setMinimumFractionDigits(int newValue) { super.setMinimumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS)); } // ---------------------------------------------------------------------- // INSTANCE VARIABLES // ---------------------------------------------------------------------- private transient DigitList digitList = new DigitList(); /** * The symbol used as a prefix when formatting positive numbers, e.g. "+". * * @serial * @see #getPositivePrefix */ private String positivePrefix = ""; /** * The symbol used as a suffix when formatting positive numbers. This is often an empty string. * * @serial * @see #getPositiveSuffix */ private String positiveSuffix = ""; /** * The symbol used as a prefix when formatting negative numbers, e.g. "-". * * @serial * @see #getNegativePrefix */ private String negativePrefix = "-"; /** * The symbol used as a suffix when formatting negative numbers. This is often an empty string. * * @serial * @see #getNegativeSuffix */ private String negativeSuffix = ""; /** * The prefix pattern for non-negative numbers. This variable corresponds to positivePrefix. * *

* This pattern is expanded by the method expandAffix() to positivePrefix to update the latter to reflect changes in * symbols. If this variable is null then positivePrefix is taken as a literal value that does not change * when symbols changes. This variable is always null for DecimalFormat objects older than stream version 2 * restored from stream. * * @serial * @since 1.3 */ private String posPrefixPattern; /** * The suffix pattern for non-negative numbers. This variable corresponds to positiveSuffix. This variable is analogous to * posPrefixPattern; see that variable for further documentation. * * @serial * @since 1.3 */ private String posSuffixPattern; /** * The prefix pattern for negative numbers. This variable corresponds to negativePrefix. This variable is analogous to * posPrefixPattern; see that variable for further documentation. * * @serial * @since 1.3 */ private String negPrefixPattern; /** * The suffix pattern for negative numbers. This variable corresponds to negativeSuffix. This variable is analogous to * posPrefixPattern; see that variable for further documentation. * * @serial * @since 1.3 */ private String negSuffixPattern; /** * The multiplier for use in percent, permill, etc. * * @serial * @see #getMultiplier */ private int multiplier = 1; /** * The number of digits between grouping separators in the integer portion of a number. Must be greater than 0 if * NumberFormat.groupingUsed is true. * * @serial * @see #getGroupingSize * @see NumberFormat#isGroupingUsed */ private byte groupingSize = 3; // invariant, > 0 if useThousands /** * If true, forces the decimal separator to always appear in a formatted number, even if the fractional part of the number is zero. * * @serial * @see #isDecimalSeparatorAlwaysShown */ private boolean decimalSeparatorAlwaysShown = false; /** * True if this object represents a currency format. This determines whether the monetary decimal separator is used instead of the normal one. */ private transient boolean isCurrencyFormat = false; /** * The DecimalFormatSymbols object used by this format. It contains the symbols used to format numbers, e.g. the grouping separator, * decimal separator, and so on. * * @serial * @see #setDecimalFormatSymbols * @see DecimalFormatSymbols */ private DecimalFormatSymbols symbols = null; // LIU new // DecimalFormatSymbols(); /** * True to force the use of exponential (i.e. scientific) notation when formatting numbers. * * @serial * @since 1.2 */ private boolean useExponentialNotation; // Newly persistent in the Java 2 // platform /** * FieldPositions describing the positive prefix String. This is lazily created. Use getPositivePrefixFieldPositions when needed. */ private transient FieldPosition[] positivePrefixFieldPositions; /** * FieldPositions describing the positive suffix String. This is lazily created. Use getPositiveSuffixFieldPositions when needed. */ private transient FieldPosition[] positiveSuffixFieldPositions; /** * FieldPositions describing the negative prefix String. This is lazily created. Use getNegativePrefixFieldPositions when needed. */ private transient FieldPosition[] negativePrefixFieldPositions; /** * FieldPositions describing the negative suffix String. This is lazily created. Use getNegativeSuffixFieldPositions when needed. */ private transient FieldPosition[] negativeSuffixFieldPositions; /** * The minimum number of digits used to display the exponent when a number is formatted in exponential notation. This field is ignored if * useExponentialNotation is not true. * * @serial * @since 1.2 */ private byte minExponentDigits; // Newly persistent in the Java 2 platform // ---------------------------------------------------------------------- // CONSTANTS // ---------------------------------------------------------------------- // Constants for characters used in programmatic (unlocalized) patterns. private static final char PATTERN_ZERO_DIGIT = '0'; private static final char PATTERN_GROUPING_SEPARATOR = ','; private static final char PATTERN_DECIMAL_SEPARATOR = '.'; private static final char PATTERN_PER_MILLE = '\u2030'; private static final char PATTERN_PERCENT = '%'; private static final char PATTERN_DIGIT = '#'; private static final char PATTERN_SEPARATOR = ';'; private static final char PATTERN_EXPONENT = 'E'; private static final char PATTERN_MINUS = '-'; /** * The CURRENCY_SIGN is the standard Unicode symbol for currency. It is used in patterns and substitued 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; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy