home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / main.bin / DecimalFormat.java < prev    next >
Text File  |  1997-10-01  |  40KB  |  1,020 lines

  1. /*
  2.  * @(#)DecimalFormat.java    1.28 97/06/20
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.text;
  32. import java.util.ResourceBundle;
  33. import java.util.Locale;
  34. import java.io.ObjectInputStream;
  35. import java.io.IOException;
  36. import java.lang.ClassNotFoundException;
  37.  
  38. /**
  39.  * <code>DecimalFormat</code> is a concrete subclass of <code>NumberFormat</code>
  40.  * for formatting decimal numbers. This class allows for a variety
  41.  * of parameters, and localization to Western, Arabic, or Indic numbers.
  42.  *
  43.  * <p>
  44.  * Normally, you get the proper <code>NumberFormat</code> for a specific
  45.  * locale (including the default locale) using one of <code>NumberFormat</code>'s
  46.  * factory methods such as <code>getInstance</code>. You may then modify it
  47.  * from there (after testing to make sure it is a <code>DecimalFormat</code>,
  48.  * of course!)
  49.  *
  50.  * <p> 
  51.  * Either the prefixes or the suffixes must be different for
  52.  * the parse to distinguish positive from negative.
  53.  * Parsing will be unreliable if the digits, thousands or decimal separators
  54.  * are the same, or if any of them occur in the prefixes or suffixes.
  55.  *
  56.  * <p>
  57.  * <strong>Special cases:</strong>
  58.  *
  59.  * <p>
  60.  * <code>NaN</code> is formatted as a single character, typically
  61.  * <code>\\uFFFD</code>.
  62.  *
  63.  * <p>
  64.  * +/-Infinity is formatted as a single character, typically <code>\\u221E</code>,
  65.  * plus the positive and negative pre/suffixes.
  66.  *
  67.  * <p><code>Note:</code> this class is designed for common users; for very
  68.  * large or small numbers, use a format that can express exponential values.
  69.  
  70.  * <p><strong>Example:</strong>
  71.  * <blockquote>
  72.  * <pre>
  73.  * // normally we would have a GUI with a menu for this
  74.  * Locale[] locales = NumberFormat.getAvailableLocales();
  75.  *
  76.  * double myNumber = -1234.56;
  77.  * NumberFormat form;
  78.  *
  79.  * // just for fun, we print out a number with the locale number, currency
  80.  * // and percent format for each locale we can.
  81.  * for (int j = 0; j < 3; ++j) {
  82.  *     System.out.println("FORMAT");
  83.  *     for (int i = 0; i < locales.length; ++i) {
  84.  *         if (locales[i].getCountry().length() == 0) {
  85.  *            // skip language-only
  86.  *            continue;
  87.  *         }
  88.  *         System.out.print(locales[i].getDisplayName());
  89.  *         switch (j) {
  90.  *         default:
  91.  *             form = NumberFormat.getInstance(locales[i]); break;
  92.  *         case 1:
  93.  *             form = NumberFormat.getCurrencyInstance(locales[i]); break;
  94.  *         case 0:
  95.  *             form = NumberFormat.getPercentInstance(locales[i]); break;
  96.  *         }
  97.  *         try {
  98.  *             System.out.print(": " + ((DecimalFormat)form).toPattern()
  99.  *                          + " -> " + form.format(myNumber));
  100.  *         } catch (IllegalArgumentException iae) { }
  101.  *         try {
  102.  *             System.out.println(" -> " + form.parse(form.format(myNumber)));
  103.  *         } catch (ParseException pe) { }
  104.  *     }
  105.  * }
  106.  * </pre>
  107.  * </blockquote>
  108.  * <strong>The following shows the structure of the pattern.</strong>
  109.  * <pre>
  110.  * pattern    := subpattern{;subpattern}
  111.  * subpattern := {prefix}integer{.fraction}{suffix}
  112.  *
  113.  * prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
  114.  * suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
  115.  * integer    := '#'* '0'* '0'
  116.  * fraction   := '0'* '#'*
  117.  *
  118.  * Notation:
  119.  *  X*       0 or more instances of X
  120.  *  (X | Y)  either X or Y.
  121.  *  X..Y     any character from X up to Y, inclusive.
  122.  *  S - T    characters in S, except those in T
  123.  * </pre>
  124.  * The first subpattern is for positive numbers. The second (optional)
  125.  * subpattern is for negative numbers. (In both cases, ',' can occur
  126.  * inside the integer portion--it is just too messy to indicate in BNF.)
  127.  *
  128.  * <p>
  129.  * Here are the special characters used in the parts of the
  130.  * subpattern, with notes on their usage.
  131.  * <pre>
  132.  * Symbol Meaning
  133.  * 0      a digit
  134.  * #      a digit, zero shows as absent
  135.  * .      placeholder for decimal separator
  136.  * ,      placeholder for grouping separator.
  137.  * ;      separates formats.
  138.  * -      default negative prefix.
  139.  * %      divide by 100 and show as percentage
  140.  * X      any other characters can be used in the prefix or suffix
  141.  * '      used to quote special characters in a prefix or suffix.
  142.  * </pre>
  143.  * <p><strong>Notes</strong>
  144.  * <p>
  145.  * If there is no explicit negative subpattern, - is prefixed to the
  146.  * positive form. That is, "0.00" alone is equivalent to "0.00;-0.00".
  147.  *
  148.  * <p>
  149.  * Illegal formats, such as "#.#.#" or mixing '_' and '*' in the
  150.  * same format, will cause an <code>ParseException</code> to be thrown.
  151.  * From that <code>ParseException</code>, you can find the place in the string
  152.  * where the error occurred.
  153.  *
  154.  * <p>
  155.  * The grouping separator is commonly used for thousands, but in some
  156.  * countries for ten-thousands. The interval is a constant number of
  157.  * digits between the grouping characters, such as 100,000,000 or 1,0000,0000.
  158.  * If you supply a pattern with multiple grouping characters, the interval
  159.  * between the last one and the end of the integer is the one that is
  160.  * used. So "#,##,###,####" == "######,####" == "##,####,####".
  161.  *
  162.  * <p>
  163.  * This class only handles localized digits where the 10 digits
  164.  * are contiguous in Unicode, from 0 to 9. Other digits sets
  165.  * (such as superscripts) would need a different subclass.
  166.  *
  167.  * @see          java.util.Format
  168.  * @see          java.util.NumberFormat
  169.  * @see          java.util.ChoiceFormat
  170.  * @version      1.28 06/20/97
  171.  * @author       Mark Davis
  172.  */
  173. /*
  174.  * Requested Features
  175.  * Symbol Meaning
  176.  * $      currency symbol as decimal point
  177.  * à   escapes text
  178.  * \u2030 divide by 1000 and show as per/mil
  179.  */
  180. public class DecimalFormat extends NumberFormat {
  181.  
  182.     /**
  183.      * Create a DecimalFormat using the default pattern and symbols
  184.      * for the default locale. This is a convenient way to obtain a
  185.      * DecimalFormat when internationalization is not the main concern.
  186.      * <p>
  187.      * To obtain standard formats for a given locale, use the factory methods
  188.      * on NumberFormat such as getNumberInstance. These factories will
  189.      * return the most appropriate sub-class of NumberFormat for a given
  190.      * locale.
  191.      * @see java.text.NumberFormat#getInstance
  192.      * @see java.text.NumberFormat#getNumberInstance
  193.      * @see java.text.NumberFormat#getCurrencyInstance
  194.      * @see java.text.NumberFormat#getPercentInstance
  195.      */
  196.     public DecimalFormat() {
  197.         // Get the pattern for the default locale.
  198.         ResourceBundle rb = ResourceBundle.getBundle
  199.                             ("java.text.resources.LocaleElements",
  200.                              Locale.getDefault());
  201.         String[] patterns = rb.getStringArray("NumberPatterns");
  202.         applyPattern( patterns[0], false );
  203.  
  204.         this.symbols = new DecimalFormatSymbols( Locale.getDefault() );
  205.     }
  206.  
  207.  
  208.     /**
  209.      * Create a DecimalFormat from the given pattern and the symbols
  210.      * for the default locale. This is a convenient way to obtain a
  211.      * DecimalFormat when internationalization is not the main concern.
  212.      * <p>
  213.      * To obtain standard formats for a given locale, use the factory methods
  214.      * on NumberFormat such as getNumberInstance. These factories will
  215.      * return the most appropriate sub-class of NumberFormat for a given
  216.      * locale.
  217.      * @param pattern A non-localized pattern string.
  218.      * @exception IllegalArgumentException if the given pattern is invalid.
  219.      * @see java.text.NumberFormat#getInstance
  220.      * @see java.text.NumberFormat#getNumberInstance
  221.      * @see java.text.NumberFormat#getCurrencyInstance
  222.      * @see java.text.NumberFormat#getPercentInstance
  223.      */
  224.     public DecimalFormat(String pattern) {
  225.         applyPattern( pattern, false );
  226.         this.symbols = new DecimalFormatSymbols( Locale.getDefault() );
  227.     }
  228.  
  229.  
  230.     /**
  231.      * Create a DecimalFormat from the given pattern and symbols.
  232.      * Use this constructor when you need to completely customize the
  233.      * behavior of the format.
  234.      * <p>
  235.      * To obtain standard formats for a given
  236.      * locale, use the factory methods on NumberFormat such as
  237.      * getInstance or getCurrencyInstance. If you need only minor adjustments
  238.      * to a standard format, you can modify the format returned by
  239.      * a NumberFormat factory method.
  240.      * @param pattern a non-localized pattern string
  241.      * @param symbols the set of symbols to be used
  242.      * @exception IllegalArgumentException if the given pattern is invalid
  243.      * @see java.text.NumberFormat#getInstance
  244.      * @see java.text.NumberFormat#getNumberInstance
  245.      * @see java.text.NumberFormat#getCurrencyInstance
  246.      * @see java.text.NumberFormat#getPercentInstance
  247.      * @see java.text.DecimalFormatSymbols
  248.      */
  249.     public DecimalFormat (String pattern, DecimalFormatSymbols symbols) {
  250.         applyPattern( pattern, false );
  251.         this.symbols = symbols;
  252.     }
  253.  
  254.  
  255.     // Overrides
  256.     public StringBuffer format(double number, StringBuffer result,
  257.                                FieldPosition fieldPosition)
  258.     {
  259.         // Initialize
  260.         fieldPosition.beginIndex = fieldPosition.endIndex = 0;
  261.  
  262.         if (Double.isNaN(number)) {
  263.             result.append(symbols.getNaN());
  264.         } else {
  265.             boolean isNegative = (number < 0);
  266.             if (!isNegative)
  267.                 result.append(positivePrefix);
  268.             else {
  269.                 result.append(negativePrefix);
  270.                 number = -number;
  271.             }
  272.             if (Double.isInfinite(number)) {
  273.                 result.append(symbols.getInfinity());
  274.             } else {
  275.                 if (multiplier != 1) number *= multiplier;
  276.                 digitList.set(number, getMaximumFractionDigits());
  277.                 appendNativeDigits(result, fieldPosition);
  278.             }
  279.             if (!isNegative)
  280.                 result.append(positiveSuffix);
  281.             else result.append(negativeSuffix);
  282.         }
  283.         return result;
  284.     }
  285.  
  286.     public StringBuffer format(long number, StringBuffer result,
  287.                                FieldPosition fieldPosition) {
  288.         // Initialize
  289.         fieldPosition.beginIndex = fieldPosition.endIndex = 0;
  290.  
  291.         if (Double.isNaN(number)) {
  292.             result.append(symbols.getNaN());
  293.         } else {
  294.             boolean isNegative = (number < 0);
  295.             if (!isNegative)
  296.                 result.append(positivePrefix);
  297.             else {
  298.                 result.append(negativePrefix);
  299.                 number = -number;
  300.             }
  301.             if (Double.isInfinite(number)) {
  302.                 result.append(symbols.getInfinity());
  303.             } else {
  304.                 if (multiplier != 1) number *= multiplier;
  305.                 digitList.set(number);
  306.                 appendNativeDigits(result, fieldPosition);
  307.             }
  308.             if (!isNegative)
  309.                 result.append(positiveSuffix);
  310.             else result.append(negativeSuffix);
  311.         }
  312.         return result;
  313.     }
  314.  
  315.     public Number parse(String text, ParsePosition status) {
  316.         int start = status.index;
  317.         // special case NaN
  318.         if (text.regionMatches(start,symbols.getNaN(),
  319.                                0,symbols.getNaN().length())) {
  320.             status.index = start + symbols.getNaN().length();
  321.             return new Double(Double.NaN);
  322.         }
  323.  
  324.         // check for positivePrefix; take longest
  325.         boolean gotPositive = text.regionMatches(start,positivePrefix,0,
  326.                                                  positivePrefix.length());
  327.         boolean gotNegative = text.regionMatches(start,negativePrefix,0,
  328.                                                  negativePrefix.length());
  329.         if (gotPositive && gotNegative) {
  330.             if (positivePrefix.length() > negativePrefix.length())
  331.                 gotNegative = false;
  332.             else if (positivePrefix.length() < negativePrefix.length())
  333.                 gotPositive = false;
  334.         }
  335.         if (false) System.out.println("positive/negative:" + gotPositive
  336.                                       + ", " + gotNegative);
  337.         if (gotPositive) start += positivePrefix.length();
  338.         else if (gotNegative) start += negativePrefix.length();
  339.         else return null;
  340.  
  341.         // process digits or Inf, find decimal position
  342.         double  doubleResult = Double.NaN;
  343.         long    longResult = Long.MIN_VALUE;
  344.         boolean gotDouble = true;
  345.         if (text.regionMatches(start,symbols.getInfinity(),0,
  346.                                symbols.getInfinity().length()))
  347.         {
  348.             start += symbols.getInfinity().length();
  349.             digitList.decimalAt = start;
  350.             doubleResult = Double.POSITIVE_INFINITY;
  351.         } else {
  352.             digitList.count = 0;
  353.             digitList.decimalAt = -1;
  354.             int backup = -1;
  355.             int tentativeDecimal = -1;
  356.         boolean sawZeroDigit = false; // Temporary fix to 4048975 [LIU]
  357.             for (;start < text.length(); ++start) {
  358.                 char ch = text.charAt(start);
  359.                 if (symbols.getZeroDigit() < ch && ch <= (char)
  360.                         (symbols.getZeroDigit() + 9)) {
  361.                     backup = -1;
  362.                     digitList.decimalAt = tentativeDecimal;
  363.                     digitList.append(ch - symbols.getZeroDigit() + '0');
  364.                 } else if (ch == symbols.getZeroDigit()) {
  365.             sawZeroDigit = true; // Temporary fix 4048975 [LIU]
  366.                     if (digitList.count == 0 && tentativeDecimal == -1) // [LIU]
  367.                         continue;
  368.                     backup = -1;
  369.                     digitList.decimalAt = tentativeDecimal;
  370.                     if ((digitList.count != 0) || (tentativeDecimal != -1)) {
  371.                         digitList.append(ch - symbols.getZeroDigit() +'0');
  372.                     }
  373.                 } else if (digitList.decimalAt >= 0) {
  374.                     break;
  375.                 } else if (ch == symbols.getDecimalSeparator() && !isParseIntegerOnly()) {
  376.                     backup = start;
  377.                     tentativeDecimal = digitList.count;
  378.                 } else if (ch == symbols.getGroupingSeparator() && isGroupingUsed()) {
  379.                     backup = start;
  380.                 } else {
  381.                     break;
  382.                 }
  383.             }
  384.             if (backup != -1) start = backup;
  385.             if (digitList.decimalAt == -1)
  386.                 digitList.decimalAt = digitList.count;
  387.             if (digitList.decimalAt == digitList.count
  388.                 && digitList.count <= DigitList.MAX_COUNT)
  389.             {
  390.                 gotDouble = false;
  391.                 if (digitList.count == DigitList.MAX_COUNT && gotNegative &&
  392.                         isLongMIN_VALUE(digitList))
  393.                     longResult = Long.MIN_VALUE;
  394.                 else {
  395.             if (digitList.count == 0)
  396.               { // Temporary fix 4048975 [LIU]
  397.             if (!sawZeroDigit) return null;
  398.             longResult = 0;
  399.               } else
  400.             longResult = digitList.getLong();
  401.         }
  402.             } else {
  403.         if (digitList.count == 0)
  404.             return null;
  405.                 doubleResult = digitList.getDouble();
  406.         }
  407.         }
  408.  
  409.         // check for positiveSuffix
  410.         if (gotPositive)
  411.             gotPositive = text.regionMatches(start,positiveSuffix,0,
  412.                                              positiveSuffix.length());
  413.         if (gotNegative)
  414.             gotNegative = text.regionMatches(start,negativeSuffix,0,
  415.                                              negativeSuffix.length());
  416.  
  417.         // fail if neither ok
  418.         if (!gotPositive && !gotNegative) return null;
  419.  
  420.         // if both match, take longest
  421.         if (gotPositive && gotNegative) {
  422.             if (positiveSuffix.length() > negativeSuffix.length())
  423.                 gotNegative = false;
  424.             else if (positiveSuffix.length() < negativeSuffix.length())
  425.                 gotPositive = false;
  426.             else
  427.                 return null; // fail if we can't distinguish!
  428.         }
  429.  
  430.         // fail if neither or both
  431.         if (gotPositive == gotNegative) return null;
  432.         if (false) System.out.println("positive/negative:" + gotPositive
  433.                                       + ", " + gotNegative);
  434.  
  435.         // return final value
  436.         if (multiplier != 1)
  437.             if (gotDouble)
  438.                 doubleResult /= multiplier;
  439.             else {
  440.                 doubleResult = ((double)longResult) / multiplier;
  441.                 gotDouble = true;
  442.             }
  443.         if (gotPositive) {
  444.             status.index = start + positiveSuffix.length();// mark success!
  445.         } else {
  446.             status.index = start + negativeSuffix.length();// mark success!
  447.             doubleResult = -doubleResult;
  448.             longResult = -longResult;
  449.         }
  450.         if (gotDouble)
  451.             return new Double(doubleResult);
  452.         else if (digitList.decimalAt == digitList.count && // no decimal
  453.             (longResult >= Long.MIN_VALUE || longResult <= Long.MAX_VALUE))
  454.             return new Long(longResult);
  455.         else
  456.             return new Double((double)longResult);
  457.     }
  458.  
  459.     /**
  460.      * Returns the decimal format symbols, which is generally not changed
  461.      * by the programmer or user.
  462.      * @return desired DecimalFormatSymbols
  463.      * @see java.util.DecimalFormatSymbols
  464.      */
  465.     public DecimalFormatSymbols getDecimalFormatSymbols() {
  466.         try {
  467.             // don't allow multiple references
  468.             return (DecimalFormatSymbols) symbols.clone();
  469.         } catch (Exception foo) {
  470.             return null; // should never happen
  471.         }
  472.     }
  473.  
  474.  
  475.     /**
  476.      * Sets the decimal format symbols, which is generally not changed
  477.      * by the programmer or user.
  478.      * @param newSymbols desired DecimalFormatSymbols
  479.      * @see java.util.DecimalFormatSymbols
  480.      */
  481.     public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
  482.         try {
  483.             // don't allow multiple references
  484.             symbols = (DecimalFormatSymbols) newSymbols.clone();
  485.         } catch (Exception foo) {
  486.             // should never happen
  487.         }
  488.     }
  489.  
  490.     /**
  491.      * Get the positive prefix.
  492.      * <P>Examples: +123, $123, sFr123
  493.      */
  494.     public String getPositivePrefix () {
  495.         return positivePrefix;
  496.     }
  497.  
  498.     /**
  499.      * Set the positive prefix.
  500.      * <P>Examples: +123, $123, sFr123
  501.      */
  502.     public void setPositivePrefix (String newValue) {
  503.         positivePrefix = newValue;
  504.     }
  505.  
  506.     /**
  507.      * Get the negative prefix.
  508.      * <P>Examples: -123, ($123) (with negative suffix), sFr-123
  509.      */
  510.     public String getNegativePrefix () {
  511.         return negativePrefix;
  512.     }
  513.  
  514.     /**
  515.      * Set the negative prefix.
  516.      * <P>Examples: -123, ($123) (with negative suffix), sFr-123
  517.      */
  518.     public void setNegativePrefix (String newValue) {
  519.         negativePrefix = newValue;
  520.     }
  521.  
  522.     /**
  523.      * Get the positive suffix.
  524.      * <P>Example: 123%
  525.      */
  526.     public String getPositiveSuffix () {
  527.         return positiveSuffix;
  528.     }
  529.  
  530.     /**
  531.      * Set the positive suffix.
  532.      * <P>Example: 123%
  533.      */
  534.     public void setPositiveSuffix (String newValue) {
  535.         positiveSuffix = newValue;
  536.     }
  537.  
  538.     /**
  539.      * Get the negative suffix.
  540.      * <P>Examples: -123%, ($123) (with positive suffixes)
  541.      */
  542.     public String getNegativeSuffix () {
  543.         return negativeSuffix;
  544.     }
  545.  
  546.     /**
  547.      * Set the positive suffix.
  548.      * <P>Examples: 123%
  549.      */
  550.     public void setNegativeSuffix (String newValue) {
  551.         negativeSuffix = newValue;
  552.     }
  553.  
  554.     /**
  555.      * Get the multiplier for use in percent, permill, etc.
  556.      * For a percentage, set the suffixes to have "%" and the multiplier to be 100.
  557.      * (For Arabic, use arabic percent symbol).
  558.      * For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000.
  559.      * <P>Examples: with 100, 1.23 -> "123", and "123" -> 1.23
  560.      */
  561.     public int getMultiplier () {
  562.         return multiplier;
  563.     }
  564.  
  565.     /**
  566.      * Set the multiplier for use in percent, permill, etc.
  567.      * For a percentage, set the suffixes to have "%" and the multiplier to be 100.
  568.      * (For Arabic, use arabic percent symbol).
  569.      * For a permill, set the suffixes to have "\u2031" and the multiplier to be 1000.
  570.      * <P>Examples: with 100, 1.23 -> "123", and "123" -> 1.23
  571.      */
  572.     public void setMultiplier (int newValue) {
  573.         multiplier = newValue;
  574.     }
  575.  
  576.     /**
  577.      * Return the grouping size. Grouping size is the number of digits between
  578.      * grouping separators in the integer portion of a number.  For example,
  579.      * in the number "123,456.78", the grouping size is 3.
  580.      * @see #setGroupingSize
  581.      * @see java.text.NumberFormat#isGroupingUsed
  582.      * @see java.text.DecimalFormatSymbols#getGroupingSeparator
  583.      */
  584.     public int getGroupingSize () {
  585.         return groupingSize;
  586.     }
  587.  
  588.     /**
  589.      * Set the grouping size. Grouping size is the number of digits between
  590.      * grouping separators in the integer portion of a number.  For example,
  591.      * in the number "123,456.78", the grouping size is 3.
  592.      * @see #getGroupingSize
  593.      * @see java.text.NumberFormat#setGroupingUsed
  594.      * @see java.text.DecimalFormatSymbols#setGroupingSeparator
  595.      */
  596.     public void setGroupingSize (int newValue) {
  597.         groupingSize = (byte)newValue;
  598.     }
  599.  
  600.     /**
  601.      * Allows you to get the behavior of the decimal separator with integers.
  602.      * (The decimal separator will always appear with decimals.)
  603.      * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
  604.      */
  605.     public boolean isDecimalSeparatorAlwaysShown() {
  606.         return decimalSeparatorAlwaysShown;
  607.     }
  608.  
  609.     /**
  610.      * Allows you to set the behavior of the decimal separator with integers.
  611.      * (The decimal separator will always appear with decimals.)
  612.      * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
  613.      */
  614.     public void setDecimalSeparatorAlwaysShown(boolean newValue) {
  615.         decimalSeparatorAlwaysShown = newValue;
  616.     }
  617.  
  618.     /**
  619.      * Standard override; no change in semantics.
  620.      */
  621.     public Object clone() {
  622.         try {
  623.             DecimalFormat other = (DecimalFormat) super.clone();
  624.             other.symbols = (DecimalFormatSymbols) symbols.clone();
  625.             return other;
  626.         } catch (Exception e) {
  627.             throw new InternalError();
  628.         }
  629.     };
  630.  
  631.     /**
  632.      * Overrides equals
  633.      */
  634.     public boolean equals(Object obj)
  635.     {
  636.       if (!super.equals(obj)) return false; // super does class check
  637.       DecimalFormat other = (DecimalFormat) obj;
  638.       return (positivePrefix.equals(other.positivePrefix)
  639.           && positiveSuffix.equals(other.positiveSuffix)
  640.           && negativePrefix.equals(other.negativePrefix)
  641.           && negativeSuffix.equals(other.negativeSuffix)
  642.           && multiplier == other.multiplier
  643.           && groupingSize == other.groupingSize
  644.           && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown
  645.           && symbols.equals(other.symbols));
  646.     }
  647.  
  648.     /**
  649.      * Overrides hashCode
  650.      */
  651.     public int hashCode() {
  652.         return super.hashCode() * 37 + positivePrefix.hashCode();
  653.         // just enough fields for a reasonable distribution
  654.     }
  655.  
  656.     /**
  657.      * Synthesizes a pattern string that represents the current state
  658.      * of this Format object.
  659.      * @see #applyPattern
  660.      */
  661.     public String toPattern() {
  662.         return toPattern( false );
  663.     }
  664.  
  665.     /**
  666.      * Synthesizes a localized pattern string that represents the current
  667.      * state of this Format object.
  668.      * @see #applyPattern
  669.      */
  670.     public String toLocalizedPattern() {
  671.         return toPattern( true );
  672.     }
  673.  
  674.     /**
  675.      * Does the real work of generating a pattern.
  676.      */
  677.     private String toPattern(boolean localized) {
  678.         StringBuffer result = new StringBuffer();
  679.         for (int j = 1; j >= 0; --j) {
  680.             if (j == 1)
  681.                 result.append(positivePrefix);
  682.             else result.append(negativePrefix);
  683.             int tempMax = Math.max(groupingSize, getMinimumIntegerDigits())+1;
  684.             int i;
  685.             for (i = tempMax; i > 0; --i) {
  686.                 if (i == groupingSize)
  687.                     result.append(localized ? symbols.getGroupingSeparator() :
  688.                                   patternGroupingSeparator);
  689.                 if (i <= getMinimumIntegerDigits()) {
  690.                     result.append(localized ? symbols.getZeroDigit() :
  691.                                   patternZeroDigit);
  692.                 } else {
  693.                     result.append(localized ? symbols.getDigit() :
  694.                                   patternDigit);
  695.                 }
  696.             }
  697.             if (getMaximumFractionDigits() > 0)
  698.                 result.append(localized ? symbols.getDecimalSeparator() :
  699.                               patternDecimalSeparator);
  700.             for (i = 0; i < getMaximumFractionDigits(); ++i) {
  701.                 if (i < getMinimumFractionDigits()) {
  702.                     result.append(localized ? symbols.getZeroDigit() :
  703.                                   patternZeroDigit);
  704.                 } else {
  705.                     result.append(localized ? symbols.getDigit() :
  706.                                   patternDigit);
  707.                 }
  708.             }
  709.             if (j == 1) {
  710.                 result.append(positiveSuffix);
  711.                 if (negativeSuffix.equals(positiveSuffix)) {
  712.                     if (negativePrefix.equals(symbols.getMinusSign() + positivePrefix))
  713.                         break;
  714.                 }
  715.                 result.append(localized ? symbols.getPatternSeparator() :
  716.                               patternSeparator);
  717.             } else result.append(negativeSuffix);
  718.         }
  719.         return result.toString();
  720.     }
  721.  
  722.  
  723.     /**
  724.      * Apply the given pattern to this Format object.  A pattern is a
  725.      * short-hand specification for the various formatting properties.
  726.      * These properties can also be changed individually through the
  727.      * various setter methods.
  728.      * <p>
  729.      * There is no limit to integer digits are set
  730.      * by this routine, since that is the typical end-user desire;
  731.      * use setMaximumInteger if you want to set a real value.
  732.      * For negative numbers, use a second pattern, separated by a semicolon
  733.      * <P>Example "#,#00.0#" -> 1,234.56
  734.      * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
  735.      * a maximum of 2 fraction digits.
  736.      * <p>Example: "#,#00.0#;(#,#00.0#)" for negatives in parantheses.
  737.      * <p>In negative patterns, the minimum and maximum counts are ignored;
  738.      * these are presumed to be set in the positive pattern.
  739.      */
  740.     public void applyPattern( String pattern ) {
  741.         applyPattern( pattern, false );
  742.     }
  743.  
  744.     /**
  745.      * Apply the given pattern to this Format object.  The pattern
  746.      * is assumed to be in a localized notation. A pattern is a
  747.      * short-hand specification for the various formatting properties.
  748.      * These properties can also be changed individually through the
  749.      * various setter methods.
  750.      * <p>
  751.      * There is no limit to integer digits are set
  752.      * by this routine, since that is the typical end-user desire;
  753.      * use setMaximumInteger if you want to set a real value.
  754.      * For negative numbers, use a second pattern, separated by a semicolon
  755.      * <P>Example "#,#00.0#" -> 1,234.56
  756.      * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
  757.      * a maximum of 2 fraction digits.
  758.      * <p>Example: "#,#00.0#;(#,#00.0#)" for negatives in parantheses.
  759.      * <p>In negative patterns, the minimum and maximum counts are ignored;
  760.      * these are presumed to be set in the positive pattern.
  761.      */
  762.     public void applyLocalizedPattern( String pattern ) {
  763.         applyPattern( pattern, true );
  764.     }
  765.  
  766.     /**
  767.      * Does the real work of applying a pattern.
  768.      */
  769.     private void applyPattern(String pattern, boolean localized)
  770.     {
  771.         int start = 0;
  772.         boolean inQuote = false;
  773.         boolean gotNegative = false;
  774.         for (int j = 1; j >= 0 && start < pattern.length(); --j) {
  775.             StringBuffer prefix = new StringBuffer();
  776.             StringBuffer suffix = new StringBuffer();
  777.             byte maxIntegerCount = 0;
  778.             byte minIntegerCount = 0;
  779.             byte maxDecimalCount = 0;
  780.             byte minDecimalCount = 0;
  781.             int multiplier = 1;
  782.             boolean useThousands = false;
  783.             boolean useDecimalAlways = false;
  784.             byte groupingSize = 0;
  785.             char zeroDigit = patternZeroDigit;
  786.             char groupingSeparator = patternGroupingSeparator;
  787.             char decimalSeparator = patternDecimalSeparator;
  788.             char percent = patternPercent;
  789.             char perMill = patternPerMill;
  790.             char digit = patternDigit;
  791.             char separator = patternSeparator;
  792.             if (localized) {
  793.                 zeroDigit = symbols.getZeroDigit();
  794.                 groupingSeparator = symbols.getGroupingSeparator();
  795.                 decimalSeparator = symbols.getDecimalSeparator();
  796.                 percent = symbols.getPercent();
  797.                 perMill = symbols.getPerMill();
  798.                 digit = symbols.getDigit();
  799.                 separator = symbols.getPatternSeparator();
  800.             }
  801.             // for now, minimal checking for syntax errors in pattern
  802.             for (; start < pattern.length(); ++start) {
  803.                 char ch = pattern.charAt(start);
  804.                 if (inQuote) {
  805.                     if (ch == '\'') {
  806.                         inQuote = false;
  807.                         continue;
  808.                     }
  809.                     if (maxIntegerCount <= 0)
  810.                         prefix.append(ch);
  811.                     else
  812.                         suffix.append(ch);
  813.                 } else if (ch == '\'') {
  814.                     inQuote = true;
  815.                     continue;
  816.                 } else if (ch == separator) {
  817.                     ++start;
  818.                     break;
  819.                 } else if (!useDecimalAlways) { // in integer part
  820.                     if (ch == digit) {
  821.                         ++maxIntegerCount;
  822.                         if (minIntegerCount > 0)
  823.                             throw new IllegalArgumentException();
  824.                         if (useThousands) ++groupingSize;
  825.                     } else if (ch == zeroDigit) {
  826.                         ++minIntegerCount;
  827.                         ++maxIntegerCount;
  828.                         if (useThousands) ++groupingSize;
  829.                     } else if (ch == groupingSeparator) {
  830.                         useThousands = true;
  831.                         groupingSize = 0;  // reset, ignore all but last
  832.                     } else if (ch == decimalSeparator) {
  833.                         if (useDecimalAlways)
  834.                             throw new IllegalArgumentException();
  835.                         useDecimalAlways = true;
  836.                     } else if (maxIntegerCount > 0) {
  837.                         // LATER: clean up the code a bit
  838.                         if (useDecimalAlways)
  839.                             throw new IllegalArgumentException();
  840.  
  841.                         useDecimalAlways = true;
  842.                         if (ch == percent) {
  843.                             suffix.append(symbols.getPercent());
  844.                             multiplier = 100;
  845.                         } else if (ch == perMill) {
  846.                             suffix.append(symbols.getPerMill());
  847.                             multiplier = 1000;
  848.                         } else {
  849.                             suffix.append(ch);
  850.                         }
  851.                     } else {
  852.                         prefix.append(ch);
  853.                     }
  854.                 } else {        // in decimal part
  855.                     if (ch == digit) {
  856.                         ++maxDecimalCount;
  857.                     } else if (ch == zeroDigit) {
  858.                         ++minDecimalCount;
  859.                         ++maxDecimalCount;
  860.                     } else if (ch == percent) {
  861.                         suffix.append(symbols.getPercent());
  862.                         multiplier = 100;
  863.                     } else if (ch == perMill) {
  864.                         suffix.append(symbols.getPerMill());
  865.                         multiplier = 1000;
  866.                     } else {
  867.                         suffix.append(ch);
  868.                     }
  869.                 }
  870.             }
  871.             if (j == 1) {
  872.                 this.positivePrefix = prefix.toString();
  873.                 this.positiveSuffix = suffix.toString();
  874.                 this.negativePrefix = positivePrefix;   // assume these for now
  875.                 this.negativeSuffix = positiveSuffix;
  876.                 setMaximumIntegerDigits(127);
  877.                 setMinimumIntegerDigits(minIntegerCount);
  878.                 setMaximumFractionDigits(maxDecimalCount);
  879.                 setMinimumFractionDigits(minDecimalCount);
  880.                 setGroupingUsed(useThousands);
  881.                 useDecimalAlways = false; // ignore pattern
  882.                 setDecimalSeparatorAlwaysShown(useDecimalAlways);
  883.                 this.groupingSize = groupingSize;
  884.                 this.multiplier = multiplier;
  885.             } else {
  886.                 // LATER; do consistency checks & throw exceptions
  887.                 this.negativePrefix = prefix.toString();
  888.                 this.negativeSuffix = suffix.toString();
  889.                 gotNegative = true;
  890.             }
  891.         }
  892.         if (!gotNegative ||
  893.             (negativePrefix.equals(positivePrefix)
  894.              && negativeSuffix.equals(positiveSuffix))) {
  895.             negativeSuffix = positiveSuffix;
  896.             negativePrefix = symbols.getMinusSign() + negativePrefix;
  897.         }
  898.     }
  899.  
  900.     // ================privates===================
  901.  
  902.  
  903.     /**
  904.      * Utility routine to add current digits to a result. Used
  905.      * in format.
  906.      */
  907.     private void appendNativeDigits(StringBuffer result,
  908.                                     FieldPosition fieldPosition)
  909.     {
  910.         int endOffset = digitList.decimalAt;
  911.  
  912.         if (fieldPosition.field == NumberFormat.INTEGER_FIELD)
  913.             fieldPosition.beginIndex = result.length();
  914.  
  915.         // do before the decimal point
  916.         int startOffset = 0;
  917.         int segmentCount = Math.max(getMinimumIntegerDigits(),
  918.                                     Math.min(endOffset - startOffset,
  919.                                              getMaximumIntegerDigits()));
  920.         for (int i = segmentCount; i > 0; --i) {
  921.             if (i > endOffset - startOffset || endOffset - i >= digitList.count) {
  922.                 result.append(symbols.getZeroDigit());
  923.             } else {
  924.                 result.append((char)(digitList.digits[endOffset - i] - '0'
  925.                     + symbols.getZeroDigit()));
  926.             }
  927.             if (isGroupingUsed() && (((i-1) % groupingSize) == 0)
  928.                 && i != 1)
  929.                 result.append(symbols.getGroupingSeparator());
  930.         }
  931.  
  932.  
  933.         if (fieldPosition.field == NumberFormat.INTEGER_FIELD)
  934.             fieldPosition.endIndex = result.length();
  935.  
  936.         // do after the decimal point
  937.         startOffset = endOffset;
  938.         endOffset = digitList.count;
  939.         segmentCount = Math.max(getMinimumFractionDigits(),
  940.                                 Math.min(endOffset - startOffset,
  941.                                          getMaximumFractionDigits()));
  942.         if (decimalSeparatorAlwaysShown || segmentCount > 0) {
  943.             result.append(symbols.getDecimalSeparator());
  944.  
  945.             if (fieldPosition.field == NumberFormat.FRACTION_FIELD)
  946.                 fieldPosition.beginIndex = result.length();
  947.  
  948.             for (int i = 0; i < segmentCount; ++i) {
  949.                 if (i >= endOffset - startOffset) {
  950.                     result.append(symbols.getZeroDigit());
  951.                 } else {
  952.                     result.append((char)(digitList.digits[startOffset + i] - '0'
  953.                         + symbols.getZeroDigit()));
  954.                 }
  955.             }
  956.             if (fieldPosition.field == NumberFormat.FRACTION_FIELD)
  957.                 fieldPosition.endIndex = result.length();
  958.         }
  959.     }
  960.  
  961.     private boolean isSpecialChar(char ch) {
  962.         return ((ch == patternZeroDigit) ||
  963.                 (ch == patternGroupingSeparator) ||
  964.                 (ch == patternDecimalSeparator) ||
  965.                 (ch == patternPercent) ||
  966.                 (ch == patternPerMill) ||
  967.                 (ch == patternDigit) ||
  968.                 (ch == patternSeparator));
  969.     }
  970.  
  971.     // Returns true if DigitList.digits is equal to Long.MIN_VALUE;
  972.     // false, otherwise.
  973.     //
  974.     private boolean isLongMIN_VALUE(DigitList dl)
  975.     {
  976.         StringBuffer temp = new StringBuffer(dl.count+1);
  977.         temp.append('-');
  978.         for (int i = 0; i < dl.count; ++i)
  979.             temp.append((char)(dl.digits[i]));
  980.         if (temp.toString().regionMatches(false, 0,
  981.             Long.toString(Long.MIN_VALUE), 0, dl.count+1))
  982.             return true;
  983.         return false;
  984.     }
  985.  
  986.     /**
  987.      * Override readObject.
  988.      */
  989.     private void readObject(ObjectInputStream stream)
  990.             throws IOException, ClassNotFoundException {
  991.         stream.defaultReadObject();
  992.         // initialize our transient field
  993.         digitList = new DigitList();
  994.     }
  995.     private transient DigitList digitList = new DigitList();
  996.  
  997.     // these are often left as localized
  998.     private String  positivePrefix = "";
  999.     private String  positiveSuffix = "";
  1000.     private String  negativePrefix = "-";
  1001.     private String  negativeSuffix = "";
  1002.     private int     multiplier = 1;
  1003.     private byte    groupingSize = 3;  // invariant, > 0 if useThousands
  1004.     private boolean decimalSeparatorAlwaysShown = false;
  1005.  
  1006.     private DecimalFormatSymbols symbols = new DecimalFormatSymbols();
  1007.  
  1008.     // Constants for characters used in programmatic (unlocalized) patterns.
  1009.     // FIXME - These should be renamed to follow the convention for constants
  1010.     // (ie UPPER_CASE)
  1011.     private static final char       patternZeroDigit = '0';
  1012.     private static final char       patternGroupingSeparator = ',';
  1013.     private static final char       patternDecimalSeparator = '.';
  1014.     private static final char       patternPerMill = '\u2030';
  1015.     private static final char       patternPercent = '%';
  1016.     private static final char       patternDigit = '#';
  1017.     private static final char       patternSeparator = ';';
  1018. }
  1019.  
  1020.