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. */
39 * This class contains few bits from ICU4J (http://icu.sourceforge.net/),
40 * Copyright by IBM and others and distributed under the
41 * distributed under MIT/X.
46 import java.math.BigDecimal;
47 import java.math.BigInteger;
49 import java.util.ArrayList;
50 import java.util.Currency;
51 import java.util.Locale;
54 * This note is here for historical reasons and because I had not the courage
57 * @author Tom Tromey (tromey@cygnus.com)
58 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
61 * Written using "Java Class Libraries", 2nd edition, plus online
62 * API docs for JDK 1.2 from http://www.javasoft.com.
63 * Status: Believed complete and correct to 1.2.
64 * Note however that the docs are very unclear about how format parsing
65 * should work. No doubt there are problems here.
69 * This class is a concrete implementation of NumberFormat used to format
70 * decimal numbers. The class can format numbers given a specific locale.
71 * Generally, to get an instance of DecimalFormat you should call the factory
72 * methods in the <code>NumberFormat</code> base class.
74 * @author Mario Torre <neugens@limasoftware.net>
75 * @author Tom Tromey (tromey@cygnus.com)
76 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
78 public class DecimalFormat extends NumberFormat
80 /** serialVersionUID for serializartion. */
81 private static final long serialVersionUID = 864413376551465018L;
83 /** Defines the default number of digits allowed while formatting integers. */
84 private static final int DEFAULT_INTEGER_DIGITS = 309;
87 * Defines the default number of digits allowed while formatting
90 private static final int DEFAULT_FRACTION_DIGITS = 340;
93 * Locale-independent pattern symbols.
95 // Happen to be the same as the US symbols.
96 private static final DecimalFormatSymbols nonLocalizedSymbols
97 = new DecimalFormatSymbols (Locale.US);
100 * Defines if parse should return a BigDecimal or not.
102 private boolean parseBigDecimal;
105 * Defines if we have to use the monetary decimal separator or
106 * the decimal separator while formatting numbers.
108 private boolean useCurrencySeparator;
110 /** Defines if the decimal separator is always shown or not. */
111 private boolean decimalSeparatorAlwaysShown;
114 * Defines if the decimal separator has to be shown.
116 * This is different then <code>decimalSeparatorAlwaysShown</code>,
117 * as it defines if the format string contains a decimal separator or no.
119 private boolean showDecimalSeparator;
122 * This field is used to determine if the grouping
123 * separator is included in the format string or not.
124 * This is only needed to match the behaviour of the RI.
126 private boolean groupingSeparatorInPattern;
128 /** Defines the size of grouping groups when grouping is used. */
129 private byte groupingSize;
132 * This is an internal parameter used to keep track of the number
133 * of digits the form the exponent, when exponential notation is used.
134 * It is used with <code>exponentRound</code>
136 private byte minExponentDigits;
138 /** This field is used to set the exponent in the engineering notation. */
139 private int exponentRound;
141 /** Multiplier used in percent style formats. */
142 private int multiplier;
144 /** Multiplier used in percent style formats. */
145 private int negativePatternMultiplier;
147 /** The negative prefix. */
148 private String negativePrefix;
150 /** The negative suffix. */
151 private String negativeSuffix;
153 /** The positive prefix. */
154 private String positivePrefix;
156 /** The positive suffix. */
157 private String positiveSuffix;
159 /** Decimal Format Symbols for the given locale. */
160 private DecimalFormatSymbols symbols;
162 /** Determine if we have to use exponential notation or not. */
163 private boolean useExponentialNotation;
166 * Defines the maximum number of integer digits to show when we use
167 * the exponential notation.
169 private int maxIntegerDigitsExponent;
171 /** Defines if the format string has a negative prefix or not. */
172 private boolean hasNegativePrefix;
174 /** Defines if the format string has a fractional pattern or not. */
175 private boolean hasFractionalPattern;
177 /** Stores a list of attributes for use by formatToCharacterIterator. */
178 private ArrayList attributes = new ArrayList();
181 * Constructs a <code>DecimalFormat</code> which uses the default
182 * pattern and symbols.
184 public DecimalFormat()
190 * Constructs a <code>DecimalFormat</code> which uses the given
191 * pattern and the default symbols for formatting and parsing.
193 * @param pattern the non-localized pattern to use.
194 * @throws NullPointerException if any argument is null.
195 * @throws IllegalArgumentException if the pattern is invalid.
197 public DecimalFormat(String pattern)
199 this (pattern, new DecimalFormatSymbols());
203 * Constructs a <code>DecimalFormat</code> using the given pattern
204 * and formatting symbols. This construction method is used to give
205 * complete control over the formatting process.
207 * @param pattern the non-localized pattern to use.
208 * @param symbols the set of symbols used for parsing and formatting.
209 * @throws NullPointerException if any argument is null.
210 * @throws IllegalArgumentException if the pattern is invalid.
212 public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
214 this.symbols = (DecimalFormatSymbols) symbols.clone();
215 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
219 * Apply the given localized patern to the current DecimalFormat object.
221 * @param pattern The localized pattern to apply.
222 * @throws IllegalArgumentException if the given pattern is invalid.
223 * @throws NullPointerException if the input pattern is null.
225 public void applyLocalizedPattern (String pattern)
227 applyPatternWithSymbols(pattern, this.symbols);
231 * Apply the given localized pattern to the current DecimalFormat object.
233 * @param pattern The localized pattern to apply.
234 * @throws IllegalArgumentException if the given pattern is invalid.
235 * @throws NullPointerException if the input pattern is null.
237 public void applyPattern(String pattern)
239 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
242 public Object clone()
244 DecimalFormat c = (DecimalFormat) super.clone();
245 c.symbols = (DecimalFormatSymbols) symbols.clone();
250 * Tests this instance for equality with an arbitrary object. This method
251 * returns <code>true</code> if:
253 * <li><code>obj</code> is not <code>null</code>;</li>
254 * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
255 * <li>this instance and <code>obj</code> have the same attributes;</li>
258 * @param obj the object (<code>null</code> permitted).
262 public boolean equals(Object obj)
264 if (! (obj instanceof DecimalFormat))
266 DecimalFormat dup = (DecimalFormat) obj;
267 return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
268 && groupingUsed == dup.groupingUsed
269 && groupingSeparatorInPattern == dup.groupingSeparatorInPattern
270 && groupingSize == dup.groupingSize
271 && multiplier == dup.multiplier
272 && useExponentialNotation == dup.useExponentialNotation
273 && minExponentDigits == dup.minExponentDigits
274 && minimumIntegerDigits == dup.minimumIntegerDigits
275 && maximumIntegerDigits == dup.maximumIntegerDigits
276 && minimumFractionDigits == dup.minimumFractionDigits
277 && maximumFractionDigits == dup.maximumFractionDigits
278 && parseBigDecimal == dup.parseBigDecimal
279 && useCurrencySeparator == dup.useCurrencySeparator
280 && showDecimalSeparator == dup.showDecimalSeparator
281 && exponentRound == dup.exponentRound
282 && negativePatternMultiplier == dup.negativePatternMultiplier
283 && maxIntegerDigitsExponent == dup.maxIntegerDigitsExponent
284 // XXX: causes equivalent patterns to fail
285 // && hasNegativePrefix == dup.hasNegativePrefix
286 && equals(negativePrefix, dup.negativePrefix)
287 && equals(negativeSuffix, dup.negativeSuffix)
288 && equals(positivePrefix, dup.positivePrefix)
289 && equals(positiveSuffix, dup.positiveSuffix)
290 && symbols.equals(dup.symbols));
294 * Returns a hash code for this object.
296 * @return A hash code.
298 public int hashCode()
300 return toPattern().hashCode();
304 * Produce a formatted {@link String} representation of this object.
305 * The passed object must be of type number.
307 * @param obj The {@link Number} to format.
308 * @param sbuf The destination String; text will be appended to this String.
309 * @param pos If used on input can be used to define an alignment
310 * field. If used on output defines the offsets of the alignment field.
311 * @return The String representation of this long.
313 public StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
315 if (obj instanceof BigInteger)
317 BigDecimal decimal = new BigDecimal((BigInteger) obj);
318 formatInternal(decimal, true, sbuf, pos);
321 else if (obj instanceof BigDecimal)
323 formatInternal((BigDecimal) obj, true, sbuf, pos);
327 return super.format(obj, sbuf, pos);
331 * Produce a formatted {@link String} representation of this double.
333 * @param number The double to format.
334 * @param dest The destination String; text will be appended to this String.
335 * @param fieldPos If used on input can be used to define an alignment
336 * field. If used on output defines the offsets of the alignment field.
337 * @return The String representation of this long.
338 * @throws NullPointerException if <code>dest</code> or fieldPos are null
340 public StringBuffer format(double number, StringBuffer dest,
341 FieldPosition fieldPos)
343 // special cases for double: NaN and negative or positive infinity
344 if (Double.isNaN(number))
347 String nan = symbols.getNaN();
350 // update field position if required
351 if ((fieldPos.getField() == INTEGER_FIELD ||
352 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
354 int index = dest.length();
355 fieldPos.setBeginIndex(index - nan.length());
356 fieldPos.setEndIndex(index);
359 else if (Double.isInfinite(number))
363 dest.append(this.negativePrefix);
365 dest.append(this.positivePrefix);
367 dest.append(symbols.getInfinity());
370 dest.append(this.negativeSuffix);
372 dest.append(this.positiveSuffix);
374 if ((fieldPos.getField() == INTEGER_FIELD ||
375 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
377 fieldPos.setBeginIndex(dest.length());
378 fieldPos.setEndIndex(0);
383 // get the number as a BigDecimal
384 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
385 formatInternal(bigDecimal, false, dest, fieldPos);
392 * Produce a formatted {@link String} representation of this long.
394 * @param number The long to format.
395 * @param dest The destination String; text will be appended to this String.
396 * @param fieldPos If used on input can be used to define an alignment
397 * field. If used on output defines the offsets of the alignment field.
398 * @return The String representation of this long.
400 public StringBuffer format(long number, StringBuffer dest,
401 FieldPosition fieldPos)
403 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
404 formatInternal(bigDecimal, true, dest, fieldPos);
409 * Return an <code>AttributedCharacterIterator</code> as a result of
410 * the formatting of the passed {@link Object}.
412 * @return An {@link AttributedCharacterIterator}.
413 * @throws NullPointerException if value is <code>null</code>.
414 * @throws IllegalArgumentException if value is not an instance of
417 public AttributedCharacterIterator formatToCharacterIterator(Object value)
420 * This method implementation derives directly from the
421 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
425 throw new NullPointerException("Passed Object is null");
427 if (!(value instanceof Number)) throw new
428 IllegalArgumentException("Cannot format given Object as a Number");
430 StringBuffer text = new StringBuffer();
432 super.format(value, text, new FieldPosition(0));
434 AttributedString as = new AttributedString(text.toString());
436 // add NumberFormat field attributes to the AttributedString
437 for (int i = 0; i < attributes.size(); i++)
439 FieldPosition pos = (FieldPosition) attributes.get(i);
440 Format.Field attribute = pos.getFieldAttribute();
442 as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos
446 // return the CharacterIterator from AttributedString
447 return as.getIterator();
451 * Returns the currency corresponding to the currency symbol stored
452 * in the instance of <code>DecimalFormatSymbols</code> used by this
453 * <code>DecimalFormat</code>.
455 * @return A new instance of <code>Currency</code> if
456 * the currency code matches a known one, null otherwise.
458 public Currency getCurrency()
460 return symbols.getCurrency();
464 * Returns a copy of the symbols used by this instance.
466 * @return A copy of the symbols.
468 public DecimalFormatSymbols getDecimalFormatSymbols()
470 return (DecimalFormatSymbols) symbols.clone();
474 * Gets the interval used between a grouping separator and the next.
475 * For example, a grouping size of 3 means that the number 1234 is
476 * formatted as 1,234.
478 * The actual character used as grouping separator depends on the
479 * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()}
481 * @return The interval used between a grouping separator and the next.
483 public int getGroupingSize()
489 * Gets the multiplier used in percent and similar formats.
491 * @return The multiplier used in percent and similar formats.
493 public int getMultiplier()
499 * Gets the negative prefix.
501 * @return The negative prefix.
503 public String getNegativePrefix()
505 return negativePrefix;
509 * Gets the negative suffix.
511 * @return The negative suffix.
513 public String getNegativeSuffix()
515 return negativeSuffix;
519 * Gets the positive prefix.
521 * @return The positive prefix.
523 public String getPositivePrefix()
525 return positivePrefix;
529 * Gets the positive suffix.
531 * @return The positive suffix.
533 public String getPositiveSuffix()
535 return positiveSuffix;
538 public boolean isDecimalSeparatorAlwaysShown()
540 return decimalSeparatorAlwaysShown;
544 * Define if <code>parse(java.lang.String, java.text.ParsePosition)</code>
545 * should return a {@link BigDecimal} or not.
549 public void setParseBigDecimal(boolean newValue)
551 this.parseBigDecimal = newValue;
555 * Returns <code>true</code> if
556 * <code>parse(java.lang.String, java.text.ParsePosition)</code> returns
557 * a <code>BigDecimal</code>, <code>false</code> otherwise.
558 * The default return value for this method is <code>false</code>.
560 * @return <code>true</code> if the parse method returns a {@link BigDecimal},
561 * <code>false</code> otherwise.
563 * @see #setParseBigDecimal(boolean)
565 public boolean isParseBigDecimal()
567 return this.parseBigDecimal;
571 * This method parses the specified string into a <code>Number</code>.
573 * The parsing starts at <code>pos</code>, which is updated as the parser
574 * consume characters in the passed string.
575 * On error, the <code>Position</code> object index is not updated, while
576 * error position is set appropriately, an <code>null</code> is returned.
578 * @param str The string to parse.
579 * @param pos The desired <code>ParsePosition</code>.
581 * @return The parsed <code>Number</code>
583 public Number parse(String str, ParsePosition pos)
585 // a special values before anything else
587 if (str.contains(this.symbols.getNaN()))
588 return Double.valueOf(Double.NaN);
590 // this will be our final number
591 StringBuffer number = new StringBuffer();
594 char minus = symbols.getMinusSign();
596 // starting parsing position
597 int start = pos.getIndex();
599 // validate the string, it have to be in the
600 // same form as the format string or parsing will fail
601 String _negativePrefix = (this.negativePrefix.compareTo("") == 0
602 ? minus + positivePrefix
603 : this.negativePrefix);
605 // we check both prefixes, because one might be empty.
606 // We want to pick the longest prefix that matches.
607 int positiveLen = positivePrefix.length();
608 int negativeLen = _negativePrefix.length();
610 boolean isNegative = str.startsWith(_negativePrefix);
611 boolean isPositive = str.startsWith(positivePrefix);
613 if (isPositive && isNegative)
615 // By checking this way, we preserve ambiguity in the case
616 // where the negative format differs only in suffix.
617 if (negativeLen > positiveLen)
619 start += _negativePrefix.length();
624 start += positivePrefix.length();
626 if (negativeLen < positiveLen)
632 start += _negativePrefix.length();
637 start += positivePrefix.length();
642 pos.setErrorIndex(start);
646 // other special characters used by the parser
647 char decimalSeparator = symbols.getDecimalSeparator();
648 char zero = symbols.getZeroDigit();
649 char exponent = symbols.getExponential();
651 // stop parsing position in the string
652 int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2;
654 if (useExponentialNotation)
655 stop += minExponentDigits + 1;
657 boolean inExponent = false;
659 // correct the size of the end parsing flag
660 int len = str.length();
661 if (len < stop) stop = len;
666 char ch = str.charAt(i);
669 if (ch >= zero && ch <= (zero + 9))
673 else if (this.parseIntegerOnly)
677 else if (ch == decimalSeparator)
681 else if (ch == exponent)
684 inExponent = !inExponent;
686 else if ((ch == '+' || ch == '-' || ch == minus))
695 // 2nd special case: infinity
696 // XXX: need to be tested
697 if (str.contains(symbols.getInfinity()))
699 int inf = str.indexOf(symbols.getInfinity());
702 // FIXME: ouch, this is really ugly and lazy code...
703 if (this.parseBigDecimal)
706 return new BigDecimal(Double.NEGATIVE_INFINITY);
708 return new BigDecimal(Double.POSITIVE_INFINITY);
712 return new Double(Double.NEGATIVE_INFINITY);
714 return new Double(Double.POSITIVE_INFINITY);
718 if (i == start || number.length() == 0)
720 pos.setErrorIndex(i);
724 // now we have to check the suffix, done here after number parsing
725 // or the index will not be updated correctly...
726 boolean isNegativeSuffix = str.endsWith(this.negativeSuffix);
727 boolean isPositiveSuffix = str.endsWith(this.positiveSuffix);
728 boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix);
730 positiveLen = positiveSuffix.length();
731 negativeLen = negativeSuffix.length();
733 if (isNegative && !isNegativeSuffix)
735 pos.setErrorIndex(i);
738 else if (isNegativeSuffix &&
739 !positiveEqualsNegative &&
740 (negativeLen > positiveLen))
744 else if (!isPositiveSuffix)
746 pos.setErrorIndex(i);
750 if (isNegative) number.insert(0, '-');
754 // now we handle the return type
755 BigDecimal bigDecimal = new BigDecimal(number.toString());
756 if (this.parseBigDecimal)
760 if (this.parseIntegerOnly)
761 return new Long(bigDecimal.longValue());
763 // 3th special case -0.0
764 if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0))
765 return new Double(-0.0);
770 = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY);
771 return new Long(integer.longValue());
773 catch (ArithmeticException e)
775 return new Double(bigDecimal.doubleValue());
780 * Sets the <code>Currency</code> on the
781 * <code>DecimalFormatSymbols</code> used, which also sets the
782 * currency symbols on those symbols.
784 * @param currency The new <code>Currency</code> on the
785 * <code>DecimalFormatSymbols</code>.
787 public void setCurrency(Currency currency)
789 symbols.setCurrency(currency);
793 * Sets the symbols used by this instance. This method makes a copy of
794 * the supplied symbols.
796 * @param newSymbols the symbols (<code>null</code> not permitted).
798 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
800 symbols = (DecimalFormatSymbols) newSymbols.clone();
804 * Define if the decimal separator should be always visible or only
805 * visible when needed. This method as effect only on integer values.
806 * Pass <code>true</code> if you want the decimal separator to be
807 * always shown, <code>false</code> otherwise.
809 * @param newValue true</code> if you want the decimal separator to be
810 * always shown, <code>false</code> otherwise.
812 public void setDecimalSeparatorAlwaysShown(boolean newValue)
814 decimalSeparatorAlwaysShown = newValue;
818 * Sets the number of digits used to group portions of the integer part of
819 * the number. For example, the number <code>123456</code>, with a grouping
820 * size of 3, is rendered <code>123,456</code>.
822 * @param groupSize The number of digits used while grouping portions
823 * of the integer part of a number.
825 public void setGroupingSize(int groupSize)
827 groupingSize = (byte) groupSize;
831 * Sets the maximum number of digits allowed in the integer
832 * portion of a number to the specified value.
833 * The new value will be the choosen as the minimum between
834 * <code>newvalue</code> and 309. Any value below zero will be
837 * @param newValue The new maximum integer digits value.
839 public void setMaximumIntegerDigits(int newValue)
841 newValue = (newValue > 0) ? newValue : 0;
842 super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
846 * Sets the minimum number of digits allowed in the integer
847 * portion of a number to the specified value.
848 * The new value will be the choosen as the minimum between
849 * <code>newvalue</code> and 309. Any value below zero will be
852 * @param newValue The new minimum integer digits value.
854 public void setMinimumIntegerDigits(int newValue)
856 newValue = (newValue > 0) ? newValue : 0;
857 super.setMinimumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
861 * Sets the maximum number of digits allowed in the fraction
862 * portion of a number to the specified value.
863 * The new value will be the choosen as the minimum between
864 * <code>newvalue</code> and 309. Any value below zero will be
867 * @param newValue The new maximum fraction digits value.
869 public void setMaximumFractionDigits(int newValue)
871 newValue = (newValue > 0) ? newValue : 0;
872 super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
876 * Sets the minimum number of digits allowed in the fraction
877 * portion of a number to the specified value.
878 * The new value will be the choosen as the minimum between
879 * <code>newvalue</code> and 309. Any value below zero will be
882 * @param newValue The new minimum fraction digits value.
884 public void setMinimumFractionDigits(int newValue)
886 newValue = (newValue > 0) ? newValue : 0;
887 super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
891 * Sets the multiplier for use in percent and similar formats.
892 * For example, for percent set the multiplier to 100, for permille, set the
893 * miltiplier to 1000.
895 * @param newValue the new value for multiplier.
897 public void setMultiplier(int newValue)
899 multiplier = newValue;
903 * Sets the negative prefix.
905 * @param newValue The new negative prefix.
907 public void setNegativePrefix(String newValue)
909 negativePrefix = newValue;
913 * Sets the negative suffix.
915 * @param newValue The new negative suffix.
917 public void setNegativeSuffix(String newValue)
919 negativeSuffix = newValue;
923 * Sets the positive prefix.
925 * @param newValue The new positive prefix.
927 public void setPositivePrefix(String newValue)
929 positivePrefix = newValue;
933 * Sets the new positive suffix.
935 * @param newValue The new positive suffix.
937 public void setPositiveSuffix(String newValue)
939 positiveSuffix = newValue;
943 * This method returns a string with the formatting pattern being used
944 * by this object. The string is localized.
946 * @return A localized <code>String</code> with the formatting pattern.
949 public String toLocalizedPattern()
951 return computePattern(this.symbols);
955 * This method returns a string with the formatting pattern being used
956 * by this object. The string is not localized.
958 * @return A <code>String</code> with the formatting pattern.
959 * @see #toLocalizedPattern()
961 public String toPattern()
963 return computePattern(nonLocalizedSymbols);
966 /* ***** private methods ***** */
969 * This is an shortcut helper method used to test if two given strings are
972 * @param s1 The first string to test for equality.
973 * @param s2 The second string to test for equality.
974 * @return <code>true</code> if the strings are both <code>null</code> or
977 private boolean equals(String s1, String s2)
979 if (s1 == null || s2 == null)
981 return s1.equals(s2);
985 /* ****** PATTERN ****** */
988 * This helper function creates a string consisting of all the
989 * characters which can appear in a pattern and must be quoted.
991 private String patternChars (DecimalFormatSymbols syms)
993 StringBuffer buf = new StringBuffer ();
995 buf.append(syms.getDecimalSeparator());
996 buf.append(syms.getDigit());
997 buf.append(syms.getExponential());
998 buf.append(syms.getGroupingSeparator());
999 buf.append(syms.getMinusSign());
1000 buf.append(syms.getPatternSeparator());
1001 buf.append(syms.getPercent());
1002 buf.append(syms.getPerMill());
1003 buf.append(syms.getZeroDigit());
1005 buf.append('\u00a4');
1007 return buf.toString();
1011 * Quote special characters as defined by <code>patChars</code> in the
1016 * @return A StringBuffer with special characters quoted.
1018 private StringBuffer quoteFix(String text, String patChars)
1020 StringBuffer buf = new StringBuffer();
1022 int len = text.length();
1024 for (int index = 0; index < len; ++index)
1026 ch = text.charAt(index);
1027 if (patChars.indexOf(ch) != -1)
1031 if (ch != '\'') buf.append('\'');
1043 * Returns the format pattern, localized to follow the given
1046 private String computePattern(DecimalFormatSymbols symbols)
1048 StringBuffer mainPattern = new StringBuffer();
1050 // We have to at least emit a zero for the minimum number of
1051 // digits. Past that we need hash marks up to the grouping
1052 // separator (and one beyond).
1053 int _groupingSize = groupingUsed ? groupingSize + 1: groupingSize;
1054 int totalDigits = Math.max(minimumIntegerDigits, _groupingSize);
1056 // if it is not in exponential notiation,
1057 // we always have a # prebended
1058 if (!useExponentialNotation) mainPattern.append(symbols.getDigit());
1060 for (int i = 1; i < totalDigits - minimumIntegerDigits; i++)
1061 mainPattern.append(symbols.getDigit());
1063 for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++)
1064 mainPattern.append(symbols.getZeroDigit());
1068 mainPattern.insert(mainPattern.length() - groupingSize,
1069 symbols.getGroupingSeparator());
1072 // See if we need decimal info.
1073 if (minimumFractionDigits > 0 || maximumFractionDigits > 0 ||
1074 decimalSeparatorAlwaysShown)
1076 mainPattern.append(symbols.getDecimalSeparator());
1079 for (int i = 0; i < minimumFractionDigits; ++i)
1080 mainPattern.append(symbols.getZeroDigit());
1082 for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
1083 mainPattern.append(symbols.getDigit());
1085 if (useExponentialNotation)
1087 mainPattern.append(symbols.getExponential());
1089 for (int i = 0; i < minExponentDigits; ++i)
1090 mainPattern.append(symbols.getZeroDigit());
1092 if (minExponentDigits == 0)
1093 mainPattern.append(symbols.getDigit());
1097 String pattern = mainPattern.toString();
1099 // so far we have the pattern itself, now we need to add
1100 // the positive and the optional negative prefixes and suffixes
1101 String patternChars = patternChars(symbols);
1102 mainPattern.insert(0, quoteFix(positivePrefix, patternChars));
1103 mainPattern.append(quoteFix(positiveSuffix, patternChars));
1105 if (hasNegativePrefix)
1107 mainPattern.append(symbols.getPatternSeparator());
1108 mainPattern.append(quoteFix(negativePrefix, patternChars));
1109 mainPattern.append(pattern);
1110 mainPattern.append(quoteFix(negativeSuffix, patternChars));
1113 // finally, return the pattern string
1114 return mainPattern.toString();
1117 /* ****** FORMAT PARSING ****** */
1120 * Scan the input string and define a pattern suitable for use
1121 * with this decimal format.
1126 private void applyPatternWithSymbols(String pattern,
1127 DecimalFormatSymbols symbols)
1129 // The pattern string is described by a BNF diagram.
1130 // we could use a recursive parser to read and prepare
1131 // the string, but this would be too slow and resource
1132 // intensive, while this code is quite critical as it is
1133 // called always when the class is instantiated and every
1134 // time a new pattern is given.
1135 // Our strategy is to divide the string into section as given by
1136 // the BNF diagram, iterating through the string and setting up
1137 // the parameters we need for formatting (which is basicly what
1138 // a descendent recursive parser would do - but without recursion).
1139 // I'm sure that there are smarter methods to do this.
1141 // Restore default values. Most of these will be overwritten
1142 // but we want to be sure that nothing is left out.
1145 int len = pattern.length();
1148 // this is another special case...
1149 this.minimumIntegerDigits = 1;
1150 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
1151 this.minimumFractionDigits = 0;
1152 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
1154 // FIXME: ...and these values may not be valid in all locales
1155 this.minExponentDigits = 0;
1156 this.showDecimalSeparator = true;
1157 this.groupingUsed = true;
1158 this.groupingSize = 3;
1163 int start = scanFix(pattern, symbols, 0, true);
1164 if (start < len) start = scanNumberInteger(pattern, symbols, start);
1167 start = scanFractionalPortion(pattern, symbols, start);
1171 // special case, pattern that ends here does not have a fractional
1173 this.minimumFractionDigits = 0;
1174 this.maximumFractionDigits = 0;
1175 //this.decimalSeparatorAlwaysShown = false;
1176 //this.showDecimalSeparator = false;
1179 // XXX: this fixes a compatibility test with the RI.
1180 // If new uses cases fail, try removing this line first.
1181 //if (!this.hasIntegerPattern && !this.hasFractionalPattern)
1182 // throw new IllegalArgumentException("No valid pattern found!");
1184 if (start < len) start = scanExponent(pattern, symbols, start);
1185 if (start < len) start = scanFix(pattern, symbols, start, false);
1186 if (start < len) scanNegativePattern(pattern, symbols, start);
1188 if (useExponentialNotation &&
1189 (maxIntegerDigitsExponent > minimumIntegerDigits) &&
1190 (maxIntegerDigitsExponent > 1))
1192 minimumIntegerDigits = 1;
1193 exponentRound = maxIntegerDigitsExponent;
1196 if (useExponentialNotation)
1197 maximumIntegerDigits = maxIntegerDigitsExponent;
1199 if (!this.hasFractionalPattern && this.showDecimalSeparator == true)
1201 this.decimalSeparatorAlwaysShown = true;
1206 * Scans for the prefix or suffix portion of the pattern string.
1207 * This method handles the positive subpattern of the pattern string.
1209 * @param pattern The pattern string to parse.
1210 * @return The position in the pattern string where parsing ended.
1212 private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols,
1213 int start, boolean prefix)
1215 StringBuffer buffer = new StringBuffer();
1217 // the number portion is always delimited by one of those
1219 char decimalSeparator = sourceSymbols.getDecimalSeparator();
1220 char patternSeparator = sourceSymbols.getPatternSeparator();
1221 char groupingSeparator = sourceSymbols.getGroupingSeparator();
1222 char digit = sourceSymbols.getDigit();
1223 char zero = sourceSymbols.getZeroDigit();
1224 char minus = sourceSymbols.getMinusSign();
1226 // other special characters, cached here to avoid method calls later
1227 char percent = sourceSymbols.getPercent();
1228 char permille = sourceSymbols.getPerMill();
1230 String currencySymbol = this.symbols.getCurrencySymbol();
1232 boolean quote = false;
1234 char ch = pattern.charAt(start);
1235 if (ch == patternSeparator)
1237 // negative subpattern
1238 this.hasNegativePrefix = true;
1243 int len = pattern.length();
1245 for (i = start; i < len; i++)
1247 ch = pattern.charAt(i);
1249 // we are entering into the negative subpattern
1250 if (!quote && ch == patternSeparator)
1252 if (this.hasNegativePrefix)
1254 throw new IllegalArgumentException("Invalid pattern found: "
1258 this.hasNegativePrefix = true;
1263 // this means we are inside the number portion
1265 (ch == minus || ch == digit || ch == zero ||
1266 ch == groupingSeparator))
1269 if (!quote && ch == decimalSeparator)
1271 this.showDecimalSeparator = true;
1274 else if (quote && ch != '\'')
1283 currencySymbol = this.symbols.getCurrencySymbol();
1285 // if \u00A4 is doubled, we use the international currency symbol
1286 if (i < len && pattern.charAt(i + 1) == '\u00A4')
1288 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1292 this.useCurrencySeparator = true;
1293 buffer.append(currencySymbol);
1295 else if (ch == percent)
1298 this.multiplier = 100;
1299 buffer.append(this.symbols.getPercent());
1301 else if (ch == permille)
1304 this.multiplier = 1000;
1305 buffer.append(this.symbols.getPerMill());
1307 else if (ch == '\'')
1310 if (i < len && pattern.charAt(i + 1) == '\'')
1312 // we need to add ' to the buffer
1330 this.positivePrefix = buffer.toString();
1331 this.negativePrefix = minus + "" + positivePrefix;
1335 this.positiveSuffix = buffer.toString();
1342 * Scan the given string for number patterns, starting
1343 * from <code>start</code>.
1344 * This method searches the integer part of the pattern only.
1346 * @param pattern The pattern string to parse.
1347 * @param start The starting parse position in the string.
1348 * @return The position in the pattern string where parsing ended,
1349 * counted from the beginning of the string (that is, 0).
1351 private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols,
1354 char digit = symbols.getDigit();
1355 char zero = symbols.getZeroDigit();
1356 char groupingSeparator = symbols.getGroupingSeparator();
1357 char decimalSeparator = symbols.getDecimalSeparator();
1358 char exponent = symbols.getExponential();
1359 char patternSeparator = symbols.getPatternSeparator();
1361 // count the number of zeroes in the pattern
1362 // this number defines the minum digits in the integer portion
1365 // count the number of digits used in grouping
1366 int _groupingSize = 0;
1368 this.maxIntegerDigitsExponent = 0;
1370 boolean intPartTouched = false;
1373 int len = pattern.length();
1375 for (i = start; i < len; i++)
1377 ch = pattern.charAt(i);
1379 // break on decimal separator or exponent or pattern separator
1380 if (ch == decimalSeparator || ch == exponent)
1383 if (this.hasNegativePrefix && ch == patternSeparator)
1384 throw new IllegalArgumentException("Invalid pattern found: "
1389 // in our implementation we could relax this strict
1390 // requirement, but this is used to keep compatibility with
1392 if (zeros > 0) throw new
1393 IllegalArgumentException("digit mark following zero in " +
1394 "positive subpattern, not allowed. Position: " + i);
1397 intPartTouched = true;
1398 this.maxIntegerDigitsExponent++;
1400 else if (ch == zero)
1404 this.maxIntegerDigitsExponent++;
1406 else if (ch == groupingSeparator)
1408 this.groupingSeparatorInPattern = true;
1409 this.groupingUsed = true;
1414 // any other character not listed above
1415 // means we are in the suffix portion
1420 if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize;
1421 this.minimumIntegerDigits = zeros;
1423 // XXX: compatibility code with the RI: the number of minimum integer
1424 // digits is at least one when maximumIntegerDigits is more than zero
1425 if (intPartTouched && this.maximumIntegerDigits > 0 &&
1426 this.minimumIntegerDigits == 0)
1427 this.minimumIntegerDigits = 1;
1433 * Scan the given string for number patterns, starting
1434 * from <code>start</code>.
1435 * This method searches the fractional part of the pattern only.
1437 * @param pattern The pattern string to parse.
1438 * @param start The starting parse position in the string.
1439 * @return The position in the pattern string where parsing ended,
1440 * counted from the beginning of the string (that is, 0).
1442 private int scanFractionalPortion(String pattern,
1443 DecimalFormatSymbols symbols,
1446 char digit = symbols.getDigit();
1447 char zero = symbols.getZeroDigit();
1448 char groupingSeparator = symbols.getGroupingSeparator();
1449 char decimalSeparator = symbols.getDecimalSeparator();
1450 char exponent = symbols.getExponential();
1451 char patternSeparator = symbols.getPatternSeparator();
1453 // first character needs to be '.' otherwise we are not parsing the
1454 // fractional portion
1455 char ch = pattern.charAt(start);
1456 if (ch != decimalSeparator)
1458 this.minimumFractionDigits = 0;
1459 this.maximumFractionDigits = 0;
1465 this.hasFractionalPattern = true;
1467 this.minimumFractionDigits = 0;
1470 int len = pattern.length();
1472 for (i = start; i < len; i++)
1474 ch = pattern.charAt(i);
1476 // we hit the exponential or negative subpattern
1477 if (ch == exponent || ch == patternSeparator)
1481 if (ch == groupingSeparator || ch == decimalSeparator) throw new
1482 IllegalArgumentException("unexpected character '" + ch + "' " +
1483 "in fractional subpattern. Position: " + i);
1489 else if (ch == zero)
1491 if (digits > 0) throw new
1492 IllegalArgumentException("digit mark following zero in " +
1493 "positive subpattern, not allowed. Position: " + i);
1495 this.minimumFractionDigits++;
1499 // we are in the suffix section of pattern
1504 if (i == start) this.hasFractionalPattern = false;
1506 this.maximumFractionDigits = this.minimumFractionDigits + digits;
1507 this.showDecimalSeparator = true;
1513 * Scan the given string for number patterns, starting
1514 * from <code>start</code>.
1515 * This method searches the expoential part of the pattern only.
1517 * @param pattern The pattern string to parse.
1518 * @param start The starting parse position in the string.
1519 * @return The position in the pattern string where parsing ended,
1520 * counted from the beginning of the string (that is, 0).
1522 private int scanExponent(String pattern, DecimalFormatSymbols symbols,
1525 char digit = symbols.getDigit();
1526 char zero = symbols.getZeroDigit();
1527 char groupingSeparator = symbols.getGroupingSeparator();
1528 char decimalSeparator = symbols.getDecimalSeparator();
1529 char exponent = symbols.getExponential();
1531 char ch = pattern.charAt(start);
1533 if (ch == decimalSeparator)
1541 this.useExponentialNotation = false;
1547 this.minExponentDigits = 0;
1549 int len = pattern.length();
1551 for (i = start; i < len; i++)
1553 ch = pattern.charAt(i);
1555 if (ch == groupingSeparator || ch == decimalSeparator ||
1556 ch == digit || ch == exponent) throw new
1557 IllegalArgumentException("unexpected character '" + ch + "' " +
1558 "in exponential subpattern. Position: " + i);
1562 this.minExponentDigits++;
1566 // any character other than zero is an exit point
1571 this.useExponentialNotation = true;
1577 * Scan the given string for number patterns, starting
1578 * from <code>start</code>.
1579 * This method searches the negative part of the pattern only and scan
1580 * throught the end of the string.
1582 * @param pattern The pattern string to parse.
1583 * @param start The starting parse position in the string.
1585 private void scanNegativePattern(String pattern,
1586 DecimalFormatSymbols sourceSymbols,
1589 StringBuffer buffer = new StringBuffer();
1591 // the number portion is always delimited by one of those
1593 char decimalSeparator = sourceSymbols.getDecimalSeparator();
1594 char patternSeparator = sourceSymbols.getPatternSeparator();
1595 char groupingSeparator = sourceSymbols.getGroupingSeparator();
1596 char digit = sourceSymbols.getDigit();
1597 char zero = sourceSymbols.getZeroDigit();
1598 char minus = sourceSymbols.getMinusSign();
1600 // other special charcaters, cached here to avoid method calls later
1601 char percent = sourceSymbols.getPercent();
1602 char permille = sourceSymbols.getPerMill();
1604 String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol();
1605 String currencySymbol = CURRENCY_SYMBOL;
1607 boolean quote = false;
1608 boolean prefixDone = false;
1610 int len = pattern.length();
1611 if (len > 0) this.hasNegativePrefix = true;
1613 char ch = pattern.charAt(start);
1614 if (ch == patternSeparator)
1616 // no pattern separator in the negative pattern
1617 if ((start + 1) > len) throw new
1618 IllegalArgumentException("unexpected character '" + ch + "' " +
1619 "in negative subpattern.");
1624 for (i = start; i < len; i++)
1626 ch = pattern.charAt(i);
1628 // this means we are inside the number portion
1630 (ch == digit || ch == zero || ch == decimalSeparator ||
1631 ch == patternSeparator || ch == groupingSeparator))
1635 this.negativePrefix = buffer.toString();
1636 buffer.delete(0, buffer.length());
1640 else if (ch == minus)
1642 buffer.append(this.symbols.getMinusSign());
1644 else if (quote && ch != '\'')
1648 else if (ch == '\u00A4')
1651 currencySymbol = CURRENCY_SYMBOL;
1653 // if \u00A4 is doubled, we use the international currency symbol
1654 if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1656 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1660 // FIXME: not sure about this, the specs says that we only have to
1661 // change prefix and suffix, so leave it as commented
1662 // unless in case of bug report/errors
1663 //this.useCurrencySeparator = true;
1665 buffer.append(currencySymbol);
1667 else if (ch == percent)
1670 this.negativePatternMultiplier = 100;
1671 buffer.append(this.symbols.getPercent());
1673 else if (ch == permille)
1676 this.negativePatternMultiplier = 1000;
1677 buffer.append(this.symbols.getPerMill());
1679 else if (ch == '\'')
1682 if (i < len && pattern.charAt(i + 1) == '\'')
1684 // we need to add ' to the buffer
1693 else if (ch == patternSeparator)
1695 // no pattern separator in the negative pattern
1696 throw new IllegalArgumentException("unexpected character '" + ch +
1697 "' in negative subpattern.");
1706 this.negativeSuffix = buffer.toString();
1708 this.negativePrefix = buffer.toString();
1711 /* ****** FORMATTING ****** */
1714 * Handles the real formatting.
1716 * We use a BigDecimal to format the number without precision loss.
1717 * All the rounding is done by methods in BigDecimal.
1718 * The <code>isLong</code> parameter is used to determine if we are
1719 * formatting a long or BigInteger. In this case, we avoid to format
1720 * the fractional part of the number (unless specified otherwise in the
1721 * format string) that would consist only of a 0 digit.
1723 * @param number A BigDecimal representation fo the input number.
1724 * @param dest The destination buffer.
1725 * @param isLong A boolean that indicates if this BigDecimal is a real
1726 * decimal or an integer.
1727 * @param fieldPos Use to keep track of the formatting position.
1729 private void formatInternal(BigDecimal number, boolean isLong,
1730 StringBuffer dest, FieldPosition fieldPos)
1732 // The specs says that fieldPos should not be null, and that we
1733 // should throw a NPE, but it seems that in few classes that
1734 // reference this one, fieldPos is set to null.
1735 // This is even defined in the javadoc, see for example MessageFormat.
1736 // I think the best here is to check for fieldPos and build one if it is
1737 // null. If it cause harms or regressions, just remove this line and
1738 // fix the classes in the point of call, insted.
1739 if (fieldPos == null) fieldPos = new FieldPosition(0);
1741 int _multiplier = this.multiplier;
1743 // used to track attribute starting position for each attribute
1744 int attributeStart = -1;
1746 // now get the sign this will be used by the special case Inifinity
1747 // and by the normal cases.
1748 boolean isNegative = (number.signum() < 0) ? true : false;
1751 attributeStart = dest.length();
1753 // append the negative prefix to the string
1754 dest.append(negativePrefix);
1756 // once got the negative prefix, we can use
1757 // the absolute value.
1758 number = number.abs();
1760 _multiplier = negativePatternMultiplier;
1762 addAttribute(Field.SIGN, attributeStart, dest.length());
1766 // not negative, use the positive prefix
1767 dest.append(positivePrefix);
1770 // these are used ot update the field position
1771 int beginIndexInt = dest.length();
1772 int endIndexInt = 0;
1773 int beginIndexFract = 0;
1774 int endIndexFract = 0;
1776 // compute the multiplier to use with percent and similar
1777 number = number.multiply(new BigDecimal(_multiplier));
1779 // XXX: special case, not sure if it belongs here or if it is
1780 // correct at all. There may be other special cases as well
1781 // these should be handled in the format string parser.
1782 if (this.maximumIntegerDigits == 0 && this.maximumFractionDigits == 0)
1784 number = BigDecimal.ZERO;
1785 this.maximumIntegerDigits = 1;
1786 this.minimumIntegerDigits = 1;
1789 // get the absolute number
1790 number = number.abs();
1792 // the scaling to use while formatting this number
1793 int scale = this.maximumFractionDigits;
1795 // this is the actual number we will use
1796 // it is corrected later on to handle exponential
1797 // notation, if needed
1800 // are we using exponential notation?
1801 if (this.useExponentialNotation)
1803 exponent = getExponent(number);
1804 number = number.movePointLeft((int) exponent);
1806 // FIXME: this makes the test ##.###E0 to pass,
1807 // but all all the other tests to fail...
1808 // this should be really something like
1809 // min + max - what is already shown...
1810 //scale = this.minimumIntegerDigits + this.maximumFractionDigits;
1813 // round the number to the nearest neighbor
1814 number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
1816 // now get the integer and fractional part of the string
1817 // that will be processed later
1818 String plain = number.toPlainString();
1820 String intPart = null;
1821 String fractPart = null;
1823 // remove - from the integer part, this is needed as
1824 // the Narrowing Primitive Conversions algorithm used may loose
1825 // information about the sign
1826 int minusIndex = plain.lastIndexOf('-', 0);
1827 if (minusIndex > -1) plain = plain.substring(minusIndex + 1);
1829 // strip the decimal portion
1830 int dot = plain.indexOf('.');
1833 intPart = plain.substring(0, dot);
1836 if (useExponentialNotation)
1837 fractPart = plain.substring(dot, dot + scale);
1839 fractPart = plain.substring(dot);
1846 // used in various places later on
1847 int intPartLen = intPart.length();
1848 endIndexInt = intPartLen;
1850 // if the number of digits in our intPart is not greater than the
1851 // minimum we have to display, we append zero to the destination
1852 // buffer before adding the integer portion of the number.
1853 int zeroes = minimumIntegerDigits - intPartLen;
1856 attributeStart = Math.max(dest.length() - 1, 0);
1857 appendZero(dest, zeroes, minimumIntegerDigits);
1860 if (this.useExponentialNotation)
1862 // For exponential numbers, the significant in mantissa are
1863 // the sum of the minimum integer and maximum fraction
1864 // digits, and does not take into account the maximun integer
1865 // digits to display.
1867 if (attributeStart < 0)
1868 attributeStart = Math.max(dest.length() - 1, 0);
1869 appendDigit(intPart, dest, this.groupingUsed);
1873 // non exponential notation
1874 intPartLen = intPart.length();
1875 int canary = Math.min(intPartLen, this.maximumIntegerDigits);
1877 // remove from the string the number in excess
1878 // use only latest digits
1879 intPart = intPart.substring(intPartLen - canary);
1880 endIndexInt = intPart.length() + 1;
1883 if (maximumIntegerDigits > 0 &&
1884 !(this.minimumIntegerDigits == 0 &&
1885 intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0))
1887 if (attributeStart < 0)
1888 attributeStart = Math.max(dest.length() - 1, 0);
1889 appendDigit(intPart, dest, this.groupingUsed);
1893 // add the INTEGER attribute
1894 addAttribute(Field.INTEGER, attributeStart, dest.length());
1896 // ...update field position, if needed, and return...
1897 if ((fieldPos.getField() == INTEGER_FIELD ||
1898 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
1900 fieldPos.setBeginIndex(beginIndexInt);
1901 fieldPos.setEndIndex(endIndexInt);
1904 handleFractionalPart(dest, fractPart, fieldPos, isLong);
1907 if (this.useExponentialNotation)
1909 attributeStart = dest.length();
1911 dest.append(symbols.getExponential());
1913 addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
1914 attributeStart = dest.length();
1918 dest.append(symbols.getMinusSign());
1919 exponent = -exponent;
1921 addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
1924 attributeStart = dest.length();
1926 String exponentString = String.valueOf(exponent);
1927 int exponentLength = exponentString.length();
1929 for (int i = 0; i < minExponentDigits - exponentLength; i++)
1930 dest.append(symbols.getZeroDigit());
1932 for (int i = 0; i < exponentLength; ++i)
1933 dest.append(exponentString.charAt(i));
1935 addAttribute(Field.EXPONENT, attributeStart, dest.length());
1938 // now include the suffixes...
1941 dest.append(negativeSuffix);
1945 dest.append(positiveSuffix);
1950 * Add to the input buffer the result of formatting the fractional
1951 * portion of the number.
1958 private void handleFractionalPart(StringBuffer dest, String fractPart,
1959 FieldPosition fieldPos, boolean isLong)
1963 boolean addDecimal = false;
1965 if (this.decimalSeparatorAlwaysShown ||
1966 ((!isLong || this.useExponentialNotation) &&
1967 this.showDecimalSeparator && this.maximumFractionDigits > 0) ||
1968 this.minimumFractionDigits > 0)
1970 dotStart = dest.length();
1972 if (this.useCurrencySeparator)
1973 dest.append(symbols.getMonetaryDecimalSeparator());
1975 dest.append(symbols.getDecimalSeparator());
1977 dotEnd = dest.length();
1981 // now handle the fraction portion of the number
1984 boolean addFractional = false;
1986 if ((!isLong || this.useExponentialNotation)
1987 && this.maximumFractionDigits > 0
1988 || this.minimumFractionDigits > 0)
1990 fractStart = dest.length();
1991 fractEnd = fractStart;
1993 int digits = this.minimumFractionDigits;
1995 if (this.useExponentialNotation)
1997 digits = (this.minimumIntegerDigits + this.minimumFractionDigits)
1999 if (digits < 0) digits = 0;
2002 fractPart = adjustTrailingZeros(fractPart, digits);
2004 // FIXME: this code must be improved
2005 // now check if the factional part is just 0, in this case
2006 // we need to remove the '.' unless requested
2007 boolean allZeros = true;
2008 char fracts[] = fractPart.toCharArray();
2009 for (int i = 0; i < fracts.length; i++)
2011 if (fracts[i] != '0')
2015 if (!allZeros || (minimumFractionDigits > 0))
2017 appendDigit(fractPart, dest, false);
2018 fractEnd = dest.length();
2021 addFractional = true;
2023 else if (!this.decimalSeparatorAlwaysShown)
2025 dest.deleteCharAt(dest.length() - 1);
2030 fractEnd = dest.length();
2031 addFractional = true;
2036 addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
2039 addAttribute(Field.FRACTION, fractStart, fractEnd);
2041 if ((fieldPos.getField() == FRACTION_FIELD ||
2042 fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION))
2044 fieldPos.setBeginIndex(fractStart);
2045 fieldPos.setEndIndex(fractEnd);
2050 * Append to <code>dest</code>the give number of zeros.
2051 * Grouping is added if needed.
2052 * The integer totalDigitCount defines the total number of digits
2053 * of the number to which we are appending zeroes.
2055 private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount)
2057 char ch = symbols.getZeroDigit();
2058 char gSeparator = symbols.getGroupingSeparator();
2061 int gPos = totalDigitCount;
2062 for (i = 0; i < zeroes; i++, gPos--)
2064 if (this.groupingSeparatorInPattern &&
2065 (this.groupingUsed && this.groupingSize != 0) &&
2066 (gPos % groupingSize == 0 && i > 0))
2067 dest.append(gSeparator);
2072 // special case, that requires adding an additional separator
2073 if (this.groupingSeparatorInPattern &&
2074 (this.groupingUsed && this.groupingSize != 0) &&
2075 (gPos % groupingSize == 0))
2076 dest.append(gSeparator);
2080 * Append src to <code>dest</code>.
2082 * Grouping is added if <code>groupingUsed</code> is set
2083 * to <code>true</code>.
2085 private void appendDigit(String src, StringBuffer dest,
2086 boolean groupingUsed)
2088 int zero = symbols.getZeroDigit() - '0';
2091 char gSeparator = symbols.getGroupingSeparator();
2093 int len = src.length();
2094 for (int i = 0, gPos = len; i < len; i++, gPos--)
2097 if (groupingUsed && this.groupingSize != 0 &&
2098 gPos % groupingSize == 0 && i > 0)
2099 dest.append(gSeparator);
2101 dest.append((char) (zero + ch));
2106 * Calculate the exponent to use if eponential notation is used.
2107 * The exponent is calculated as a power of ten.
2108 * <code>number</code> should be positive, if is zero, or less than zero,
2111 private long getExponent(BigDecimal number)
2115 if (number.signum() > 0)
2117 double _number = number.doubleValue();
2118 exponent = (long) Math.floor (Math.log10(_number));
2120 // get the right value for the exponent
2121 exponent = exponent - (exponent % this.exponentRound);
2123 // if the minimumIntegerDigits is more than zero
2124 // we display minimumIntegerDigits of digits.
2125 // so, for example, if minimumIntegerDigits == 2
2126 // and the actual number is 0.123 it will be
2127 // formatted as 12.3E-2
2128 // this means that the exponent have to be shifted
2129 // to the correct value.
2130 if (minimumIntegerDigits > 0)
2131 exponent -= minimumIntegerDigits - 1;
2138 * Remove contiguos zeros from the end of the <code>src</code> string,
2139 * if src contains more than <code>minimumDigits</code> digits.
2140 * if src contains less that <code>minimumDigits</code>,
2141 * then append zeros to the string.
2143 * Only the first block of zero digits is removed from the string
2144 * and only if they fall in the src.length - minimumDigits
2145 * portion of the string.
2147 * @param src The string with the correct number of zeros.
2149 private String adjustTrailingZeros(String src, int minimumDigits)
2151 int len = src.length();
2154 // remove all trailing zero
2155 if (len > minimumDigits)
2158 for (int i = len - 1; i > minimumDigits; i--)
2160 if (src.charAt(i) == '0')
2165 result = src.substring(0, len - zeros);
2169 char zero = symbols.getZeroDigit();
2170 StringBuffer _result = new StringBuffer(src);
2171 for (int i = len; i < minimumDigits; i++)
2173 _result.append(zero);
2175 result = _result.toString();
2182 * Adds an attribute to the attributes list.
2188 private void addAttribute(Field field, int begin, int end)
2191 * This method and its implementation derives directly from the
2192 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
2195 FieldPosition pos = new FieldPosition(field);
2196 pos.setBeginIndex(begin);
2197 pos.setEndIndex(end);
2198 attributes.add(pos);
2202 * Sets the default values for the various properties in this DecimaFormat.
2204 private void setDefaultValues()
2206 // Maybe we should add these values to the message bundle and take
2207 // the most appropriate for them for any locale.
2208 // Anyway, these seem to be good values for a default in most languages.
2209 // Note that most of these will change based on the format string.
2211 this.negativePrefix = String.valueOf(symbols.getMinusSign());
2212 this.negativeSuffix = "";
2213 this.positivePrefix = "";
2214 this.positiveSuffix = "";
2216 this.multiplier = 1;
2217 this.negativePatternMultiplier = 1;
2218 this.exponentRound = 1;
2220 this.hasNegativePrefix = false;
2222 this.minimumIntegerDigits = 1;
2223 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
2224 this.minimumFractionDigits = 0;
2225 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
2226 this.minExponentDigits = 0;
2228 this.groupingSize = 0;
2230 this.decimalSeparatorAlwaysShown = false;
2231 this.showDecimalSeparator = false;
2232 this.useExponentialNotation = false;
2233 this.groupingUsed = false;
2234 this.groupingSeparatorInPattern = false;
2236 this.useCurrencySeparator = false;
2238 this.hasFractionalPattern = false;