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