Import GNU Classpath (20121202).
[official-gcc.git] / libjava / classpath / java / text / DecimalFormat.java
blob77af0d396c4c995486281d19ce321403894de3ae
1 /* DecimalFormat.java -- Formats and parses numbers
2 Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2012 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)
9 any later version.
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
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
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.
44 package java.text;
46 import gnu.java.lang.CPStringBuilder;
48 import java.math.BigDecimal;
49 import java.math.BigInteger;
51 import java.util.ArrayList;
52 import java.util.Currency;
53 import java.util.Locale;
56 * This note is here for historical reasons and because I had not the courage
57 * to remove it :)
59 * @author Tom Tromey (tromey@cygnus.com)
60 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
61 * @date March 4, 1999
63 * Written using "Java Class Libraries", 2nd edition, plus online
64 * API docs for JDK 1.2 from http://www.javasoft.com.
65 * Status: Believed complete and correct to 1.2.
66 * Note however that the docs are very unclear about how format parsing
67 * should work. No doubt there are problems here.
70 /**
71 * This class is a concrete implementation of NumberFormat used to format
72 * decimal numbers. The class can format numbers given a specific locale.
73 * Generally, to get an instance of DecimalFormat you should call the factory
74 * methods in the <code>NumberFormat</code> base class.
76 * @author Mario Torre (neugens@limasoftware.net)
77 * @author Tom Tromey (tromey@cygnus.com)
78 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
80 public class DecimalFormat extends NumberFormat
82 /** serialVersionUID for serializartion. */
83 private static final long serialVersionUID = 864413376551465018L;
85 /** Defines the default number of digits allowed while formatting integers. */
86 private static final int DEFAULT_INTEGER_DIGITS = 309;
88 /**
89 * Defines the default number of digits allowed while formatting
90 * fractions.
92 private static final int DEFAULT_FRACTION_DIGITS = 340;
94 /**
95 * Locale-independent pattern symbols.
97 // Happen to be the same as the US symbols.
98 private static final DecimalFormatSymbols nonLocalizedSymbols
99 = new DecimalFormatSymbols (Locale.US);
102 * Defines if parse should return a BigDecimal or not.
104 private boolean parseBigDecimal;
107 * Defines if we have to use the monetary decimal separator or
108 * the decimal separator while formatting numbers.
110 private boolean useCurrencySeparator;
112 /** Defines if the decimal separator is always shown or not. */
113 private boolean decimalSeparatorAlwaysShown;
116 * Defines if the decimal separator has to be shown.
118 * This is different then <code>decimalSeparatorAlwaysShown</code>,
119 * as it defines if the format string contains a decimal separator or no.
121 private boolean showDecimalSeparator;
124 * This field is used to determine if the grouping
125 * separator is included in the format string or not.
126 * This is only needed to match the behaviour of the RI.
128 private boolean groupingSeparatorInPattern;
130 /** Defines the size of grouping groups when grouping is used. */
131 private byte groupingSize;
134 * This is an internal parameter used to keep track of the number
135 * of digits the form the exponent, when exponential notation is used.
136 * It is used with <code>exponentRound</code>
138 private byte minExponentDigits;
140 /** This field is used to set the exponent in the engineering notation. */
141 private int exponentRound;
143 /** Multiplier used in percent style formats. */
144 private int multiplier;
146 /** Multiplier used in percent style formats. */
147 private int negativePatternMultiplier;
149 /** The negative prefix. */
150 private String negativePrefix;
152 /** The negative suffix. */
153 private String negativeSuffix;
155 /** The positive prefix. */
156 private String positivePrefix;
158 /** The positive suffix. */
159 private String positiveSuffix;
161 /** Decimal Format Symbols for the given locale. */
162 private DecimalFormatSymbols symbols;
164 /** Determine if we have to use exponential notation or not. */
165 private boolean useExponentialNotation;
168 * Defines the maximum number of integer digits to show when we use
169 * the exponential notation.
171 private int maxIntegerDigitsExponent;
173 /** Defines if the format string has a negative prefix or not. */
174 private boolean hasNegativePrefix;
176 /** Defines if the format string has a fractional pattern or not. */
177 private boolean hasFractionalPattern;
179 /** Stores a list of attributes for use by formatToCharacterIterator. */
180 private ArrayList<FieldPosition> attributes = new ArrayList<FieldPosition>();
183 * Constructs a <code>DecimalFormat</code> which uses the default
184 * pattern and symbols.
186 public DecimalFormat()
188 this ("#,##0.###");
192 * Constructs a <code>DecimalFormat</code> which uses the given
193 * pattern and the default symbols for formatting and parsing.
195 * @param pattern the non-localized pattern to use.
196 * @throws NullPointerException if any argument is null.
197 * @throws IllegalArgumentException if the pattern is invalid.
199 public DecimalFormat(String pattern)
201 this (pattern, new DecimalFormatSymbols());
205 * Constructs a <code>DecimalFormat</code> using the given pattern
206 * and formatting symbols. This construction method is used to give
207 * complete control over the formatting process.
209 * @param pattern the non-localized pattern to use.
210 * @param symbols the set of symbols used for parsing and formatting.
211 * @throws NullPointerException if any argument is null.
212 * @throws IllegalArgumentException if the pattern is invalid.
214 public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
216 this.symbols = (DecimalFormatSymbols) symbols.clone();
217 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
221 * Apply the given localized patern to the current DecimalFormat object.
223 * @param pattern The localized pattern to apply.
224 * @throws IllegalArgumentException if the given pattern is invalid.
225 * @throws NullPointerException if the input pattern is null.
227 public void applyLocalizedPattern (String pattern)
229 applyPatternWithSymbols(pattern, this.symbols);
233 * Apply the given localized pattern to the current DecimalFormat object.
235 * @param pattern The localized pattern to apply.
236 * @throws IllegalArgumentException if the given pattern is invalid.
237 * @throws NullPointerException if the input pattern is null.
239 public void applyPattern(String pattern)
241 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
244 public Object clone()
246 DecimalFormat c = (DecimalFormat) super.clone();
247 c.symbols = (DecimalFormatSymbols) symbols.clone();
248 return c;
252 * Tests this instance for equality with an arbitrary object. This method
253 * returns <code>true</code> if:
254 * <ul>
255 * <li><code>obj</code> is not <code>null</code>;</li>
256 * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
257 * <li>this instance and <code>obj</code> have the same attributes;</li>
258 * </ul>
260 * @param obj the object (<code>null</code> permitted).
262 * @return A boolean.
264 public boolean equals(Object obj)
266 if (! (obj instanceof DecimalFormat))
267 return false;
268 DecimalFormat dup = (DecimalFormat) obj;
269 return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
270 && groupingUsed == dup.groupingUsed
271 && groupingSeparatorInPattern == dup.groupingSeparatorInPattern
272 && groupingSize == dup.groupingSize
273 && multiplier == dup.multiplier
274 && useExponentialNotation == dup.useExponentialNotation
275 && minExponentDigits == dup.minExponentDigits
276 && minimumIntegerDigits == dup.minimumIntegerDigits
277 && maximumIntegerDigits == dup.maximumIntegerDigits
278 && minimumFractionDigits == dup.minimumFractionDigits
279 && maximumFractionDigits == dup.maximumFractionDigits
280 && parseBigDecimal == dup.parseBigDecimal
281 && useCurrencySeparator == dup.useCurrencySeparator
282 && showDecimalSeparator == dup.showDecimalSeparator
283 && exponentRound == dup.exponentRound
284 && negativePatternMultiplier == dup.negativePatternMultiplier
285 && maxIntegerDigitsExponent == dup.maxIntegerDigitsExponent
286 // XXX: causes equivalent patterns to fail
287 // && hasNegativePrefix == dup.hasNegativePrefix
288 && equals(negativePrefix, dup.negativePrefix)
289 && equals(negativeSuffix, dup.negativeSuffix)
290 && equals(positivePrefix, dup.positivePrefix)
291 && equals(positiveSuffix, dup.positiveSuffix)
292 && symbols.equals(dup.symbols));
296 * Returns a hash code for this object.
298 * @return A hash code.
300 public int hashCode()
302 return toPattern().hashCode();
306 * Produce a formatted {@link String} representation of this object.
307 * The passed object must be of type number.
309 * @param obj The {@link Number} to format.
310 * @param sbuf The destination String; text will be appended to this String.
311 * @param pos If used on input can be used to define an alignment
312 * field. If used on output defines the offsets of the alignment field.
313 * @return The String representation of this long.
315 public final StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
317 if (obj instanceof BigInteger)
319 BigDecimal decimal = new BigDecimal((BigInteger) obj);
320 formatInternal(decimal, true, sbuf, pos);
321 return sbuf;
323 else if (obj instanceof BigDecimal)
325 formatInternal((BigDecimal) obj, true, sbuf, pos);
326 return sbuf;
329 return super.format(obj, sbuf, pos);
333 * Produce a formatted {@link String} representation of this double.
335 * @param number The double to format.
336 * @param dest The destination String; text will be appended to this String.
337 * @param fieldPos If used on input can be used to define an alignment
338 * field. If used on output defines the offsets of the alignment field.
339 * @return The String representation of this long.
340 * @throws NullPointerException if <code>dest</code> or fieldPos are null
342 public StringBuffer format(double number, StringBuffer dest,
343 FieldPosition fieldPos)
345 // special cases for double: NaN and negative or positive infinity
346 if (Double.isNaN(number))
348 // 1. NaN
349 String nan = symbols.getNaN();
350 dest.append(nan);
352 // update field position if required
353 if ((fieldPos.getField() == INTEGER_FIELD ||
354 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
356 int index = dest.length();
357 fieldPos.setBeginIndex(index - nan.length());
358 fieldPos.setEndIndex(index);
361 else if (Double.isInfinite(number))
363 // 2. Infinity
364 if (number < 0)
365 dest.append(this.negativePrefix);
366 else
367 dest.append(this.positivePrefix);
369 dest.append(symbols.getInfinity());
371 if (number < 0)
372 dest.append(this.negativeSuffix);
373 else
374 dest.append(this.positiveSuffix);
376 if ((fieldPos.getField() == INTEGER_FIELD ||
377 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
379 fieldPos.setBeginIndex(dest.length());
380 fieldPos.setEndIndex(0);
383 else
385 // get the number as a BigDecimal
386 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
387 formatInternal(bigDecimal, false, dest, fieldPos);
390 return dest;
394 * Produce a formatted {@link String} representation of this long.
396 * @param number The long to format.
397 * @param dest The destination String; text will be appended to this String.
398 * @param fieldPos If used on input can be used to define an alignment
399 * field. If used on output defines the offsets of the alignment field.
400 * @return The String representation of this long.
402 public StringBuffer format(long number, StringBuffer dest,
403 FieldPosition fieldPos)
405 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
406 formatInternal(bigDecimal, true, dest, fieldPos);
407 return dest;
411 * Return an <code>AttributedCharacterIterator</code> as a result of
412 * the formatting of the passed {@link Object}.
414 * @return An {@link AttributedCharacterIterator}.
415 * @throws NullPointerException if value is <code>null</code>.
416 * @throws IllegalArgumentException if value is not an instance of
417 * {@link Number}.
419 public AttributedCharacterIterator formatToCharacterIterator(Object value)
422 * This method implementation derives directly from the
423 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
426 if (value == null)
427 throw new NullPointerException("Passed Object is null");
429 if (!(value instanceof Number)) throw new
430 IllegalArgumentException("Cannot format given Object as a Number");
432 StringBuffer text = new StringBuffer();
433 attributes.clear();
434 super.format(value, text, new FieldPosition(0));
436 AttributedString as = new AttributedString(text.toString());
438 // add NumberFormat field attributes to the AttributedString
439 for (int i = 0; i < attributes.size(); i++)
441 FieldPosition pos = attributes.get(i);
442 Format.Field attribute = pos.getFieldAttribute();
444 as.addAttribute(attribute, attribute, pos.getBeginIndex(),
445 pos.getEndIndex());
448 // return the CharacterIterator from AttributedString
449 return as.getIterator();
453 * Returns the currency corresponding to the currency symbol stored
454 * in the instance of <code>DecimalFormatSymbols</code> used by this
455 * <code>DecimalFormat</code>.
457 * @return A new instance of <code>Currency</code> if
458 * the currency code matches a known one, null otherwise.
460 public Currency getCurrency()
462 return symbols.getCurrency();
466 * Returns a copy of the symbols used by this instance.
468 * @return A copy of the symbols.
470 public DecimalFormatSymbols getDecimalFormatSymbols()
472 return (DecimalFormatSymbols) symbols.clone();
476 * Gets the interval used between a grouping separator and the next.
477 * For example, a grouping size of 3 means that the number 1234 is
478 * formatted as 1,234.
480 * The actual character used as grouping separator depends on the
481 * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()}
483 * @return The interval used between a grouping separator and the next.
485 public int getGroupingSize()
487 return groupingSize;
491 * Gets the multiplier used in percent and similar formats.
493 * @return The multiplier used in percent and similar formats.
495 public int getMultiplier()
497 return multiplier;
501 * Gets the negative prefix.
503 * @return The negative prefix.
505 public String getNegativePrefix()
507 return negativePrefix;
511 * Gets the negative suffix.
513 * @return The negative suffix.
515 public String getNegativeSuffix()
517 return negativeSuffix;
521 * Gets the positive prefix.
523 * @return The positive prefix.
525 public String getPositivePrefix()
527 return positivePrefix;
531 * Gets the positive suffix.
533 * @return The positive suffix.
535 public String getPositiveSuffix()
537 return positiveSuffix;
540 public boolean isDecimalSeparatorAlwaysShown()
542 return decimalSeparatorAlwaysShown;
546 * Define if <code>parse(java.lang.String, java.text.ParsePosition)</code>
547 * should return a {@link BigDecimal} or not.
549 * @param newValue
551 public void setParseBigDecimal(boolean newValue)
553 this.parseBigDecimal = newValue;
557 * Returns <code>true</code> if
558 * <code>parse(java.lang.String, java.text.ParsePosition)</code> returns
559 * a <code>BigDecimal</code>, <code>false</code> otherwise.
560 * The default return value for this method is <code>false</code>.
562 * @return <code>true</code> if the parse method returns a {@link BigDecimal},
563 * <code>false</code> otherwise.
564 * @since 1.5
565 * @see #setParseBigDecimal(boolean)
567 public boolean isParseBigDecimal()
569 return this.parseBigDecimal;
573 * This method parses the specified string into a <code>Number</code>.
575 * The parsing starts at <code>pos</code>, which is updated as the parser
576 * consume characters in the passed string.
577 * On error, the <code>Position</code> object index is not updated, while
578 * error position is set appropriately, an <code>null</code> is returned.
580 * @param str The string to parse.
581 * @param pos The desired <code>ParsePosition</code>.
583 * @return The parsed <code>Number</code>
585 public Number parse(String str, ParsePosition pos)
587 // a special values before anything else
588 // NaN
589 if (str.contains(this.symbols.getNaN()))
590 return Double.valueOf(Double.NaN);
592 // this will be our final number
593 CPStringBuilder number = new CPStringBuilder();
595 // special character
596 char minus = symbols.getMinusSign();
598 // starting parsing position
599 int start = pos.getIndex();
601 // validate the string, it have to be in the
602 // same form as the format string or parsing will fail
603 String _negativePrefix = (this.negativePrefix.compareTo("") == 0
604 ? minus + positivePrefix
605 : this.negativePrefix);
607 // we check both prefixes, because one might be empty.
608 // We want to pick the longest prefix that matches.
609 int positiveLen = positivePrefix.length();
610 int negativeLen = _negativePrefix.length();
612 boolean isNegative = str.startsWith(_negativePrefix);
613 boolean isPositive = str.startsWith(positivePrefix);
615 if (isPositive && isNegative)
617 // By checking this way, we preserve ambiguity in the case
618 // where the negative format differs only in suffix.
619 if (negativeLen > positiveLen)
621 start += _negativePrefix.length();
622 isNegative = true;
624 else
626 start += positivePrefix.length();
627 isPositive = true;
628 if (negativeLen < positiveLen)
629 isNegative = false;
632 else if (isNegative)
634 start += _negativePrefix.length();
635 isPositive = false;
637 else if (isPositive)
639 start += positivePrefix.length();
640 isNegative = false;
642 else
644 pos.setErrorIndex(start);
645 return null;
648 // other special characters used by the parser
649 char decimalSeparator = symbols.getDecimalSeparator();
650 char zero = symbols.getZeroDigit();
651 char exponent = symbols.getExponential();
653 // stop parsing position in the string
654 int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2;
656 if (useExponentialNotation)
657 stop += minExponentDigits + 1;
659 boolean inExponent = false;
661 // correct the size of the end parsing flag
662 int len = str.length();
663 if (len < stop) stop = len;
664 char groupingSeparator = symbols.getGroupingSeparator();
666 int i = start;
667 while (i < stop)
669 char ch = str.charAt(i);
670 i++;
672 if (ch >= zero && ch <= (zero + 9))
674 number.append(ch);
676 else if (this.parseIntegerOnly)
678 i--;
679 break;
681 else if (ch == decimalSeparator)
683 number.append('.');
685 else if (ch == exponent)
687 number.append(ch);
688 inExponent = !inExponent;
690 else if ((ch == '+' || ch == '-' || ch == minus))
692 if (inExponent)
693 number.append(ch);
694 else
696 i--;
697 break;
700 else
702 if (!groupingUsed || ch != groupingSeparator)
704 i--;
705 break;
710 // 2nd special case: infinity
711 // XXX: need to be tested
712 if (str.contains(symbols.getInfinity()))
714 int inf = str.indexOf(symbols.getInfinity());
715 pos.setIndex(inf);
717 // FIXME: ouch, this is really ugly and lazy code...
718 if (this.parseBigDecimal)
720 if (isNegative)
721 return BigDecimal.valueOf(Double.NEGATIVE_INFINITY);
723 return BigDecimal.valueOf(Double.POSITIVE_INFINITY);
726 if (isNegative)
727 return Double.valueOf(Double.NEGATIVE_INFINITY);
729 return Double.valueOf(Double.POSITIVE_INFINITY);
732 // no number...
733 if (i == start || number.length() == 0)
735 pos.setErrorIndex(i);
736 return null;
739 // now we have to check the suffix, done here after number parsing
740 // or the index will not be updated correctly...
741 boolean hasNegativeSuffix = str.endsWith(this.negativeSuffix);
742 boolean hasPositiveSuffix = str.endsWith(this.positiveSuffix);
743 boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix);
745 positiveLen = positiveSuffix.length();
746 negativeLen = negativeSuffix.length();
748 if (isNegative && !hasNegativeSuffix)
750 pos.setErrorIndex(i);
751 return null;
753 else if (hasNegativeSuffix &&
754 !positiveEqualsNegative &&
755 (negativeLen > positiveLen))
757 isNegative = true;
759 else if (!hasPositiveSuffix)
761 pos.setErrorIndex(i);
762 return null;
765 if (isNegative) number.insert(0, '-');
767 pos.setIndex(i);
769 // now we handle the return type
770 BigDecimal bigDecimal = new BigDecimal(number.toString());
771 if (this.parseBigDecimal)
772 return bigDecimal;
774 // want integer?
775 if (this.parseIntegerOnly)
776 return Long.valueOf(bigDecimal.longValue());
778 // 3th special case -0.0
779 if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0))
780 return Double.valueOf(-0.0);
784 BigDecimal integer
785 = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY);
786 return Long.valueOf(integer.longValue());
788 catch (ArithmeticException e)
790 return Double.valueOf(bigDecimal.doubleValue());
795 * Sets the <code>Currency</code> on the
796 * <code>DecimalFormatSymbols</code> used, which also sets the
797 * currency symbols on those symbols.
799 * @param currency The new <code>Currency</code> on the
800 * <code>DecimalFormatSymbols</code>.
802 public void setCurrency(Currency currency)
804 Currency current = symbols.getCurrency();
805 if (current != currency)
807 String oldSymbol = symbols.getCurrencySymbol();
808 int len = oldSymbol.length();
809 symbols.setCurrency(currency);
810 String newSymbol = symbols.getCurrencySymbol();
811 int posPre = positivePrefix.indexOf(oldSymbol);
812 if (posPre != -1)
813 positivePrefix = positivePrefix.substring(0, posPre) +
814 newSymbol + positivePrefix.substring(posPre+len);
815 int negPre = negativePrefix.indexOf(oldSymbol);
816 if (negPre != -1)
817 negativePrefix = negativePrefix.substring(0, negPre) +
818 newSymbol + negativePrefix.substring(negPre+len);
819 int posSuf = positiveSuffix.indexOf(oldSymbol);
820 if (posSuf != -1)
821 positiveSuffix = positiveSuffix.substring(0, posSuf) +
822 newSymbol + positiveSuffix.substring(posSuf+len);
823 int negSuf = negativeSuffix.indexOf(oldSymbol);
824 if (negSuf != -1)
825 negativeSuffix = negativeSuffix.substring(0, negSuf) +
826 newSymbol + negativeSuffix.substring(negSuf+len);
831 * Sets the symbols used by this instance. This method makes a copy of
832 * the supplied symbols.
834 * @param newSymbols the symbols (<code>null</code> not permitted).
836 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
838 symbols = (DecimalFormatSymbols) newSymbols.clone();
842 * Define if the decimal separator should be always visible or only
843 * visible when needed. This method as effect only on integer values.
844 * Pass <code>true</code> if you want the decimal separator to be
845 * always shown, <code>false</code> otherwise.
847 * @param newValue true</code> if you want the decimal separator to be
848 * always shown, <code>false</code> otherwise.
850 public void setDecimalSeparatorAlwaysShown(boolean newValue)
852 decimalSeparatorAlwaysShown = newValue;
856 * Sets the number of digits used to group portions of the integer part of
857 * the number. For example, the number <code>123456</code>, with a grouping
858 * size of 3, is rendered <code>123,456</code>.
860 * @param groupSize The number of digits used while grouping portions
861 * of the integer part of a number.
863 public void setGroupingSize(int groupSize)
865 groupingSize = (byte) groupSize;
869 * Sets the maximum number of digits allowed in the integer
870 * portion of a number to the specified value.
871 * The new value will be the choosen as the minimum between
872 * <code>newvalue</code> and 309. Any value below zero will be
873 * replaced by zero.
875 * @param newValue The new maximum integer digits value.
877 public void setMaximumIntegerDigits(int newValue)
879 newValue = (newValue > 0) ? newValue : 0;
880 super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
884 * Sets the minimum number of digits allowed in the integer
885 * portion of a number to the specified value.
886 * The new value will be the choosen as the minimum between
887 * <code>newvalue</code> and 309. Any value below zero will be
888 * replaced by zero.
890 * @param newValue The new minimum integer digits value.
892 public void setMinimumIntegerDigits(int newValue)
894 newValue = (newValue > 0) ? newValue : 0;
895 super.setMinimumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
899 * Sets the maximum number of digits allowed in the fraction
900 * portion of a number to the specified value.
901 * The new value will be the choosen as the minimum between
902 * <code>newvalue</code> and 309. Any value below zero will be
903 * replaced by zero.
905 * @param newValue The new maximum fraction digits value.
907 public void setMaximumFractionDigits(int newValue)
909 newValue = (newValue > 0) ? newValue : 0;
910 super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
914 * Sets the minimum number of digits allowed in the fraction
915 * portion of a number to the specified value.
916 * The new value will be the choosen as the minimum between
917 * <code>newvalue</code> and 309. Any value below zero will be
918 * replaced by zero.
920 * @param newValue The new minimum fraction digits value.
922 public void setMinimumFractionDigits(int newValue)
924 newValue = (newValue > 0) ? newValue : 0;
925 super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
929 * Sets the multiplier for use in percent and similar formats.
930 * For example, for percent set the multiplier to 100, for permille, set the
931 * miltiplier to 1000.
933 * @param newValue the new value for multiplier.
935 public void setMultiplier(int newValue)
937 multiplier = newValue;
941 * Sets the negative prefix.
943 * @param newValue The new negative prefix.
945 public void setNegativePrefix(String newValue)
947 negativePrefix = newValue;
951 * Sets the negative suffix.
953 * @param newValue The new negative suffix.
955 public void setNegativeSuffix(String newValue)
957 negativeSuffix = newValue;
961 * Sets the positive prefix.
963 * @param newValue The new positive prefix.
965 public void setPositivePrefix(String newValue)
967 positivePrefix = newValue;
971 * Sets the new positive suffix.
973 * @param newValue The new positive suffix.
975 public void setPositiveSuffix(String newValue)
977 positiveSuffix = newValue;
981 * This method returns a string with the formatting pattern being used
982 * by this object. The string is localized.
984 * @return A localized <code>String</code> with the formatting pattern.
985 * @see #toPattern()
987 public String toLocalizedPattern()
989 return computePattern(this.symbols);
993 * This method returns a string with the formatting pattern being used
994 * by this object. The string is not localized.
996 * @return A <code>String</code> with the formatting pattern.
997 * @see #toLocalizedPattern()
999 public String toPattern()
1001 return computePattern(nonLocalizedSymbols);
1004 /* ***** private methods ***** */
1007 * This is an shortcut helper method used to test if two given strings are
1008 * equals.
1010 * @param s1 The first string to test for equality.
1011 * @param s2 The second string to test for equality.
1012 * @return <code>true</code> if the strings are both <code>null</code> or
1013 * equals.
1015 private boolean equals(String s1, String s2)
1017 if (s1 == null || s2 == null)
1018 return s1 == s2;
1019 return s1.equals(s2);
1023 /* ****** PATTERN ****** */
1026 * This helper function creates a string consisting of all the
1027 * characters which can appear in a pattern and must be quoted.
1029 private String patternChars (DecimalFormatSymbols syms)
1031 CPStringBuilder buf = new CPStringBuilder ();
1033 buf.append(syms.getDecimalSeparator());
1034 buf.append(syms.getDigit());
1035 buf.append(syms.getExponential());
1036 buf.append(syms.getGroupingSeparator());
1037 buf.append(syms.getMinusSign());
1038 buf.append(syms.getPatternSeparator());
1039 buf.append(syms.getPercent());
1040 buf.append(syms.getPerMill());
1041 buf.append(syms.getZeroDigit());
1042 buf.append('\'');
1043 buf.append('\u00a4');
1045 return buf.toString();
1049 * Quote special characters as defined by <code>patChars</code> in the
1050 * input string.
1052 * @param text
1053 * @param patChars
1054 * @return A StringBuffer with special characters quoted.
1056 private CPStringBuilder quoteFix(String text, String patChars)
1058 CPStringBuilder buf = new CPStringBuilder();
1060 int len = text.length();
1061 char ch;
1062 for (int index = 0; index < len; ++index)
1064 ch = text.charAt(index);
1065 if (patChars.indexOf(ch) != -1)
1067 buf.append('\'');
1068 buf.append(ch);
1069 if (ch != '\'') buf.append('\'');
1071 else
1073 buf.append(ch);
1077 return buf;
1081 * Returns the format pattern, localized to follow the given
1082 * symbols.
1084 private String computePattern(DecimalFormatSymbols symbols)
1086 StringBuilder mainPattern = new StringBuilder();
1088 // We have to at least emit a zero for the minimum number of
1089 // digits. Past that we need hash marks up to the grouping
1090 // separator (and one beyond).
1091 int _groupingSize = groupingUsed ? groupingSize + 1: groupingSize;
1092 int totalDigits = Math.max(minimumIntegerDigits, _groupingSize);
1094 // if it is not in exponential notiation,
1095 // we always have a # prebended
1096 if (!useExponentialNotation) mainPattern.append(symbols.getDigit());
1098 for (int i = 1; i < totalDigits - minimumIntegerDigits; i++)
1099 mainPattern.append(symbols.getDigit());
1101 for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++)
1102 mainPattern.append(symbols.getZeroDigit());
1104 if (groupingUsed)
1106 mainPattern.insert(mainPattern.length() - groupingSize,
1107 symbols.getGroupingSeparator());
1110 // See if we need decimal info.
1111 if (minimumFractionDigits > 0 || maximumFractionDigits > 0 ||
1112 decimalSeparatorAlwaysShown)
1114 mainPattern.append(symbols.getDecimalSeparator());
1117 for (int i = 0; i < minimumFractionDigits; ++i)
1118 mainPattern.append(symbols.getZeroDigit());
1120 for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
1121 mainPattern.append(symbols.getDigit());
1123 if (useExponentialNotation)
1125 mainPattern.append(symbols.getExponential());
1127 for (int i = 0; i < minExponentDigits; ++i)
1128 mainPattern.append(symbols.getZeroDigit());
1130 if (minExponentDigits == 0)
1131 mainPattern.append(symbols.getDigit());
1134 // save the pattern
1135 String pattern = mainPattern.toString();
1137 // so far we have the pattern itself, now we need to add
1138 // the positive and the optional negative prefixes and suffixes
1139 String patternChars = patternChars(symbols);
1140 mainPattern.insert(0, quoteFix(positivePrefix, patternChars));
1141 mainPattern.append(quoteFix(positiveSuffix, patternChars));
1143 if (hasNegativePrefix)
1145 mainPattern.append(symbols.getPatternSeparator());
1146 mainPattern.append(quoteFix(negativePrefix, patternChars));
1147 mainPattern.append(pattern);
1148 mainPattern.append(quoteFix(negativeSuffix, patternChars));
1151 // finally, return the pattern string
1152 return mainPattern.toString();
1155 /* ****** FORMAT PARSING ****** */
1158 * Scan the input string and define a pattern suitable for use
1159 * with this decimal format.
1161 * @param pattern
1162 * @param symbols
1164 private void applyPatternWithSymbols(String pattern,
1165 DecimalFormatSymbols symbols)
1167 // The pattern string is described by a BNF diagram.
1168 // we could use a recursive parser to read and prepare
1169 // the string, but this would be too slow and resource
1170 // intensive, while this code is quite critical as it is
1171 // called always when the class is instantiated and every
1172 // time a new pattern is given.
1173 // Our strategy is to divide the string into section as given by
1174 // the BNF diagram, iterating through the string and setting up
1175 // the parameters we need for formatting (which is basicly what
1176 // a descendent recursive parser would do - but without recursion).
1177 // I'm sure that there are smarter methods to do this.
1179 // Restore default values. Most of these will be overwritten
1180 // but we want to be sure that nothing is left out.
1181 setDefaultValues();
1183 int len = pattern.length();
1184 if (len == 0)
1186 // this is another special case...
1187 this.minimumIntegerDigits = 1;
1188 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
1189 this.minimumFractionDigits = 0;
1190 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
1192 // FIXME: ...and these values may not be valid in all locales
1193 this.minExponentDigits = 0;
1194 this.showDecimalSeparator = true;
1195 this.groupingUsed = true;
1196 this.groupingSize = 3;
1198 return;
1201 int start = scanFix(pattern, symbols, 0, true);
1202 if (start < len) start = scanNumberInteger(pattern, symbols, start);
1203 if (start < len)
1205 start = scanFractionalPortion(pattern, symbols, start);
1207 else
1209 // special case, pattern that ends here does not have a fractional
1210 // portion
1211 this.minimumFractionDigits = 0;
1212 this.maximumFractionDigits = 0;
1213 //this.decimalSeparatorAlwaysShown = false;
1214 //this.showDecimalSeparator = false;
1217 // XXX: this fixes a compatibility test with the RI.
1218 // If new uses cases fail, try removing this line first.
1219 //if (!this.hasIntegerPattern && !this.hasFractionalPattern)
1220 // throw new IllegalArgumentException("No valid pattern found!");
1222 if (start < len) start = scanExponent(pattern, symbols, start);
1223 if (start < len) start = scanFix(pattern, symbols, start, false);
1224 if (start < len) scanNegativePattern(pattern, symbols, start);
1226 if (useExponentialNotation &&
1227 (maxIntegerDigitsExponent > minimumIntegerDigits) &&
1228 (maxIntegerDigitsExponent > 1))
1230 minimumIntegerDigits = 1;
1231 exponentRound = maxIntegerDigitsExponent;
1234 if (useExponentialNotation)
1235 maximumIntegerDigits = maxIntegerDigitsExponent;
1237 if (!this.hasFractionalPattern && this.showDecimalSeparator == true)
1239 this.decimalSeparatorAlwaysShown = true;
1244 * Scans for the prefix or suffix portion of the pattern string.
1245 * This method handles the positive subpattern of the pattern string.
1247 * @param pattern The pattern string to parse.
1248 * @return The position in the pattern string where parsing ended.
1250 private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols,
1251 int start, boolean prefix)
1253 CPStringBuilder buffer = new CPStringBuilder();
1255 // the number portion is always delimited by one of those
1256 // characters
1257 char decimalSeparator = sourceSymbols.getDecimalSeparator();
1258 char patternSeparator = sourceSymbols.getPatternSeparator();
1259 char groupingSeparator = sourceSymbols.getGroupingSeparator();
1260 char digit = sourceSymbols.getDigit();
1261 char zero = sourceSymbols.getZeroDigit();
1262 char minus = sourceSymbols.getMinusSign();
1264 // other special characters, cached here to avoid method calls later
1265 char percent = sourceSymbols.getPercent();
1266 char permille = sourceSymbols.getPerMill();
1268 String currencySymbol = this.symbols.getCurrencySymbol();
1270 boolean quote = false;
1272 char ch = pattern.charAt(start);
1273 if (ch == patternSeparator)
1275 // negative subpattern
1276 this.hasNegativePrefix = true;
1277 ++start;
1278 return start;
1281 int len = pattern.length();
1282 int i;
1283 for (i = start; i < len; i++)
1285 ch = pattern.charAt(i);
1287 // we are entering into the negative subpattern
1288 if (!quote && ch == patternSeparator)
1290 if (this.hasNegativePrefix)
1292 throw new IllegalArgumentException("Invalid pattern found: "
1293 + start);
1296 this.hasNegativePrefix = true;
1297 ++i;
1298 break;
1301 // this means we are inside the number portion
1302 if (!quote &&
1303 (ch == minus || ch == digit || ch == zero ||
1304 ch == groupingSeparator))
1305 break;
1307 if (!quote && ch == decimalSeparator)
1309 this.showDecimalSeparator = true;
1310 break;
1312 else if (quote && ch != '\'')
1314 buffer.append(ch);
1315 continue;
1318 if (ch == '\u00A4')
1320 // CURRENCY
1321 currencySymbol = this.symbols.getCurrencySymbol();
1323 // if \u00A4 is doubled, we use the international currency symbol
1324 if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1326 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1327 i++;
1330 this.useCurrencySeparator = true;
1331 buffer.append(currencySymbol);
1333 else if (ch == percent)
1335 // PERCENT
1336 this.multiplier = 100;
1337 buffer.append(this.symbols.getPercent());
1339 else if (ch == permille)
1341 // PERMILLE
1342 this.multiplier = 1000;
1343 buffer.append(this.symbols.getPerMill());
1345 else if (ch == '\'')
1347 // QUOTE
1348 if ((i + 1) < len && pattern.charAt(i + 1) == '\'')
1350 // we need to add ' to the buffer
1351 buffer.append(ch);
1352 i++;
1354 else
1356 quote = !quote;
1357 continue;
1360 else
1362 buffer.append(ch);
1366 if (prefix)
1368 this.positivePrefix = buffer.toString();
1369 this.negativePrefix = minus + "" + positivePrefix;
1371 else
1373 this.positiveSuffix = buffer.toString();
1376 return i;
1380 * Scan the given string for number patterns, starting
1381 * from <code>start</code>.
1382 * This method searches the integer part of the pattern only.
1384 * @param pattern The pattern string to parse.
1385 * @param start The starting parse position in the string.
1386 * @return The position in the pattern string where parsing ended,
1387 * counted from the beginning of the string (that is, 0).
1389 private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols,
1390 int start)
1392 char digit = symbols.getDigit();
1393 char zero = symbols.getZeroDigit();
1394 char groupingSeparator = symbols.getGroupingSeparator();
1395 char decimalSeparator = symbols.getDecimalSeparator();
1396 char exponent = symbols.getExponential();
1397 char patternSeparator = symbols.getPatternSeparator();
1399 // count the number of zeroes in the pattern
1400 // this number defines the minum digits in the integer portion
1401 int zeros = 0;
1403 // count the number of digits used in grouping
1404 int _groupingSize = 0;
1406 this.maxIntegerDigitsExponent = 0;
1408 boolean intPartTouched = false;
1410 char ch;
1411 int len = pattern.length();
1412 int i;
1413 for (i = start; i < len; i++)
1415 ch = pattern.charAt(i);
1417 // break on decimal separator or exponent or pattern separator
1418 if (ch == decimalSeparator || ch == exponent)
1419 break;
1421 if (this.hasNegativePrefix && ch == patternSeparator)
1422 throw new IllegalArgumentException("Invalid pattern found: "
1423 + start);
1425 if (ch == digit)
1427 // in our implementation we could relax this strict
1428 // requirement, but this is used to keep compatibility with
1429 // the RI
1430 if (zeros > 0) throw new
1431 IllegalArgumentException("digit mark following zero in " +
1432 "positive subpattern, not allowed. Position: " + i);
1434 _groupingSize++;
1435 intPartTouched = true;
1436 this.maxIntegerDigitsExponent++;
1438 else if (ch == zero)
1440 zeros++;
1441 _groupingSize++;
1442 this.maxIntegerDigitsExponent++;
1444 else if (ch == groupingSeparator)
1446 this.groupingSeparatorInPattern = true;
1447 this.groupingUsed = true;
1448 _groupingSize = 0;
1450 else
1452 // any other character not listed above
1453 // means we are in the suffix portion
1454 break;
1458 if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize;
1459 this.minimumIntegerDigits = zeros;
1461 // XXX: compatibility code with the RI: the number of minimum integer
1462 // digits is at least one when maximumIntegerDigits is more than zero
1463 if (intPartTouched && this.maximumIntegerDigits > 0 &&
1464 this.minimumIntegerDigits == 0)
1465 this.minimumIntegerDigits = 1;
1467 return i;
1471 * Scan the given string for number patterns, starting
1472 * from <code>start</code>.
1473 * This method searches the fractional part of the pattern only.
1475 * @param pattern The pattern string to parse.
1476 * @param start The starting parse position in the string.
1477 * @return The position in the pattern string where parsing ended,
1478 * counted from the beginning of the string (that is, 0).
1480 private int scanFractionalPortion(String pattern,
1481 DecimalFormatSymbols symbols,
1482 int start)
1484 char digit = symbols.getDigit();
1485 char zero = symbols.getZeroDigit();
1486 char groupingSeparator = symbols.getGroupingSeparator();
1487 char decimalSeparator = symbols.getDecimalSeparator();
1488 char exponent = symbols.getExponential();
1489 char patternSeparator = symbols.getPatternSeparator();
1491 // first character needs to be '.' otherwise we are not parsing the
1492 // fractional portion
1493 char ch = pattern.charAt(start);
1494 if (ch != decimalSeparator)
1496 this.minimumFractionDigits = 0;
1497 this.maximumFractionDigits = 0;
1498 return start;
1501 ++start;
1503 this.hasFractionalPattern = true;
1505 this.minimumFractionDigits = 0;
1506 int digits = 0;
1508 int len = pattern.length();
1509 int i;
1510 for (i = start; i < len; i++)
1512 ch = pattern.charAt(i);
1514 // we hit the exponential or negative subpattern
1515 if (ch == exponent || ch == patternSeparator)
1516 break;
1518 // pattern error
1519 if (ch == groupingSeparator || ch == decimalSeparator) throw new
1520 IllegalArgumentException("unexpected character '" + ch + "' " +
1521 "in fractional subpattern. Position: " + i);
1523 if (ch == digit)
1525 digits++;
1527 else if (ch == zero)
1529 if (digits > 0) throw new
1530 IllegalArgumentException("digit mark following zero in " +
1531 "positive subpattern, not allowed. Position: " + i);
1533 this.minimumFractionDigits++;
1535 else
1537 // we are in the suffix section of pattern
1538 break;
1542 if (i == start) this.hasFractionalPattern = false;
1544 this.maximumFractionDigits = this.minimumFractionDigits + digits;
1545 this.showDecimalSeparator = true;
1547 return i;
1551 * Scan the given string for number patterns, starting
1552 * from <code>start</code>.
1553 * This method searches the expoential part of the pattern only.
1555 * @param pattern The pattern string to parse.
1556 * @param start The starting parse position in the string.
1557 * @return The position in the pattern string where parsing ended,
1558 * counted from the beginning of the string (that is, 0).
1560 private int scanExponent(String pattern, DecimalFormatSymbols symbols,
1561 int start)
1563 char digit = symbols.getDigit();
1564 char zero = symbols.getZeroDigit();
1565 char groupingSeparator = symbols.getGroupingSeparator();
1566 char decimalSeparator = symbols.getDecimalSeparator();
1567 char exponent = symbols.getExponential();
1569 char ch = pattern.charAt(start);
1571 if (ch == decimalSeparator)
1573 // ignore dots
1574 ++start;
1577 if (ch != exponent)
1579 this.useExponentialNotation = false;
1580 return start;
1583 ++start;
1585 this.minExponentDigits = 0;
1587 int len = pattern.length();
1588 int i;
1589 for (i = start; i < len; i++)
1591 ch = pattern.charAt(i);
1593 if (ch == groupingSeparator || ch == decimalSeparator ||
1594 ch == digit || ch == exponent) throw new
1595 IllegalArgumentException("unexpected character '" + ch + "' " +
1596 "in exponential subpattern. Position: " + i);
1598 if (ch == zero)
1600 this.minExponentDigits++;
1602 else
1604 // any character other than zero is an exit point
1605 break;
1609 this.useExponentialNotation = true;
1611 return i;
1615 * Scan the given string for number patterns, starting
1616 * from <code>start</code>.
1617 * This method searches the negative part of the pattern only and scan
1618 * throught the end of the string.
1620 * @param pattern The pattern string to parse.
1621 * @param start The starting parse position in the string.
1623 private void scanNegativePattern(String pattern,
1624 DecimalFormatSymbols sourceSymbols,
1625 int start)
1627 StringBuilder buffer = new StringBuilder();
1629 // the number portion is always delimited by one of those
1630 // characters
1631 char decimalSeparator = sourceSymbols.getDecimalSeparator();
1632 char patternSeparator = sourceSymbols.getPatternSeparator();
1633 char groupingSeparator = sourceSymbols.getGroupingSeparator();
1634 char digit = sourceSymbols.getDigit();
1635 char zero = sourceSymbols.getZeroDigit();
1636 char minus = sourceSymbols.getMinusSign();
1638 // other special charcaters, cached here to avoid method calls later
1639 char percent = sourceSymbols.getPercent();
1640 char permille = sourceSymbols.getPerMill();
1642 String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol();
1643 String currencySymbol = CURRENCY_SYMBOL;
1645 boolean quote = false;
1646 boolean prefixDone = false;
1648 int len = pattern.length();
1649 if (len > 0) this.hasNegativePrefix = true;
1651 char ch = pattern.charAt(start);
1652 if (ch == patternSeparator)
1654 // no pattern separator in the negative pattern
1655 if ((start + 1) > len) throw new
1656 IllegalArgumentException("unexpected character '" + ch + "' " +
1657 "in negative subpattern.");
1658 start++;
1661 int i;
1662 for (i = start; i < len; i++)
1664 ch = pattern.charAt(i);
1666 // this means we are inside the number portion
1667 if (!quote &&
1668 (ch == digit || ch == zero || ch == decimalSeparator ||
1669 ch == patternSeparator || ch == groupingSeparator))
1671 if (!prefixDone)
1673 this.negativePrefix = buffer.toString();
1674 buffer.delete(0, buffer.length());
1675 prefixDone = true;
1678 else if (ch == minus)
1680 buffer.append(this.symbols.getMinusSign());
1682 else if (quote && ch != '\'')
1684 buffer.append(ch);
1686 else if (ch == '\u00A4')
1688 // CURRENCY
1689 currencySymbol = CURRENCY_SYMBOL;
1691 // if \u00A4 is doubled, we use the international currency symbol
1692 if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1694 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1695 i = i + 2;
1698 // FIXME: not sure about this, the specs says that we only have to
1699 // change prefix and suffix, so leave it as commented
1700 // unless in case of bug report/errors
1701 //this.useCurrencySeparator = true;
1703 buffer.append(currencySymbol);
1705 else if (ch == percent)
1707 // PERCENT
1708 this.negativePatternMultiplier = 100;
1709 buffer.append(this.symbols.getPercent());
1711 else if (ch == permille)
1713 // PERMILLE
1714 this.negativePatternMultiplier = 1000;
1715 buffer.append(this.symbols.getPerMill());
1717 else if (ch == '\'')
1719 // QUOTE
1720 if ((i + 1) < len && pattern.charAt(i + 1) == '\'')
1722 // we need to add ' to the buffer
1723 buffer.append(ch);
1724 i++;
1726 else
1728 quote = !quote;
1731 else if (ch == patternSeparator)
1733 // no pattern separator in the negative pattern
1734 throw new IllegalArgumentException("unexpected character '" + ch +
1735 "' in negative subpattern.");
1737 else
1739 buffer.append(ch);
1743 if (prefixDone)
1744 this.negativeSuffix = buffer.toString();
1745 else
1746 this.negativePrefix = buffer.toString();
1749 /* ****** FORMATTING ****** */
1752 * Handles the real formatting.
1754 * We use a BigDecimal to format the number without precision loss.
1755 * All the rounding is done by methods in BigDecimal.
1756 * The <code>isLong</code> parameter is used to determine if we are
1757 * formatting a long or BigInteger. In this case, we avoid to format
1758 * the fractional part of the number (unless specified otherwise in the
1759 * format string) that would consist only of a 0 digit.
1761 * @param number A BigDecimal representation fo the input number.
1762 * @param dest The destination buffer.
1763 * @param isLong A boolean that indicates if this BigDecimal is a real
1764 * decimal or an integer.
1765 * @param fieldPos Use to keep track of the formatting position.
1767 private void formatInternal(BigDecimal number, boolean isLong,
1768 StringBuffer dest, FieldPosition fieldPos)
1770 // The specs says that fieldPos should not be null, and that we
1771 // should throw a NPE, but it seems that in few classes that
1772 // reference this one, fieldPos is set to null.
1773 // This is even defined in the javadoc, see for example MessageFormat.
1774 // I think the best here is to check for fieldPos and build one if it is
1775 // null. If it cause harms or regressions, just remove this line and
1776 // fix the classes in the point of call, insted.
1777 if (fieldPos == null) fieldPos = new FieldPosition(0);
1779 int _multiplier = this.multiplier;
1781 // used to track attribute starting position for each attribute
1782 int attributeStart = -1;
1784 // now get the sign this will be used by the special case Inifinity
1785 // and by the normal cases.
1786 boolean isNegative = (number.signum() < 0) ? true : false;
1787 if (isNegative)
1789 attributeStart = dest.length();
1791 // append the negative prefix to the string
1792 dest.append(negativePrefix);
1794 // once got the negative prefix, we can use
1795 // the absolute value.
1796 number = number.abs();
1798 _multiplier = negativePatternMultiplier;
1800 addAttribute(Field.SIGN, attributeStart, dest.length());
1802 else
1804 // not negative, use the positive prefix
1805 dest.append(positivePrefix);
1808 // these are used ot update the field position
1809 int beginIndexInt = dest.length();
1810 int endIndexInt = 0;
1811 int beginIndexFract = 0;
1812 int endIndexFract = 0;
1814 // compute the multiplier to use with percent and similar
1815 number = number.multiply(BigDecimal.valueOf(_multiplier));
1817 // XXX: special case, not sure if it belongs here or if it is
1818 // correct at all. There may be other special cases as well
1819 // these should be handled in the format string parser.
1820 if (this.maximumIntegerDigits == 0 && this.maximumFractionDigits == 0)
1822 number = BigDecimal.ZERO;
1823 this.maximumIntegerDigits = 1;
1824 this.minimumIntegerDigits = 1;
1827 // get the absolute number
1828 number = number.abs();
1830 // the scaling to use while formatting this number
1831 int scale = this.maximumFractionDigits;
1833 // this is the actual number we will use
1834 // it is corrected later on to handle exponential
1835 // notation, if needed
1836 long exponent = 0;
1838 // are we using exponential notation?
1839 if (this.useExponentialNotation)
1841 exponent = getExponent(number);
1842 number = number.movePointLeft((int) exponent);
1844 // FIXME: this makes the test ##.###E0 to pass,
1845 // but all all the other tests to fail...
1846 // this should be really something like
1847 // min + max - what is already shown...
1848 //scale = this.minimumIntegerDigits + this.maximumFractionDigits;
1851 // round the number to the nearest neighbor
1852 number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
1854 // now get the integer and fractional part of the string
1855 // that will be processed later
1856 String plain = number.toPlainString();
1858 String intPart = null;
1859 String fractPart = null;
1861 // remove - from the integer part, this is needed as
1862 // the Narrowing Primitive Conversions algorithm used may loose
1863 // information about the sign
1864 int minusIndex = plain.lastIndexOf('-', 0);
1865 if (minusIndex > -1) plain = plain.substring(minusIndex + 1);
1867 // strip the decimal portion
1868 int dot = plain.indexOf('.');
1869 if (dot > -1)
1871 intPart = plain.substring(0, dot);
1872 dot++;
1874 if (useExponentialNotation)
1875 fractPart = plain.substring(dot, dot + scale);
1876 else
1877 fractPart = plain.substring(dot);
1879 else
1881 intPart = plain;
1884 // used in various places later on
1885 int intPartLen = intPart.length();
1886 endIndexInt = intPartLen;
1888 // if the number of digits in our intPart is not greater than the
1889 // minimum we have to display, we append zero to the destination
1890 // buffer before adding the integer portion of the number.
1891 int zeroes = minimumIntegerDigits - intPartLen;
1892 if (zeroes > 0)
1894 attributeStart = Math.max(dest.length() - 1, 0);
1895 appendZero(dest, zeroes, minimumIntegerDigits);
1898 if (this.useExponentialNotation)
1900 // For exponential numbers, the significant in mantissa are
1901 // the sum of the minimum integer and maximum fraction
1902 // digits, and does not take into account the maximun integer
1903 // digits to display.
1905 if (attributeStart < 0)
1906 attributeStart = Math.max(dest.length() - 1, 0);
1907 appendDigit(intPart, dest, this.groupingUsed);
1909 else
1911 // non exponential notation
1912 intPartLen = intPart.length();
1913 int canary = Math.min(intPartLen, this.maximumIntegerDigits);
1915 // remove from the string the number in excess
1916 // use only latest digits
1917 intPart = intPart.substring(intPartLen - canary);
1918 endIndexInt = intPart.length() + 1;
1920 // append it
1921 if (maximumIntegerDigits > 0 &&
1922 !(this.minimumIntegerDigits == 0 &&
1923 intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0))
1925 if (attributeStart < 0)
1926 attributeStart = Math.max(dest.length() - 1, 0);
1927 appendDigit(intPart, dest, this.groupingUsed);
1931 // add the INTEGER attribute
1932 addAttribute(Field.INTEGER, attributeStart, dest.length());
1934 // ...update field position, if needed, and return...
1935 if ((fieldPos.getField() == INTEGER_FIELD ||
1936 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
1938 fieldPos.setBeginIndex(beginIndexInt);
1939 fieldPos.setEndIndex(endIndexInt);
1942 handleFractionalPart(dest, fractPart, fieldPos, isLong);
1944 // and the exponent
1945 if (this.useExponentialNotation)
1947 attributeStart = dest.length();
1949 dest.append(symbols.getExponential());
1951 addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
1952 attributeStart = dest.length();
1954 if (exponent < 0)
1956 dest.append(symbols.getMinusSign());
1957 exponent = -exponent;
1959 addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
1962 attributeStart = dest.length();
1964 String exponentString = String.valueOf(exponent);
1965 int exponentLength = exponentString.length();
1967 for (int i = 0; i < minExponentDigits - exponentLength; i++)
1968 dest.append(symbols.getZeroDigit());
1970 for (int i = 0; i < exponentLength; ++i)
1971 dest.append(exponentString.charAt(i));
1973 addAttribute(Field.EXPONENT, attributeStart, dest.length());
1976 // now include the suffixes...
1977 if (isNegative)
1979 dest.append(negativeSuffix);
1981 else
1983 dest.append(positiveSuffix);
1988 * Add to the input buffer the result of formatting the fractional
1989 * portion of the number.
1991 * @param dest
1992 * @param fractPart
1993 * @param fieldPos
1994 * @param isLong
1996 private void handleFractionalPart(StringBuffer dest, String fractPart,
1997 FieldPosition fieldPos, boolean isLong)
1999 int dotStart = 0;
2000 int dotEnd = 0;
2001 boolean addDecimal = false;
2003 if (this.decimalSeparatorAlwaysShown ||
2004 ((!isLong || this.useExponentialNotation) &&
2005 this.showDecimalSeparator && this.maximumFractionDigits > 0) ||
2006 this.minimumFractionDigits > 0)
2008 dotStart = dest.length();
2010 if (this.useCurrencySeparator)
2011 dest.append(symbols.getMonetaryDecimalSeparator());
2012 else
2013 dest.append(symbols.getDecimalSeparator());
2015 dotEnd = dest.length();
2016 addDecimal = true;
2019 // now handle the fraction portion of the number
2020 int fractStart = 0;
2021 int fractEnd = 0;
2022 boolean addFractional = false;
2024 if ((!isLong || this.useExponentialNotation)
2025 && this.maximumFractionDigits > 0
2026 || this.minimumFractionDigits > 0)
2028 fractStart = dest.length();
2029 fractEnd = fractStart;
2031 int digits = this.minimumFractionDigits;
2033 if (this.useExponentialNotation)
2035 digits = (this.minimumIntegerDigits + this.minimumFractionDigits)
2036 - dest.length();
2037 if (digits < 0) digits = 0;
2040 fractPart = adjustTrailingZeros(fractPart, digits);
2042 // FIXME: this code must be improved
2043 // now check if the factional part is just 0, in this case
2044 // we need to remove the '.' unless requested
2045 boolean allZeros = true;
2046 char fracts[] = fractPart.toCharArray();
2047 for (int i = 0; i < fracts.length; i++)
2049 if (fracts[i] != '0')
2050 allZeros = false;
2053 if (!allZeros || (minimumFractionDigits > 0))
2055 appendDigit(fractPart, dest, false);
2056 fractEnd = dest.length();
2058 addDecimal = true;
2059 addFractional = true;
2061 else if (!this.decimalSeparatorAlwaysShown)
2063 dest.deleteCharAt(dest.length() - 1);
2064 addDecimal = false;
2066 else
2068 fractEnd = dest.length();
2069 addFractional = true;
2073 if (addDecimal)
2074 addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
2076 if (addFractional)
2077 addAttribute(Field.FRACTION, fractStart, fractEnd);
2079 if ((fieldPos.getField() == FRACTION_FIELD ||
2080 fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION))
2082 fieldPos.setBeginIndex(fractStart);
2083 fieldPos.setEndIndex(fractEnd);
2088 * Append to <code>dest</code>the give number of zeros.
2089 * Grouping is added if needed.
2090 * The integer totalDigitCount defines the total number of digits
2091 * of the number to which we are appending zeroes.
2093 private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount)
2095 char ch = symbols.getZeroDigit();
2096 char gSeparator = symbols.getGroupingSeparator();
2098 int i = 0;
2099 int gPos = totalDigitCount;
2100 for (i = 0; i < zeroes; i++, gPos--)
2102 if (this.groupingSeparatorInPattern &&
2103 (this.groupingUsed && this.groupingSize != 0) &&
2104 (gPos % groupingSize == 0 && i > 0))
2105 dest.append(gSeparator);
2107 dest.append(ch);
2110 // special case, that requires adding an additional separator
2111 if (this.groupingSeparatorInPattern &&
2112 (this.groupingUsed && this.groupingSize != 0) &&
2113 (gPos % groupingSize == 0))
2114 dest.append(gSeparator);
2118 * Append src to <code>dest</code>.
2120 * Grouping is added if <code>groupingUsed</code> is set
2121 * to <code>true</code>.
2123 private void appendDigit(String src, StringBuffer dest,
2124 boolean groupingUsed)
2126 int zero = symbols.getZeroDigit() - '0';
2128 int ch;
2129 char gSeparator = symbols.getGroupingSeparator();
2131 int len = src.length();
2132 for (int i = 0, gPos = len; i < len; i++, gPos--)
2134 ch = src.charAt(i);
2135 if (groupingUsed && this.groupingSize != 0 &&
2136 gPos % groupingSize == 0 && i > 0)
2137 dest.append(gSeparator);
2139 dest.append((char) (zero + ch));
2144 * Calculate the exponent to use if eponential notation is used.
2145 * The exponent is calculated as a power of ten.
2146 * <code>number</code> should be positive, if is zero, or less than zero,
2147 * zero is returned.
2149 private long getExponent(BigDecimal number)
2151 long exponent = 0;
2153 if (number.signum() > 0)
2155 double _number = number.doubleValue();
2156 exponent = (long) Math.floor (Math.log10(_number));
2158 // get the right value for the exponent
2159 exponent = exponent - (exponent % this.exponentRound);
2161 // if the minimumIntegerDigits is more than zero
2162 // we display minimumIntegerDigits of digits.
2163 // so, for example, if minimumIntegerDigits == 2
2164 // and the actual number is 0.123 it will be
2165 // formatted as 12.3E-2
2166 // this means that the exponent have to be shifted
2167 // to the correct value.
2168 if (minimumIntegerDigits > 0)
2169 exponent -= minimumIntegerDigits - 1;
2172 return exponent;
2176 * Remove contiguos zeros from the end of the <code>src</code> string,
2177 * if src contains more than <code>minimumDigits</code> digits.
2178 * if src contains less that <code>minimumDigits</code>,
2179 * then append zeros to the string.
2181 * Only the first block of zero digits is removed from the string
2182 * and only if they fall in the src.length - minimumDigits
2183 * portion of the string.
2185 * @param src The string with the correct number of zeros.
2187 private String adjustTrailingZeros(String src, int minimumDigits)
2189 int len = src.length();
2190 String result;
2192 // remove all trailing zero
2193 if (len > minimumDigits)
2195 int zeros = 0;
2196 for (int i = len - 1; i > minimumDigits; i--)
2198 if (src.charAt(i) == '0')
2199 ++zeros;
2200 else
2201 break;
2203 result = src.substring(0, len - zeros);
2205 else
2207 char zero = symbols.getZeroDigit();
2208 CPStringBuilder _result = new CPStringBuilder(src);
2209 for (int i = len; i < minimumDigits; i++)
2211 _result.append(zero);
2213 result = _result.toString();
2216 return result;
2220 * Adds an attribute to the attributes list.
2222 * @param field
2223 * @param begin
2224 * @param end
2226 private void addAttribute(Field field, int begin, int end)
2229 * This method and its implementation derives directly from the
2230 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
2233 FieldPosition pos = new FieldPosition(field);
2234 pos.setBeginIndex(begin);
2235 pos.setEndIndex(end);
2236 attributes.add(pos);
2240 * Sets the default values for the various properties in this DecimaFormat.
2242 private void setDefaultValues()
2244 // Maybe we should add these values to the message bundle and take
2245 // the most appropriate for them for any locale.
2246 // Anyway, these seem to be good values for a default in most languages.
2247 // Note that most of these will change based on the format string.
2249 this.negativePrefix = String.valueOf(symbols.getMinusSign());
2250 this.negativeSuffix = "";
2251 this.positivePrefix = "";
2252 this.positiveSuffix = "";
2254 this.multiplier = 1;
2255 this.negativePatternMultiplier = 1;
2256 this.exponentRound = 1;
2258 this.hasNegativePrefix = false;
2260 this.minimumIntegerDigits = 1;
2261 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
2262 this.minimumFractionDigits = 0;
2263 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
2264 this.minExponentDigits = 0;
2266 this.groupingSize = 0;
2268 this.decimalSeparatorAlwaysShown = false;
2269 this.showDecimalSeparator = false;
2270 this.useExponentialNotation = false;
2271 this.groupingUsed = false;
2272 this.groupingSeparatorInPattern = false;
2274 this.useCurrencySeparator = false;
2276 this.hasFractionalPattern = false;