OSDN Git Service

42c81078cb29c21fee12b6ab282b6dcb6fc2cbf9
[pf3gnuchains/gcc-fork.git] / libjava / java / text / DecimalFormat.java
1 // DecimalFormat.java - Localized number formatting.
2
3 /* Copyright (C) 1999, 2000  Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 package java.text;
12
13 import java.util.Locale;
14 import java.util.MissingResourceException;
15 import java.util.ResourceBundle;
16 import java.io.ObjectInputStream;
17 import java.io.IOException;
18
19 /**
20  * @author Tom Tromey <tromey@cygnus.com>
21  * @date March 4, 1999
22  */
23 /* Written using "Java Class Libraries", 2nd edition, plus online
24  * API docs for JDK 1.2 from http://www.javasoft.com.
25  * Status:  Believed complete and correct to 1.2.
26  * Note however that the docs are very unclear about how format parsing
27  * should work.  No doubt there are problems here.
28  */
29
30 public class DecimalFormat extends NumberFormat
31 {
32   // This is a helper for applyPatternWithSymbols.  It reads a prefix
33   // or a suffix.  It can cause some side-effects.
34   private final int scanFix (String pattern, int index, StringBuffer buf,
35                              String patChars, DecimalFormatSymbols syms,
36                              boolean is_suffix)
37     {
38       int len = pattern.length();
39       buf.setLength(0);
40       boolean multiplierSet = false;
41       while (index < len)
42         {
43           char c = pattern.charAt(index);
44           if (c == '\'' && index + 1 < len
45               && pattern.charAt(index + 1) == '\'')
46             {
47               buf.append(c);
48               ++index;
49             }
50           else if (c == '\'' && index + 2 < len
51                    && pattern.charAt(index + 2) == '\'')
52             {
53               buf.append(pattern.charAt(index + 1));
54               index += 2;
55             }
56           else if (c == '\u00a4')
57             {
58               if (index + 1 < len && pattern.charAt(index + 1) == '\u00a4')
59                 {
60                   buf.append(syms.getInternationalCurrencySymbol());
61                   ++index;
62                 }
63               else
64                 buf.append(syms.getCurrencySymbol());
65             }
66           else if (is_suffix && c == syms.getPercent())
67             {
68               if (multiplierSet)
69                 throw new IllegalArgumentException ("multiplier already set " +
70                                                     "- index: " + index);
71               multiplierSet = true;
72               multiplier = 100;
73               buf.append(c);
74             }
75           else if (is_suffix && c == syms.getPerMill())
76             {
77               if (multiplierSet)
78                 throw new IllegalArgumentException ("multiplier already set " +
79                                                     "- index: " + index);
80               multiplierSet = true;
81               multiplier = 1000;
82               buf.append(c);
83             }
84           else if (patChars.indexOf(c) != -1)
85             {
86               // This is a pattern character.
87               break;
88             }
89           else
90             buf.append(c);
91           ++index;
92         }
93
94       return index;
95     }
96
97   // A helper which reads a number format.
98   private final int scanFormat (String pattern, int index,
99                                 String patChars, DecimalFormatSymbols syms,
100                                 boolean is_positive)
101     {
102       int max = pattern.length();
103
104       int countSinceGroup = 0;
105       int zeroCount = 0;
106       boolean saw_group = false;
107
108       //
109       // Scan integer part.
110       //
111       while (index < max)
112         {
113           char c = pattern.charAt(index);
114
115           if (c == syms.getDigit())
116             {
117               if (zeroCount > 0)
118                 throw new IllegalArgumentException ("digit mark following " +
119                                                     "zero - index: " + index);
120               ++countSinceGroup;
121             }
122           else if (c == syms.getZeroDigit())
123             {
124               ++zeroCount;
125               ++countSinceGroup;
126             }
127           else if (c == syms.getGroupingSeparator())
128             {
129               countSinceGroup = 0;
130               saw_group = true;
131             }
132           else
133             break;
134
135           ++index;
136         }
137
138       // We can only side-effect when parsing the positive format.
139       if (is_positive)
140         {
141           groupingUsed = saw_group;
142           groupingSize = (byte) countSinceGroup;
143           minimumIntegerDigits = zeroCount;
144         }
145
146       // Early termination.
147       if (index == max || pattern.charAt(index) == syms.getGroupingSeparator())
148         {
149           if (is_positive)
150             decimalSeparatorAlwaysShown = false;
151           return index;
152         }
153
154       if (pattern.charAt(index) == syms.getDecimalSeparator())
155         {
156           ++index;
157
158           //
159           // Scan fractional part.
160           //
161           int hashCount = 0;
162           zeroCount = 0;
163           while (index < max)
164             {
165               char c = pattern.charAt(index);
166               if (c == syms.getZeroDigit())
167                 {
168                   if (hashCount > 0)
169                     throw new IllegalArgumentException ("zero mark " +
170                                         "following digit - index: " + index);
171                   ++zeroCount;
172                 }
173               else if (c == syms.getDigit())
174                 {
175                   ++hashCount;
176                 }
177               else if (c != syms.getExponential()
178                        && c != syms.getPatternSeparator()
179                        && patChars.indexOf(c) != -1)
180                 throw new IllegalArgumentException ("unexpected special " +
181                                                 "character - index: " + index);
182               else
183                 break;
184
185               ++index;
186             }
187
188           if (is_positive)
189             {
190               maximumFractionDigits = hashCount + zeroCount;
191               minimumFractionDigits = zeroCount;
192             }
193
194           if (index == max)
195             return index;
196         }
197
198       if (pattern.charAt(index) == syms.getExponential())
199         {
200           //
201           // Scan exponential format.
202           //
203           zeroCount = 0;
204           ++index;
205           while (index < max)
206             {
207               char c = pattern.charAt(index);
208               if (c == syms.getZeroDigit())
209                 ++zeroCount;
210               else if (c == syms.getDigit())
211                 {
212                   if (zeroCount > 0)
213                     throw new
214                       IllegalArgumentException ("digit mark following zero " +
215                                                 "in exponent - index: " +
216                                                 index);
217                 }
218               else if (patChars.indexOf(c) != -1)
219                 throw new IllegalArgumentException ("unexpected special " +
220                                                     "character - index: " +
221                                                     index);
222               else
223                 break;
224
225               ++index;
226             }
227
228           if (is_positive)
229             {
230               useExponentialNotation = true;
231               minExponentDigits = (byte) zeroCount;
232             }
233         }
234
235       return index;
236     }
237
238   // This helper function creates a string consisting of all the
239   // characters which can appear in a pattern and must be quoted.
240   private final String patternChars (DecimalFormatSymbols syms)
241     {
242       StringBuffer buf = new StringBuffer ();
243       buf.append(syms.getDecimalSeparator());
244       buf.append(syms.getDigit());
245       buf.append(syms.getExponential());
246       buf.append(syms.getGroupingSeparator());
247       // Adding this one causes pattern application to fail.
248       // Of course, omitting is causes toPattern to fail.
249       // ... but we already have bugs there.  FIXME.
250       // buf.append(syms.getMinusSign());
251       buf.append(syms.getPatternSeparator());
252       buf.append(syms.getPercent());
253       buf.append(syms.getPerMill());
254       buf.append(syms.getZeroDigit());
255       buf.append('\u00a4');
256       return buf.toString();
257     }
258
259   private final void applyPatternWithSymbols (String pattern,
260                                               DecimalFormatSymbols syms)
261     {
262       // Initialize to the state the parser expects.
263       negativePrefix = "";
264       negativeSuffix = "";
265       positivePrefix = "";
266       positiveSuffix = "";
267       decimalSeparatorAlwaysShown = false;
268       groupingSize = 0;
269       minExponentDigits = 0;
270       multiplier = 1;
271       useExponentialNotation = false;
272       groupingUsed = false;
273       maximumFractionDigits = 0;
274       maximumIntegerDigits = 309;
275       minimumFractionDigits = 0;
276       minimumIntegerDigits = 1;
277
278       StringBuffer buf = new StringBuffer ();
279       String patChars = patternChars (syms);
280
281       int max = pattern.length();
282       int index = scanFix (pattern, 0, buf, patChars, syms, false);
283       positivePrefix = buf.toString();
284
285       index = scanFormat (pattern, index, patChars, syms, true);
286
287       index = scanFix (pattern, index, buf, patChars, syms, true);
288       positiveSuffix = buf.toString();
289
290       if (index == pattern.length())
291         {
292           // No negative info.
293           negativePrefix = null;
294           negativeSuffix = null;
295         }
296       else
297         {
298           if (pattern.charAt(index) != syms.getPatternSeparator())
299             throw new IllegalArgumentException ("separator character " +
300                                                 "expected - index: " + index);
301
302           index = scanFix (pattern, index + 1, buf, patChars, syms, false);
303           negativePrefix = buf.toString();
304
305           // We parse the negative format for errors but we don't let
306           // it side-effect this object.
307           index = scanFormat (pattern, index, patChars, syms, false);
308
309           index = scanFix (pattern, index, buf, patChars, syms, true);
310           negativeSuffix = buf.toString();
311
312           if (index != pattern.length())
313             throw new IllegalArgumentException ("end of pattern expected " +
314                                                 "- index: " + index);
315         }
316     }
317
318   public void applyLocalizedPattern (String pattern)
319     {
320       // JCL p. 638 claims this throws a ParseException but p. 629
321       // contradicts this.  Empirical tests with patterns of "0,###.0"
322       // and "#.#.#" corroborate the p. 629 statement that an
323       // IllegalArgumentException is thrown.
324       applyPatternWithSymbols (pattern, symbols);
325     }
326
327   public void applyPattern (String pattern)
328     {
329       // JCL p. 638 claims this throws a ParseException but p. 629
330       // contradicts this.  Empirical tests with patterns of "0,###.0"
331       // and "#.#.#" corroborate the p. 629 statement that an
332       // IllegalArgumentException is thrown.
333       applyPatternWithSymbols (pattern, nonLocalizedSymbols);
334     }
335
336   public Object clone ()
337     {
338       return new DecimalFormat (this);
339     }
340
341   private DecimalFormat (DecimalFormat dup)
342     {
343       decimalSeparatorAlwaysShown = dup.decimalSeparatorAlwaysShown;
344       groupingSize = dup.groupingSize;
345       minExponentDigits = dup.minExponentDigits;
346       multiplier = dup.multiplier;
347       negativePrefix = dup.negativePrefix;
348       negativeSuffix = dup.negativeSuffix;
349       positivePrefix = dup.positivePrefix;
350       positiveSuffix = dup.positiveSuffix;
351       symbols = (DecimalFormatSymbols) dup.symbols.clone();
352       useExponentialNotation = dup.useExponentialNotation;
353     }
354
355   public DecimalFormat ()
356     {
357       this ("#,##0.###");
358     }
359
360   public DecimalFormat (String pattern)
361     {
362       this (pattern, new DecimalFormatSymbols ());
363     }
364
365   public DecimalFormat (String pattern, DecimalFormatSymbols symbols)
366     {
367       this.symbols = symbols;
368       applyPattern (pattern);
369     }
370
371   private final boolean equals (String s1, String s2)
372     {
373       if (s1 == null || s2 == null)
374         return s1 == s2;
375       return s1.equals(s2);
376     }
377
378   public boolean equals (Object obj)
379     {
380       if (! (obj instanceof DecimalFormat))
381         return false;
382       DecimalFormat dup = (DecimalFormat) obj;
383       return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
384               && groupingSize == dup.groupingSize
385               && minExponentDigits == dup.minExponentDigits
386               && multiplier == dup.multiplier
387               && equals(negativePrefix, dup.negativePrefix)
388               && equals(negativeSuffix, dup.negativeSuffix)
389               && equals(positivePrefix, dup.positivePrefix)
390               && equals(positiveSuffix, dup.positiveSuffix)
391               && symbols.equals(dup.symbols)
392               && useExponentialNotation == dup.useExponentialNotation);
393     }
394
395   public StringBuffer format (double number, StringBuffer dest,
396                               FieldPosition fieldPos)
397     {
398       // A very special case.
399       if (Double.isNaN(number))
400         {
401           dest.append(symbols.getNaN());
402           if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
403             {
404               int index = dest.length();
405               fieldPos.setBeginIndex(index - symbols.getNaN().length());
406               fieldPos.setEndIndex(index);
407             }
408           return dest;
409         }
410
411       boolean is_neg = number < 0;
412       if (is_neg)
413         {
414           if (negativePrefix != null)
415             dest.append(negativePrefix);
416           else
417             {
418               dest.append(symbols.getMinusSign());
419               dest.append(positivePrefix);
420             }
421           number = - number;
422         }
423       else
424         dest.append(positivePrefix);
425
426       int integerBeginIndex = dest.length();
427       int integerEndIndex = 0;
428       if (Double.isInfinite (number))
429         {
430           dest.append(symbols.getInfinity());
431           integerEndIndex = dest.length();
432         }
433       else
434         {
435           number *= multiplier;
436
437           // Compute exponent.
438           long exponent = 0;
439           double baseNumber;
440           if (useExponentialNotation)
441             {
442               exponent = (long) (Math.log(number) / Math.log(10));
443               if (minimumIntegerDigits > 0)
444                 exponent -= minimumIntegerDigits - 1;
445               baseNumber = (long) (number / Math.pow(10.0, exponent));
446             }
447           else
448             baseNumber = number;
449
450           // Round to the correct number of digits.
451           baseNumber += 5 * Math.pow(10.0, - maximumFractionDigits - 1);
452
453           int index = dest.length();
454           double intPart = Math.floor(baseNumber);
455           int count = 0;
456           while (count < maximumIntegerDigits
457                  && (intPart > 0 || count < minimumIntegerDigits))
458             {
459               long dig = (long) (intPart % 10);
460               intPart = Math.floor(intPart / 10);
461
462               // Append group separator if required.
463               if (groupingUsed && count > 0 && count % groupingSize == 0)
464                 dest.insert(index, symbols.getGroupingSeparator());
465
466               dest.insert(index, (char) (symbols.getZeroDigit() + dig));
467
468               ++count;
469             }
470
471           integerEndIndex = dest.length();
472
473           int decimal_index = integerEndIndex;
474           int consecutive_zeros = 0;
475           int total_digits = 0;
476
477           // Strip integer part from NUMBER.
478           double fracPart = baseNumber - Math.floor(baseNumber);
479           for (count = 0;
480                count < maximumFractionDigits
481                  && (fracPart != 0 || count < minimumFractionDigits);
482                ++count)
483             {
484               ++total_digits;
485               fracPart *= 10;
486               long dig = (long) fracPart;
487               if (dig == 0)
488                 ++consecutive_zeros;
489               else
490                 consecutive_zeros = 0;
491               dest.append((char) (symbols.getZeroDigit() + dig));
492
493               // Strip integer part from FRACPART.
494               fracPart = fracPart - Math.floor (fracPart);
495             }
496
497           // Strip extraneous trailing `0's.  We can't always detect
498           // these in the loop.
499           int extra_zeros = Math.min (consecutive_zeros,
500                                       total_digits - minimumFractionDigits);
501           if (extra_zeros > 0)
502             {
503               dest.setLength(dest.length() - extra_zeros);
504               total_digits -= extra_zeros;
505             }
506
507           // If required, add the decimal symbol.
508           if (decimalSeparatorAlwaysShown
509               || total_digits > 0)
510             {
511               dest.insert(decimal_index, symbols.getDecimalSeparator());
512               if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
513                 {
514                   fieldPos.setBeginIndex(decimal_index + 1);
515                   fieldPos.setEndIndex(dest.length());
516                 }
517             }
518
519           // Finally, print the exponent.
520           if (useExponentialNotation)
521             {
522               dest.append(symbols.getExponential());
523               dest.append(exponent < 0 ? '-' : '+');
524               index = dest.length();
525               for (count = 0;
526                    exponent > 0 || count < minExponentDigits;
527                    ++count)
528                 {
529                   long dig = exponent % 10;
530                   exponent /= 10;
531                   dest.insert(index, (char) (symbols.getZeroDigit() + dig));
532                 }
533             }
534         }
535
536       if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
537         {
538           fieldPos.setBeginIndex(integerBeginIndex);
539           fieldPos.setEndIndex(integerEndIndex);
540         }
541
542       dest.append((is_neg && negativeSuffix != null)
543                   ? negativeSuffix
544                   : positiveSuffix);
545       return dest;
546     }
547
548   public StringBuffer format (long number, StringBuffer dest,
549                               FieldPosition fieldPos)
550     {
551       // If using exponential notation, we just format as a double.
552       if (useExponentialNotation)
553         return format ((double) number, dest, fieldPos);
554
555       boolean is_neg = number < 0;
556       if (is_neg)
557         {
558           if (negativePrefix != null)
559             dest.append(negativePrefix);
560           else
561             {
562               dest.append(symbols.getMinusSign());
563               dest.append(positivePrefix);
564             }
565           number = - number;
566         }
567       else
568         dest.append(positivePrefix);
569
570       int integerBeginIndex = dest.length();
571       int index = dest.length();
572       int count = 0;
573       while (count < maximumIntegerDigits
574              && (number > 0 || count < minimumIntegerDigits))
575         {
576           long dig = number % 10;
577           number /= 10;
578           // NUMBER and DIG will be less than 0 if the original number
579           // was the most negative long.
580           if (dig < 0)
581             {
582               dig = - dig;
583               number = - number;
584             }
585
586           // Append group separator if required.
587           if (groupingUsed && count > 0 && count % groupingSize == 0)
588             dest.insert(index, symbols.getGroupingSeparator());
589
590           dest.insert(index, (char) (symbols.getZeroDigit() + dig));
591
592           ++count;
593         }
594
595       if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
596         {
597           fieldPos.setBeginIndex(integerBeginIndex);
598           fieldPos.setEndIndex(dest.length());
599         }
600
601       if (decimalSeparatorAlwaysShown || minimumFractionDigits > 0)
602         {
603           dest.append(symbols.getDecimalSeparator());
604           if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
605             {
606               fieldPos.setBeginIndex(dest.length());
607               fieldPos.setEndIndex(dest.length() + minimumFractionDigits);
608             }
609         }
610
611       for (count = 0; count < minimumFractionDigits; ++count)
612         dest.append(symbols.getZeroDigit());
613
614       dest.append((is_neg && negativeSuffix != null)
615                   ? negativeSuffix
616                   : positiveSuffix);
617       return dest;
618     }
619
620   public DecimalFormatSymbols getDecimalFormatSymbols ()
621     {
622       return symbols;
623     }
624
625   public int getGroupingSize ()
626     {
627       return groupingSize;
628     }
629
630   public int getMultiplier ()
631     {
632       return multiplier;
633     }
634
635   public String getNegativePrefix ()
636     {
637       return negativePrefix;
638     }
639
640   public String getNegativeSuffix ()
641     {
642       return negativeSuffix;
643     }
644
645   public String getPositivePrefix ()
646     {
647       return positivePrefix;
648     }
649
650   public String getPositiveSuffix ()
651     {
652       return positiveSuffix;
653     }
654
655   public int hashCode ()
656     {
657       int hash = (negativeSuffix.hashCode() ^ negativePrefix.hashCode()
658                   ^positivePrefix.hashCode() ^ positiveSuffix.hashCode());
659       // FIXME.
660       return hash;
661     }
662
663   public boolean isDecimalSeparatorAlwaysShown ()
664     {
665       return decimalSeparatorAlwaysShown;
666     }
667
668   public Number parse (String str, ParsePosition pos)
669     {
670       // Our strategy is simple: copy the text into a buffer,
671       // translating or omitting locale-specific information.  Then
672       // let Double or Long convert the number for us.
673
674       boolean is_neg = false;
675       int index = pos.getIndex();
676       StringBuffer buf = new StringBuffer ();
677
678       // We have to check both prefixes, because one might be empty.
679       // We want to pick the longest prefix that matches.
680       boolean got_pos = str.startsWith(positivePrefix, index);
681       String np = (negativePrefix != null
682                    ? negativePrefix
683                    : positivePrefix + symbols.getMinusSign());
684       boolean got_neg = str.startsWith(np, index);
685
686       if (got_pos && got_neg)
687         {
688           // By checking this way, we preserve ambiguity in the case
689           // where the negative format differs only in suffix.  We
690           // check this again later.
691           if (np.length() > positivePrefix.length())
692             {
693               is_neg = true;
694               index += np.length();
695             }
696           else
697             index += positivePrefix.length();
698         }
699       else if (got_neg)
700         {
701           is_neg = true;
702           index += np.length();
703         }
704       else if (got_pos)
705         index += positivePrefix.length();
706       else
707         {
708           pos.setErrorIndex (index);
709           return null;
710         }
711
712       // FIXME: handle Inf and NaN.
713
714       // FIXME: do we have to respect minimum/maxmimum digit stuff?
715       // What about leading zeros?  What about multiplier?
716
717       int start_index = index;
718       int max = str.length();
719       char zero = symbols.getZeroDigit();
720       int last_group = -1;
721       boolean int_part = true;
722       boolean exp_part = false;
723       for (; index < max; ++index)
724         {
725           char c = str.charAt(index);
726
727           // FIXME: what about grouping size?
728           if (groupingUsed && c == symbols.getGroupingSeparator())
729             {
730               if (last_group != -1
731                   && (index - last_group) % groupingSize != 0)
732                 {
733                   pos.setErrorIndex(index);
734                   return null;
735                 }
736               last_group = index;
737             }
738           else if (c >= zero && c <= zero + 9)
739             {
740               buf.append((char) (c - zero + '0'));
741               exp_part = false;
742             }
743           else if (parseIntegerOnly)
744             break;
745           else if (c == symbols.getDecimalSeparator())
746             {
747               if (last_group != -1
748                   && (index - last_group) % groupingSize != 0)
749                 {
750                   pos.setErrorIndex(index);
751                   return null;
752                 }
753               buf.append('.');
754               int_part = false;
755             }
756           else if (c == symbols.getExponential())
757             {
758               buf.append('E');
759               int_part = false;
760               exp_part = true;
761             }
762           else if (exp_part
763                    && (c == '+' || c == '-' || c == symbols.getMinusSign()))
764             {
765               // For exponential notation.
766               buf.append(c);
767             }
768           else
769             break;
770         }
771
772       if (index == start_index)
773         {
774           // Didn't see any digits.
775           pos.setErrorIndex(index);
776           return null;
777         }
778
779       // Check the suffix.  We must do this before converting the
780       // buffer to a number to handle the case of a number which is
781       // the most negative Long.
782       boolean got_pos_suf = str.startsWith(positiveSuffix, index);
783       String ns = (negativePrefix == null ? positiveSuffix : negativeSuffix);
784       boolean got_neg_suf = str.startsWith(ns, index);
785       if (is_neg)
786         {
787           if (! got_neg_suf)
788             {
789               pos.setErrorIndex(index);
790               return null;
791             }
792         }
793       else if (got_pos && got_neg && got_neg_suf)
794         {
795           is_neg = true;
796         }
797       else if (got_pos != got_pos_suf && got_neg != got_neg_suf)
798         {
799           pos.setErrorIndex(index);
800           return null;
801         }
802
803       String suffix = is_neg ? ns : positiveSuffix;
804       if (is_neg)
805         buf.insert(0, '-');
806
807       String t = buf.toString();
808       Number result = null;
809       try
810         {
811           result = new Long (t);
812         }
813       catch (NumberFormatException x1)
814         {
815           try
816             {
817               result = new Double (t);
818             }
819           catch (NumberFormatException x2)
820             {
821             }
822         }
823       if (result == null)
824         {
825           pos.setErrorIndex(index);
826           return null;
827         }
828
829       pos.setIndex(index + suffix.length());
830
831       return result;
832     }
833
834   public void setDecimalFormatSymbols (DecimalFormatSymbols newSymbols)
835     {
836       symbols = newSymbols;
837     }
838
839   public void setDecimalSeparatorAlwaysShown (boolean newValue)
840     {
841       decimalSeparatorAlwaysShown = newValue;
842     }
843
844   public void setGroupingSize (int groupSize)
845     {
846       groupingSize = (byte) groupSize;
847     }
848
849   public void setMaximumFractionDigits (int newValue)
850     {
851       maximumFractionDigits = Math.min(newValue, 340);
852     }
853
854   public void setMaximumIntegerDigits (int newValue)
855     {
856       maximumIntegerDigits = Math.min(newValue, 309);
857     }
858
859   public void setMinimumFractionDigits (int newValue)
860     {
861       minimumFractionDigits = Math.min(newValue, 340);
862     }
863
864   public void setMinimumIntegerDigits (int newValue)
865     {
866       minimumIntegerDigits = Math.min(newValue, 309);
867     }
868
869   public void setMultiplier (int newValue)
870     {
871       multiplier = newValue;
872     }
873
874   public void setNegativePrefix (String newValue)
875     {
876       negativePrefix = newValue;
877     }
878
879   public void setNegativeSuffix (String newValue)
880     {
881       negativeSuffix = newValue;
882     }
883
884   public void setPositivePrefix (String newValue)
885     {
886       positivePrefix = newValue;
887     }
888
889   public void setPositiveSuffix (String newValue)
890     {
891       positiveSuffix = newValue;
892     }
893
894   private final void quoteFix (StringBuffer buf, String text, String patChars)
895     {
896       int len = text.length();
897       for (int index = 0; index < len; ++index)
898         {
899           char c = text.charAt(index);
900           if (patChars.indexOf(c) != -1)
901             {
902               buf.append('\'');
903               buf.append(c);
904               buf.append('\'');
905             }
906           else
907             buf.append(c);
908         }
909     }
910
911   private final String computePattern (DecimalFormatSymbols syms)
912     {
913       StringBuffer mainPattern = new StringBuffer ();
914       // We have to at least emit a zero for the minimum number of
915       // digits.  Past that we need hash marks up to the grouping
916       // separator (and one beyond).
917       int total_digits = Math.max(minimumIntegerDigits,
918                                   groupingUsed ? groupingSize + 1: 0);
919       for (int i = 0; i < total_digits - minimumIntegerDigits; ++i)
920         mainPattern.append(syms.getDigit());
921       for (int i = total_digits - minimumIntegerDigits; i < total_digits; ++i)
922         mainPattern.append(syms.getZeroDigit());
923       // Inserting the gropuing operator afterwards is easier.
924       if (groupingUsed)
925         mainPattern.insert(mainPattern.length() - groupingSize,
926                            syms.getGroupingSeparator());
927       // See if we need decimal info.
928       if (minimumFractionDigits > 0 || maximumFractionDigits > 0
929           || decimalSeparatorAlwaysShown)
930         mainPattern.append(syms.getDecimalSeparator());
931       for (int i = 0; i < minimumFractionDigits; ++i)
932         mainPattern.append(syms.getZeroDigit());
933       for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
934         mainPattern.append(syms.getDigit());
935       if (useExponentialNotation)
936         {
937           mainPattern.append(syms.getExponential());
938           for (int i = 0; i < minExponentDigits; ++i)
939             mainPattern.append(syms.getZeroDigit());
940           if (minExponentDigits == 0)
941             mainPattern.append(syms.getDigit());
942         }
943
944       String main = mainPattern.toString();
945       String patChars = patternChars (syms);
946       mainPattern.setLength(0);
947
948       quoteFix (mainPattern, positivePrefix, patChars);
949       mainPattern.append(main);
950       quoteFix (mainPattern, positiveSuffix, patChars);
951
952       if (negativePrefix != null)
953         {
954           quoteFix (mainPattern, negativePrefix, patChars);
955           mainPattern.append(main);
956           quoteFix (mainPattern, negativeSuffix, patChars);
957         }
958
959       return mainPattern.toString();
960     }
961
962   public String toLocalizedPattern ()
963     {
964       return computePattern (symbols);
965     }
966
967   public String toPattern ()
968     {
969       return computePattern (nonLocalizedSymbols);
970     }
971
972   // These names are fixed by the serialization spec.
973   private boolean decimalSeparatorAlwaysShown;
974   private byte groupingSize;
975   private byte minExponentDigits;
976   private int multiplier;
977   private String negativePrefix;
978   private String negativeSuffix;
979   private String positivePrefix;
980   private String positiveSuffix;
981   private int serialVersionOnStream = 1;
982   private DecimalFormatSymbols symbols;
983   private boolean useExponentialNotation;
984   private static final long serialVersionUID = 864413376551465018L;
985
986   private void readObject(ObjectInputStream stream)
987     throws IOException, ClassNotFoundException
988   {
989     stream.defaultReadObject();
990     if (serialVersionOnStream < 1)
991       {
992         useExponentialNotation = false;
993         serialVersionOnStream = 1;
994       }
995   }
996
997   // The locale-independent pattern symbols happen to be the same as
998   // the US symbols.
999   private static final DecimalFormatSymbols nonLocalizedSymbols
1000     = new DecimalFormatSymbols (Locale.US);
1001 }