mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1020 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1020 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* DecimalFormat.java -- Formats and parses numbers
 | |
|    Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
|  
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 | |
| 02111-1307 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| package java.text;
 | |
| 
 | |
| import java.util.Locale;
 | |
| import java.util.MissingResourceException;
 | |
| import java.util.ResourceBundle;
 | |
| import java.io.ObjectInputStream;
 | |
| import java.io.IOException;
 | |
| 
 | |
| /**
 | |
|  * @author Tom Tromey <tromey@cygnus.com>
 | |
|  * @date March 4, 1999
 | |
|  */
 | |
| /* Written using "Java Class Libraries", 2nd edition, plus online
 | |
|  * API docs for JDK 1.2 from http://www.javasoft.com.
 | |
|  * Status:  Believed complete and correct to 1.2.
 | |
|  * Note however that the docs are very unclear about how format parsing
 | |
|  * should work.  No doubt there are problems here.
 | |
|  */
 | |
| public class DecimalFormat extends NumberFormat
 | |
| {
 | |
|   // This is a helper for applyPatternWithSymbols.  It reads a prefix
 | |
|   // or a suffix.  It can cause some side-effects.
 | |
|   private final int scanFix (String pattern, int index, StringBuffer buf,
 | |
| 			     String patChars, DecimalFormatSymbols syms,
 | |
| 			     boolean is_suffix)
 | |
|   {
 | |
|     int len = pattern.length();
 | |
|     buf.setLength(0);
 | |
|     boolean multiplierSet = false;
 | |
|     while (index < len)
 | |
|       {
 | |
| 	char c = pattern.charAt(index);
 | |
| 	if (c == '\'' && index + 1 < len
 | |
| 	    && pattern.charAt(index + 1) == '\'')
 | |
| 	  {
 | |
| 	    buf.append(c);
 | |
| 	    ++index;
 | |
| 	  }
 | |
| 	else if (c == '\'' && index + 2 < len
 | |
| 		 && pattern.charAt(index + 2) == '\'')
 | |
| 	  {
 | |
| 	    buf.append(pattern.charAt(index + 1));
 | |
| 	    index += 2;
 | |
| 	  }
 | |
| 	else if (c == '\u00a4')
 | |
| 	  {
 | |
| 	    if (index + 1 < len && pattern.charAt(index + 1) == '\u00a4')
 | |
| 	      {
 | |
| 		buf.append(syms.getInternationalCurrencySymbol());
 | |
| 		++index;
 | |
| 	      }
 | |
| 	    else
 | |
| 	      buf.append(syms.getCurrencySymbol());
 | |
| 	  }
 | |
| 	else if (is_suffix && c == syms.getPercent())
 | |
| 	  {
 | |
| 	    if (multiplierSet)
 | |
| 	      throw new IllegalArgumentException ("multiplier already set " +
 | |
| 						  "- index: " + index);
 | |
| 	    multiplierSet = true;
 | |
| 	    multiplier = 100;
 | |
| 	    buf.append(c);
 | |
| 	  }
 | |
| 	else if (is_suffix && c == syms.getPerMill())
 | |
| 	  {
 | |
| 	    if (multiplierSet)
 | |
| 	      throw new IllegalArgumentException ("multiplier already set " +
 | |
| 						  "- index: " + index);
 | |
| 	    multiplierSet = true;
 | |
| 	    multiplier = 1000;
 | |
| 	    buf.append(c);
 | |
| 	  }
 | |
| 	else if (patChars.indexOf(c) != -1)
 | |
| 	  {
 | |
| 	    // This is a pattern character.
 | |
| 	    break;
 | |
| 	  }
 | |
| 	else
 | |
| 	  buf.append(c);
 | |
| 	++index;
 | |
|       }
 | |
| 
 | |
|     return index;
 | |
|   }
 | |
| 
 | |
|   // A helper which reads a number format.
 | |
|   private final int scanFormat (String pattern, int index,
 | |
| 				String patChars, DecimalFormatSymbols syms,
 | |
| 				boolean is_positive)
 | |
|   {
 | |
|     int max = pattern.length();
 | |
| 
 | |
|     int countSinceGroup = 0;
 | |
|     int zeroCount = 0;
 | |
|     boolean saw_group = false;
 | |
| 
 | |
|     //
 | |
|     // Scan integer part.
 | |
|     //
 | |
|     while (index < max)
 | |
|       {
 | |
| 	char c = pattern.charAt(index);
 | |
| 
 | |
| 	if (c == syms.getDigit())
 | |
| 	  {
 | |
| 	    if (zeroCount > 0)
 | |
| 	      throw new IllegalArgumentException ("digit mark following " +
 | |
| 						  "zero - index: " + index);
 | |
| 	    ++countSinceGroup;
 | |
| 	  }
 | |
| 	else if (c == syms.getZeroDigit())
 | |
| 	  {
 | |
| 	    ++zeroCount;
 | |
| 	    ++countSinceGroup;
 | |
| 	  }
 | |
| 	else if (c == syms.getGroupingSeparator())
 | |
| 	  {
 | |
| 	    countSinceGroup = 0;
 | |
| 	    saw_group = true;
 | |
| 	  }
 | |
| 	else
 | |
| 	  break;
 | |
| 
 | |
| 	++index;
 | |
|       }
 | |
| 
 | |
|     // We can only side-effect when parsing the positive format.
 | |
|     if (is_positive)
 | |
|       {
 | |
| 	groupingUsed = saw_group;
 | |
| 	groupingSize = (byte) countSinceGroup;
 | |
| 	minimumIntegerDigits = zeroCount;
 | |
|       }
 | |
| 
 | |
|     // Early termination.
 | |
|     if (index == max || pattern.charAt(index) == syms.getGroupingSeparator())
 | |
|       {
 | |
| 	if (is_positive)
 | |
| 	  decimalSeparatorAlwaysShown = false;
 | |
| 	return index;
 | |
|       }
 | |
| 
 | |
|     if (pattern.charAt(index) == syms.getDecimalSeparator())
 | |
|       {
 | |
| 	++index;
 | |
| 
 | |
| 	//
 | |
| 	// Scan fractional part.
 | |
| 	//
 | |
| 	int hashCount = 0;
 | |
| 	zeroCount = 0;
 | |
| 	while (index < max)
 | |
| 	  {
 | |
| 	    char c = pattern.charAt(index);
 | |
| 	    if (c == syms.getZeroDigit())
 | |
| 	      {
 | |
| 		if (hashCount > 0)
 | |
| 		  throw new IllegalArgumentException ("zero mark " +
 | |
| 						      "following digit - index: " + index);
 | |
| 		++zeroCount;
 | |
| 	      }
 | |
| 	    else if (c == syms.getDigit())
 | |
| 	      {
 | |
| 		++hashCount;
 | |
| 	      }
 | |
| 	    else if (c != syms.getExponential()
 | |
| 		     && c != syms.getPatternSeparator()
 | |
| 		     && patChars.indexOf(c) != -1)
 | |
| 	      throw new IllegalArgumentException ("unexpected special " +
 | |
| 						  "character - index: " + index);
 | |
| 	    else
 | |
| 	      break;
 | |
| 
 | |
| 	    ++index;
 | |
| 	  }
 | |
| 
 | |
| 	if (is_positive)
 | |
| 	  {
 | |
| 	    maximumFractionDigits = hashCount + zeroCount;
 | |
| 	    minimumFractionDigits = zeroCount;
 | |
| 	  }
 | |
| 
 | |
| 	if (index == max)
 | |
| 	  return index;
 | |
|       }
 | |
| 
 | |
|     if (pattern.charAt(index) == syms.getExponential())
 | |
|       {
 | |
| 	//
 | |
| 	// Scan exponential format.
 | |
| 	//
 | |
| 	zeroCount = 0;
 | |
| 	++index;
 | |
| 	while (index < max)
 | |
| 	  {
 | |
| 	    char c = pattern.charAt(index);
 | |
| 	    if (c == syms.getZeroDigit())
 | |
| 	      ++zeroCount;
 | |
| 	    else if (c == syms.getDigit())
 | |
| 	      {
 | |
| 		if (zeroCount > 0)
 | |
| 		  throw new
 | |
| 		    IllegalArgumentException ("digit mark following zero " +
 | |
| 					      "in exponent - index: " +
 | |
| 					      index);
 | |
| 	      }
 | |
| 	    else if (patChars.indexOf(c) != -1)
 | |
| 	      throw new IllegalArgumentException ("unexpected special " +
 | |
| 						  "character - index: " +
 | |
| 						  index);
 | |
| 	    else
 | |
| 	      break;
 | |
| 
 | |
| 	    ++index;
 | |
| 	  }
 | |
| 
 | |
| 	if (is_positive)
 | |
| 	  {
 | |
| 	    useExponentialNotation = true;
 | |
| 	    minExponentDigits = (byte) zeroCount;
 | |
| 	  }
 | |
|       }
 | |
| 
 | |
|     return index;
 | |
|   }
 | |
| 
 | |
|   // This helper function creates a string consisting of all the
 | |
|   // characters which can appear in a pattern and must be quoted.
 | |
|   private final String patternChars (DecimalFormatSymbols syms)
 | |
|   {
 | |
|     StringBuffer buf = new StringBuffer ();
 | |
|     buf.append(syms.getDecimalSeparator());
 | |
|     buf.append(syms.getDigit());
 | |
|     buf.append(syms.getExponential());
 | |
|     buf.append(syms.getGroupingSeparator());
 | |
|     // Adding this one causes pattern application to fail.
 | |
|     // Of course, omitting is causes toPattern to fail.
 | |
|     // ... but we already have bugs there.  FIXME.
 | |
|     // buf.append(syms.getMinusSign());
 | |
|     buf.append(syms.getPatternSeparator());
 | |
|     buf.append(syms.getPercent());
 | |
|     buf.append(syms.getPerMill());
 | |
|     buf.append(syms.getZeroDigit());
 | |
|     buf.append('\u00a4');
 | |
|     return buf.toString();
 | |
|   }
 | |
| 
 | |
|   private final void applyPatternWithSymbols (String pattern,
 | |
| 					      DecimalFormatSymbols syms)
 | |
|   {
 | |
|     // Initialize to the state the parser expects.
 | |
|     negativePrefix = "";
 | |
|     negativeSuffix = "";
 | |
|     positivePrefix = "";
 | |
|     positiveSuffix = "";
 | |
|     decimalSeparatorAlwaysShown = false;
 | |
|     groupingSize = 0;
 | |
|     minExponentDigits = 0;
 | |
|     multiplier = 1;
 | |
|     useExponentialNotation = false;
 | |
|     groupingUsed = false;
 | |
|     maximumFractionDigits = 0;
 | |
|     maximumIntegerDigits = 309;
 | |
|     minimumFractionDigits = 0;
 | |
|     minimumIntegerDigits = 1;
 | |
| 
 | |
|     StringBuffer buf = new StringBuffer ();
 | |
|     String patChars = patternChars (syms);
 | |
| 
 | |
|     int max = pattern.length();
 | |
|     int index = scanFix (pattern, 0, buf, patChars, syms, false);
 | |
|     positivePrefix = buf.toString();
 | |
| 
 | |
|     index = scanFormat (pattern, index, patChars, syms, true);
 | |
| 
 | |
|     index = scanFix (pattern, index, buf, patChars, syms, true);
 | |
|     positiveSuffix = buf.toString();
 | |
| 
 | |
|     if (index == pattern.length())
 | |
|       {
 | |
| 	// No negative info.
 | |
| 	negativePrefix = null;
 | |
| 	negativeSuffix = null;
 | |
|       }
 | |
|     else
 | |
|       {
 | |
| 	if (pattern.charAt(index) != syms.getPatternSeparator())
 | |
| 	  throw new IllegalArgumentException ("separator character " +
 | |
| 					      "expected - index: " + index);
 | |
| 
 | |
| 	index = scanFix (pattern, index + 1, buf, patChars, syms, false);
 | |
| 	negativePrefix = buf.toString();
 | |
| 
 | |
| 	// We parse the negative format for errors but we don't let
 | |
| 	// it side-effect this object.
 | |
| 	index = scanFormat (pattern, index, patChars, syms, false);
 | |
| 
 | |
| 	index = scanFix (pattern, index, buf, patChars, syms, true);
 | |
| 	negativeSuffix = buf.toString();
 | |
| 
 | |
| 	if (index != pattern.length())
 | |
| 	  throw new IllegalArgumentException ("end of pattern expected " +
 | |
| 					      "- index: " + index);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   public void applyLocalizedPattern (String pattern)
 | |
|   {
 | |
|     // JCL p. 638 claims this throws a ParseException but p. 629
 | |
|     // contradicts this.  Empirical tests with patterns of "0,###.0"
 | |
|     // and "#.#.#" corroborate the p. 629 statement that an
 | |
|     // IllegalArgumentException is thrown.
 | |
|     applyPatternWithSymbols (pattern, symbols);
 | |
|   }
 | |
| 
 | |
|   public void applyPattern (String pattern)
 | |
|   {
 | |
|     // JCL p. 638 claims this throws a ParseException but p. 629
 | |
|     // contradicts this.  Empirical tests with patterns of "0,###.0"
 | |
|     // and "#.#.#" corroborate the p. 629 statement that an
 | |
|     // IllegalArgumentException is thrown.
 | |
|     applyPatternWithSymbols (pattern, nonLocalizedSymbols);
 | |
|   }
 | |
| 
 | |
|   public Object clone ()
 | |
|   {
 | |
|     DecimalFormat c = (DecimalFormat) super.clone ();
 | |
|     c.symbols = (DecimalFormatSymbols) symbols.clone ();
 | |
|     return c;
 | |
|   }
 | |
| 
 | |
|   public DecimalFormat ()
 | |
|   {
 | |
|     this ("#,##0.###");
 | |
|   }
 | |
| 
 | |
|   public DecimalFormat (String pattern)
 | |
|   {
 | |
|     this (pattern, new DecimalFormatSymbols ());
 | |
|   }
 | |
| 
 | |
|   public DecimalFormat (String pattern, DecimalFormatSymbols symbols)
 | |
|   {
 | |
|     this.symbols = symbols;
 | |
|     applyPattern (pattern);
 | |
|   }
 | |
| 
 | |
|   private final boolean equals (String s1, String s2)
 | |
|   {
 | |
|     if (s1 == null || s2 == null)
 | |
|       return s1 == s2;
 | |
|     return s1.equals(s2);
 | |
|   }
 | |
| 
 | |
|   public boolean equals (Object obj)
 | |
|   {
 | |
|     if (! (obj instanceof DecimalFormat))
 | |
|       return false;
 | |
|     DecimalFormat dup = (DecimalFormat) obj;
 | |
|     return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
 | |
| 	    && groupingSize == dup.groupingSize
 | |
| 	    && minExponentDigits == dup.minExponentDigits
 | |
| 	    && multiplier == dup.multiplier
 | |
| 	    && equals(negativePrefix, dup.negativePrefix)
 | |
| 	    && equals(negativeSuffix, dup.negativeSuffix)
 | |
| 	    && equals(positivePrefix, dup.positivePrefix)
 | |
| 	    && equals(positiveSuffix, dup.positiveSuffix)
 | |
| 	    && symbols.equals(dup.symbols)
 | |
| 	    && useExponentialNotation == dup.useExponentialNotation);
 | |
|   }
 | |
| 
 | |
|   public StringBuffer format (double number, StringBuffer dest,
 | |
| 			      FieldPosition fieldPos)
 | |
|   {
 | |
|     // A very special case.
 | |
|     if (Double.isNaN(number))
 | |
|       {
 | |
| 	dest.append(symbols.getNaN());
 | |
| 	if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
 | |
| 	  {
 | |
| 	    int index = dest.length();
 | |
| 	    fieldPos.setBeginIndex(index - symbols.getNaN().length());
 | |
| 	    fieldPos.setEndIndex(index);
 | |
| 	  }
 | |
| 	return dest;
 | |
|       }
 | |
| 
 | |
|     boolean is_neg = number < 0;
 | |
|     if (is_neg)
 | |
|       {
 | |
| 	if (negativePrefix != null)
 | |
| 	  dest.append(negativePrefix);
 | |
| 	else
 | |
| 	  {
 | |
| 	    dest.append(symbols.getMinusSign());
 | |
| 	    dest.append(positivePrefix);
 | |
| 	  }
 | |
| 	number = - number;
 | |
|       }
 | |
|     else
 | |
|       dest.append(positivePrefix);
 | |
| 
 | |
|     int integerBeginIndex = dest.length();
 | |
|     int integerEndIndex = 0;
 | |
|     if (Double.isInfinite (number))
 | |
|       {
 | |
| 	dest.append(symbols.getInfinity());
 | |
| 	integerEndIndex = dest.length();
 | |
|       }
 | |
|     else
 | |
|       {
 | |
| 	number *= multiplier;
 | |
| 
 | |
| 	// Compute exponent.
 | |
| 	long exponent = 0;
 | |
| 	double baseNumber;
 | |
| 	if (useExponentialNotation)
 | |
| 	  {
 | |
| 	    exponent = (long) Math.floor (Math.log(number) / Math.log(10));
 | |
| 	    if (minimumIntegerDigits > 0)
 | |
| 	      exponent -= minimumIntegerDigits - 1;
 | |
| 	    baseNumber = (long) (number / Math.pow(10.0, exponent));
 | |
| 	  }
 | |
| 	else
 | |
| 	  baseNumber = number;
 | |
| 
 | |
| 	// Round to the correct number of digits.
 | |
| 	baseNumber += 5 * Math.pow(10.0, - maximumFractionDigits - 1);
 | |
| 
 | |
| 	int index = dest.length();
 | |
| 	double intPart = Math.floor(baseNumber);
 | |
| 	int count = 0;
 | |
| 	while (count < maximumIntegerDigits
 | |
| 	       && (intPart > 0 || count < minimumIntegerDigits))
 | |
| 	  {
 | |
| 	    long dig = (long) (intPart % 10);
 | |
| 	    intPart = Math.floor(intPart / 10);
 | |
| 
 | |
| 	    // Append group separator if required.
 | |
| 	    if (groupingUsed && count > 0 && count % groupingSize == 0)
 | |
| 	      dest.insert(index, symbols.getGroupingSeparator());
 | |
| 
 | |
| 	    dest.insert(index, (char) (symbols.getZeroDigit() + dig));
 | |
| 
 | |
| 	    ++count;
 | |
| 	  }
 | |
| 
 | |
| 	integerEndIndex = dest.length();
 | |
| 
 | |
| 	int decimal_index = integerEndIndex;
 | |
| 	int consecutive_zeros = 0;
 | |
| 	int total_digits = 0;
 | |
| 
 | |
| 	// Strip integer part from NUMBER.
 | |
| 	double fracPart = baseNumber - Math.floor(baseNumber);
 | |
| 	for (count = 0;
 | |
| 	     count < maximumFractionDigits
 | |
| 	       && (fracPart != 0 || count < minimumFractionDigits);
 | |
| 	     ++count)
 | |
| 	  {
 | |
| 	    ++total_digits;
 | |
| 	    fracPart *= 10;
 | |
| 	    long dig = (long) fracPart;
 | |
| 	    if (dig == 0)
 | |
| 	      ++consecutive_zeros;
 | |
| 	    else
 | |
| 	      consecutive_zeros = 0;
 | |
| 	    dest.append((char) (symbols.getZeroDigit() + dig));
 | |
| 
 | |
| 	    // Strip integer part from FRACPART.
 | |
| 	    fracPart = fracPart - Math.floor (fracPart);
 | |
| 	  }
 | |
| 
 | |
| 	// Strip extraneous trailing `0's.  We can't always detect
 | |
| 	// these in the loop.
 | |
| 	int extra_zeros = Math.min (consecutive_zeros,
 | |
| 				    total_digits - minimumFractionDigits);
 | |
| 	if (extra_zeros > 0)
 | |
| 	  {
 | |
| 	    dest.setLength(dest.length() - extra_zeros);
 | |
| 	    total_digits -= extra_zeros;
 | |
| 	  }
 | |
| 
 | |
| 	// If required, add the decimal symbol.
 | |
| 	if (decimalSeparatorAlwaysShown
 | |
| 	    || total_digits > 0)
 | |
| 	  {
 | |
| 	    dest.insert(decimal_index, symbols.getDecimalSeparator());
 | |
| 	    if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
 | |
| 	      {
 | |
| 		fieldPos.setBeginIndex(decimal_index + 1);
 | |
| 		fieldPos.setEndIndex(dest.length());
 | |
| 	      }
 | |
| 	  }
 | |
| 
 | |
| 	// Finally, print the exponent.
 | |
| 	if (useExponentialNotation)
 | |
| 	  {
 | |
| 	    dest.append(symbols.getExponential());
 | |
| 	    if (exponent < 0)
 | |
| 	      {
 | |
| 		dest.append (symbols.getMinusSign ());
 | |
| 		exponent = - exponent;
 | |
| 	      }
 | |
| 	    index = dest.length();
 | |
| 	    for (count = 0;
 | |
| 		 exponent > 0 || count < minExponentDigits;
 | |
| 		 ++count)
 | |
| 	      {
 | |
| 		long dig = exponent % 10;
 | |
| 		exponent /= 10;
 | |
| 		dest.insert(index, (char) (symbols.getZeroDigit() + dig));
 | |
| 	      }
 | |
| 	  }
 | |
|       }
 | |
| 
 | |
|     if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
 | |
|       {
 | |
| 	fieldPos.setBeginIndex(integerBeginIndex);
 | |
| 	fieldPos.setEndIndex(integerEndIndex);
 | |
|       }
 | |
| 
 | |
|     dest.append((is_neg && negativeSuffix != null)
 | |
| 		? negativeSuffix
 | |
| 		: positiveSuffix);
 | |
|     return dest;
 | |
|   }
 | |
| 
 | |
|   public StringBuffer format (long number, StringBuffer dest,
 | |
| 			      FieldPosition fieldPos)
 | |
|   {
 | |
|     // If using exponential notation, we just format as a double.
 | |
|     if (useExponentialNotation)
 | |
|       return format ((double) number, dest, fieldPos);
 | |
| 
 | |
|     boolean is_neg = number < 0;
 | |
|     if (is_neg)
 | |
|       {
 | |
| 	if (negativePrefix != null)
 | |
| 	  dest.append(negativePrefix);
 | |
| 	else
 | |
| 	  {
 | |
| 	    dest.append(symbols.getMinusSign());
 | |
| 	    dest.append(positivePrefix);
 | |
| 	  }
 | |
| 	number = - number;
 | |
|       }
 | |
|     else
 | |
|       dest.append(positivePrefix);
 | |
| 
 | |
|     int integerBeginIndex = dest.length();
 | |
|     int index = dest.length();
 | |
|     int count = 0;
 | |
|     while (count < maximumIntegerDigits
 | |
| 	   && (number > 0 || count < minimumIntegerDigits))
 | |
|       {
 | |
| 	long dig = number % 10;
 | |
| 	number /= 10;
 | |
| 	// NUMBER and DIG will be less than 0 if the original number
 | |
| 	// was the most negative long.
 | |
| 	if (dig < 0)
 | |
| 	  {
 | |
| 	    dig = - dig;
 | |
| 	    number = - number;
 | |
| 	  }
 | |
| 
 | |
| 	// Append group separator if required.
 | |
| 	if (groupingUsed && count > 0 && count % groupingSize == 0)
 | |
| 	  dest.insert(index, symbols.getGroupingSeparator());
 | |
| 
 | |
| 	dest.insert(index, (char) (symbols.getZeroDigit() + dig));
 | |
| 
 | |
| 	++count;
 | |
|       }
 | |
| 
 | |
|     if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
 | |
|       {
 | |
| 	fieldPos.setBeginIndex(integerBeginIndex);
 | |
| 	fieldPos.setEndIndex(dest.length());
 | |
|       }
 | |
| 
 | |
|     if (decimalSeparatorAlwaysShown || minimumFractionDigits > 0)
 | |
|       {
 | |
| 	dest.append(symbols.getDecimalSeparator());
 | |
| 	if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
 | |
| 	  {
 | |
| 	    fieldPos.setBeginIndex(dest.length());
 | |
| 	    fieldPos.setEndIndex(dest.length() + minimumFractionDigits);
 | |
| 	  }
 | |
|       }
 | |
| 
 | |
|     for (count = 0; count < minimumFractionDigits; ++count)
 | |
|       dest.append(symbols.getZeroDigit());
 | |
| 
 | |
|     dest.append((is_neg && negativeSuffix != null)
 | |
| 		? negativeSuffix
 | |
| 		: positiveSuffix);
 | |
|     return dest;
 | |
|   }
 | |
| 
 | |
|   public DecimalFormatSymbols getDecimalFormatSymbols ()
 | |
|   {
 | |
|     return symbols;
 | |
|   }
 | |
| 
 | |
|   public int getGroupingSize ()
 | |
|   {
 | |
|     return groupingSize;
 | |
|   }
 | |
| 
 | |
|   public int getMultiplier ()
 | |
|   {
 | |
|     return multiplier;
 | |
|   }
 | |
| 
 | |
|   public String getNegativePrefix ()
 | |
|   {
 | |
|     return negativePrefix;
 | |
|   }
 | |
| 
 | |
|   public String getNegativeSuffix ()
 | |
|   {
 | |
|     return negativeSuffix;
 | |
|   }
 | |
| 
 | |
|   public String getPositivePrefix ()
 | |
|   {
 | |
|     return positivePrefix;
 | |
|   }
 | |
| 
 | |
|   public String getPositiveSuffix ()
 | |
|   {
 | |
|     return positiveSuffix;
 | |
|   }
 | |
| 
 | |
|   public int hashCode ()
 | |
|   {
 | |
|     int hash = (negativeSuffix.hashCode() ^ negativePrefix.hashCode()
 | |
| 		^positivePrefix.hashCode() ^ positiveSuffix.hashCode());
 | |
|     // FIXME.
 | |
|     return hash;
 | |
|   }
 | |
| 
 | |
|   public boolean isDecimalSeparatorAlwaysShown ()
 | |
|   {
 | |
|     return decimalSeparatorAlwaysShown;
 | |
|   }
 | |
| 
 | |
|   public Number parse (String str, ParsePosition pos)
 | |
|   {
 | |
|     // Our strategy is simple: copy the text into a buffer,
 | |
|     // translating or omitting locale-specific information.  Then
 | |
|     // let Double or Long convert the number for us.
 | |
| 
 | |
|     boolean is_neg = false;
 | |
|     int index = pos.getIndex();
 | |
|     StringBuffer buf = new StringBuffer ();
 | |
| 
 | |
|       // We have to check both prefixes, because one might be empty.
 | |
|       // We want to pick the longest prefix that matches.
 | |
|     boolean got_pos = str.startsWith(positivePrefix, index);
 | |
|     String np = (negativePrefix != null
 | |
| 		 ? negativePrefix
 | |
| 		 : positivePrefix + symbols.getMinusSign());
 | |
|     boolean got_neg = str.startsWith(np, index);
 | |
| 
 | |
|     if (got_pos && got_neg)
 | |
|       {
 | |
| 	// By checking this way, we preserve ambiguity in the case
 | |
| 	// where the negative format differs only in suffix.  We
 | |
| 	// check this again later.
 | |
| 	if (np.length() > positivePrefix.length())
 | |
| 	  {
 | |
| 	    is_neg = true;
 | |
| 	    index += np.length();
 | |
| 	  }
 | |
| 	else
 | |
| 	  index += positivePrefix.length();
 | |
|       }
 | |
|     else if (got_neg)
 | |
|       {
 | |
| 	is_neg = true;
 | |
| 	index += np.length();
 | |
|       }
 | |
|     else if (got_pos)
 | |
|       index += positivePrefix.length();
 | |
|     else
 | |
|       {
 | |
| 	pos.setErrorIndex (index);
 | |
| 	return null;
 | |
|       }
 | |
| 
 | |
|     // FIXME: handle Inf and NaN.
 | |
| 
 | |
|       // FIXME: do we have to respect minimum/maxmimum digit stuff?
 | |
|       // What about leading zeros?  What about multiplier?
 | |
| 
 | |
|     int start_index = index;
 | |
|     int max = str.length();
 | |
|     char zero = symbols.getZeroDigit();
 | |
|     int last_group = -1;
 | |
|     boolean int_part = true;
 | |
|     boolean exp_part = false;
 | |
|     for (; index < max; ++index)
 | |
|       {
 | |
| 	char c = str.charAt(index);
 | |
| 
 | |
| 	// FIXME: what about grouping size?
 | |
| 	if (groupingUsed && c == symbols.getGroupingSeparator())
 | |
| 	  {
 | |
| 	    if (last_group != -1
 | |
| 		&& (index - last_group) % groupingSize != 0)
 | |
| 	      {
 | |
| 		pos.setErrorIndex(index);
 | |
| 		return null;
 | |
| 	      }
 | |
| 	    last_group = index;
 | |
| 	  }
 | |
| 	else if (c >= zero && c <= zero + 9)
 | |
| 	  {
 | |
| 	    buf.append((char) (c - zero + '0'));
 | |
| 	    exp_part = false;
 | |
| 	  }
 | |
| 	else if (parseIntegerOnly)
 | |
| 	  break;
 | |
| 	else if (c == symbols.getDecimalSeparator())
 | |
| 	  {
 | |
| 	    if (last_group != -1
 | |
| 		&& (index - last_group) % groupingSize != 0)
 | |
| 	      {
 | |
| 		pos.setErrorIndex(index);
 | |
| 		return null;
 | |
| 	      }
 | |
| 	    buf.append('.');
 | |
| 	    int_part = false;
 | |
| 	  }
 | |
| 	else if (c == symbols.getExponential())
 | |
| 	  {
 | |
| 	    buf.append('E');
 | |
| 	    int_part = false;
 | |
| 	    exp_part = true;
 | |
| 	  }
 | |
| 	else if (exp_part
 | |
| 		 && (c == '+' || c == '-' || c == symbols.getMinusSign()))
 | |
| 	  {
 | |
| 	    // For exponential notation.
 | |
| 	    buf.append(c);
 | |
| 	  }
 | |
| 	else
 | |
| 	  break;
 | |
|       }
 | |
| 
 | |
|     if (index == start_index)
 | |
|       {
 | |
| 	// Didn't see any digits.
 | |
| 	pos.setErrorIndex(index);
 | |
| 	return null;
 | |
|       }
 | |
| 
 | |
|     // Check the suffix.  We must do this before converting the
 | |
|     // buffer to a number to handle the case of a number which is
 | |
|     // the most negative Long.
 | |
|     boolean got_pos_suf = str.startsWith(positiveSuffix, index);
 | |
|     String ns = (negativePrefix == null ? positiveSuffix : negativeSuffix);
 | |
|     boolean got_neg_suf = str.startsWith(ns, index);
 | |
|     if (is_neg)
 | |
|       {
 | |
| 	if (! got_neg_suf)
 | |
| 	  {
 | |
| 	    pos.setErrorIndex(index);
 | |
| 	    return null;
 | |
| 	  }
 | |
|       }
 | |
|     else if (got_pos && got_neg && got_neg_suf)
 | |
|       {
 | |
| 	is_neg = true;
 | |
|       }
 | |
|     else if (got_pos != got_pos_suf && got_neg != got_neg_suf)
 | |
|       {
 | |
| 	pos.setErrorIndex(index);
 | |
| 	return null;
 | |
|       }
 | |
| 
 | |
|     String suffix = is_neg ? ns : positiveSuffix;
 | |
|     if (is_neg)
 | |
|       buf.insert(0, '-');
 | |
| 
 | |
|     String t = buf.toString();
 | |
|     Number result = null;
 | |
|     try
 | |
|       {
 | |
| 	result = new Long (t);
 | |
|       }
 | |
|     catch (NumberFormatException x1)
 | |
|       {
 | |
| 	try
 | |
| 	  {
 | |
| 	    result = new Double (t);
 | |
| 	  }
 | |
| 	catch (NumberFormatException x2)
 | |
| 	  {
 | |
| 	  }
 | |
|       }
 | |
|     if (result == null)
 | |
|       {
 | |
| 	pos.setErrorIndex(index);
 | |
| 	return null;
 | |
|       }
 | |
| 
 | |
|     pos.setIndex(index + suffix.length());
 | |
| 
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   public void setDecimalFormatSymbols (DecimalFormatSymbols newSymbols)
 | |
|   {
 | |
|     symbols = newSymbols;
 | |
|   }
 | |
| 
 | |
|   public void setDecimalSeparatorAlwaysShown (boolean newValue)
 | |
|   {
 | |
|     decimalSeparatorAlwaysShown = newValue;
 | |
|   }
 | |
| 
 | |
|   public void setGroupingSize (int groupSize)
 | |
|   {
 | |
|     groupingSize = (byte) groupSize;
 | |
|   }
 | |
| 
 | |
|   public void setMaximumFractionDigits (int newValue)
 | |
|   {
 | |
|     maximumFractionDigits = Math.min(newValue, 340);
 | |
|   }
 | |
| 
 | |
|   public void setMaximumIntegerDigits (int newValue)
 | |
|   {
 | |
|     maximumIntegerDigits = Math.min(newValue, 309);
 | |
|   }
 | |
| 
 | |
|   public void setMinimumFractionDigits (int newValue)
 | |
|   {
 | |
|     minimumFractionDigits = Math.min(newValue, 340);
 | |
|   }
 | |
| 
 | |
|   public void setMinimumIntegerDigits (int newValue)
 | |
|   {
 | |
|     minimumIntegerDigits = Math.min(newValue, 309);
 | |
|   }
 | |
| 
 | |
|   public void setMultiplier (int newValue)
 | |
|   {
 | |
|     multiplier = newValue;
 | |
|   }
 | |
| 
 | |
|   public void setNegativePrefix (String newValue)
 | |
|   {
 | |
|     negativePrefix = newValue;
 | |
|   }
 | |
| 
 | |
|   public void setNegativeSuffix (String newValue)
 | |
|   {
 | |
|     negativeSuffix = newValue;
 | |
|   }
 | |
| 
 | |
|   public void setPositivePrefix (String newValue)
 | |
|   {
 | |
|     positivePrefix = newValue;
 | |
|   }
 | |
| 
 | |
|   public void setPositiveSuffix (String newValue)
 | |
|   {
 | |
|     positiveSuffix = newValue;
 | |
|   }
 | |
| 
 | |
|   private final void quoteFix (StringBuffer buf, String text, String patChars)
 | |
|   {
 | |
|     int len = text.length();
 | |
|     for (int index = 0; index < len; ++index)
 | |
|       {
 | |
| 	char c = text.charAt(index);
 | |
| 	if (patChars.indexOf(c) != -1)
 | |
| 	  {
 | |
| 	    buf.append('\'');
 | |
| 	    buf.append(c);
 | |
| 	    buf.append('\'');
 | |
| 	  }
 | |
| 	else
 | |
| 	  buf.append(c);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   private final String computePattern (DecimalFormatSymbols syms)
 | |
|   {
 | |
|     StringBuffer mainPattern = new StringBuffer ();
 | |
|     // We have to at least emit a zero for the minimum number of
 | |
|     // digits.  Past that we need hash marks up to the grouping
 | |
|     // separator (and one beyond).
 | |
|     int total_digits = Math.max(minimumIntegerDigits,
 | |
| 				groupingUsed ? groupingSize + 1: 0);
 | |
|     for (int i = 0; i < total_digits - minimumIntegerDigits; ++i)
 | |
|       mainPattern.append(syms.getDigit());
 | |
|     for (int i = total_digits - minimumIntegerDigits; i < total_digits; ++i)
 | |
|       mainPattern.append(syms.getZeroDigit());
 | |
|     // Inserting the gropuing operator afterwards is easier.
 | |
|     if (groupingUsed)
 | |
|       mainPattern.insert(mainPattern.length() - groupingSize,
 | |
| 			 syms.getGroupingSeparator());
 | |
|     // See if we need decimal info.
 | |
|     if (minimumFractionDigits > 0 || maximumFractionDigits > 0
 | |
| 	|| decimalSeparatorAlwaysShown)
 | |
|       mainPattern.append(syms.getDecimalSeparator());
 | |
|     for (int i = 0; i < minimumFractionDigits; ++i)
 | |
|       mainPattern.append(syms.getZeroDigit());
 | |
|     for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
 | |
|       mainPattern.append(syms.getDigit());
 | |
|     if (useExponentialNotation)
 | |
|       {
 | |
| 	mainPattern.append(syms.getExponential());
 | |
| 	for (int i = 0; i < minExponentDigits; ++i)
 | |
| 	  mainPattern.append(syms.getZeroDigit());
 | |
| 	if (minExponentDigits == 0)
 | |
| 	  mainPattern.append(syms.getDigit());
 | |
|       }
 | |
| 
 | |
|     String main = mainPattern.toString();
 | |
|     String patChars = patternChars (syms);
 | |
|     mainPattern.setLength(0);
 | |
| 
 | |
|     quoteFix (mainPattern, positivePrefix, patChars);
 | |
|     mainPattern.append(main);
 | |
|     quoteFix (mainPattern, positiveSuffix, patChars);
 | |
| 
 | |
|     if (negativePrefix != null)
 | |
|       {
 | |
| 	quoteFix (mainPattern, negativePrefix, patChars);
 | |
| 	mainPattern.append(main);
 | |
| 	quoteFix (mainPattern, negativeSuffix, patChars);
 | |
|       }
 | |
| 
 | |
|     return mainPattern.toString();
 | |
|   }
 | |
| 
 | |
|   public String toLocalizedPattern ()
 | |
|   {
 | |
|     return computePattern (symbols);
 | |
|   }
 | |
| 
 | |
|   public String toPattern ()
 | |
|   {
 | |
|     return computePattern (nonLocalizedSymbols);
 | |
|   }
 | |
| 
 | |
|   // These names are fixed by the serialization spec.
 | |
|   private boolean decimalSeparatorAlwaysShown;
 | |
|   private byte groupingSize;
 | |
|   private byte minExponentDigits;
 | |
|   private int multiplier;
 | |
|   private String negativePrefix;
 | |
|   private String negativeSuffix;
 | |
|   private String positivePrefix;
 | |
|   private String positiveSuffix;
 | |
|   private int serialVersionOnStream = 1;
 | |
|   private DecimalFormatSymbols symbols;
 | |
|   private boolean useExponentialNotation;
 | |
|   private static final long serialVersionUID = 864413376551465018L;
 | |
| 
 | |
|   private void readObject(ObjectInputStream stream)
 | |
|     throws IOException, ClassNotFoundException
 | |
|   {
 | |
|     stream.defaultReadObject();
 | |
|     if (serialVersionOnStream < 1)
 | |
|       {
 | |
|         useExponentialNotation = false;
 | |
| 	serialVersionOnStream = 1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   // The locale-independent pattern symbols happen to be the same as
 | |
|   // the US symbols.
 | |
|   private static final DecimalFormatSymbols nonLocalizedSymbols
 | |
|     = new DecimalFormatSymbols (Locale.US);
 | |
| }
 |