001 /* DecimalFormat.java -- Formats and parses numbers
002 Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038 /*
039 * This class contains few bits from ICU4J (http://icu.sourceforge.net/),
040 * Copyright by IBM and others and distributed under the
041 * distributed under MIT/X.
042 */
043
044 package java.text;
045
046 import gnu.java.lang.CPStringBuilder;
047
048 import java.math.BigDecimal;
049 import java.math.BigInteger;
050
051 import java.util.ArrayList;
052 import java.util.Currency;
053 import java.util.Locale;
054
055 /*
056 * This note is here for historical reasons and because I had not the courage
057 * to remove it :)
058 *
059 * @author Tom Tromey (tromey@cygnus.com)
060 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
061 * @date March 4, 1999
062 *
063 * Written using "Java Class Libraries", 2nd edition, plus online
064 * API docs for JDK 1.2 from http://www.javasoft.com.
065 * Status: Believed complete and correct to 1.2.
066 * Note however that the docs are very unclear about how format parsing
067 * should work. No doubt there are problems here.
068 */
069
070 /**
071 * This class is a concrete implementation of NumberFormat used to format
072 * decimal numbers. The class can format numbers given a specific locale.
073 * Generally, to get an instance of DecimalFormat you should call the factory
074 * methods in the <code>NumberFormat</code> base class.
075 *
076 * @author Mario Torre (neugens@limasoftware.net)
077 * @author Tom Tromey (tromey@cygnus.com)
078 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
079 */
080 public class DecimalFormat extends NumberFormat
081 {
082 /** serialVersionUID for serializartion. */
083 private static final long serialVersionUID = 864413376551465018L;
084
085 /** Defines the default number of digits allowed while formatting integers. */
086 private static final int DEFAULT_INTEGER_DIGITS = 309;
087
088 /**
089 * Defines the default number of digits allowed while formatting
090 * fractions.
091 */
092 private static final int DEFAULT_FRACTION_DIGITS = 340;
093
094 /**
095 * Locale-independent pattern symbols.
096 */
097 // Happen to be the same as the US symbols.
098 private static final DecimalFormatSymbols nonLocalizedSymbols
099 = new DecimalFormatSymbols (Locale.US);
100
101 /**
102 * Defines if parse should return a BigDecimal or not.
103 */
104 private boolean parseBigDecimal;
105
106 /**
107 * Defines if we have to use the monetary decimal separator or
108 * the decimal separator while formatting numbers.
109 */
110 private boolean useCurrencySeparator;
111
112 /** Defines if the decimal separator is always shown or not. */
113 private boolean decimalSeparatorAlwaysShown;
114
115 /**
116 * Defines if the decimal separator has to be shown.
117 *
118 * This is different then <code>decimalSeparatorAlwaysShown</code>,
119 * as it defines if the format string contains a decimal separator or no.
120 */
121 private boolean showDecimalSeparator;
122
123 /**
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.
127 */
128 private boolean groupingSeparatorInPattern;
129
130 /** Defines the size of grouping groups when grouping is used. */
131 private byte groupingSize;
132
133 /**
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>
137 */
138 private byte minExponentDigits;
139
140 /** This field is used to set the exponent in the engineering notation. */
141 private int exponentRound;
142
143 /** Multiplier used in percent style formats. */
144 private int multiplier;
145
146 /** Multiplier used in percent style formats. */
147 private int negativePatternMultiplier;
148
149 /** The negative prefix. */
150 private String negativePrefix;
151
152 /** The negative suffix. */
153 private String negativeSuffix;
154
155 /** The positive prefix. */
156 private String positivePrefix;
157
158 /** The positive suffix. */
159 private String positiveSuffix;
160
161 /** Decimal Format Symbols for the given locale. */
162 private DecimalFormatSymbols symbols;
163
164 /** Determine if we have to use exponential notation or not. */
165 private boolean useExponentialNotation;
166
167 /**
168 * Defines the maximum number of integer digits to show when we use
169 * the exponential notation.
170 */
171 private int maxIntegerDigitsExponent;
172
173 /** Defines if the format string has a negative prefix or not. */
174 private boolean hasNegativePrefix;
175
176 /** Defines if the format string has a fractional pattern or not. */
177 private boolean hasFractionalPattern;
178
179 /** Stores a list of attributes for use by formatToCharacterIterator. */
180 private ArrayList attributes = new ArrayList();
181
182 /**
183 * Constructs a <code>DecimalFormat</code> which uses the default
184 * pattern and symbols.
185 */
186 public DecimalFormat()
187 {
188 this ("#,##0.###");
189 }
190
191 /**
192 * Constructs a <code>DecimalFormat</code> which uses the given
193 * pattern and the default symbols for formatting and parsing.
194 *
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.
198 */
199 public DecimalFormat(String pattern)
200 {
201 this (pattern, new DecimalFormatSymbols());
202 }
203
204 /**
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.
208 *
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.
213 */
214 public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
215 {
216 this.symbols = (DecimalFormatSymbols) symbols.clone();
217 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
218 }
219
220 /**
221 * Apply the given localized patern to the current DecimalFormat object.
222 *
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.
226 */
227 public void applyLocalizedPattern (String pattern)
228 {
229 applyPatternWithSymbols(pattern, this.symbols);
230 }
231
232 /**
233 * Apply the given localized pattern to the current DecimalFormat object.
234 *
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.
238 */
239 public void applyPattern(String pattern)
240 {
241 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
242 }
243
244 public Object clone()
245 {
246 DecimalFormat c = (DecimalFormat) super.clone();
247 c.symbols = (DecimalFormatSymbols) symbols.clone();
248 return c;
249 }
250
251 /**
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>
259 *
260 * @param obj the object (<code>null</code> permitted).
261 *
262 * @return A boolean.
263 */
264 public boolean equals(Object obj)
265 {
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));
293 }
294
295 /**
296 * Returns a hash code for this object.
297 *
298 * @return A hash code.
299 */
300 public int hashCode()
301 {
302 return toPattern().hashCode();
303 }
304
305 /**
306 * Produce a formatted {@link String} representation of this object.
307 * The passed object must be of type number.
308 *
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.
314 */
315 public final StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
316 {
317 if (obj instanceof BigInteger)
318 {
319 BigDecimal decimal = new BigDecimal((BigInteger) obj);
320 formatInternal(decimal, true, sbuf, pos);
321 return sbuf;
322 }
323 else if (obj instanceof BigDecimal)
324 {
325 formatInternal((BigDecimal) obj, true, sbuf, pos);
326 return sbuf;
327 }
328
329 return super.format(obj, sbuf, pos);
330 }
331
332 /**
333 * Produce a formatted {@link String} representation of this double.
334 *
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
341 */
342 public StringBuffer format(double number, StringBuffer dest,
343 FieldPosition fieldPos)
344 {
345 // special cases for double: NaN and negative or positive infinity
346 if (Double.isNaN(number))
347 {
348 // 1. NaN
349 String nan = symbols.getNaN();
350 dest.append(nan);
351
352 // update field position if required
353 if ((fieldPos.getField() == INTEGER_FIELD ||
354 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
355 {
356 int index = dest.length();
357 fieldPos.setBeginIndex(index - nan.length());
358 fieldPos.setEndIndex(index);
359 }
360 }
361 else if (Double.isInfinite(number))
362 {
363 // 2. Infinity
364 if (number < 0)
365 dest.append(this.negativePrefix);
366 else
367 dest.append(this.positivePrefix);
368
369 dest.append(symbols.getInfinity());
370
371 if (number < 0)
372 dest.append(this.negativeSuffix);
373 else
374 dest.append(this.positiveSuffix);
375
376 if ((fieldPos.getField() == INTEGER_FIELD ||
377 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
378 {
379 fieldPos.setBeginIndex(dest.length());
380 fieldPos.setEndIndex(0);
381 }
382 }
383 else
384 {
385 // get the number as a BigDecimal
386 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
387 formatInternal(bigDecimal, false, dest, fieldPos);
388 }
389
390 return dest;
391 }
392
393 /**
394 * Produce a formatted {@link String} representation of this long.
395 *
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.
401 */
402 public StringBuffer format(long number, StringBuffer dest,
403 FieldPosition fieldPos)
404 {
405 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
406 formatInternal(bigDecimal, true, dest, fieldPos);
407 return dest;
408 }
409
410 /**
411 * Return an <code>AttributedCharacterIterator</code> as a result of
412 * the formatting of the passed {@link Object}.
413 *
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}.
418 */
419 public AttributedCharacterIterator formatToCharacterIterator(Object value)
420 {
421 /*
422 * This method implementation derives directly from the
423 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
424 */
425
426 if (value == null)
427 throw new NullPointerException("Passed Object is null");
428
429 if (!(value instanceof Number)) throw new
430 IllegalArgumentException("Cannot format given Object as a Number");
431
432 StringBuffer text = new StringBuffer();
433 attributes.clear();
434 super.format(value, text, new FieldPosition(0));
435
436 AttributedString as = new AttributedString(text.toString());
437
438 // add NumberFormat field attributes to the AttributedString
439 for (int i = 0; i < attributes.size(); i++)
440 {
441 FieldPosition pos = (FieldPosition) attributes.get(i);
442 Format.Field attribute = pos.getFieldAttribute();
443
444 as.addAttribute(attribute, attribute, pos.getBeginIndex(),
445 pos.getEndIndex());
446 }
447
448 // return the CharacterIterator from AttributedString
449 return as.getIterator();
450 }
451
452 /**
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>.
456 *
457 * @return A new instance of <code>Currency</code> if
458 * the currency code matches a known one, null otherwise.
459 */
460 public Currency getCurrency()
461 {
462 return symbols.getCurrency();
463 }
464
465 /**
466 * Returns a copy of the symbols used by this instance.
467 *
468 * @return A copy of the symbols.
469 */
470 public DecimalFormatSymbols getDecimalFormatSymbols()
471 {
472 return (DecimalFormatSymbols) symbols.clone();
473 }
474
475 /**
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.
479 *
480 * The actual character used as grouping separator depends on the
481 * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()}
482 *
483 * @return The interval used between a grouping separator and the next.
484 */
485 public int getGroupingSize()
486 {
487 return groupingSize;
488 }
489
490 /**
491 * Gets the multiplier used in percent and similar formats.
492 *
493 * @return The multiplier used in percent and similar formats.
494 */
495 public int getMultiplier()
496 {
497 return multiplier;
498 }
499
500 /**
501 * Gets the negative prefix.
502 *
503 * @return The negative prefix.
504 */
505 public String getNegativePrefix()
506 {
507 return negativePrefix;
508 }
509
510 /**
511 * Gets the negative suffix.
512 *
513 * @return The negative suffix.
514 */
515 public String getNegativeSuffix()
516 {
517 return negativeSuffix;
518 }
519
520 /**
521 * Gets the positive prefix.
522 *
523 * @return The positive prefix.
524 */
525 public String getPositivePrefix()
526 {
527 return positivePrefix;
528 }
529
530 /**
531 * Gets the positive suffix.
532 *
533 * @return The positive suffix.
534 */
535 public String getPositiveSuffix()
536 {
537 return positiveSuffix;
538 }
539
540 public boolean isDecimalSeparatorAlwaysShown()
541 {
542 return decimalSeparatorAlwaysShown;
543 }
544
545 /**
546 * Define if <code>parse(java.lang.String, java.text.ParsePosition)</code>
547 * should return a {@link BigDecimal} or not.
548 *
549 * @param newValue
550 */
551 public void setParseBigDecimal(boolean newValue)
552 {
553 this.parseBigDecimal = newValue;
554 }
555
556 /**
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>.
561 *
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)
566 */
567 public boolean isParseBigDecimal()
568 {
569 return this.parseBigDecimal;
570 }
571
572 /**
573 * This method parses the specified string into a <code>Number</code>.
574 *
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.
579 *
580 * @param str The string to parse.
581 * @param pos The desired <code>ParsePosition</code>.
582 *
583 * @return The parsed <code>Number</code>
584 */
585 public Number parse(String str, ParsePosition pos)
586 {
587 // a special values before anything else
588 // NaN
589 if (str.contains(this.symbols.getNaN()))
590 return Double.valueOf(Double.NaN);
591
592 // this will be our final number
593 CPStringBuilder number = new CPStringBuilder();
594
595 // special character
596 char minus = symbols.getMinusSign();
597
598 // starting parsing position
599 int start = pos.getIndex();
600
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);
606
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();
611
612 boolean isNegative = str.startsWith(_negativePrefix);
613 boolean isPositive = str.startsWith(positivePrefix);
614
615 if (isPositive && isNegative)
616 {
617 // By checking this way, we preserve ambiguity in the case
618 // where the negative format differs only in suffix.
619 if (negativeLen > positiveLen)
620 {
621 start += _negativePrefix.length();
622 isNegative = true;
623 }
624 else
625 {
626 start += positivePrefix.length();
627 isPositive = true;
628 if (negativeLen < positiveLen)
629 isNegative = false;
630 }
631 }
632 else if (isNegative)
633 {
634 start += _negativePrefix.length();
635 isPositive = false;
636 }
637 else if (isPositive)
638 {
639 start += positivePrefix.length();
640 isNegative = false;
641 }
642 else
643 {
644 pos.setErrorIndex(start);
645 return null;
646 }
647
648 // other special characters used by the parser
649 char decimalSeparator = symbols.getDecimalSeparator();
650 char zero = symbols.getZeroDigit();
651 char exponent = symbols.getExponential();
652
653 // stop parsing position in the string
654 int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2;
655
656 if (useExponentialNotation)
657 stop += minExponentDigits + 1;
658
659 boolean inExponent = false;
660
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();
665
666 int i = start;
667 while (i < stop)
668 {
669 char ch = str.charAt(i);
670 i++;
671
672 if (ch >= zero && ch <= (zero + 9))
673 {
674 number.append(ch);
675 }
676 else if (this.parseIntegerOnly)
677 {
678 i--;
679 break;
680 }
681 else if (ch == decimalSeparator)
682 {
683 number.append('.');
684 }
685 else if (ch == exponent)
686 {
687 number.append(ch);
688 inExponent = !inExponent;
689 }
690 else if ((ch == '+' || ch == '-' || ch == minus))
691 {
692 if (inExponent)
693 number.append(ch);
694 else
695 {
696 i--;
697 break;
698 }
699 }
700 else
701 {
702 if (!groupingUsed || ch != groupingSeparator)
703 {
704 i--;
705 break;
706 }
707 }
708 }
709
710 // 2nd special case: infinity
711 // XXX: need to be tested
712 if (str.contains(symbols.getInfinity()))
713 {
714 int inf = str.indexOf(symbols.getInfinity());
715 pos.setIndex(inf);
716
717 // FIXME: ouch, this is really ugly and lazy code...
718 if (this.parseBigDecimal)
719 {
720 if (isNegative)
721 return BigDecimal.valueOf(Double.NEGATIVE_INFINITY);
722
723 return BigDecimal.valueOf(Double.POSITIVE_INFINITY);
724 }
725
726 if (isNegative)
727 return Double.valueOf(Double.NEGATIVE_INFINITY);
728
729 return Double.valueOf(Double.POSITIVE_INFINITY);
730 }
731
732 // no number...
733 if (i == start || number.length() == 0)
734 {
735 pos.setErrorIndex(i);
736 return null;
737 }
738
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);
744
745 positiveLen = positiveSuffix.length();
746 negativeLen = negativeSuffix.length();
747
748 if (isNegative && !hasNegativeSuffix)
749 {
750 pos.setErrorIndex(i);
751 return null;
752 }
753 else if (hasNegativeSuffix &&
754 !positiveEqualsNegative &&
755 (negativeLen > positiveLen))
756 {
757 isNegative = true;
758 }
759 else if (!hasPositiveSuffix)
760 {
761 pos.setErrorIndex(i);
762 return null;
763 }
764
765 if (isNegative) number.insert(0, '-');
766
767 pos.setIndex(i);
768
769 // now we handle the return type
770 BigDecimal bigDecimal = new BigDecimal(number.toString());
771 if (this.parseBigDecimal)
772 return bigDecimal;
773
774 // want integer?
775 if (this.parseIntegerOnly)
776 return Long.valueOf(bigDecimal.longValue());
777
778 // 3th special case -0.0
779 if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0))
780 return Double.valueOf(-0.0);
781
782 try
783 {
784 BigDecimal integer
785 = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY);
786 return Long.valueOf(integer.longValue());
787 }
788 catch (ArithmeticException e)
789 {
790 return Double.valueOf(bigDecimal.doubleValue());
791 }
792 }
793
794 /**
795 * Sets the <code>Currency</code> on the
796 * <code>DecimalFormatSymbols</code> used, which also sets the
797 * currency symbols on those symbols.
798 *
799 * @param currency The new <code>Currency</code> on the
800 * <code>DecimalFormatSymbols</code>.
801 */
802 public void setCurrency(Currency currency)
803 {
804 Currency current = symbols.getCurrency();
805 if (current != currency)
806 {
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);
827 }
828 }
829
830 /**
831 * Sets the symbols used by this instance. This method makes a copy of
832 * the supplied symbols.
833 *
834 * @param newSymbols the symbols (<code>null</code> not permitted).
835 */
836 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
837 {
838 symbols = (DecimalFormatSymbols) newSymbols.clone();
839 }
840
841 /**
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.
846 *
847 * @param newValue true</code> if you want the decimal separator to be
848 * always shown, <code>false</code> otherwise.
849 */
850 public void setDecimalSeparatorAlwaysShown(boolean newValue)
851 {
852 decimalSeparatorAlwaysShown = newValue;
853 }
854
855 /**
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>.
859 *
860 * @param groupSize The number of digits used while grouping portions
861 * of the integer part of a number.
862 */
863 public void setGroupingSize(int groupSize)
864 {
865 groupingSize = (byte) groupSize;
866 }
867
868 /**
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.
874 *
875 * @param newValue The new maximum integer digits value.
876 */
877 public void setMaximumIntegerDigits(int newValue)
878 {
879 newValue = (newValue > 0) ? newValue : 0;
880 super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
881 }
882
883 /**
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.
889 *
890 * @param newValue The new minimum integer digits value.
891 */
892 public void setMinimumIntegerDigits(int newValue)
893 {
894 newValue = (newValue > 0) ? newValue : 0;
895 super.setMinimumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
896 }
897
898 /**
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.
904 *
905 * @param newValue The new maximum fraction digits value.
906 */
907 public void setMaximumFractionDigits(int newValue)
908 {
909 newValue = (newValue > 0) ? newValue : 0;
910 super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
911 }
912
913 /**
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.
919 *
920 * @param newValue The new minimum fraction digits value.
921 */
922 public void setMinimumFractionDigits(int newValue)
923 {
924 newValue = (newValue > 0) ? newValue : 0;
925 super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
926 }
927
928 /**
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.
932 *
933 * @param newValue the new value for multiplier.
934 */
935 public void setMultiplier(int newValue)
936 {
937 multiplier = newValue;
938 }
939
940 /**
941 * Sets the negative prefix.
942 *
943 * @param newValue The new negative prefix.
944 */
945 public void setNegativePrefix(String newValue)
946 {
947 negativePrefix = newValue;
948 }
949
950 /**
951 * Sets the negative suffix.
952 *
953 * @param newValue The new negative suffix.
954 */
955 public void setNegativeSuffix(String newValue)
956 {
957 negativeSuffix = newValue;
958 }
959
960 /**
961 * Sets the positive prefix.
962 *
963 * @param newValue The new positive prefix.
964 */
965 public void setPositivePrefix(String newValue)
966 {
967 positivePrefix = newValue;
968 }
969
970 /**
971 * Sets the new positive suffix.
972 *
973 * @param newValue The new positive suffix.
974 */
975 public void setPositiveSuffix(String newValue)
976 {
977 positiveSuffix = newValue;
978 }
979
980 /**
981 * This method returns a string with the formatting pattern being used
982 * by this object. The string is localized.
983 *
984 * @return A localized <code>String</code> with the formatting pattern.
985 * @see #toPattern()
986 */
987 public String toLocalizedPattern()
988 {
989 return computePattern(this.symbols);
990 }
991
992 /**
993 * This method returns a string with the formatting pattern being used
994 * by this object. The string is not localized.
995 *
996 * @return A <code>String</code> with the formatting pattern.
997 * @see #toLocalizedPattern()
998 */
999 public String toPattern()
1000 {
1001 return computePattern(nonLocalizedSymbols);
1002 }
1003
1004 /* ***** private methods ***** */
1005
1006 /**
1007 * This is an shortcut helper method used to test if two given strings are
1008 * equals.
1009 *
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.
1014 */
1015 private boolean equals(String s1, String s2)
1016 {
1017 if (s1 == null || s2 == null)
1018 return s1 == s2;
1019 return s1.equals(s2);
1020 }
1021
1022
1023 /* ****** PATTERN ****** */
1024
1025 /**
1026 * This helper function creates a string consisting of all the
1027 * characters which can appear in a pattern and must be quoted.
1028 */
1029 private String patternChars (DecimalFormatSymbols syms)
1030 {
1031 CPStringBuilder buf = new CPStringBuilder ();
1032
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');
1044
1045 return buf.toString();
1046 }
1047
1048 /**
1049 * Quote special characters as defined by <code>patChars</code> in the
1050 * input string.
1051 *
1052 * @param text
1053 * @param patChars
1054 * @return A StringBuffer with special characters quoted.
1055 */
1056 private CPStringBuilder quoteFix(String text, String patChars)
1057 {
1058 CPStringBuilder buf = new CPStringBuilder();
1059
1060 int len = text.length();
1061 char ch;
1062 for (int index = 0; index < len; ++index)
1063 {
1064 ch = text.charAt(index);
1065 if (patChars.indexOf(ch) != -1)
1066 {
1067 buf.append('\'');
1068 buf.append(ch);
1069 if (ch != '\'') buf.append('\'');
1070 }
1071 else
1072 {
1073 buf.append(ch);
1074 }
1075 }
1076
1077 return buf;
1078 }
1079
1080 /**
1081 * Returns the format pattern, localized to follow the given
1082 * symbols.
1083 */
1084 private String computePattern(DecimalFormatSymbols symbols)
1085 {
1086 StringBuilder mainPattern = new StringBuilder();
1087
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);
1093
1094 // if it is not in exponential notiation,
1095 // we always have a # prebended
1096 if (!useExponentialNotation) mainPattern.append(symbols.getDigit());
1097
1098 for (int i = 1; i < totalDigits - minimumIntegerDigits; i++)
1099 mainPattern.append(symbols.getDigit());
1100
1101 for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++)
1102 mainPattern.append(symbols.getZeroDigit());
1103
1104 if (groupingUsed)
1105 {
1106 mainPattern.insert(mainPattern.length() - groupingSize,
1107 symbols.getGroupingSeparator());
1108 }
1109
1110 // See if we need decimal info.
1111 if (minimumFractionDigits > 0 || maximumFractionDigits > 0 ||
1112 decimalSeparatorAlwaysShown)
1113 {
1114 mainPattern.append(symbols.getDecimalSeparator());
1115 }
1116
1117 for (int i = 0; i < minimumFractionDigits; ++i)
1118 mainPattern.append(symbols.getZeroDigit());
1119
1120 for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
1121 mainPattern.append(symbols.getDigit());
1122
1123 if (useExponentialNotation)
1124 {
1125 mainPattern.append(symbols.getExponential());
1126
1127 for (int i = 0; i < minExponentDigits; ++i)
1128 mainPattern.append(symbols.getZeroDigit());
1129
1130 if (minExponentDigits == 0)
1131 mainPattern.append(symbols.getDigit());
1132 }
1133
1134 // save the pattern
1135 String pattern = mainPattern.toString();
1136
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));
1142
1143 if (hasNegativePrefix)
1144 {
1145 mainPattern.append(symbols.getPatternSeparator());
1146 mainPattern.append(quoteFix(negativePrefix, patternChars));
1147 mainPattern.append(pattern);
1148 mainPattern.append(quoteFix(negativeSuffix, patternChars));
1149 }
1150
1151 // finally, return the pattern string
1152 return mainPattern.toString();
1153 }
1154
1155 /* ****** FORMAT PARSING ****** */
1156
1157 /**
1158 * Scan the input string and define a pattern suitable for use
1159 * with this decimal format.
1160 *
1161 * @param pattern
1162 * @param symbols
1163 */
1164 private void applyPatternWithSymbols(String pattern,
1165 DecimalFormatSymbols symbols)
1166 {
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.
1178
1179 // Restore default values. Most of these will be overwritten
1180 // but we want to be sure that nothing is left out.
1181 setDefaultValues();
1182
1183 int len = pattern.length();
1184 if (len == 0)
1185 {
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;
1191
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;
1197
1198 return;
1199 }
1200
1201 int start = scanFix(pattern, symbols, 0, true);
1202 if (start < len) start = scanNumberInteger(pattern, symbols, start);
1203 if (start < len)
1204 {
1205 start = scanFractionalPortion(pattern, symbols, start);
1206 }
1207 else
1208 {
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;
1215 }
1216
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!");
1221
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);
1225
1226 if (useExponentialNotation &&
1227 (maxIntegerDigitsExponent > minimumIntegerDigits) &&
1228 (maxIntegerDigitsExponent > 1))
1229 {
1230 minimumIntegerDigits = 1;
1231 exponentRound = maxIntegerDigitsExponent;
1232 }
1233
1234 if (useExponentialNotation)
1235 maximumIntegerDigits = maxIntegerDigitsExponent;
1236
1237 if (!this.hasFractionalPattern && this.showDecimalSeparator == true)
1238 {
1239 this.decimalSeparatorAlwaysShown = true;
1240 }
1241 }
1242
1243 /**
1244 * Scans for the prefix or suffix portion of the pattern string.
1245 * This method handles the positive subpattern of the pattern string.
1246 *
1247 * @param pattern The pattern string to parse.
1248 * @return The position in the pattern string where parsing ended.
1249 */
1250 private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols,
1251 int start, boolean prefix)
1252 {
1253 CPStringBuilder buffer = new CPStringBuilder();
1254
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();
1263
1264 // other special characters, cached here to avoid method calls later
1265 char percent = sourceSymbols.getPercent();
1266 char permille = sourceSymbols.getPerMill();
1267
1268 String currencySymbol = this.symbols.getCurrencySymbol();
1269
1270 boolean quote = false;
1271
1272 char ch = pattern.charAt(start);
1273 if (ch == patternSeparator)
1274 {
1275 // negative subpattern
1276 this.hasNegativePrefix = true;
1277 ++start;
1278 return start;
1279 }
1280
1281 int len = pattern.length();
1282 int i;
1283 for (i = start; i < len; i++)
1284 {
1285 ch = pattern.charAt(i);
1286
1287 // we are entering into the negative subpattern
1288 if (!quote && ch == patternSeparator)
1289 {
1290 if (this.hasNegativePrefix)
1291 {
1292 throw new IllegalArgumentException("Invalid pattern found: "
1293 + start);
1294 }
1295
1296 this.hasNegativePrefix = true;
1297 ++i;
1298 break;
1299 }
1300
1301 // this means we are inside the number portion
1302 if (!quote &&
1303 (ch == minus || ch == digit || ch == zero ||
1304 ch == groupingSeparator))
1305 break;
1306
1307 if (!quote && ch == decimalSeparator)
1308 {
1309 this.showDecimalSeparator = true;
1310 break;
1311 }
1312 else if (quote && ch != '\'')
1313 {
1314 buffer.append(ch);
1315 continue;
1316 }
1317
1318 if (ch == '\u00A4')
1319 {
1320 // CURRENCY
1321 currencySymbol = this.symbols.getCurrencySymbol();
1322
1323 // if \u00A4 is doubled, we use the international currency symbol
1324 if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1325 {
1326 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1327 i++;
1328 }
1329
1330 this.useCurrencySeparator = true;
1331 buffer.append(currencySymbol);
1332 }
1333 else if (ch == percent)
1334 {
1335 // PERCENT
1336 this.multiplier = 100;
1337 buffer.append(this.symbols.getPercent());
1338 }
1339 else if (ch == permille)
1340 {
1341 // PERMILLE
1342 this.multiplier = 1000;
1343 buffer.append(this.symbols.getPerMill());
1344 }
1345 else if (ch == '\'')
1346 {
1347 // QUOTE
1348 if ((i + 1) < len && pattern.charAt(i + 1) == '\'')
1349 {
1350 // we need to add ' to the buffer
1351 buffer.append(ch);
1352 i++;
1353 }
1354 else
1355 {
1356 quote = !quote;
1357 continue;
1358 }
1359 }
1360 else
1361 {
1362 buffer.append(ch);
1363 }
1364 }
1365
1366 if (prefix)
1367 {
1368 this.positivePrefix = buffer.toString();
1369 this.negativePrefix = minus + "" + positivePrefix;
1370 }
1371 else
1372 {
1373 this.positiveSuffix = buffer.toString();
1374 }
1375
1376 return i;
1377 }
1378
1379 /**
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.
1383 *
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).
1388 */
1389 private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols,
1390 int start)
1391 {
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();
1398
1399 // count the number of zeroes in the pattern
1400 // this number defines the minum digits in the integer portion
1401 int zeros = 0;
1402
1403 // count the number of digits used in grouping
1404 int _groupingSize = 0;
1405
1406 this.maxIntegerDigitsExponent = 0;
1407
1408 boolean intPartTouched = false;
1409
1410 char ch;
1411 int len = pattern.length();
1412 int i;
1413 for (i = start; i < len; i++)
1414 {
1415 ch = pattern.charAt(i);
1416
1417 // break on decimal separator or exponent or pattern separator
1418 if (ch == decimalSeparator || ch == exponent)
1419 break;
1420
1421 if (this.hasNegativePrefix && ch == patternSeparator)
1422 throw new IllegalArgumentException("Invalid pattern found: "
1423 + start);
1424
1425 if (ch == digit)
1426 {
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);
1433
1434 _groupingSize++;
1435 intPartTouched = true;
1436 this.maxIntegerDigitsExponent++;
1437 }
1438 else if (ch == zero)
1439 {
1440 zeros++;
1441 _groupingSize++;
1442 this.maxIntegerDigitsExponent++;
1443 }
1444 else if (ch == groupingSeparator)
1445 {
1446 this.groupingSeparatorInPattern = true;
1447 this.groupingUsed = true;
1448 _groupingSize = 0;
1449 }
1450 else
1451 {
1452 // any other character not listed above
1453 // means we are in the suffix portion
1454 break;
1455 }
1456 }
1457
1458 if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize;
1459 this.minimumIntegerDigits = zeros;
1460
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;
1466
1467 return i;
1468 }
1469
1470 /**
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.
1474 *
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).
1479 */
1480 private int scanFractionalPortion(String pattern,
1481 DecimalFormatSymbols symbols,
1482 int start)
1483 {
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();
1490
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)
1495 {
1496 this.minimumFractionDigits = 0;
1497 this.maximumFractionDigits = 0;
1498 return start;
1499 }
1500
1501 ++start;
1502
1503 this.hasFractionalPattern = true;
1504
1505 this.minimumFractionDigits = 0;
1506 int digits = 0;
1507
1508 int len = pattern.length();
1509 int i;
1510 for (i = start; i < len; i++)
1511 {
1512 ch = pattern.charAt(i);
1513
1514 // we hit the exponential or negative subpattern
1515 if (ch == exponent || ch == patternSeparator)
1516 break;
1517
1518 // pattern error
1519 if (ch == groupingSeparator || ch == decimalSeparator) throw new
1520 IllegalArgumentException("unexpected character '" + ch + "' " +
1521 "in fractional subpattern. Position: " + i);
1522
1523 if (ch == digit)
1524 {
1525 digits++;
1526 }
1527 else if (ch == zero)
1528 {
1529 if (digits > 0) throw new
1530 IllegalArgumentException("digit mark following zero in " +
1531 "positive subpattern, not allowed. Position: " + i);
1532
1533 this.minimumFractionDigits++;
1534 }
1535 else
1536 {
1537 // we are in the suffix section of pattern
1538 break;
1539 }
1540 }
1541
1542 if (i == start) this.hasFractionalPattern = false;
1543
1544 this.maximumFractionDigits = this.minimumFractionDigits + digits;
1545 this.showDecimalSeparator = true;
1546
1547 return i;
1548 }
1549
1550 /**
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.
1554 *
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).
1559 */
1560 private int scanExponent(String pattern, DecimalFormatSymbols symbols,
1561 int start)
1562 {
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();
1568
1569 char ch = pattern.charAt(start);
1570
1571 if (ch == decimalSeparator)
1572 {
1573 // ignore dots
1574 ++start;
1575 }
1576
1577 if (ch != exponent)
1578 {
1579 this.useExponentialNotation = false;
1580 return start;
1581 }
1582
1583 ++start;
1584
1585 this.minExponentDigits = 0;
1586
1587 int len = pattern.length();
1588 int i;
1589 for (i = start; i < len; i++)
1590 {
1591 ch = pattern.charAt(i);
1592
1593 if (ch == groupingSeparator || ch == decimalSeparator ||
1594 ch == digit || ch == exponent) throw new
1595 IllegalArgumentException("unexpected character '" + ch + "' " +
1596 "in exponential subpattern. Position: " + i);
1597
1598 if (ch == zero)
1599 {
1600 this.minExponentDigits++;
1601 }
1602 else
1603 {
1604 // any character other than zero is an exit point
1605 break;
1606 }
1607 }
1608
1609 this.useExponentialNotation = true;
1610
1611 return i;
1612 }
1613
1614 /**
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.
1619 *
1620 * @param pattern The pattern string to parse.
1621 * @param start The starting parse position in the string.
1622 */
1623 private void scanNegativePattern(String pattern,
1624 DecimalFormatSymbols sourceSymbols,
1625 int start)
1626 {
1627 StringBuilder buffer = new StringBuilder();
1628
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();
1637
1638 // other special charcaters, cached here to avoid method calls later
1639 char percent = sourceSymbols.getPercent();
1640 char permille = sourceSymbols.getPerMill();
1641
1642 String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol();
1643 String currencySymbol = CURRENCY_SYMBOL;
1644
1645 boolean quote = false;
1646 boolean prefixDone = false;
1647
1648 int len = pattern.length();
1649 if (len > 0) this.hasNegativePrefix = true;
1650
1651 char ch = pattern.charAt(start);
1652 if (ch == patternSeparator)
1653 {
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++;
1659 }
1660
1661 int i;
1662 for (i = start; i < len; i++)
1663 {
1664 ch = pattern.charAt(i);
1665
1666 // this means we are inside the number portion
1667 if (!quote &&
1668 (ch == digit || ch == zero || ch == decimalSeparator ||
1669 ch == patternSeparator || ch == groupingSeparator))
1670 {
1671 if (!prefixDone)
1672 {
1673 this.negativePrefix = buffer.toString();
1674 buffer.delete(0, buffer.length());
1675 prefixDone = true;
1676 }
1677 }
1678 else if (ch == minus)
1679 {
1680 buffer.append(this.symbols.getMinusSign());
1681 }
1682 else if (quote && ch != '\'')
1683 {
1684 buffer.append(ch);
1685 }
1686 else if (ch == '\u00A4')
1687 {
1688 // CURRENCY
1689 currencySymbol = CURRENCY_SYMBOL;
1690
1691 // if \u00A4 is doubled, we use the international currency symbol
1692 if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1693 {
1694 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1695 i = i + 2;
1696 }
1697
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;
1702
1703 buffer.append(currencySymbol);
1704 }
1705 else if (ch == percent)
1706 {
1707 // PERCENT
1708 this.negativePatternMultiplier = 100;
1709 buffer.append(this.symbols.getPercent());
1710 }
1711 else if (ch == permille)
1712 {
1713 // PERMILLE
1714 this.negativePatternMultiplier = 1000;
1715 buffer.append(this.symbols.getPerMill());
1716 }
1717 else if (ch == '\'')
1718 {
1719 // QUOTE
1720 if ((i + 1) < len && pattern.charAt(i + 1) == '\'')
1721 {
1722 // we need to add ' to the buffer
1723 buffer.append(ch);
1724 i++;
1725 }
1726 else
1727 {
1728 quote = !quote;
1729 }
1730 }
1731 else if (ch == patternSeparator)
1732 {
1733 // no pattern separator in the negative pattern
1734 throw new IllegalArgumentException("unexpected character '" + ch +
1735 "' in negative subpattern.");
1736 }
1737 else
1738 {
1739 buffer.append(ch);
1740 }
1741 }
1742
1743 if (prefixDone)
1744 this.negativeSuffix = buffer.toString();
1745 else
1746 this.negativePrefix = buffer.toString();
1747 }
1748
1749 /* ****** FORMATTING ****** */
1750
1751 /**
1752 * Handles the real formatting.
1753 *
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.
1760 *
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.
1766 */
1767 private void formatInternal(BigDecimal number, boolean isLong,
1768 StringBuffer dest, FieldPosition fieldPos)
1769 {
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);
1778
1779 int _multiplier = this.multiplier;
1780
1781 // used to track attribute starting position for each attribute
1782 int attributeStart = -1;
1783
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)
1788 {
1789 attributeStart = dest.length();
1790
1791 // append the negative prefix to the string
1792 dest.append(negativePrefix);
1793
1794 // once got the negative prefix, we can use
1795 // the absolute value.
1796 number = number.abs();
1797
1798 _multiplier = negativePatternMultiplier;
1799
1800 addAttribute(Field.SIGN, attributeStart, dest.length());
1801 }
1802 else
1803 {
1804 // not negative, use the positive prefix
1805 dest.append(positivePrefix);
1806 }
1807
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;
1813
1814 // compute the multiplier to use with percent and similar
1815 number = number.multiply(BigDecimal.valueOf(_multiplier));
1816
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)
1821 {
1822 number = BigDecimal.ZERO;
1823 this.maximumIntegerDigits = 1;
1824 this.minimumIntegerDigits = 1;
1825 }
1826
1827 // get the absolute number
1828 number = number.abs();
1829
1830 // the scaling to use while formatting this number
1831 int scale = this.maximumFractionDigits;
1832
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;
1837
1838 // are we using exponential notation?
1839 if (this.useExponentialNotation)
1840 {
1841 exponent = getExponent(number);
1842 number = number.movePointLeft((int) exponent);
1843
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;
1849 }
1850
1851 // round the number to the nearest neighbor
1852 number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
1853
1854 // now get the integer and fractional part of the string
1855 // that will be processed later
1856 String plain = number.toPlainString();
1857
1858 String intPart = null;
1859 String fractPart = null;
1860
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);
1866
1867 // strip the decimal portion
1868 int dot = plain.indexOf('.');
1869 if (dot > -1)
1870 {
1871 intPart = plain.substring(0, dot);
1872 dot++;
1873
1874 if (useExponentialNotation)
1875 fractPart = plain.substring(dot, dot + scale);
1876 else
1877 fractPart = plain.substring(dot);
1878 }
1879 else
1880 {
1881 intPart = plain;
1882 }
1883
1884 // used in various places later on
1885 int intPartLen = intPart.length();
1886 endIndexInt = intPartLen;
1887
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)
1893 {
1894 attributeStart = Math.max(dest.length() - 1, 0);
1895 appendZero(dest, zeroes, minimumIntegerDigits);
1896 }
1897
1898 if (this.useExponentialNotation)
1899 {
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.
1904
1905 if (attributeStart < 0)
1906 attributeStart = Math.max(dest.length() - 1, 0);
1907 appendDigit(intPart, dest, this.groupingUsed);
1908 }
1909 else
1910 {
1911 // non exponential notation
1912 intPartLen = intPart.length();
1913 int canary = Math.min(intPartLen, this.maximumIntegerDigits);
1914
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;
1919
1920 // append it
1921 if (maximumIntegerDigits > 0 &&
1922 !(this.minimumIntegerDigits == 0 &&
1923 intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0))
1924 {
1925 if (attributeStart < 0)
1926 attributeStart = Math.max(dest.length() - 1, 0);
1927 appendDigit(intPart, dest, this.groupingUsed);
1928 }
1929 }
1930
1931 // add the INTEGER attribute
1932 addAttribute(Field.INTEGER, attributeStart, dest.length());
1933
1934 // ...update field position, if needed, and return...
1935 if ((fieldPos.getField() == INTEGER_FIELD ||
1936 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
1937 {
1938 fieldPos.setBeginIndex(beginIndexInt);
1939 fieldPos.setEndIndex(endIndexInt);
1940 }
1941
1942 handleFractionalPart(dest, fractPart, fieldPos, isLong);
1943
1944 // and the exponent
1945 if (this.useExponentialNotation)
1946 {
1947 attributeStart = dest.length();
1948
1949 dest.append(symbols.getExponential());
1950
1951 addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
1952 attributeStart = dest.length();
1953
1954 if (exponent < 0)
1955 {
1956 dest.append(symbols.getMinusSign());
1957 exponent = -exponent;
1958
1959 addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
1960 }
1961
1962 attributeStart = dest.length();
1963
1964 String exponentString = String.valueOf(exponent);
1965 int exponentLength = exponentString.length();
1966
1967 for (int i = 0; i < minExponentDigits - exponentLength; i++)
1968 dest.append(symbols.getZeroDigit());
1969
1970 for (int i = 0; i < exponentLength; ++i)
1971 dest.append(exponentString.charAt(i));
1972
1973 addAttribute(Field.EXPONENT, attributeStart, dest.length());
1974 }
1975
1976 // now include the suffixes...
1977 if (isNegative)
1978 {
1979 dest.append(negativeSuffix);
1980 }
1981 else
1982 {
1983 dest.append(positiveSuffix);
1984 }
1985 }
1986
1987 /**
1988 * Add to the input buffer the result of formatting the fractional
1989 * portion of the number.
1990 *
1991 * @param dest
1992 * @param fractPart
1993 * @param fieldPos
1994 * @param isLong
1995 */
1996 private void handleFractionalPart(StringBuffer dest, String fractPart,
1997 FieldPosition fieldPos, boolean isLong)
1998 {
1999 int dotStart = 0;
2000 int dotEnd = 0;
2001 boolean addDecimal = false;
2002
2003 if (this.decimalSeparatorAlwaysShown ||
2004 ((!isLong || this.useExponentialNotation) &&
2005 this.showDecimalSeparator && this.maximumFractionDigits > 0) ||
2006 this.minimumFractionDigits > 0)
2007 {
2008 dotStart = dest.length();
2009
2010 if (this.useCurrencySeparator)
2011 dest.append(symbols.getMonetaryDecimalSeparator());
2012 else
2013 dest.append(symbols.getDecimalSeparator());
2014
2015 dotEnd = dest.length();
2016 addDecimal = true;
2017 }
2018
2019 // now handle the fraction portion of the number
2020 int fractStart = 0;
2021 int fractEnd = 0;
2022 boolean addFractional = false;
2023
2024 if ((!isLong || this.useExponentialNotation)
2025 && this.maximumFractionDigits > 0
2026 || this.minimumFractionDigits > 0)
2027 {
2028 fractStart = dest.length();
2029 fractEnd = fractStart;
2030
2031 int digits = this.minimumFractionDigits;
2032
2033 if (this.useExponentialNotation)
2034 {
2035 digits = (this.minimumIntegerDigits + this.minimumFractionDigits)
2036 - dest.length();
2037 if (digits < 0) digits = 0;
2038 }
2039
2040 fractPart = adjustTrailingZeros(fractPart, digits);
2041
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++)
2048 {
2049 if (fracts[i] != '0')
2050 allZeros = false;
2051 }
2052
2053 if (!allZeros || (minimumFractionDigits > 0))
2054 {
2055 appendDigit(fractPart, dest, false);
2056 fractEnd = dest.length();
2057
2058 addDecimal = true;
2059 addFractional = true;
2060 }
2061 else if (!this.decimalSeparatorAlwaysShown)
2062 {
2063 dest.deleteCharAt(dest.length() - 1);
2064 addDecimal = false;
2065 }
2066 else
2067 {
2068 fractEnd = dest.length();
2069 addFractional = true;
2070 }
2071 }
2072
2073 if (addDecimal)
2074 addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
2075
2076 if (addFractional)
2077 addAttribute(Field.FRACTION, fractStart, fractEnd);
2078
2079 if ((fieldPos.getField() == FRACTION_FIELD ||
2080 fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION))
2081 {
2082 fieldPos.setBeginIndex(fractStart);
2083 fieldPos.setEndIndex(fractEnd);
2084 }
2085 }
2086
2087 /**
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.
2092 */
2093 private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount)
2094 {
2095 char ch = symbols.getZeroDigit();
2096 char gSeparator = symbols.getGroupingSeparator();
2097
2098 int i = 0;
2099 int gPos = totalDigitCount;
2100 for (i = 0; i < zeroes; i++, gPos--)
2101 {
2102 if (this.groupingSeparatorInPattern &&
2103 (this.groupingUsed && this.groupingSize != 0) &&
2104 (gPos % groupingSize == 0 && i > 0))
2105 dest.append(gSeparator);
2106
2107 dest.append(ch);
2108 }
2109
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);
2115 }
2116
2117 /**
2118 * Append src to <code>dest</code>.
2119 *
2120 * Grouping is added if <code>groupingUsed</code> is set
2121 * to <code>true</code>.
2122 */
2123 private void appendDigit(String src, StringBuffer dest,
2124 boolean groupingUsed)
2125 {
2126 int zero = symbols.getZeroDigit() - '0';
2127
2128 int ch;
2129 char gSeparator = symbols.getGroupingSeparator();
2130
2131 int len = src.length();
2132 for (int i = 0, gPos = len; i < len; i++, gPos--)
2133 {
2134 ch = src.charAt(i);
2135 if (groupingUsed && this.groupingSize != 0 &&
2136 gPos % groupingSize == 0 && i > 0)
2137 dest.append(gSeparator);
2138
2139 dest.append((char) (zero + ch));
2140 }
2141 }
2142
2143 /**
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.
2148 */
2149 private long getExponent(BigDecimal number)
2150 {
2151 long exponent = 0;
2152
2153 if (number.signum() > 0)
2154 {
2155 double _number = number.doubleValue();
2156 exponent = (long) Math.floor (Math.log10(_number));
2157
2158 // get the right value for the exponent
2159 exponent = exponent - (exponent % this.exponentRound);
2160
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;
2170 }
2171
2172 return exponent;
2173 }
2174
2175 /**
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.
2180 *
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.
2184 *
2185 * @param src The string with the correct number of zeros.
2186 */
2187 private String adjustTrailingZeros(String src, int minimumDigits)
2188 {
2189 int len = src.length();
2190 String result;
2191
2192 // remove all trailing zero
2193 if (len > minimumDigits)
2194 {
2195 int zeros = 0;
2196 for (int i = len - 1; i > minimumDigits; i--)
2197 {
2198 if (src.charAt(i) == '0')
2199 ++zeros;
2200 else
2201 break;
2202 }
2203 result = src.substring(0, len - zeros);
2204 }
2205 else
2206 {
2207 char zero = symbols.getZeroDigit();
2208 CPStringBuilder _result = new CPStringBuilder(src);
2209 for (int i = len; i < minimumDigits; i++)
2210 {
2211 _result.append(zero);
2212 }
2213 result = _result.toString();
2214 }
2215
2216 return result;
2217 }
2218
2219 /**
2220 * Adds an attribute to the attributes list.
2221 *
2222 * @param field
2223 * @param begin
2224 * @param end
2225 */
2226 private void addAttribute(Field field, int begin, int end)
2227 {
2228 /*
2229 * This method and its implementation derives directly from the
2230 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
2231 */
2232
2233 FieldPosition pos = new FieldPosition(field);
2234 pos.setBeginIndex(begin);
2235 pos.setEndIndex(end);
2236 attributes.add(pos);
2237 }
2238
2239 /**
2240 * Sets the default values for the various properties in this DecimaFormat.
2241 */
2242 private void setDefaultValues()
2243 {
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.
2248
2249 this.negativePrefix = String.valueOf(symbols.getMinusSign());
2250 this.negativeSuffix = "";
2251 this.positivePrefix = "";
2252 this.positiveSuffix = "";
2253
2254 this.multiplier = 1;
2255 this.negativePatternMultiplier = 1;
2256 this.exponentRound = 1;
2257
2258 this.hasNegativePrefix = false;
2259
2260 this.minimumIntegerDigits = 1;
2261 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
2262 this.minimumFractionDigits = 0;
2263 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
2264 this.minExponentDigits = 0;
2265
2266 this.groupingSize = 0;
2267
2268 this.decimalSeparatorAlwaysShown = false;
2269 this.showDecimalSeparator = false;
2270 this.useExponentialNotation = false;
2271 this.groupingUsed = false;
2272 this.groupingSeparatorInPattern = false;
2273
2274 this.useCurrencySeparator = false;
2275
2276 this.hasFractionalPattern = false;
2277 }
2278 }