1 /* DecimalFormat.java -- Formats and parses numbers
2 Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
40 import gnu
.java
.text
.AttributedFormatBuffer
;
41 import gnu
.java
.text
.FormatBuffer
;
42 import gnu
.java
.text
.FormatCharacterIterator
;
43 import gnu
.java
.text
.StringFormatBuffer
;
45 import java
.io
.IOException
;
46 import java
.io
.ObjectInputStream
;
47 import java
.util
.Currency
;
48 import java
.util
.HashMap
;
49 import java
.util
.Locale
;
52 * @author Tom Tromey (tromey@cygnus.com)
53 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
56 /* Written using "Java Class Libraries", 2nd edition, plus online
57 * API docs for JDK 1.2 from http://www.javasoft.com.
58 * Status: Believed complete and correct to 1.2.
59 * Note however that the docs are very unclear about how format parsing
60 * should work. No doubt there are problems here.
62 public class DecimalFormat
extends NumberFormat
64 // This is a helper for applyPatternWithSymbols. It reads a prefix
65 // or a suffix. It can cause some side-effects.
66 private int scanFix (String pattern
, int index
, FormatBuffer buf
,
67 String patChars
, DecimalFormatSymbols syms
,
70 int len
= pattern
.length();
71 boolean quoteStarted
= false;
74 boolean multiplierSet
= false;
77 char c
= pattern
.charAt(index
);
89 if (c
== '\'' && index
+ 1 < len
90 && pattern
.charAt(index
+ 1) == '\'')
99 else if (c
== '\u00a4')
101 /* Currency interpreted later */
104 else if (c
== syms
.getPercent())
107 throw new IllegalArgumentException ("multiplier already set " +
108 "- index: " + index
);
109 multiplierSet
= true;
111 buf
.append(c
, NumberFormat
.Field
.PERCENT
);
113 else if (c
== syms
.getPerMill())
116 throw new IllegalArgumentException ("multiplier already set " +
117 "- index: " + index
);
118 multiplierSet
= true;
120 buf
.append(c
, NumberFormat
.Field
.PERMILLE
);
122 else if (patChars
.indexOf(c
) != -1)
124 // This is a pattern character.
135 throw new IllegalArgumentException ("pattern is lacking a closing quote");
140 // A helper which reads a number format.
141 private int scanFormat (String pattern
, int index
, String patChars
,
142 DecimalFormatSymbols syms
, boolean is_positive
)
144 int max
= pattern
.length();
146 int countSinceGroup
= 0;
148 boolean saw_group
= false;
151 // Scan integer part.
155 char c
= pattern
.charAt(index
);
157 if (c
== syms
.getDigit())
160 throw new IllegalArgumentException ("digit mark following " +
161 "zero - index: " + index
);
164 else if (c
== syms
.getZeroDigit())
169 else if (c
== syms
.getGroupingSeparator())
180 // We can only side-effect when parsing the positive format.
183 groupingUsed
= saw_group
;
184 groupingSize
= (byte) countSinceGroup
;
185 // Checking "zeroCount > 0" avoids 0 being formatted into "" with "#".
187 minimumIntegerDigits
= zeroCount
;
190 // Early termination.
191 if (index
== max
|| pattern
.charAt(index
) == syms
.getGroupingSeparator())
194 decimalSeparatorAlwaysShown
= false;
198 if (pattern
.charAt(index
) == syms
.getDecimalSeparator())
203 // Scan fractional part.
209 char c
= pattern
.charAt(index
);
210 if (c
== syms
.getZeroDigit())
213 throw new IllegalArgumentException ("zero mark " +
214 "following digit - index: " + index
);
217 else if (c
== syms
.getDigit())
221 else if (c
!= syms
.getExponential()
222 && c
!= syms
.getPatternSeparator()
223 && c
!= syms
.getPercent()
224 && c
!= syms
.getPerMill()
225 && patChars
.indexOf(c
) != -1)
226 throw new IllegalArgumentException ("unexpected special " +
227 "character - index: " + index
);
236 maximumFractionDigits
= hashCount
+ zeroCount
;
237 minimumFractionDigits
= zeroCount
;
244 if (pattern
.charAt(index
) == syms
.getExponential())
247 // Scan exponential format.
253 char c
= pattern
.charAt(index
);
254 if (c
== syms
.getZeroDigit())
256 else if (c
== syms
.getDigit())
260 IllegalArgumentException ("digit mark following zero " +
261 "in exponent - index: " +
264 else if (patChars
.indexOf(c
) != -1)
265 throw new IllegalArgumentException ("unexpected special " +
266 "character - index: " +
276 useExponentialNotation
= true;
277 minExponentDigits
= (byte) zeroCount
;
280 maximumIntegerDigits
= groupingSize
;
282 if (maximumIntegerDigits
> minimumIntegerDigits
&& maximumIntegerDigits
> 0)
284 minimumIntegerDigits
= 1;
285 exponentRound
= maximumIntegerDigits
;
294 // This helper function creates a string consisting of all the
295 // characters which can appear in a pattern and must be quoted.
296 private String
patternChars (DecimalFormatSymbols syms
)
298 StringBuffer buf
= new StringBuffer ();
299 buf
.append(syms
.getDecimalSeparator());
300 buf
.append(syms
.getDigit());
301 buf
.append(syms
.getExponential());
302 buf
.append(syms
.getGroupingSeparator());
303 // Adding this one causes pattern application to fail.
304 // Of course, omitting is causes toPattern to fail.
305 // ... but we already have bugs there. FIXME.
306 // buf.append(syms.getMinusSign());
307 buf
.append(syms
.getPatternSeparator());
308 buf
.append(syms
.getPercent());
309 buf
.append(syms
.getPerMill());
310 buf
.append(syms
.getZeroDigit());
311 buf
.append('\u00a4');
312 return buf
.toString();
315 private void applyPatternWithSymbols(String pattern
, DecimalFormatSymbols syms
)
317 // Initialize to the state the parser expects.
322 decimalSeparatorAlwaysShown
= false;
324 minExponentDigits
= 0;
326 useExponentialNotation
= false;
327 groupingUsed
= false;
328 maximumFractionDigits
= 0;
329 maximumIntegerDigits
= MAXIMUM_INTEGER_DIGITS
;
330 minimumFractionDigits
= 0;
331 minimumIntegerDigits
= 1;
333 AttributedFormatBuffer buf
= new AttributedFormatBuffer ();
334 String patChars
= patternChars (syms
);
336 int max
= pattern
.length();
337 int index
= scanFix (pattern
, 0, buf
, patChars
, syms
, false);
339 positivePrefix
= buf
.getBuffer().toString();
340 positivePrefixRanges
= buf
.getRanges();
341 positivePrefixAttrs
= buf
.getAttributes();
343 index
= scanFormat (pattern
, index
, patChars
, syms
, true);
345 index
= scanFix (pattern
, index
, buf
, patChars
, syms
, true);
347 positiveSuffix
= buf
.getBuffer().toString();
348 positiveSuffixRanges
= buf
.getRanges();
349 positiveSuffixAttrs
= buf
.getAttributes();
351 if (index
== pattern
.length())
354 negativePrefix
= null;
355 negativeSuffix
= null;
359 if (pattern
.charAt(index
) != syms
.getPatternSeparator())
360 throw new IllegalArgumentException ("separator character " +
361 "expected - index: " + index
);
363 index
= scanFix (pattern
, index
+ 1, buf
, patChars
, syms
, false);
365 negativePrefix
= buf
.getBuffer().toString();
366 negativePrefixRanges
= buf
.getRanges();
367 negativePrefixAttrs
= buf
.getAttributes();
369 // We parse the negative format for errors but we don't let
370 // it side-effect this object.
371 index
= scanFormat (pattern
, index
, patChars
, syms
, false);
373 index
= scanFix (pattern
, index
, buf
, patChars
, syms
, true);
375 negativeSuffix
= buf
.getBuffer().toString();
376 negativeSuffixRanges
= buf
.getRanges();
377 negativeSuffixAttrs
= buf
.getAttributes();
379 if (index
!= pattern
.length())
380 throw new IllegalArgumentException ("end of pattern expected " +
381 "- index: " + index
);
385 public void applyLocalizedPattern (String pattern
)
387 // JCL p. 638 claims this throws a ParseException but p. 629
388 // contradicts this. Empirical tests with patterns of "0,###.0"
389 // and "#.#.#" corroborate the p. 629 statement that an
390 // IllegalArgumentException is thrown.
391 applyPatternWithSymbols (pattern
, symbols
);
394 public void applyPattern (String pattern
)
396 // JCL p. 638 claims this throws a ParseException but p. 629
397 // contradicts this. Empirical tests with patterns of "0,###.0"
398 // and "#.#.#" corroborate the p. 629 statement that an
399 // IllegalArgumentException is thrown.
400 applyPatternWithSymbols (pattern
, nonLocalizedSymbols
);
403 public Object
clone ()
405 DecimalFormat c
= (DecimalFormat
) super.clone ();
406 c
.symbols
= (DecimalFormatSymbols
) symbols
.clone ();
411 * Constructs a <code>DecimalFormat</code> which uses the default
412 * pattern and symbols.
414 public DecimalFormat ()
420 * Constructs a <code>DecimalFormat</code> which uses the given
421 * pattern and the default symbols for formatting and parsing.
423 * @param pattern the non-localized pattern to use.
424 * @throws NullPointerException if any argument is null.
425 * @throws IllegalArgumentException if the pattern is invalid.
427 public DecimalFormat (String pattern
)
429 this (pattern
, new DecimalFormatSymbols ());
433 * Constructs a <code>DecimalFormat</code> using the given pattern
434 * and formatting symbols. This construction method is used to give
435 * complete control over the formatting process.
437 * @param pattern the non-localized pattern to use.
438 * @param symbols the set of symbols used for parsing and formatting.
439 * @throws NullPointerException if any argument is null.
440 * @throws IllegalArgumentException if the pattern is invalid.
442 public DecimalFormat(String pattern
, DecimalFormatSymbols symbols
)
444 this.symbols
= (DecimalFormatSymbols
) symbols
.clone();
445 applyPattern(pattern
);
448 private boolean equals(String s1
, String s2
)
450 if (s1
== null || s2
== null)
452 return s1
.equals(s2
);
456 * Tests this instance for equality with an arbitrary object. This method
457 * returns <code>true</code> if:
459 * <li><code>obj</code> is not <code>null</code>;</li>
460 * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
461 * <li>this instance and <code>obj</code> have the same attributes;</li>
464 * @param obj the object (<code>null</code> permitted).
468 public boolean equals(Object obj
)
470 if (! (obj
instanceof DecimalFormat
))
472 DecimalFormat dup
= (DecimalFormat
) obj
;
473 return (decimalSeparatorAlwaysShown
== dup
.decimalSeparatorAlwaysShown
474 && groupingUsed
== dup
.groupingUsed
475 && groupingSize
== dup
.groupingSize
476 && multiplier
== dup
.multiplier
477 && useExponentialNotation
== dup
.useExponentialNotation
478 && minExponentDigits
== dup
.minExponentDigits
479 && minimumIntegerDigits
== dup
.minimumIntegerDigits
480 && maximumIntegerDigits
== dup
.maximumIntegerDigits
481 && minimumFractionDigits
== dup
.minimumFractionDigits
482 && maximumFractionDigits
== dup
.maximumFractionDigits
483 && equals(negativePrefix
, dup
.negativePrefix
)
484 && equals(negativeSuffix
, dup
.negativeSuffix
)
485 && equals(positivePrefix
, dup
.positivePrefix
)
486 && equals(positiveSuffix
, dup
.positiveSuffix
)
487 && symbols
.equals(dup
.symbols
));
490 private void formatInternal (double number
, FormatBuffer dest
,
491 FieldPosition fieldPos
)
493 // A very special case.
494 if (Double
.isNaN(number
))
496 dest
.append(symbols
.getNaN());
497 if (fieldPos
!= null &&
498 (fieldPos
.getField() == INTEGER_FIELD
||
499 fieldPos
.getFieldAttribute() == NumberFormat
.Field
.INTEGER
))
501 int index
= dest
.length();
502 fieldPos
.setBeginIndex(index
- symbols
.getNaN().length());
503 fieldPos
.setEndIndex(index
);
508 boolean is_neg
= number
< 0;
511 if (negativePrefix
!= null)
513 dest
.append(substituteCurrency(negativePrefix
, number
),
514 negativePrefixRanges
, negativePrefixAttrs
);
518 dest
.append(symbols
.getMinusSign(), NumberFormat
.Field
.SIGN
);
519 dest
.append(substituteCurrency(positivePrefix
, number
),
520 positivePrefixRanges
, positivePrefixAttrs
);
526 dest
.append(substituteCurrency(positivePrefix
, number
),
527 positivePrefixRanges
, positivePrefixAttrs
);
529 int integerBeginIndex
= dest
.length();
530 int integerEndIndex
= 0;
531 int zeroStart
= symbols
.getZeroDigit() - '0';
533 if (Double
.isInfinite (number
))
535 dest
.append(symbols
.getInfinity());
536 integerEndIndex
= dest
.length();
540 number
*= multiplier
;
545 if (useExponentialNotation
)
547 exponent
= (long) Math
.floor (Math
.log(number
) / Math
.log(10));
548 exponent
= exponent
- (exponent
% exponentRound
);
549 if (minimumIntegerDigits
> 0)
550 exponent
-= minimumIntegerDigits
- 1;
551 baseNumber
= (number
/ Math
.pow(10.0, exponent
));
556 // Round to the correct number of digits.
557 baseNumber
+= 5 * Math
.pow(10.0, - maximumFractionDigits
- 1);
559 int index
= dest
.length();
560 //double intPart = Math.floor(baseNumber);
561 String intPart
= Long
.toString((long)Math
.floor(baseNumber
));
562 int count
, groupPosition
= intPart
.length();
564 dest
.setDefaultAttribute(NumberFormat
.Field
.INTEGER
);
566 for (count
= 0; count
< minimumIntegerDigits
-intPart
.length(); count
++)
567 dest
.append(symbols
.getZeroDigit());
570 count
< maximumIntegerDigits
&& count
< intPart
.length();
573 int dig
= intPart
.charAt(count
);
575 // Append group separator if required.
576 if (groupingUsed
&& count
> 0 && groupingSize
!= 0 && groupPosition
% groupingSize
== 0)
578 dest
.append(symbols
.getGroupingSeparator(), NumberFormat
.Field
.GROUPING_SEPARATOR
);
579 dest
.setDefaultAttribute(NumberFormat
.Field
.INTEGER
);
581 dest
.append((char) (zeroStart
+ dig
));
585 dest
.setDefaultAttribute(null);
587 integerEndIndex
= dest
.length();
589 int decimal_index
= integerEndIndex
;
590 int consecutive_zeros
= 0;
591 int total_digits
= 0;
593 int localMaximumFractionDigits
= maximumFractionDigits
;
595 if (useExponentialNotation
)
596 localMaximumFractionDigits
+= minimumIntegerDigits
- count
;
598 // Strip integer part from NUMBER.
599 double fracPart
= baseNumber
- Math
.floor(baseNumber
);
601 if ( ((fracPart
!= 0 || minimumFractionDigits
> 0) && localMaximumFractionDigits
> 0)
602 || decimalSeparatorAlwaysShown
)
604 dest
.append (symbols
.getDecimalSeparator(), NumberFormat
.Field
.DECIMAL_SEPARATOR
);
607 int fraction_begin
= dest
.length();
608 dest
.setDefaultAttribute(NumberFormat
.Field
.FRACTION
);
610 count
< localMaximumFractionDigits
611 && (fracPart
!= 0 || count
< minimumFractionDigits
);
616 long dig
= (long) fracPart
;
620 consecutive_zeros
= 0;
621 dest
.append((char) (symbols
.getZeroDigit() + dig
));
623 // Strip integer part from FRACPART.
624 fracPart
= fracPart
- Math
.floor (fracPart
);
627 // Strip extraneous trailing `0's. We can't always detect
628 // these in the loop.
629 int extra_zeros
= Math
.min (consecutive_zeros
,
630 total_digits
- minimumFractionDigits
);
633 dest
.cutTail(extra_zeros
);
634 total_digits
-= extra_zeros
;
635 if (total_digits
== 0 && !decimalSeparatorAlwaysShown
)
639 if (fieldPos
!= null && fieldPos
.getField() == FRACTION_FIELD
)
641 fieldPos
.setBeginIndex(fraction_begin
);
642 fieldPos
.setEndIndex(dest
.length());
645 // Finally, print the exponent.
646 if (useExponentialNotation
)
648 dest
.append(symbols
.getExponential(), NumberFormat
.Field
.EXPONENT_SYMBOL
);
651 dest
.append (symbols
.getMinusSign (), NumberFormat
.Field
.EXPONENT_SIGN
);
652 exponent
= - exponent
;
654 index
= dest
.length();
655 dest
.setDefaultAttribute(NumberFormat
.Field
.EXPONENT
);
656 String exponentString
= Long
.toString ((long) exponent
);
658 for (count
= 0; count
< minExponentDigits
-exponentString
.length();
660 dest
.append((char) symbols
.getZeroDigit());
663 count
< exponentString
.length();
666 int dig
= exponentString
.charAt(count
);
667 dest
.append((char) (zeroStart
+ dig
));
672 if (fieldPos
!= null &&
673 (fieldPos
.getField() == INTEGER_FIELD
||
674 fieldPos
.getFieldAttribute() == NumberFormat
.Field
.INTEGER
))
676 fieldPos
.setBeginIndex(integerBeginIndex
);
677 fieldPos
.setEndIndex(integerEndIndex
);
680 if (is_neg
&& negativeSuffix
!= null)
682 dest
.append(substituteCurrency(negativeSuffix
, number
),
683 negativeSuffixRanges
, negativeSuffixAttrs
);
687 dest
.append(substituteCurrency(positiveSuffix
, number
),
688 positiveSuffixRanges
, positiveSuffixAttrs
);
692 public StringBuffer
format (double number
, StringBuffer dest
,
693 FieldPosition fieldPos
)
695 formatInternal (number
, new StringFormatBuffer(dest
), fieldPos
);
699 public AttributedCharacterIterator
formatToCharacterIterator (Object value
)
701 AttributedFormatBuffer sbuf
= new AttributedFormatBuffer();
703 if (value
instanceof Number
)
704 formatInternal(((Number
) value
).doubleValue(), sbuf
, null);
706 throw new IllegalArgumentException
707 ("Cannot format given Object as a Number");
710 return new FormatCharacterIterator(sbuf
.getBuffer().toString(),
712 sbuf
.getAttributes());
715 public StringBuffer
format (long number
, StringBuffer dest
,
716 FieldPosition fieldPos
)
718 // If using exponential notation, we just format as a double.
719 if (useExponentialNotation
)
720 return format ((double) number
, dest
, fieldPos
);
722 boolean is_neg
= number
< 0;
725 if (negativePrefix
!= null)
726 dest
.append(substituteCurrency(negativePrefix
, number
));
729 dest
.append(symbols
.getMinusSign());
730 dest
.append(substituteCurrency(positivePrefix
, number
));
735 dest
.append(substituteCurrency(positivePrefix
, number
));
737 int integerBeginIndex
= dest
.length();
738 int index
= dest
.length();
741 /* Handle percentages, etc. */
742 number
*= multiplier
;
743 while (count
< maximumIntegerDigits
744 && (number
> 0 || count
< minimumIntegerDigits
))
746 long dig
= number
% 10;
748 // NUMBER and DIG will be less than 0 if the original number
749 // was the most negative long.
756 // Append group separator if required.
757 if (groupingUsed
&& count
> 0 && groupingSize
!= 0 && count
% groupingSize
== 0)
758 dest
.insert(index
, symbols
.getGroupingSeparator());
760 dest
.insert(index
, (char) (symbols
.getZeroDigit() + dig
));
765 if (fieldPos
!= null && fieldPos
.getField() == INTEGER_FIELD
)
767 fieldPos
.setBeginIndex(integerBeginIndex
);
768 fieldPos
.setEndIndex(dest
.length());
771 if (decimalSeparatorAlwaysShown
|| minimumFractionDigits
> 0)
773 dest
.append(symbols
.getDecimalSeparator());
774 if (fieldPos
!= null && fieldPos
.getField() == FRACTION_FIELD
)
776 fieldPos
.setBeginIndex(dest
.length());
777 fieldPos
.setEndIndex(dest
.length() + minimumFractionDigits
);
781 for (count
= 0; count
< minimumFractionDigits
; ++count
)
782 dest
.append(symbols
.getZeroDigit());
784 dest
.append((is_neg
&& negativeSuffix
!= null)
785 ?
substituteCurrency(negativeSuffix
, number
)
786 : substituteCurrency(positiveSuffix
, number
));
791 * Returns the currency corresponding to the currency symbol stored
792 * in the instance of <code>DecimalFormatSymbols</code> used by this
793 * <code>DecimalFormat</code>.
795 * @return A new instance of <code>Currency</code> if
796 * the currency code matches a known one, null otherwise.
798 public Currency
getCurrency()
800 return symbols
.getCurrency();
804 * Returns a copy of the symbols used by this instance.
806 * @return A copy of the symbols.
808 public DecimalFormatSymbols
getDecimalFormatSymbols()
810 return (DecimalFormatSymbols
) symbols
.clone();
813 public int getGroupingSize ()
818 public int getMultiplier ()
823 public String
getNegativePrefix ()
825 return negativePrefix
;
828 public String
getNegativeSuffix ()
830 return negativeSuffix
;
833 public String
getPositivePrefix ()
835 return positivePrefix
;
838 public String
getPositiveSuffix ()
840 return positiveSuffix
;
844 * Returns a hash code for this object.
846 * @return A hash code.
848 public int hashCode()
850 return toPattern().hashCode();
853 public boolean isDecimalSeparatorAlwaysShown ()
855 return decimalSeparatorAlwaysShown
;
858 public Number
parse (String str
, ParsePosition pos
)
861 * Our strategy is simple: copy the text into separate buffers: one for the int part,
862 * one for the fraction part and for the exponential part.
863 * We translate or omit locale-specific information.
864 * If exponential is sufficiently big we merge the fraction and int part and
865 * remove the '.' and then we use Long to convert the number. In the other
866 * case, we use Double to convert the full number.
869 boolean is_neg
= false;
870 int index
= pos
.getIndex();
871 StringBuffer int_buf
= new StringBuffer ();
873 // We have to check both prefixes, because one might be empty. We
874 // want to pick the longest prefix that matches.
875 boolean got_pos
= str
.startsWith(positivePrefix
, index
);
876 String np
= (negativePrefix
!= null
878 : positivePrefix
+ symbols
.getMinusSign());
879 boolean got_neg
= str
.startsWith(np
, index
);
881 if (got_pos
&& got_neg
)
883 // By checking this way, we preserve ambiguity in the case
884 // where the negative format differs only in suffix. We
885 // check this again later.
886 if (np
.length() > positivePrefix
.length())
889 index
+= np
.length();
892 index
+= positivePrefix
.length();
897 index
+= np
.length();
900 index
+= positivePrefix
.length();
903 pos
.setErrorIndex (index
);
907 // FIXME: handle Inf and NaN.
909 // FIXME: do we have to respect minimum digits?
910 // What about multiplier?
912 StringBuffer buf
= int_buf
;
913 StringBuffer frac_buf
= null;
914 StringBuffer exp_buf
= null;
915 int start_index
= index
;
916 int max
= str
.length();
918 int last
= index
+ maximumIntegerDigits
;
920 if (maximumFractionDigits
> 0)
921 last
+= maximumFractionDigits
+ 1;
923 if (useExponentialNotation
)
924 last
+= minExponentDigits
+ 1;
926 if (last
> 0 && max
> last
)
929 char zero
= symbols
.getZeroDigit();
931 boolean int_part
= true;
932 boolean exp_part
= false;
933 for (; index
< max
; ++index
)
935 char c
= str
.charAt(index
);
937 // FIXME: what about grouping size?
938 if (groupingUsed
&& c
== symbols
.getGroupingSeparator())
942 && (index
- last_group
) % groupingSize
!= 0)
944 pos
.setErrorIndex(index
);
947 last_group
= index
+1;
949 else if (c
>= zero
&& c
<= zero
+ 9)
951 buf
.append((char) (c
- zero
+ '0'));
953 else if (parseIntegerOnly
)
955 else if (c
== symbols
.getDecimalSeparator())
959 && (index
- last_group
) % groupingSize
!= 0)
961 pos
.setErrorIndex(index
);
964 buf
= frac_buf
= new StringBuffer();
965 frac_buf
.append('.');
968 else if (c
== symbols
.getExponential())
970 buf
= exp_buf
= new StringBuffer();
976 && (c
== '+' || c
== '-' || c
== symbols
.getMinusSign()))
978 // For exponential notation.
985 if (index
== start_index
)
987 // Didn't see any digits.
988 pos
.setErrorIndex(index
);
992 // Check the suffix. We must do this before converting the
993 // buffer to a number to handle the case of a number which is
994 // the most negative Long.
995 boolean got_pos_suf
= str
.startsWith(positiveSuffix
, index
);
996 String ns
= (negativePrefix
== null ? positiveSuffix
: negativeSuffix
);
997 boolean got_neg_suf
= str
.startsWith(ns
, index
);
1002 pos
.setErrorIndex(index
);
1006 else if (got_pos
&& got_neg
&& got_neg_suf
)
1010 else if (got_pos
!= got_pos_suf
&& got_neg
!= got_neg_suf
)
1012 pos
.setErrorIndex(index
);
1015 else if (! got_pos_suf
)
1017 pos
.setErrorIndex(index
);
1021 String suffix
= is_neg ? ns
: positiveSuffix
;
1022 long parsedMultiplier
= 1;
1026 int_buf
.insert(0, '-');
1028 // Now handle the exponential part if there is one.
1029 if (exp_buf
!= null)
1035 exponent_value
= Integer
.parseInt(exp_buf
.toString());
1037 catch (NumberFormatException x1
)
1039 pos
.setErrorIndex(exp_index
);
1043 if (frac_buf
== null)
1045 // We only have to add some zeros to the int part.
1046 // Build a multiplier.
1047 for (int i
= 0; i
< exponent_value
; i
++)
1048 int_buf
.append('0');
1054 boolean long_sufficient
;
1056 if (exponent_value
< frac_buf
.length()-1)
1058 int lastNonNull
= -1;
1059 /* We have to check the fraction buffer: it may only be full of '0'
1060 * or be sufficiently filled with it to convert the number into Long.
1062 for (int i
= 1; i
< frac_buf
.length(); i
++)
1063 if (frac_buf
.charAt(i
) != '0')
1066 long_sufficient
= (lastNonNull
< 0 || lastNonNull
<= exponent_value
);
1069 long_sufficient
= true;
1071 if (long_sufficient
)
1073 for (int i
= 1; i
< frac_buf
.length() && i
< exponent_value
; i
++)
1074 int_buf
.append(frac_buf
.charAt(i
));
1075 for (int i
= frac_buf
.length()-1; i
< exponent_value
; i
++)
1076 int_buf
.append('0');
1082 * A long type is not sufficient, we build the full buffer to
1083 * be parsed by Double.
1085 int_buf
.append(frac_buf
);
1086 int_buf
.append('E');
1087 int_buf
.append(exp_buf
);
1094 if (frac_buf
!= null)
1096 /* Check whether the fraction buffer contains only '0' */
1098 for (i
= 1; i
< frac_buf
.length(); i
++)
1099 if (frac_buf
.charAt(i
) != '0')
1102 if (i
!= frac_buf
.length())
1105 int_buf
.append(frac_buf
);
1114 String t
= int_buf
.toString();
1115 Number result
= null;
1120 result
= new Long (t
);
1122 catch (NumberFormatException x1
)
1130 result
= new Double (t
);
1132 catch (NumberFormatException x2
)
1138 pos
.setErrorIndex(index
);
1142 pos
.setIndex(index
+ suffix
.length());
1148 * Sets the <code>Currency</code> on the
1149 * <code>DecimalFormatSymbols</code> used, which also sets the
1150 * currency symbols on those symbols.
1152 public void setCurrency(Currency currency
)
1154 symbols
.setCurrency(currency
);
1158 * Sets the symbols used by this instance. This method makes a copy of
1159 * the supplied symbols.
1161 * @param newSymbols the symbols (<code>null</code> not permitted).
1163 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols
)
1165 symbols
= (DecimalFormatSymbols
) newSymbols
.clone();
1168 public void setDecimalSeparatorAlwaysShown (boolean newValue
)
1170 decimalSeparatorAlwaysShown
= newValue
;
1173 public void setGroupingSize (int groupSize
)
1175 groupingSize
= (byte) groupSize
;
1178 public void setMaximumFractionDigits (int newValue
)
1180 super.setMaximumFractionDigits(Math
.min(newValue
, 340));
1183 public void setMaximumIntegerDigits (int newValue
)
1185 super.setMaximumIntegerDigits(Math
.min(newValue
, 309));
1188 public void setMinimumFractionDigits (int newValue
)
1190 super.setMinimumFractionDigits(Math
.min(newValue
, 340));
1193 public void setMinimumIntegerDigits (int newValue
)
1195 super.setMinimumIntegerDigits(Math
.min(newValue
, 309));
1198 public void setMultiplier (int newValue
)
1200 multiplier
= newValue
;
1203 public void setNegativePrefix (String newValue
)
1205 negativePrefix
= newValue
;
1208 public void setNegativeSuffix (String newValue
)
1210 negativeSuffix
= newValue
;
1213 public void setPositivePrefix (String newValue
)
1215 positivePrefix
= newValue
;
1218 public void setPositiveSuffix (String newValue
)
1220 positiveSuffix
= newValue
;
1223 private void quoteFix(StringBuffer buf
, String text
, String patChars
)
1225 int len
= text
.length();
1226 for (int index
= 0; index
< len
; ++index
)
1228 char c
= text
.charAt(index
);
1229 if (patChars
.indexOf(c
) != -1)
1240 private String
computePattern(DecimalFormatSymbols syms
)
1242 StringBuffer mainPattern
= new StringBuffer ();
1243 // We have to at least emit a zero for the minimum number of
1244 // digits. Past that we need hash marks up to the grouping
1245 // separator (and one beyond).
1246 int total_digits
= Math
.max(minimumIntegerDigits
,
1247 groupingUsed ? groupingSize
+ 1: groupingSize
);
1248 for (int i
= 0; i
< total_digits
- minimumIntegerDigits
; ++i
)
1249 mainPattern
.append(syms
.getDigit());
1250 for (int i
= total_digits
- minimumIntegerDigits
; i
< total_digits
; ++i
)
1251 mainPattern
.append(syms
.getZeroDigit());
1252 // Inserting the gropuing operator afterwards is easier.
1254 mainPattern
.insert(mainPattern
.length() - groupingSize
,
1255 syms
.getGroupingSeparator());
1256 // See if we need decimal info.
1257 if (minimumFractionDigits
> 0 || maximumFractionDigits
> 0
1258 || decimalSeparatorAlwaysShown
)
1259 mainPattern
.append(syms
.getDecimalSeparator());
1260 for (int i
= 0; i
< minimumFractionDigits
; ++i
)
1261 mainPattern
.append(syms
.getZeroDigit());
1262 for (int i
= minimumFractionDigits
; i
< maximumFractionDigits
; ++i
)
1263 mainPattern
.append(syms
.getDigit());
1264 if (useExponentialNotation
)
1266 mainPattern
.append(syms
.getExponential());
1267 for (int i
= 0; i
< minExponentDigits
; ++i
)
1268 mainPattern
.append(syms
.getZeroDigit());
1269 if (minExponentDigits
== 0)
1270 mainPattern
.append(syms
.getDigit());
1273 String main
= mainPattern
.toString();
1274 String patChars
= patternChars (syms
);
1275 mainPattern
.setLength(0);
1277 quoteFix (mainPattern
, positivePrefix
, patChars
);
1278 mainPattern
.append(main
);
1279 quoteFix (mainPattern
, positiveSuffix
, patChars
);
1281 if (negativePrefix
!= null)
1283 quoteFix (mainPattern
, negativePrefix
, patChars
);
1284 mainPattern
.append(main
);
1285 quoteFix (mainPattern
, negativeSuffix
, patChars
);
1288 return mainPattern
.toString();
1291 public String
toLocalizedPattern ()
1293 return computePattern (symbols
);
1296 public String
toPattern ()
1298 return computePattern (nonLocalizedSymbols
);
1301 private static final int MAXIMUM_INTEGER_DIGITS
= 309;
1303 // These names are fixed by the serialization spec.
1304 private boolean decimalSeparatorAlwaysShown
;
1305 private byte groupingSize
;
1306 private byte minExponentDigits
;
1307 private int exponentRound
;
1308 private int multiplier
;
1309 private String negativePrefix
;
1310 private String negativeSuffix
;
1311 private String positivePrefix
;
1312 private String positiveSuffix
;
1313 private int[] negativePrefixRanges
, positivePrefixRanges
;
1314 private HashMap
[] negativePrefixAttrs
, positivePrefixAttrs
;
1315 private int[] negativeSuffixRanges
, positiveSuffixRanges
;
1316 private HashMap
[] negativeSuffixAttrs
, positiveSuffixAttrs
;
1317 private int serialVersionOnStream
= 1;
1318 private DecimalFormatSymbols symbols
;
1319 private boolean useExponentialNotation
;
1320 private static final long serialVersionUID
= 864413376551465018L;
1322 private void readObject(ObjectInputStream stream
)
1323 throws IOException
, ClassNotFoundException
1325 stream
.defaultReadObject();
1326 if (serialVersionOnStream
< 1)
1328 useExponentialNotation
= false;
1329 serialVersionOnStream
= 1;
1333 // The locale-independent pattern symbols happen to be the same as
1335 private static final DecimalFormatSymbols nonLocalizedSymbols
1336 = new DecimalFormatSymbols (Locale
.US
);
1340 * Substitutes the currency symbol into the given string,
1341 * based on the value used. Currency symbols can either
1342 * be a simple series of characters (e.g. '$'), which are
1343 * simply used as is, or they can be of a more complex
1347 * (lower bound)|(mid value)|(upper bound)
1350 * where each bound has the syntax '(value)(# or <)(symbol)',
1351 * to indicate the bounding value and the symbol used.
1354 * The currency symbol replaces the currency specifier, '\u00a4',
1355 * an unlocalised character, which thus is used as such in all formats.
1356 * If this symbol occurs twice, the international currency code is used
1360 * @param string The string containing the currency specifier, '\u00a4'.
1361 * @param number the number being formatted.
1362 * @return a string formatted for the correct currency.
1364 private String
substituteCurrency(String string
, double number
)
1372 length
= string
.length();
1373 buf
= new StringBuffer();
1375 while (index
< length
)
1377 currentChar
= string
.charAt(index
);
1378 if (string
.charAt(index
) == '\u00a4')
1380 if ((index
+ 1) < length
&& string
.charAt(index
+ 1) == '\u00a4')
1382 buf
.append(symbols
.getInternationalCurrencySymbol());
1389 symbol
= symbols
.getCurrencySymbol();
1390 if (symbol
.startsWith("="))
1394 String
[] boundSymbols
;
1396 bounds
= symbol
.substring(1).split("\\|");
1397 boundValues
= new int[3];
1398 boundSymbols
= new String
[3];
1399 for (int a
= 0; a
< 3; ++a
)
1403 bound
= bounds
[a
].split("[#<]");
1404 boundValues
[a
] = Integer
.parseInt(bound
[0]);
1405 boundSymbols
[a
] = bound
[1];
1407 if (number
<= boundValues
[0])
1409 buf
.append(boundSymbols
[0]);
1411 else if (number
>= boundValues
[2])
1413 buf
.append(boundSymbols
[2]);
1417 buf
.append(boundSymbols
[1]);
1430 buf
.append(string
.charAt(index
));
1434 return buf
.toString();