OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / text / DecimalFormat.java
1 /* DecimalFormat.java -- Formats and parses numbers
2    Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 /* 
39  * This class contains few bits from ICU4J (http://icu.sourceforge.net/),
40  * Copyright by IBM and others and distributed under the
41  * distributed under MIT/X.
42  */
43
44 package java.text;
45
46 import java.math.BigDecimal;
47 import java.math.BigInteger;
48
49 import java.util.ArrayList;
50 import java.util.Currency;
51 import java.util.Locale;
52
53 /*
54  * This note is here for historical reasons and because I had not the courage
55  * to remove it :)
56  *  
57  * @author Tom Tromey (tromey@cygnus.com)
58  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
59  * @date March 4, 1999
60  *
61  * Written using "Java Class Libraries", 2nd edition, plus online
62  * API docs for JDK 1.2 from http://www.javasoft.com.
63  * Status:  Believed complete and correct to 1.2.
64  * Note however that the docs are very unclear about how format parsing
65  * should work.  No doubt there are problems here.
66  */
67
68 /**
69  * This class is a concrete implementation of NumberFormat used to format
70  * decimal numbers. The class can format numbers given a specific locale.
71  * Generally, to get an instance of DecimalFormat you should call the factory
72  * methods in the <code>NumberFormat</code> base class.
73  * 
74  * @author Mario Torre <neugens@limasoftware.net>
75  * @author Tom Tromey (tromey@cygnus.com)
76  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
77  */
78 public class DecimalFormat extends NumberFormat
79 {
80   /** serialVersionUID for serializartion. */
81   private static final long serialVersionUID = 864413376551465018L;
82   
83   /** Defines the default number of digits allowed while formatting integers. */
84   private static final int DEFAULT_INTEGER_DIGITS = 309; 
85
86   /**
87    * Defines the default number of digits allowed while formatting
88    * fractions.
89    */
90   private static final int DEFAULT_FRACTION_DIGITS = 340;
91   
92   /**
93    * Locale-independent pattern symbols.
94    */
95   // Happen to be the same as the US symbols.
96   private static final DecimalFormatSymbols nonLocalizedSymbols
97     = new DecimalFormatSymbols (Locale.US);
98   
99   /**
100    * Defines if parse should return a BigDecimal or not.
101    */
102   private boolean parseBigDecimal;
103   
104   /**
105    * Defines if we have to use the monetary decimal separator or
106    * the decimal separator while formatting numbers.
107    */
108   private boolean useCurrencySeparator;
109     
110   /** Defines if the decimal separator is always shown or not. */
111   private boolean decimalSeparatorAlwaysShown;
112   
113   /**
114    * Defines if the decimal separator has to be shown.
115    * 
116    * This is different then <code>decimalSeparatorAlwaysShown</code>,
117    * as it defines if the format string contains a decimal separator or no.
118    */
119   private boolean showDecimalSeparator;
120   
121   /**
122    * This field is used to determine if the grouping
123    * separator is included in the format string or not.
124    * This is only needed to match the behaviour of the RI.
125    */
126   private boolean groupingSeparatorInPattern;
127   
128   /** Defines the size of grouping groups when grouping is used. */
129   private byte groupingSize;
130   
131   /**
132    * This is an internal parameter used to keep track of the number
133    * of digits the form the exponent, when exponential notation is used.
134    * It is used with <code>exponentRound</code>
135    */
136   private byte minExponentDigits;
137  
138   /** This field is used to set the exponent in the engineering notation. */
139   private int exponentRound;
140   
141   /** Multiplier used in percent style formats. */
142   private int multiplier;
143   
144   /** Multiplier used in percent style formats. */
145   private int negativePatternMultiplier;
146   
147   /** The negative prefix. */
148   private String negativePrefix;
149   
150   /** The negative suffix. */
151   private String negativeSuffix;
152   
153   /** The positive prefix. */
154   private String positivePrefix;
155   
156   /** The positive suffix. */
157   private String positiveSuffix;
158   
159   /** Decimal Format Symbols for the given locale. */
160   private DecimalFormatSymbols symbols;
161   
162   /** Determine if we have to use exponential notation or not. */
163   private boolean useExponentialNotation;
164   
165   /**
166    * Defines the maximum number of integer digits to show when we use
167    * the exponential notation.
168    */
169   private int maxIntegerDigitsExponent;
170   
171   /** Defines if the format string has a negative prefix or not. */
172   private boolean hasNegativePrefix;
173   
174   /** Defines if the format string has a fractional pattern or not. */
175   private boolean hasFractionalPattern;
176  
177   /** Stores a list of attributes for use by formatToCharacterIterator. */
178   private ArrayList attributes = new ArrayList();
179   
180   /**
181    * Constructs a <code>DecimalFormat</code> which uses the default
182    * pattern and symbols.
183    */
184   public DecimalFormat()
185   {
186     this ("#,##0.###");
187   }
188   
189   /**
190    * Constructs a <code>DecimalFormat</code> which uses the given
191    * pattern and the default symbols for formatting and parsing.
192    *
193    * @param pattern the non-localized pattern to use.
194    * @throws NullPointerException if any argument is null.
195    * @throws IllegalArgumentException if the pattern is invalid.
196    */
197   public DecimalFormat(String pattern)
198   {
199     this (pattern, new DecimalFormatSymbols());
200   }
201
202   /**
203    * Constructs a <code>DecimalFormat</code> using the given pattern
204    * and formatting symbols.  This construction method is used to give
205    * complete control over the formatting process.  
206    *
207    * @param pattern the non-localized pattern to use.
208    * @param symbols the set of symbols used for parsing and formatting.
209    * @throws NullPointerException if any argument is null.
210    * @throws IllegalArgumentException if the pattern is invalid.
211    */
212   public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
213   {
214     this.symbols = (DecimalFormatSymbols) symbols.clone();
215     applyPatternWithSymbols(pattern, nonLocalizedSymbols);
216   }
217   
218   /**
219    * Apply the given localized patern to the current DecimalFormat object.
220    * 
221    * @param pattern The localized pattern to apply.
222    * @throws IllegalArgumentException if the given pattern is invalid.
223    * @throws NullPointerException if the input pattern is null.
224    */
225   public void applyLocalizedPattern (String pattern)
226   {
227     applyPatternWithSymbols(pattern, this.symbols);
228   }
229
230   /**
231    * Apply the given localized pattern to the current DecimalFormat object.
232    * 
233    * @param pattern The localized pattern to apply.
234    * @throws IllegalArgumentException if the given pattern is invalid.
235    * @throws NullPointerException if the input pattern is null.
236    */
237   public void applyPattern(String pattern)
238   {
239     applyPatternWithSymbols(pattern, nonLocalizedSymbols);
240   }
241
242   public Object clone()
243   {
244     DecimalFormat c = (DecimalFormat) super.clone();
245     c.symbols = (DecimalFormatSymbols) symbols.clone();
246     return c;
247   }
248
249   /**
250    * Tests this instance for equality with an arbitrary object.  This method
251    * returns <code>true</code> if:
252    * <ul>
253    * <li><code>obj</code> is not <code>null</code>;</li>
254    * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
255    * <li>this instance and <code>obj</code> have the same attributes;</li>
256    * </ul>
257    * 
258    * @param obj  the object (<code>null</code> permitted).
259    * 
260    * @return A boolean.
261    */
262   public boolean equals(Object obj)
263   {
264     if (! (obj instanceof DecimalFormat))
265       return false;
266     DecimalFormat dup = (DecimalFormat) obj;
267     return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown 
268            && groupingUsed == dup.groupingUsed
269            && groupingSeparatorInPattern == dup.groupingSeparatorInPattern 
270            && groupingSize == dup.groupingSize
271            && multiplier == dup.multiplier
272            && useExponentialNotation == dup.useExponentialNotation
273            && minExponentDigits == dup.minExponentDigits
274            && minimumIntegerDigits == dup.minimumIntegerDigits
275            && maximumIntegerDigits == dup.maximumIntegerDigits
276            && minimumFractionDigits == dup.minimumFractionDigits
277            && maximumFractionDigits == dup.maximumFractionDigits
278            && parseBigDecimal == dup.parseBigDecimal
279            && useCurrencySeparator == dup.useCurrencySeparator
280            && showDecimalSeparator == dup.showDecimalSeparator
281            && exponentRound == dup.exponentRound
282            && negativePatternMultiplier == dup.negativePatternMultiplier
283            && maxIntegerDigitsExponent == dup.maxIntegerDigitsExponent
284            // XXX: causes equivalent patterns to fail
285            // && hasNegativePrefix == dup.hasNegativePrefix
286            && equals(negativePrefix, dup.negativePrefix)
287            && equals(negativeSuffix, dup.negativeSuffix)
288            && equals(positivePrefix, dup.positivePrefix)
289            && equals(positiveSuffix, dup.positiveSuffix)
290            && symbols.equals(dup.symbols));
291   }
292
293   /**
294    * Returns a hash code for this object.
295    *
296    * @return A hash code.
297    */
298   public int hashCode()
299   {
300     return toPattern().hashCode();
301   }
302   
303   /**
304    * Produce a formatted {@link String} representation of this object.
305    * The passed object must be of type number. 
306    * 
307    * @param obj The {@link Number} to format.
308    * @param sbuf The destination String; text will be appended to this String. 
309    * @param pos If used on input can be used to define an alignment
310    * field. If used on output defines the offsets of the alignment field.
311    * @return The String representation of this long.
312    */
313   public StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
314   {
315     if (obj instanceof BigInteger)
316       {
317         BigDecimal decimal = new BigDecimal((BigInteger) obj);
318         formatInternal(decimal, true, sbuf, pos);
319         return sbuf;
320       }
321     else if (obj instanceof BigDecimal)
322       {
323         formatInternal((BigDecimal) obj, true, sbuf, pos);
324         return sbuf;
325       }
326     
327     return super.format(obj, sbuf, pos);
328   }
329   
330   /**
331    * Produce a formatted {@link String} representation of this double.
332    * 
333    * @param number The double to format.
334    * @param dest The destination String; text will be appended to this String. 
335    * @param fieldPos If used on input can be used to define an alignment
336    * field. If used on output defines the offsets of the alignment field.
337    * @return The String representation of this long.
338    * @throws NullPointerException if <code>dest</code> or fieldPos are null
339    */
340   public StringBuffer format(double number, StringBuffer dest,
341                              FieldPosition fieldPos)
342   {
343     // special cases for double: NaN and negative or positive infinity
344     if (Double.isNaN(number))
345       {
346         // 1. NaN
347         String nan = symbols.getNaN();
348         dest.append(nan);
349         
350         // update field position if required
351         if ((fieldPos.getField() == INTEGER_FIELD ||
352              fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
353           {
354             int index = dest.length();
355             fieldPos.setBeginIndex(index - nan.length());
356             fieldPos.setEndIndex(index);
357           }
358       }
359     else if (Double.isInfinite(number))
360       {
361         // 2. Infinity
362         if (number < 0)
363           dest.append(this.negativePrefix);
364         else
365           dest.append(this.positivePrefix);
366         
367         dest.append(symbols.getInfinity());
368         
369         if (number < 0)
370           dest.append(this.negativeSuffix);
371         else
372           dest.append(this.positiveSuffix);
373         
374         if ((fieldPos.getField() == INTEGER_FIELD ||
375             fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
376          {
377            fieldPos.setBeginIndex(dest.length());
378            fieldPos.setEndIndex(0);
379          }
380       }
381     else
382       {
383         // get the number as a BigDecimal
384         BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
385         formatInternal(bigDecimal, false, dest, fieldPos);
386       }
387     
388     return dest;
389   }
390
391   /**
392    * Produce a formatted {@link String} representation of this long.
393    * 
394    * @param number The long to format.
395    * @param dest The destination String; text will be appended to this String. 
396    * @param fieldPos If used on input can be used to define an alignment
397    * field. If used on output defines the offsets of the alignment field.
398    * @return The String representation of this long.
399    */
400   public StringBuffer format(long number, StringBuffer dest,
401                              FieldPosition fieldPos)
402   {
403     BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
404     formatInternal(bigDecimal, true, dest, fieldPos);
405     return dest;
406   }
407   
408   /**
409    * Return an <code>AttributedCharacterIterator</code> as a result of
410    * the formatting of the passed {@link Object}.
411    * 
412    * @return An {@link AttributedCharacterIterator}.
413    * @throws NullPointerException if value is <code>null</code>. 
414    * @throws IllegalArgumentException if value is not an instance of
415    * {@link Number}.
416    */
417   public AttributedCharacterIterator formatToCharacterIterator(Object value)
418   {
419     /*
420      * This method implementation derives directly from the
421      * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
422      */
423     
424     if (value == null)
425       throw new NullPointerException("Passed Object is null");
426     
427     if (!(value instanceof Number)) throw new
428       IllegalArgumentException("Cannot format given Object as a Number");
429     
430     StringBuffer text = new StringBuffer();
431     attributes.clear();
432     super.format(value, text, new FieldPosition(0));
433
434     AttributedString as = new AttributedString(text.toString());
435
436     // add NumberFormat field attributes to the AttributedString
437     for (int i = 0; i < attributes.size(); i++)
438       {
439         FieldPosition pos = (FieldPosition) attributes.get(i);
440         Format.Field attribute = pos.getFieldAttribute();
441         
442         as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos
443                         .getEndIndex());
444       }
445     
446     // return the CharacterIterator from AttributedString
447     return as.getIterator();
448   }
449   
450   /**
451    * Returns the currency corresponding to the currency symbol stored
452    * in the instance of <code>DecimalFormatSymbols</code> used by this
453    * <code>DecimalFormat</code>.
454    *
455    * @return A new instance of <code>Currency</code> if
456    * the currency code matches a known one, null otherwise.
457    */
458   public Currency getCurrency()
459   {
460     return symbols.getCurrency();
461   }
462   
463   /**
464    * Returns a copy of the symbols used by this instance.
465    * 
466    * @return A copy of the symbols.
467    */
468   public DecimalFormatSymbols getDecimalFormatSymbols()
469   {
470     return (DecimalFormatSymbols) symbols.clone();
471   }
472   
473   /**
474    * Gets the interval used between a grouping separator and the next.
475    * For example, a grouping size of 3 means that the number 1234 is
476    * formatted as 1,234.
477    * 
478    * The actual character used as grouping separator depends on the
479    * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()}
480    * 
481    * @return The interval used between a grouping separator and the next.
482    */
483   public int getGroupingSize()
484   {
485     return groupingSize;
486   }
487
488   /**
489    * Gets the multiplier used in percent and similar formats.
490    * 
491    * @return The multiplier used in percent and similar formats.
492    */
493   public int getMultiplier()
494   {
495     return multiplier;
496   }
497   
498   /**
499    * Gets the negative prefix.
500    * 
501    * @return The negative prefix.
502    */
503   public String getNegativePrefix()
504   {
505     return negativePrefix;
506   }
507
508   /**
509    * Gets the negative suffix.
510    * 
511    * @return The negative suffix.
512    */
513   public String getNegativeSuffix()
514   {
515     return negativeSuffix;
516   }
517   
518   /**
519    * Gets the positive prefix.
520    * 
521    * @return The positive prefix.
522    */
523   public String getPositivePrefix()
524   {
525     return positivePrefix;
526   }
527   
528   /**
529    * Gets the positive suffix.
530    *  
531    * @return The positive suffix.
532    */
533   public String getPositiveSuffix()
534   {
535     return positiveSuffix;
536   }
537   
538   public boolean isDecimalSeparatorAlwaysShown()
539   {
540     return decimalSeparatorAlwaysShown;
541   }
542   
543   /**
544    * Define if <code>parse(java.lang.String, java.text.ParsePosition)</code>
545    * should return a {@link BigDecimal} or not. 
546    * 
547    * @param newValue
548    */
549   public void setParseBigDecimal(boolean newValue)
550   {
551     this.parseBigDecimal = newValue;
552   }
553   
554   /**
555    * Returns <code>true</code> if
556    * <code>parse(java.lang.String, java.text.ParsePosition)</code> returns
557    * a <code>BigDecimal</code>, <code>false</code> otherwise.
558    * The default return value for this method is <code>false</code>.
559    * 
560    * @return <code>true</code> if the parse method returns a {@link BigDecimal},
561    * <code>false</code> otherwise.
562    * @since 1.5
563    * @see #setParseBigDecimal(boolean)
564    */
565   public boolean isParseBigDecimal()
566   {
567     return this.parseBigDecimal;
568   }
569   
570   /**
571    * This method parses the specified string into a <code>Number</code>.
572    * 
573    * The parsing starts at <code>pos</code>, which is updated as the parser
574    * consume characters in the passed string.
575    * On error, the <code>Position</code> object index is not updated, while
576    * error position is set appropriately, an <code>null</code> is returned.
577    * 
578    * @param str The string to parse.
579    * @param pos The desired <code>ParsePosition</code>.
580    *
581    * @return The parsed <code>Number</code>
582    */
583   public Number parse(String str, ParsePosition pos)
584   {
585     // a special values before anything else
586     // NaN
587     if (str.contains(this.symbols.getNaN()))
588       return Double.valueOf(Double.NaN);
589    
590     // this will be our final number
591     StringBuffer number = new StringBuffer();
592     
593     // special character
594     char minus = symbols.getMinusSign();
595     
596     // starting parsing position
597     int start = pos.getIndex();
598     
599     // validate the string, it have to be in the 
600     // same form as the format string or parsing will fail
601     String _negativePrefix = (this.negativePrefix.compareTo("") == 0
602                               ? minus + positivePrefix
603                               : this.negativePrefix);
604     
605     // we check both prefixes, because one might be empty.
606     // We want to pick the longest prefix that matches.
607     int positiveLen = positivePrefix.length();
608     int negativeLen = _negativePrefix.length();
609     
610     boolean isNegative = str.startsWith(_negativePrefix);
611     boolean isPositive = str.startsWith(positivePrefix);
612     
613     if (isPositive && isNegative)
614       {
615         // By checking this way, we preserve ambiguity in the case
616         // where the negative format differs only in suffix.
617         if (negativeLen > positiveLen)
618           {
619             start += _negativePrefix.length();
620             isNegative = true;
621           }
622         else
623           {
624             start += positivePrefix.length();
625             isPositive = true;
626             if (negativeLen < positiveLen)
627               isNegative = false;
628           }
629       }
630     else if (isNegative)
631       {
632         start += _negativePrefix.length();
633         isPositive = false;
634       }
635     else if (isPositive)
636       {
637         start += positivePrefix.length();
638         isNegative = false;
639       }
640     else
641       {
642         pos.setErrorIndex(start);
643         return null;
644       }
645     
646     // other special characters used by the parser
647     char decimalSeparator = symbols.getDecimalSeparator();
648     char zero = symbols.getZeroDigit();
649     char exponent = symbols.getExponential(); 
650     
651     // stop parsing position in the string
652     int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2;
653     
654     if (useExponentialNotation)
655       stop += minExponentDigits + 1;
656     
657     boolean inExponent = false;
658
659     // correct the size of the end parsing flag
660     int len = str.length();
661     if (len < stop) stop = len;
662     
663     int i = start;
664     while (i < stop)
665       {
666         char ch = str.charAt(i);
667         i++;
668        
669         if (ch >= zero && ch <= (zero + 9))
670           {
671             number.append(ch);
672           }
673         else if (this.parseIntegerOnly)
674           {
675             break;
676           }
677         else if (ch == decimalSeparator)
678           {
679             number.append('.');
680           }
681         else if (ch == exponent)
682           {
683             number.append(ch);
684             inExponent = !inExponent;
685           }
686         else if ((ch == '+' || ch == '-' || ch == minus))
687           {
688             if (inExponent)
689               number.append(ch);
690             else
691               break;
692           }
693       }
694
695     // 2nd special case: infinity
696     // XXX: need to be tested
697     if (str.contains(symbols.getInfinity()))
698       {
699         int inf = str.indexOf(symbols.getInfinity()); 
700         pos.setIndex(inf);
701         
702         // FIXME: ouch, this is really ugly and lazy code...
703         if (this.parseBigDecimal)
704           {
705             if (isNegative)
706               return new BigDecimal(Double.NEGATIVE_INFINITY);
707             
708             return new BigDecimal(Double.POSITIVE_INFINITY);
709           }
710         
711         if (isNegative)
712           return new Double(Double.NEGATIVE_INFINITY);
713
714         return new Double(Double.POSITIVE_INFINITY);
715       }
716     
717     // no number...
718     if (i == start || number.length() == 0)
719       {
720         pos.setErrorIndex(i);
721         return null;
722       }
723
724     // now we have to check the suffix, done here after number parsing
725     // or the index will not be updated correctly...
726     boolean isNegativeSuffix = str.endsWith(this.negativeSuffix);
727     boolean isPositiveSuffix = str.endsWith(this.positiveSuffix);
728     boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix);
729
730     positiveLen = positiveSuffix.length();
731     negativeLen = negativeSuffix.length();
732     
733     if (isNegative && !isNegativeSuffix)
734       {
735         pos.setErrorIndex(i);
736         return null;
737       }
738     else if (isNegativeSuffix &&
739              !positiveEqualsNegative &&
740              (negativeLen > positiveLen))
741       {
742         isNegative = true;
743       }
744     else if (!isPositiveSuffix)
745       {
746         pos.setErrorIndex(i);
747         return null;
748       }
749     
750     if (isNegative) number.insert(0, '-');
751    
752     pos.setIndex(i - 1);
753     
754     // now we handle the return type
755     BigDecimal bigDecimal = new BigDecimal(number.toString());
756     if (this.parseBigDecimal)
757       return bigDecimal;
758     
759     // want integer?
760     if (this.parseIntegerOnly)
761       return new Long(bigDecimal.longValue());
762
763     // 3th special case -0.0
764     if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0))
765       return new Double(-0.0);
766     
767     try
768       {
769         BigDecimal integer
770           = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY);
771         return new Long(integer.longValue());
772       }
773     catch (ArithmeticException e)
774       {
775         return new Double(bigDecimal.doubleValue());
776       }
777   }
778
779   /**
780    * Sets the <code>Currency</code> on the
781    * <code>DecimalFormatSymbols</code> used, which also sets the
782    * currency symbols on those symbols.
783    * 
784    * @param currency The new <code>Currency</code> on the
785    * <code>DecimalFormatSymbols</code>.
786    */
787   public void setCurrency(Currency currency)
788   {
789     symbols.setCurrency(currency);
790   }
791   
792   /**
793    * Sets the symbols used by this instance.  This method makes a copy of 
794    * the supplied symbols.
795    * 
796    * @param newSymbols  the symbols (<code>null</code> not permitted).
797    */
798   public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
799   {
800     symbols = (DecimalFormatSymbols) newSymbols.clone();
801   }
802   
803   /**
804    * Define if the decimal separator should be always visible or only
805    * visible when needed. This method as effect only on integer values.
806    * Pass <code>true</code> if you want the decimal separator to be
807    * always shown, <code>false</code> otherwise.
808    * 
809    * @param newValue true</code> if you want the decimal separator to be
810    * always shown, <code>false</code> otherwise.
811    */
812   public void setDecimalSeparatorAlwaysShown(boolean newValue)
813   {
814     decimalSeparatorAlwaysShown = newValue;
815   }
816   
817   /**
818    * Sets the number of digits used to group portions of the integer part of
819    * the number. For example, the number <code>123456</code>, with a grouping
820    * size of 3, is rendered <code>123,456</code>.
821    * 
822    * @param groupSize The number of digits used while grouping portions
823    * of the integer part of a number.
824    */
825   public void setGroupingSize(int groupSize)
826   {
827     groupingSize = (byte) groupSize;
828   }
829   
830   /**
831    * Sets the maximum number of digits allowed in the integer
832    * portion of a number to the specified value.
833    * The new value will be the choosen as the minimum between
834    * <code>newvalue</code> and 309. Any value below zero will be
835    * replaced by zero.
836    * 
837    * @param newValue The new maximum integer digits value.
838    */
839   public void setMaximumIntegerDigits(int newValue)
840   {
841     newValue = (newValue > 0) ? newValue : 0;
842     super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
843   }
844   
845   /**
846    * Sets the minimum number of digits allowed in the integer
847    * portion of a number to the specified value.
848    * The new value will be the choosen as the minimum between
849    * <code>newvalue</code> and 309. Any value below zero will be
850    * replaced by zero.
851    * 
852    * @param newValue The new minimum integer digits value.
853    */
854   public void setMinimumIntegerDigits(int newValue)
855   {
856     newValue = (newValue > 0) ? newValue : 0;
857     super.setMinimumIntegerDigits(Math.min(newValue,  DEFAULT_INTEGER_DIGITS));
858   }
859   
860   /**
861    * Sets the maximum number of digits allowed in the fraction
862    * portion of a number to the specified value.
863    * The new value will be the choosen as the minimum between
864    * <code>newvalue</code> and 309. Any value below zero will be
865    * replaced by zero.
866    * 
867    * @param newValue The new maximum fraction digits value.
868    */
869   public void setMaximumFractionDigits(int newValue)
870   {
871     newValue = (newValue > 0) ? newValue : 0;
872     super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
873   }
874   
875   /**
876    * Sets the minimum number of digits allowed in the fraction
877    * portion of a number to the specified value.
878    * The new value will be the choosen as the minimum between
879    * <code>newvalue</code> and 309. Any value below zero will be
880    * replaced by zero.
881    * 
882    * @param newValue The new minimum fraction digits value.
883    */
884   public void setMinimumFractionDigits(int newValue)
885   {
886     newValue = (newValue > 0) ? newValue : 0;
887     super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
888   }
889   
890   /**
891    * Sets the multiplier for use in percent and similar formats.
892    * For example, for percent set the multiplier to 100, for permille, set the
893    * miltiplier to 1000.
894    * 
895    * @param newValue the new value for multiplier.
896    */
897   public void setMultiplier(int newValue)
898   {
899     multiplier = newValue;
900   }
901   
902   /**
903    * Sets the negative prefix.
904    * 
905    * @param newValue The new negative prefix.
906    */
907   public void setNegativePrefix(String newValue)
908   {
909     negativePrefix = newValue;
910   }
911
912   /**
913    * Sets the negative suffix.
914    * 
915    * @param newValue The new negative suffix.
916    */
917   public void setNegativeSuffix(String newValue)
918   {
919     negativeSuffix = newValue;
920   }
921   
922   /**
923    * Sets the positive prefix.
924    * 
925    * @param newValue The new positive prefix.
926    */
927   public void setPositivePrefix(String newValue)
928   {
929     positivePrefix = newValue;
930   }
931   
932   /**
933    * Sets the new positive suffix.
934    * 
935    * @param newValue The new positive suffix.
936    */
937   public void setPositiveSuffix(String newValue)
938   {
939     positiveSuffix = newValue;
940   }
941   
942   /**
943    * This method returns a string with the formatting pattern being used
944    * by this object. The string is localized.
945    * 
946    * @return A localized <code>String</code> with the formatting pattern.
947    * @see #toPattern()
948    */
949   public String toLocalizedPattern()
950   {
951     return computePattern(this.symbols);
952   }
953   
954   /**
955    * This method returns a string with the formatting pattern being used
956    * by this object. The string is not localized.
957    * 
958    * @return A <code>String</code> with the formatting pattern.
959    * @see #toLocalizedPattern()
960    */
961   public String toPattern()
962   {
963     return computePattern(nonLocalizedSymbols);
964   }
965   
966   /* ***** private methods ***** */
967   
968   /**
969    * This is an shortcut helper method used to test if two given strings are
970    * equals.
971    * 
972    * @param s1 The first string to test for equality.
973    * @param s2 The second string to test for equality.
974    * @return <code>true</code> if the strings are both <code>null</code> or
975    * equals.
976    */
977   private boolean equals(String s1, String s2)
978   {
979     if (s1 == null || s2 == null)
980       return s1 == s2;
981     return s1.equals(s2);
982   }
983   
984   
985   /* ****** PATTERN ****** */
986   
987   /**
988    * This helper function creates a string consisting of all the
989    * characters which can appear in a pattern and must be quoted.
990    */
991   private String patternChars (DecimalFormatSymbols syms)
992   {
993     StringBuffer buf = new StringBuffer ();
994     
995     buf.append(syms.getDecimalSeparator());
996     buf.append(syms.getDigit());
997     buf.append(syms.getExponential());
998     buf.append(syms.getGroupingSeparator());
999     buf.append(syms.getMinusSign());
1000     buf.append(syms.getPatternSeparator());
1001     buf.append(syms.getPercent());
1002     buf.append(syms.getPerMill());
1003     buf.append(syms.getZeroDigit());
1004     buf.append('\'');
1005     buf.append('\u00a4');
1006     
1007     return buf.toString();
1008   }
1009
1010   /**
1011    * Quote special characters as defined by <code>patChars</code> in the
1012    * input string.
1013    * 
1014    * @param text
1015    * @param patChars
1016    * @return A StringBuffer with special characters quoted.
1017    */
1018   private StringBuffer quoteFix(String text, String patChars)
1019   {
1020     StringBuffer buf = new StringBuffer();
1021     
1022     int len = text.length();
1023     char ch;
1024     for (int index = 0; index < len; ++index)
1025       {
1026         ch = text.charAt(index);
1027         if (patChars.indexOf(ch) != -1)
1028           {
1029             buf.append('\'');
1030             buf.append(ch);
1031             if (ch != '\'') buf.append('\'');
1032           }
1033         else
1034           {
1035             buf.append(ch);
1036           }
1037       }
1038     
1039     return buf;
1040   }
1041   
1042   /**
1043    * Returns the format pattern, localized to follow the given
1044    * symbols.
1045    */
1046   private String computePattern(DecimalFormatSymbols symbols)
1047   {
1048     StringBuffer mainPattern = new StringBuffer();
1049     
1050     // We have to at least emit a zero for the minimum number of
1051     // digits. Past that we need hash marks up to the grouping
1052     // separator (and one beyond).
1053     int _groupingSize = groupingUsed ? groupingSize + 1: groupingSize;
1054     int totalDigits = Math.max(minimumIntegerDigits, _groupingSize);
1055     
1056     // if it is not in exponential notiation,
1057     // we always have a # prebended
1058     if (!useExponentialNotation) mainPattern.append(symbols.getDigit());
1059     
1060     for (int i = 1; i < totalDigits - minimumIntegerDigits; i++)
1061       mainPattern.append(symbols.getDigit());
1062     
1063     for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++)
1064       mainPattern.append(symbols.getZeroDigit());
1065     
1066     if (groupingUsed)
1067       {
1068         mainPattern.insert(mainPattern.length() - groupingSize,
1069                            symbols.getGroupingSeparator());
1070       }
1071
1072     // See if we need decimal info.
1073     if (minimumFractionDigits > 0 || maximumFractionDigits > 0 ||
1074         decimalSeparatorAlwaysShown)
1075       {
1076         mainPattern.append(symbols.getDecimalSeparator());
1077       }
1078     
1079     for (int i = 0; i < minimumFractionDigits; ++i)
1080       mainPattern.append(symbols.getZeroDigit());
1081     
1082     for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
1083       mainPattern.append(symbols.getDigit());
1084     
1085     if (useExponentialNotation)
1086       {
1087         mainPattern.append(symbols.getExponential());
1088         
1089         for (int i = 0; i < minExponentDigits; ++i)
1090           mainPattern.append(symbols.getZeroDigit());
1091         
1092         if (minExponentDigits == 0)
1093           mainPattern.append(symbols.getDigit());
1094       }
1095     
1096     // save the pattern
1097     String pattern = mainPattern.toString();
1098     
1099     // so far we have the pattern itself, now we need to add
1100     // the positive and the optional negative prefixes and suffixes
1101     String patternChars = patternChars(symbols);
1102     mainPattern.insert(0, quoteFix(positivePrefix, patternChars));
1103     mainPattern.append(quoteFix(positiveSuffix, patternChars));
1104     
1105     if (hasNegativePrefix)
1106       {
1107         mainPattern.append(symbols.getPatternSeparator());
1108         mainPattern.append(quoteFix(negativePrefix, patternChars));
1109         mainPattern.append(pattern);
1110         mainPattern.append(quoteFix(negativeSuffix, patternChars));
1111       }
1112     
1113     // finally, return the pattern string
1114     return mainPattern.toString();
1115   }
1116   
1117   /* ****** FORMAT PARSING ****** */
1118   
1119   /**
1120    * Scan the input string and define a pattern suitable for use
1121    * with this decimal format.
1122    * 
1123    * @param pattern
1124    * @param symbols
1125    */
1126   private void applyPatternWithSymbols(String pattern,
1127                                        DecimalFormatSymbols symbols)
1128   {
1129     // The pattern string is described by a BNF diagram.
1130     // we could use a recursive parser to read and prepare
1131     // the string, but this would be too slow and resource
1132     // intensive, while this code is quite critical as it is
1133     // called always when the class is instantiated and every
1134     // time a new pattern is given.
1135     // Our strategy is to divide the string into section as given by
1136     // the BNF diagram, iterating through the string and setting up
1137     // the parameters we need for formatting (which is basicly what
1138     // a descendent recursive parser would do - but without recursion).
1139     // I'm sure that there are smarter methods to do this.
1140     
1141     // Restore default values. Most of these will be overwritten
1142     // but we want to be sure that nothing is left out.
1143     setDefaultValues();
1144     
1145     int len = pattern.length();
1146     if (len == 0)
1147       {
1148         // this is another special case...
1149         this.minimumIntegerDigits = 1;
1150         this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
1151         this.minimumFractionDigits = 0;
1152         this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
1153         
1154         // FIXME: ...and these values may not be valid in all locales
1155         this.minExponentDigits = 0;
1156         this.showDecimalSeparator = true;
1157         this.groupingUsed = true;
1158         this.groupingSize = 3;
1159         
1160         return;
1161       }
1162     
1163     int start = scanFix(pattern, symbols, 0, true);
1164     if (start < len) start = scanNumberInteger(pattern, symbols, start);
1165     if (start < len)
1166       {
1167         start = scanFractionalPortion(pattern, symbols, start);
1168       }
1169     else
1170       {
1171         // special case, pattern that ends here does not have a fractional
1172         // portion
1173         this.minimumFractionDigits = 0;
1174         this.maximumFractionDigits = 0;
1175         //this.decimalSeparatorAlwaysShown = false;
1176         //this.showDecimalSeparator = false;
1177       }
1178     
1179     // XXX: this fixes a compatibility test with the RI.
1180     // If new uses cases fail, try removing this line first.
1181     //if (!this.hasIntegerPattern && !this.hasFractionalPattern)
1182     //  throw new IllegalArgumentException("No valid pattern found!");
1183     
1184     if (start < len) start = scanExponent(pattern, symbols, start);
1185     if (start < len) start = scanFix(pattern, symbols, start, false);
1186     if (start < len) scanNegativePattern(pattern, symbols, start);
1187     
1188     if (useExponentialNotation &&
1189         (maxIntegerDigitsExponent > minimumIntegerDigits) &&
1190         (maxIntegerDigitsExponent > 1))
1191       {
1192         minimumIntegerDigits = 1;
1193         exponentRound = maxIntegerDigitsExponent;
1194       }
1195     
1196     if (useExponentialNotation)
1197       maximumIntegerDigits = maxIntegerDigitsExponent;
1198     
1199     if (!this.hasFractionalPattern && this.showDecimalSeparator == true)
1200       {
1201         this.decimalSeparatorAlwaysShown = true;
1202       }
1203   }
1204   
1205   /**
1206    * Scans for the prefix or suffix portion of the pattern string.
1207    * This method handles the positive subpattern of the pattern string.
1208    *  
1209    * @param pattern The pattern string to parse.
1210    * @return The position in the pattern string where parsing ended.
1211    */
1212   private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols,
1213                       int start, boolean prefix)
1214   {
1215     StringBuffer buffer = new StringBuffer();
1216     
1217     // the number portion is always delimited by one of those
1218     // characters
1219     char decimalSeparator = sourceSymbols.getDecimalSeparator();
1220     char patternSeparator = sourceSymbols.getPatternSeparator();
1221     char groupingSeparator = sourceSymbols.getGroupingSeparator();
1222     char digit = sourceSymbols.getDigit();
1223     char zero = sourceSymbols.getZeroDigit();
1224     char minus = sourceSymbols.getMinusSign();
1225     
1226     // other special characters, cached here to avoid method calls later
1227     char percent = sourceSymbols.getPercent();
1228     char permille = sourceSymbols.getPerMill();
1229     
1230     String currencySymbol = this.symbols.getCurrencySymbol();
1231     
1232     boolean quote = false;
1233     
1234     char ch = pattern.charAt(start);
1235     if (ch == patternSeparator)
1236       {
1237         // negative subpattern
1238         this.hasNegativePrefix = true;
1239         ++start;
1240         return start;
1241       }
1242     
1243     int len = pattern.length();
1244     int i;
1245     for (i = start; i < len; i++)
1246       {
1247         ch = pattern.charAt(i);
1248
1249         // we are entering into the negative subpattern
1250         if (!quote && ch == patternSeparator)
1251           {
1252             if (this.hasNegativePrefix)
1253               {
1254                 throw new IllegalArgumentException("Invalid pattern found: "
1255                                                    + start);
1256               }
1257             
1258             this.hasNegativePrefix = true;
1259             ++i;
1260             break;
1261           }
1262         
1263         // this means we are inside the number portion
1264         if (!quote &&
1265             (ch == minus || ch == digit || ch == zero ||
1266              ch == groupingSeparator))
1267           break;
1268
1269         if (!quote && ch == decimalSeparator)
1270           {
1271             this.showDecimalSeparator = true;
1272             break;
1273           }
1274         else if (quote && ch != '\'')
1275           {
1276             buffer.append(ch);
1277             continue;
1278           }
1279         
1280         if (ch == '\u00A4')
1281           {
1282             // CURRENCY
1283             currencySymbol = this.symbols.getCurrencySymbol();
1284
1285             // if \u00A4 is doubled, we use the international currency symbol
1286             if (i < len && pattern.charAt(i + 1) == '\u00A4')
1287               {
1288                 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1289                 i++;
1290               }
1291
1292             this.useCurrencySeparator = true;
1293             buffer.append(currencySymbol);
1294           }
1295         else if (ch == percent)
1296           {
1297             // PERCENT
1298             this.multiplier = 100;
1299             buffer.append(this.symbols.getPercent());
1300           }
1301         else if (ch == permille)
1302           {
1303             // PERMILLE
1304             this.multiplier = 1000;
1305             buffer.append(this.symbols.getPerMill());
1306           }
1307         else if (ch == '\'')
1308           {
1309             // QUOTE
1310             if (i < len && pattern.charAt(i + 1) == '\'')
1311               {
1312                 // we need to add ' to the buffer 
1313                 buffer.append(ch);
1314                 i++;
1315               }
1316             else
1317               {
1318                 quote = !quote;
1319                 continue;
1320               }
1321           }
1322         else
1323           {
1324             buffer.append(ch);
1325           }
1326       }
1327     
1328     if (prefix)
1329       {
1330         this.positivePrefix = buffer.toString();
1331         this.negativePrefix = minus + "" + positivePrefix;
1332       }
1333     else
1334       {
1335         this.positiveSuffix = buffer.toString();
1336       }
1337     
1338     return i;
1339   }
1340   
1341   /**
1342    * Scan the given string for number patterns, starting
1343    * from <code>start</code>.
1344    * This method searches the integer part of the pattern only.
1345    * 
1346    * @param pattern The pattern string to parse.
1347    * @param start The starting parse position in the string.
1348    * @return The position in the pattern string where parsing ended,
1349    * counted from the beginning of the string (that is, 0).
1350    */
1351   private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols,
1352                                 int start)
1353   {
1354     char digit = symbols.getDigit();
1355     char zero = symbols.getZeroDigit();
1356     char groupingSeparator = symbols.getGroupingSeparator();
1357     char decimalSeparator = symbols.getDecimalSeparator();
1358     char exponent = symbols.getExponential();
1359     char patternSeparator = symbols.getPatternSeparator();
1360     
1361     // count the number of zeroes in the pattern
1362     // this number defines the minum digits in the integer portion
1363     int zeros = 0;
1364     
1365     // count the number of digits used in grouping
1366     int _groupingSize = 0;
1367     
1368     this.maxIntegerDigitsExponent = 0;
1369     
1370     boolean intPartTouched = false;
1371     
1372     char ch;
1373     int len = pattern.length();
1374     int i;
1375     for (i = start; i < len; i++)
1376       {
1377         ch = pattern.charAt(i);
1378  
1379         // break on decimal separator or exponent or pattern separator
1380         if (ch == decimalSeparator || ch == exponent)
1381           break;
1382         
1383         if (this.hasNegativePrefix && ch == patternSeparator)
1384           throw new IllegalArgumentException("Invalid pattern found: "
1385                                              + start);
1386         
1387         if (ch == digit)
1388           {
1389             // in our implementation we could relax this strict
1390             // requirement, but this is used to keep compatibility with
1391             // the RI
1392             if (zeros > 0) throw new
1393               IllegalArgumentException("digit mark following zero in " +
1394                         "positive subpattern, not allowed. Position: " + i);
1395             
1396             _groupingSize++;
1397             intPartTouched = true;
1398             this.maxIntegerDigitsExponent++;
1399           }
1400         else if (ch == zero)
1401           {
1402             zeros++;
1403             _groupingSize++;
1404             this.maxIntegerDigitsExponent++;
1405           }
1406         else if (ch == groupingSeparator)
1407           {
1408             this.groupingSeparatorInPattern = true;
1409             this.groupingUsed = true;
1410             _groupingSize = 0;
1411           }
1412         else
1413           {
1414             // any other character not listed above
1415             // means we are in the suffix portion
1416             break;
1417           }
1418       }
1419     
1420     if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize;
1421     this.minimumIntegerDigits = zeros;
1422     
1423     // XXX: compatibility code with the RI: the number of minimum integer
1424     // digits is at least one when maximumIntegerDigits is more than zero
1425     if (intPartTouched && this.maximumIntegerDigits > 0 &&
1426         this.minimumIntegerDigits == 0)
1427       this.minimumIntegerDigits = 1;
1428
1429     return i;
1430   }
1431   
1432   /**
1433    * Scan the given string for number patterns, starting
1434    * from <code>start</code>.
1435    * This method searches the fractional part of the pattern only.
1436    * 
1437    * @param pattern The pattern string to parse.
1438    * @param start The starting parse position in the string.
1439    * @return The position in the pattern string where parsing ended,
1440    * counted from the beginning of the string (that is, 0).
1441    */
1442   private int scanFractionalPortion(String pattern,
1443                                     DecimalFormatSymbols symbols,
1444                                     int start)
1445   {
1446     char digit = symbols.getDigit();
1447     char zero = symbols.getZeroDigit();
1448     char groupingSeparator = symbols.getGroupingSeparator();
1449     char decimalSeparator = symbols.getDecimalSeparator();
1450     char exponent = symbols.getExponential();
1451     char patternSeparator = symbols.getPatternSeparator();
1452     
1453     // first character needs to be '.' otherwise we are not parsing the
1454     // fractional portion
1455     char ch = pattern.charAt(start);
1456     if (ch != decimalSeparator)
1457       {
1458         this.minimumFractionDigits = 0;
1459         this.maximumFractionDigits = 0;
1460         return start;
1461       }
1462     
1463     ++start;
1464     
1465     this.hasFractionalPattern = true;
1466     
1467     this.minimumFractionDigits = 0;
1468     int digits = 0;
1469     
1470     int len = pattern.length();
1471     int i;
1472     for (i = start; i < len; i++)
1473       {
1474         ch = pattern.charAt(i);
1475         
1476         // we hit the exponential or negative subpattern
1477         if (ch == exponent || ch == patternSeparator)
1478           break;
1479         
1480         // pattern error
1481         if (ch == groupingSeparator || ch == decimalSeparator) throw new
1482           IllegalArgumentException("unexpected character '" + ch + "' " +
1483                                    "in fractional subpattern. Position: " + i);
1484         
1485         if (ch == digit)
1486           {
1487             digits++;
1488           }
1489         else if (ch == zero)
1490           {
1491             if (digits > 0) throw new
1492             IllegalArgumentException("digit mark following zero in " +
1493                       "positive subpattern, not allowed. Position: " + i);
1494             
1495             this.minimumFractionDigits++;
1496           }
1497         else
1498           {
1499             // we are in the suffix section of pattern
1500             break;
1501           }
1502       }
1503     
1504     if (i == start) this.hasFractionalPattern = false;
1505     
1506     this.maximumFractionDigits = this.minimumFractionDigits + digits;
1507     this.showDecimalSeparator = true;
1508     
1509     return i;
1510   }
1511   
1512   /**
1513    * Scan the given string for number patterns, starting
1514    * from <code>start</code>.
1515    * This method searches the expoential part of the pattern only.
1516    * 
1517    * @param pattern The pattern string to parse.
1518    * @param start The starting parse position in the string.
1519    * @return The position in the pattern string where parsing ended,
1520    * counted from the beginning of the string (that is, 0).
1521    */
1522   private int scanExponent(String pattern, DecimalFormatSymbols symbols,
1523                            int start)
1524   {
1525     char digit = symbols.getDigit();
1526     char zero = symbols.getZeroDigit();
1527     char groupingSeparator = symbols.getGroupingSeparator();
1528     char decimalSeparator = symbols.getDecimalSeparator();
1529     char exponent = symbols.getExponential();
1530     
1531     char ch = pattern.charAt(start);
1532     
1533     if (ch == decimalSeparator)
1534       {
1535         // ignore dots
1536         ++start;
1537       }
1538     
1539     if (ch != exponent)
1540       {
1541         this.useExponentialNotation = false;
1542         return start;
1543       }
1544     
1545     ++start;
1546     
1547     this.minExponentDigits = 0;
1548     
1549     int len = pattern.length();
1550     int i;
1551     for (i = start; i < len; i++)
1552       {
1553         ch = pattern.charAt(i);
1554         
1555         if (ch == groupingSeparator || ch == decimalSeparator ||
1556             ch == digit || ch == exponent) throw new
1557         IllegalArgumentException("unexpected character '" + ch + "' " + 
1558                                  "in exponential subpattern. Position: " + i);
1559         
1560         if (ch == zero)
1561           {
1562             this.minExponentDigits++;
1563           }
1564         else
1565           {
1566             // any character other than zero is an exit point
1567             break;
1568           }
1569       }
1570     
1571     this.useExponentialNotation = true; 
1572     
1573     return i;
1574   }
1575   
1576   /**
1577    * Scan the given string for number patterns, starting
1578    * from <code>start</code>.
1579    * This method searches the negative part of the pattern only and scan
1580    * throught the end of the string.
1581    * 
1582    * @param pattern The pattern string to parse.
1583    * @param start The starting parse position in the string.
1584    */
1585   private void scanNegativePattern(String pattern,
1586                                    DecimalFormatSymbols sourceSymbols,
1587                                    int start)
1588   {
1589     StringBuffer buffer = new StringBuffer();
1590     
1591     // the number portion is always delimited by one of those
1592     // characters
1593     char decimalSeparator = sourceSymbols.getDecimalSeparator();
1594     char patternSeparator = sourceSymbols.getPatternSeparator();
1595     char groupingSeparator = sourceSymbols.getGroupingSeparator();
1596     char digit = sourceSymbols.getDigit();
1597     char zero = sourceSymbols.getZeroDigit();
1598     char minus = sourceSymbols.getMinusSign();
1599     
1600     // other special charcaters, cached here to avoid method calls later
1601     char percent = sourceSymbols.getPercent();
1602     char permille = sourceSymbols.getPerMill();
1603     
1604     String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol();
1605     String currencySymbol = CURRENCY_SYMBOL;
1606     
1607     boolean quote = false;
1608     boolean prefixDone = false;
1609     
1610     int len = pattern.length();
1611     if (len > 0) this.hasNegativePrefix = true;
1612     
1613     char ch = pattern.charAt(start);
1614     if (ch == patternSeparator)
1615       {
1616         // no pattern separator in the negative pattern
1617         if ((start + 1) > len) throw new
1618           IllegalArgumentException("unexpected character '" + ch + "' " +
1619                                    "in negative subpattern.");    
1620         start++;
1621       }
1622     
1623     int i;
1624     for (i = start; i < len; i++)
1625       {
1626         ch = pattern.charAt(i);
1627         
1628         // this means we are inside the number portion
1629         if (!quote &&
1630             (ch == digit || ch == zero || ch == decimalSeparator ||
1631              ch == patternSeparator || ch == groupingSeparator))
1632           {
1633             if (!prefixDone)
1634               {
1635                 this.negativePrefix = buffer.toString();
1636                 buffer.delete(0, buffer.length());
1637                 prefixDone = true;
1638               }
1639           }
1640         else if (ch == minus)
1641           {
1642             buffer.append(this.symbols.getMinusSign());
1643           }
1644         else if (quote && ch != '\'')
1645           {
1646             buffer.append(ch);
1647           }
1648         else if (ch == '\u00A4')
1649           {
1650             // CURRENCY
1651             currencySymbol = CURRENCY_SYMBOL;
1652
1653             // if \u00A4 is doubled, we use the international currency symbol
1654             if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1655               {
1656                 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1657                 i = i + 2;
1658               }
1659
1660             // FIXME: not sure about this, the specs says that we only have to
1661             // change prefix and suffix, so leave it as commented
1662             // unless in case of bug report/errors
1663             //this.useCurrencySeparator = true;
1664             
1665             buffer.append(currencySymbol);
1666           }
1667         else if (ch == percent)
1668           {
1669             // PERCENT
1670             this.negativePatternMultiplier = 100;
1671             buffer.append(this.symbols.getPercent());
1672           }
1673         else if (ch == permille)
1674           {
1675             // PERMILLE
1676             this.negativePatternMultiplier = 1000;
1677             buffer.append(this.symbols.getPerMill());
1678           }
1679         else if (ch == '\'')
1680           {
1681             // QUOTE
1682             if (i < len && pattern.charAt(i + 1) == '\'')
1683               {
1684                 // we need to add ' to the buffer 
1685                 buffer.append(ch);
1686                 i++;
1687               }
1688             else
1689               {
1690                 quote = !quote;
1691               }
1692           }
1693         else if (ch == patternSeparator)
1694           {
1695             // no pattern separator in the negative pattern
1696             throw new IllegalArgumentException("unexpected character '" + ch +
1697                                                "' in negative subpattern.");
1698           }
1699         else
1700           {
1701             buffer.append(ch);
1702           }
1703       }
1704     
1705     if (prefixDone)
1706       this.negativeSuffix = buffer.toString();
1707     else
1708       this.negativePrefix = buffer.toString();
1709   }
1710   
1711   /* ****** FORMATTING ****** */
1712   
1713   /**
1714    * Handles the real formatting.
1715    * 
1716    * We use a BigDecimal to format the number without precision loss.
1717    * All the rounding is done by methods in BigDecimal.
1718    * The <code>isLong</code> parameter is used to determine if we are
1719    * formatting a long or BigInteger. In this case, we avoid to format
1720    * the fractional part of the number (unless specified otherwise in the
1721    * format string) that would consist only of a 0 digit.
1722    * 
1723    * @param number A BigDecimal representation fo the input number.
1724    * @param dest The destination buffer.
1725    * @param isLong A boolean that indicates if this BigDecimal is a real
1726    * decimal or an integer.
1727    * @param fieldPos Use to keep track of the formatting position.
1728    */
1729   private void formatInternal(BigDecimal number, boolean isLong,
1730                               StringBuffer dest, FieldPosition fieldPos)
1731   {  
1732     // The specs says that fieldPos should not be null, and that we
1733     // should throw a NPE, but it seems that in few classes that
1734     // reference this one, fieldPos is set to null.
1735     // This is even defined in the javadoc, see for example MessageFormat.
1736     // I think the best here is to check for fieldPos and build one if it is
1737     // null. If it cause harms or regressions, just remove this line and
1738     // fix the classes in the point of call, insted.
1739     if (fieldPos == null) fieldPos = new FieldPosition(0);
1740     
1741     int _multiplier = this.multiplier;
1742     
1743     // used to track attribute starting position for each attribute
1744     int attributeStart = -1;
1745     
1746     // now get the sign this will be used by the special case Inifinity
1747     // and by the normal cases.
1748     boolean isNegative = (number.signum() < 0) ? true : false;
1749     if (isNegative)
1750       {
1751         attributeStart = dest.length();
1752         
1753         // append the negative prefix to the string
1754         dest.append(negativePrefix);
1755         
1756         // once got the negative prefix, we can use
1757         // the absolute value.
1758         number = number.abs();
1759         
1760         _multiplier = negativePatternMultiplier;
1761         
1762         addAttribute(Field.SIGN, attributeStart, dest.length());
1763       }
1764     else
1765       {
1766         // not negative, use the positive prefix
1767         dest.append(positivePrefix);
1768       }
1769     
1770     // these are used ot update the field position
1771     int beginIndexInt = dest.length();
1772     int endIndexInt = 0;
1773     int beginIndexFract = 0;
1774     int endIndexFract = 0;
1775     
1776     // compute the multiplier to use with percent and similar
1777     number = number.multiply(new BigDecimal(_multiplier));
1778     
1779     // XXX: special case, not sure if it belongs here or if it is
1780     // correct at all. There may be other special cases as well
1781     // these should be handled in the format string parser.
1782     if (this.maximumIntegerDigits == 0 && this.maximumFractionDigits == 0)
1783       {
1784         number = BigDecimal.ZERO;
1785         this.maximumIntegerDigits = 1;
1786         this.minimumIntegerDigits = 1;
1787       }
1788     
1789     //  get the absolute number
1790     number = number.abs();
1791
1792     // the scaling to use while formatting this number
1793     int scale = this.maximumFractionDigits;
1794     
1795     // this is the actual number we will use
1796     // it is corrected later on to handle exponential
1797     // notation, if needed
1798     long exponent = 0;
1799     
1800     // are we using exponential notation?
1801     if (this.useExponentialNotation)
1802       {
1803         exponent = getExponent(number);
1804         number = number.movePointLeft((int) exponent);
1805         
1806         // FIXME: this makes the test ##.###E0 to pass,
1807         // but all all the other tests to fail...
1808         // this should be really something like
1809         // min + max - what is already shown...
1810         //scale = this.minimumIntegerDigits + this.maximumFractionDigits;
1811       }
1812     
1813     // round the number to the nearest neighbor
1814     number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
1815
1816     // now get the integer and fractional part of the string
1817     // that will be processed later
1818     String plain = number.toPlainString();
1819     
1820     String intPart = null;
1821     String fractPart = null;
1822     
1823     // remove - from the integer part, this is needed as
1824     // the Narrowing Primitive Conversions algorithm used may loose
1825     // information about the sign
1826     int minusIndex = plain.lastIndexOf('-', 0);
1827     if (minusIndex > -1) plain = plain.substring(minusIndex + 1);
1828     
1829     // strip the decimal portion
1830     int dot = plain.indexOf('.');
1831     if (dot > -1)
1832       {
1833         intPart = plain.substring(0, dot);
1834         dot++;
1835         
1836         if (useExponentialNotation)
1837           fractPart = plain.substring(dot, dot + scale);
1838         else
1839           fractPart = plain.substring(dot);
1840       }
1841     else
1842       {
1843         intPart = plain;
1844       }
1845     
1846     // used in various places later on
1847     int intPartLen = intPart.length();
1848     endIndexInt = intPartLen;
1849     
1850     // if the number of digits in our intPart is not greater than the
1851     // minimum we have to display, we append zero to the destination
1852     // buffer before adding the integer portion of the number.
1853     int zeroes = minimumIntegerDigits - intPartLen;
1854     if (zeroes > 0)
1855       {
1856         attributeStart = Math.max(dest.length() - 1, 0);
1857         appendZero(dest, zeroes, minimumIntegerDigits);
1858       }
1859
1860     if (this.useExponentialNotation)
1861       {
1862         // For exponential numbers, the significant in mantissa are
1863         // the sum of the minimum integer and maximum fraction
1864         // digits, and does not take into account the maximun integer
1865         // digits to display.
1866         
1867         if (attributeStart < 0)
1868           attributeStart = Math.max(dest.length() - 1, 0);
1869         appendDigit(intPart, dest, this.groupingUsed);
1870       }
1871     else
1872       {
1873         // non exponential notation
1874         intPartLen = intPart.length();
1875         int canary = Math.min(intPartLen, this.maximumIntegerDigits);
1876         
1877         // remove from the string the number in excess
1878         // use only latest digits
1879         intPart = intPart.substring(intPartLen - canary);
1880         endIndexInt = intPart.length() + 1;
1881         
1882         // append it
1883         if (maximumIntegerDigits > 0 &&
1884             !(this.minimumIntegerDigits == 0 &&
1885              intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0))
1886           {
1887             if (attributeStart < 0)
1888               attributeStart = Math.max(dest.length() - 1, 0);
1889             appendDigit(intPart, dest, this.groupingUsed);
1890           }
1891       }
1892     
1893     // add the INTEGER attribute
1894     addAttribute(Field.INTEGER, attributeStart, dest.length());
1895     
1896     // ...update field position, if needed, and return...
1897     if ((fieldPos.getField() == INTEGER_FIELD ||
1898         fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
1899       {
1900         fieldPos.setBeginIndex(beginIndexInt);
1901         fieldPos.setEndIndex(endIndexInt);
1902       }
1903     
1904     handleFractionalPart(dest, fractPart, fieldPos, isLong);
1905         
1906     // and the exponent
1907     if (this.useExponentialNotation)
1908       {
1909         attributeStart = dest.length();
1910         
1911         dest.append(symbols.getExponential());
1912         
1913         addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
1914         attributeStart = dest.length();
1915         
1916         if (exponent < 0)
1917           {
1918             dest.append(symbols.getMinusSign());
1919             exponent = -exponent;
1920             
1921             addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
1922           }
1923         
1924         attributeStart = dest.length();
1925         
1926         String exponentString = String.valueOf(exponent);
1927         int exponentLength = exponentString.length();
1928         
1929         for (int i = 0; i < minExponentDigits - exponentLength; i++)
1930           dest.append(symbols.getZeroDigit());
1931         
1932         for (int i = 0; i < exponentLength; ++i)
1933           dest.append(exponentString.charAt(i));
1934         
1935         addAttribute(Field.EXPONENT, attributeStart, dest.length());
1936       }
1937  
1938     // now include the suffixes...
1939     if (isNegative)
1940       {
1941         dest.append(negativeSuffix);
1942       }
1943     else
1944       {
1945         dest.append(positiveSuffix);
1946       }
1947   }
1948
1949   /**
1950    * Add to the input buffer the result of formatting the fractional
1951    * portion of the number.
1952    * 
1953    * @param dest
1954    * @param fractPart
1955    * @param fieldPos
1956    * @param isLong
1957    */
1958   private void handleFractionalPart(StringBuffer dest, String fractPart,
1959                                     FieldPosition fieldPos, boolean isLong)
1960   {
1961     int dotStart = 0;
1962     int dotEnd = 0;
1963     boolean addDecimal = false;
1964     
1965     if (this.decimalSeparatorAlwaysShown  ||
1966          ((!isLong || this.useExponentialNotation) &&
1967            this.showDecimalSeparator && this.maximumFractionDigits > 0) ||
1968         this.minimumFractionDigits > 0)
1969       {
1970         dotStart = dest.length();
1971         
1972         if (this.useCurrencySeparator)
1973           dest.append(symbols.getMonetaryDecimalSeparator());
1974         else
1975           dest.append(symbols.getDecimalSeparator());
1976         
1977         dotEnd = dest.length();
1978         addDecimal = true;
1979       }
1980     
1981     // now handle the fraction portion of the number
1982     int fractStart = 0;
1983     int fractEnd = 0;
1984     boolean addFractional = false;
1985     
1986     if ((!isLong || this.useExponentialNotation)
1987         && this.maximumFractionDigits > 0
1988         || this.minimumFractionDigits > 0)
1989       {
1990         fractStart = dest.length();
1991         fractEnd = fractStart;
1992         
1993         int digits = this.minimumFractionDigits;
1994         
1995         if (this.useExponentialNotation)
1996           {
1997             digits = (this.minimumIntegerDigits + this.minimumFractionDigits)
1998               - dest.length();
1999             if (digits < 0) digits = 0;
2000           }
2001         
2002         fractPart = adjustTrailingZeros(fractPart, digits);
2003         
2004         // FIXME: this code must be improved
2005         // now check if the factional part is just 0, in this case
2006         // we need to remove the '.' unless requested
2007         boolean allZeros = true;
2008         char fracts[] = fractPart.toCharArray();
2009         for (int i = 0; i < fracts.length; i++)
2010           {
2011             if (fracts[i] != '0')
2012               allZeros = false;
2013           }
2014         
2015         if (!allZeros || (minimumFractionDigits > 0))
2016           {
2017             appendDigit(fractPart, dest, false);
2018             fractEnd = dest.length();
2019             
2020             addDecimal = true;
2021             addFractional = true;
2022           }
2023         else if (!this.decimalSeparatorAlwaysShown)
2024           {
2025             dest.deleteCharAt(dest.length() - 1);
2026             addDecimal = false;
2027           }
2028         else
2029           {
2030             fractEnd = dest.length();
2031             addFractional = true;
2032           }
2033       }
2034     
2035     if (addDecimal)
2036       addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
2037     
2038     if (addFractional)
2039       addAttribute(Field.FRACTION, fractStart, fractEnd);
2040     
2041     if ((fieldPos.getField() == FRACTION_FIELD ||
2042         fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION))
2043       {
2044         fieldPos.setBeginIndex(fractStart);
2045         fieldPos.setEndIndex(fractEnd);
2046       }
2047   }
2048   
2049   /**
2050    * Append to <code>dest</code>the give number of zeros.
2051    * Grouping is added if needed.
2052    * The integer totalDigitCount defines the total number of digits
2053    * of the number to which we are appending zeroes.
2054    */
2055   private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount)
2056   {
2057     char ch = symbols.getZeroDigit();
2058     char gSeparator = symbols.getGroupingSeparator();
2059     
2060     int i = 0;
2061     int gPos = totalDigitCount;
2062     for (i = 0; i < zeroes; i++, gPos--)
2063       {
2064         if (this.groupingSeparatorInPattern &&
2065             (this.groupingUsed && this.groupingSize != 0) &&
2066             (gPos % groupingSize == 0 && i > 0))
2067           dest.append(gSeparator);
2068         
2069         dest.append(ch);
2070       }
2071     
2072     // special case, that requires adding an additional separator
2073     if (this.groupingSeparatorInPattern &&
2074         (this.groupingUsed && this.groupingSize != 0) &&
2075         (gPos % groupingSize == 0))
2076       dest.append(gSeparator);
2077   }
2078   
2079   /**
2080    * Append src to <code>dest</code>.
2081    * 
2082    * Grouping is added if <code>groupingUsed</code> is set
2083    * to <code>true</code>.
2084    */
2085   private void appendDigit(String src, StringBuffer dest,
2086                              boolean groupingUsed)
2087   {
2088     int zero = symbols.getZeroDigit() - '0';
2089     
2090     int ch;
2091     char gSeparator = symbols.getGroupingSeparator();
2092         
2093     int len = src.length();
2094     for (int i = 0, gPos = len; i < len; i++, gPos--)
2095       {
2096         ch = src.charAt(i);
2097         if (groupingUsed && this.groupingSize != 0 &&
2098             gPos % groupingSize == 0 && i > 0)
2099           dest.append(gSeparator);
2100
2101         dest.append((char) (zero + ch));
2102       }
2103   }
2104   
2105   /**
2106    * Calculate the exponent to use if eponential notation is used.
2107    * The exponent is calculated as a power of ten.
2108    * <code>number</code> should be positive, if is zero, or less than zero,
2109    * zero is returned.
2110    */
2111   private long getExponent(BigDecimal number)
2112   {
2113     long exponent = 0;
2114     
2115     if (number.signum() > 0)
2116       {
2117         double _number = number.doubleValue();
2118         exponent = (long) Math.floor (Math.log10(_number));
2119         
2120         // get the right value for the exponent
2121         exponent = exponent - (exponent % this.exponentRound);
2122         
2123         // if the minimumIntegerDigits is more than zero
2124         // we display minimumIntegerDigits of digits.
2125         // so, for example, if minimumIntegerDigits == 2
2126         // and the actual number is 0.123 it will be
2127         // formatted as 12.3E-2
2128         // this means that the exponent have to be shifted
2129         // to the correct value.
2130         if (minimumIntegerDigits > 0)
2131               exponent -= minimumIntegerDigits - 1;
2132       }
2133     
2134     return exponent;
2135   }
2136  
2137   /**
2138    * Remove contiguos zeros from the end of the <code>src</code> string,
2139    * if src contains more than <code>minimumDigits</code> digits.
2140    * if src contains less that <code>minimumDigits</code>,
2141    * then append zeros to the string.
2142    * 
2143    * Only the first block of zero digits is removed from the string
2144    * and only if they fall in the src.length - minimumDigits
2145    * portion of the string.
2146    * 
2147    * @param src The string with the correct number of zeros.
2148    */
2149   private String adjustTrailingZeros(String src, int minimumDigits)
2150   {
2151     int len = src.length();
2152     String result;
2153     
2154     // remove all trailing zero
2155     if (len > minimumDigits)
2156       {
2157         int zeros = 0;    
2158         for (int i = len - 1; i > minimumDigits; i--)
2159           {
2160             if (src.charAt(i) == '0')
2161               ++zeros;
2162             else
2163               break;
2164           }
2165         result =  src.substring(0, len - zeros);                
2166       }
2167     else
2168       {
2169         char zero = symbols.getZeroDigit();
2170         StringBuffer _result = new StringBuffer(src);
2171         for (int i = len; i < minimumDigits; i++)
2172           {
2173             _result.append(zero);
2174           }
2175         result = _result.toString();
2176       }
2177     
2178     return result;
2179   }
2180   
2181   /**
2182    * Adds an attribute to the attributes list.
2183    * 
2184    * @param field
2185    * @param begin
2186    * @param end
2187    */
2188   private void addAttribute(Field field, int begin, int end)
2189   {
2190     /*
2191      * This method and its implementation derives directly from the
2192      * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
2193      */
2194     
2195     FieldPosition pos = new FieldPosition(field);
2196     pos.setBeginIndex(begin);
2197     pos.setEndIndex(end);
2198     attributes.add(pos);
2199   }
2200   
2201   /**
2202    * Sets the default values for the various properties in this DecimaFormat.
2203    */
2204   private void setDefaultValues()
2205   {
2206     // Maybe we should add these values to the message bundle and take
2207     // the most appropriate for them for any locale.
2208     // Anyway, these seem to be good values for a default in most languages.
2209     // Note that most of these will change based on the format string.
2210     
2211     this.negativePrefix = String.valueOf(symbols.getMinusSign());
2212     this.negativeSuffix = "";
2213     this.positivePrefix = "";
2214     this.positiveSuffix = "";
2215     
2216     this.multiplier = 1;
2217     this.negativePatternMultiplier = 1;
2218     this.exponentRound = 1;
2219     
2220     this.hasNegativePrefix = false;
2221     
2222     this.minimumIntegerDigits = 1;
2223     this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
2224     this.minimumFractionDigits = 0;
2225     this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
2226     this.minExponentDigits = 0;
2227     
2228     this.groupingSize = 0;
2229     
2230     this.decimalSeparatorAlwaysShown = false;
2231     this.showDecimalSeparator = false;
2232     this.useExponentialNotation = false;
2233     this.groupingUsed = false;
2234     this.groupingSeparatorInPattern = false;
2235     
2236     this.useCurrencySeparator = false;
2237     
2238     this.hasFractionalPattern = false;
2239   }
2240 }