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 java.math.BigDecimal;
047 import java.math.BigInteger;
048
049 import java.util.ArrayList;
050 import java.util.Currency;
051 import java.util.Locale;
052
053 /*
054 * This note is here for historical reasons and because I had not the courage
055 * to remove it :)
056 *
057 * @author Tom Tromey (tromey@cygnus.com)
058 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
059 * @date March 4, 1999
060 *
061 * Written using "Java Class Libraries", 2nd edition, plus online
062 * API docs for JDK 1.2 from http://www.javasoft.com.
063 * Status: Believed complete and correct to 1.2.
064 * Note however that the docs are very unclear about how format parsing
065 * should work. No doubt there are problems here.
066 */
067
068 /**
069 * This class is a concrete implementation of NumberFormat used to format
070 * decimal numbers. The class can format numbers given a specific locale.
071 * Generally, to get an instance of DecimalFormat you should call the factory
072 * methods in the <code>NumberFormat</code> base class.
073 *
074 * @author Mario Torre <neugens@limasoftware.net>
075 * @author Tom Tromey (tromey@cygnus.com)
076 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
077 */
078 public class DecimalFormat extends NumberFormat
079 {
080 /** serialVersionUID for serializartion. */
081 private static final long serialVersionUID = 864413376551465018L;
082
083 /** Defines the default number of digits allowed while formatting integers. */
084 private static final int DEFAULT_INTEGER_DIGITS = 309;
085
086 /**
087 * Defines the default number of digits allowed while formatting
088 * fractions.
089 */
090 private static final int DEFAULT_FRACTION_DIGITS = 340;
091
092 /**
093 * Locale-independent pattern symbols.
094 */
095 // Happen to be the same as the US symbols.
096 private static final DecimalFormatSymbols nonLocalizedSymbols
097 = new DecimalFormatSymbols (Locale.US);
098
099 /**
100 * Defines if parse should return a BigDecimal or not.
101 */
102 private boolean parseBigDecimal;
103
104 /**
105 * Defines if we have to use the monetary decimal separator or
106 * the decimal separator while formatting numbers.
107 */
108 private boolean useCurrencySeparator;
109
110 /** Defines if the decimal separator is always shown or not. */
111 private boolean decimalSeparatorAlwaysShown;
112
113 /**
114 * Defines if the decimal separator has to be shown.
115 *
116 * This is different then <code>decimalSeparatorAlwaysShown</code>,
117 * as it defines if the format string contains a decimal separator or no.
118 */
119 private boolean showDecimalSeparator;
120
121 /**
122 * This field is used to determine if the grouping
123 * separator is included in the format string or not.
124 * This is only needed to match the behaviour of the RI.
125 */
126 private boolean groupingSeparatorInPattern;
127
128 /** Defines the size of grouping groups when grouping is used. */
129 private byte groupingSize;
130
131 /**
132 * This is an internal parameter used to keep track of the number
133 * of digits the form the exponent, when exponential notation is used.
134 * It is used with <code>exponentRound</code>
135 */
136 private byte minExponentDigits;
137
138 /** This field is used to set the exponent in the engineering notation. */
139 private int exponentRound;
140
141 /** Multiplier used in percent style formats. */
142 private int multiplier;
143
144 /** Multiplier used in percent style formats. */
145 private int negativePatternMultiplier;
146
147 /** The negative prefix. */
148 private String negativePrefix;
149
150 /** The negative suffix. */
151 private String negativeSuffix;
152
153 /** The positive prefix. */
154 private String positivePrefix;
155
156 /** The positive suffix. */
157 private String positiveSuffix;
158
159 /** Decimal Format Symbols for the given locale. */
160 private DecimalFormatSymbols symbols;
161
162 /** Determine if we have to use exponential notation or not. */
163 private boolean useExponentialNotation;
164
165 /**
166 * Defines the maximum number of integer digits to show when we use
167 * the exponential notation.
168 */
169 private int maxIntegerDigitsExponent;
170
171 /** Defines if the format string has a negative prefix or not. */
172 private boolean hasNegativePrefix;
173
174 /** Defines if the format string has a fractional pattern or not. */
175 private boolean hasFractionalPattern;
176
177 /** Stores a list of attributes for use by formatToCharacterIterator. */
178 private ArrayList attributes = new ArrayList();
179
180 /**
181 * Constructs a <code>DecimalFormat</code> which uses the default
182 * pattern and symbols.
183 */
184 public DecimalFormat()
185 {
186 this ("#,##0.###");
187 }
188
189 /**
190 * Constructs a <code>DecimalFormat</code> which uses the given
191 * pattern and the default symbols for formatting and parsing.
192 *
193 * @param pattern the non-localized pattern to use.
194 * @throws NullPointerException if any argument is null.
195 * @throws IllegalArgumentException if the pattern is invalid.
196 */
197 public DecimalFormat(String pattern)
198 {
199 this (pattern, new DecimalFormatSymbols());
200 }
201
202 /**
203 * Constructs a <code>DecimalFormat</code> using the given pattern
204 * and formatting symbols. This construction method is used to give
205 * complete control over the formatting process.
206 *
207 * @param pattern the non-localized pattern to use.
208 * @param symbols the set of symbols used for parsing and formatting.
209 * @throws NullPointerException if any argument is null.
210 * @throws IllegalArgumentException if the pattern is invalid.
211 */
212 public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
213 {
214 this.symbols = (DecimalFormatSymbols) symbols.clone();
215 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
216 }
217
218 /**
219 * Apply the given localized patern to the current DecimalFormat object.
220 *
221 * @param pattern The localized pattern to apply.
222 * @throws IllegalArgumentException if the given pattern is invalid.
223 * @throws NullPointerException if the input pattern is null.
224 */
225 public void applyLocalizedPattern (String pattern)
226 {
227 applyPatternWithSymbols(pattern, this.symbols);
228 }
229
230 /**
231 * Apply the given localized pattern to the current DecimalFormat object.
232 *
233 * @param pattern The localized pattern to apply.
234 * @throws IllegalArgumentException if the given pattern is invalid.
235 * @throws NullPointerException if the input pattern is null.
236 */
237 public void applyPattern(String pattern)
238 {
239 applyPatternWithSymbols(pattern, nonLocalizedSymbols);
240 }
241
242 public Object clone()
243 {
244 DecimalFormat c = (DecimalFormat) super.clone();
245 c.symbols = (DecimalFormatSymbols) symbols.clone();
246 return c;
247 }
248
249 /**
250 * Tests this instance for equality with an arbitrary object. This method
251 * returns <code>true</code> if:
252 * <ul>
253 * <li><code>obj</code> is not <code>null</code>;</li>
254 * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
255 * <li>this instance and <code>obj</code> have the same attributes;</li>
256 * </ul>
257 *
258 * @param obj the object (<code>null</code> permitted).
259 *
260 * @return A boolean.
261 */
262 public boolean equals(Object obj)
263 {
264 if (! (obj instanceof DecimalFormat))
265 return false;
266 DecimalFormat dup = (DecimalFormat) obj;
267 return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown
268 && groupingUsed == dup.groupingUsed
269 && groupingSeparatorInPattern == dup.groupingSeparatorInPattern
270 && groupingSize == dup.groupingSize
271 && multiplier == dup.multiplier
272 && useExponentialNotation == dup.useExponentialNotation
273 && minExponentDigits == dup.minExponentDigits
274 && minimumIntegerDigits == dup.minimumIntegerDigits
275 && maximumIntegerDigits == dup.maximumIntegerDigits
276 && minimumFractionDigits == dup.minimumFractionDigits
277 && maximumFractionDigits == dup.maximumFractionDigits
278 && parseBigDecimal == dup.parseBigDecimal
279 && useCurrencySeparator == dup.useCurrencySeparator
280 && showDecimalSeparator == dup.showDecimalSeparator
281 && exponentRound == dup.exponentRound
282 && negativePatternMultiplier == dup.negativePatternMultiplier
283 && maxIntegerDigitsExponent == dup.maxIntegerDigitsExponent
284 // XXX: causes equivalent patterns to fail
285 // && hasNegativePrefix == dup.hasNegativePrefix
286 && equals(negativePrefix, dup.negativePrefix)
287 && equals(negativeSuffix, dup.negativeSuffix)
288 && equals(positivePrefix, dup.positivePrefix)
289 && equals(positiveSuffix, dup.positiveSuffix)
290 && symbols.equals(dup.symbols));
291 }
292
293 /**
294 * Returns a hash code for this object.
295 *
296 * @return A hash code.
297 */
298 public int hashCode()
299 {
300 return toPattern().hashCode();
301 }
302
303 /**
304 * Produce a formatted {@link String} representation of this object.
305 * The passed object must be of type number.
306 *
307 * @param obj The {@link Number} to format.
308 * @param sbuf The destination String; text will be appended to this String.
309 * @param pos If used on input can be used to define an alignment
310 * field. If used on output defines the offsets of the alignment field.
311 * @return The String representation of this long.
312 */
313 public StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
314 {
315 if (obj instanceof BigInteger)
316 {
317 BigDecimal decimal = new BigDecimal((BigInteger) obj);
318 formatInternal(decimal, true, sbuf, pos);
319 return sbuf;
320 }
321 else if (obj instanceof BigDecimal)
322 {
323 formatInternal((BigDecimal) obj, true, sbuf, pos);
324 return sbuf;
325 }
326
327 return super.format(obj, sbuf, pos);
328 }
329
330 /**
331 * Produce a formatted {@link String} representation of this double.
332 *
333 * @param number The double to format.
334 * @param dest The destination String; text will be appended to this String.
335 * @param fieldPos If used on input can be used to define an alignment
336 * field. If used on output defines the offsets of the alignment field.
337 * @return The String representation of this long.
338 * @throws NullPointerException if <code>dest</code> or fieldPos are null
339 */
340 public StringBuffer format(double number, StringBuffer dest,
341 FieldPosition fieldPos)
342 {
343 // special cases for double: NaN and negative or positive infinity
344 if (Double.isNaN(number))
345 {
346 // 1. NaN
347 String nan = symbols.getNaN();
348 dest.append(nan);
349
350 // update field position if required
351 if ((fieldPos.getField() == INTEGER_FIELD ||
352 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
353 {
354 int index = dest.length();
355 fieldPos.setBeginIndex(index - nan.length());
356 fieldPos.setEndIndex(index);
357 }
358 }
359 else if (Double.isInfinite(number))
360 {
361 // 2. Infinity
362 if (number < 0)
363 dest.append(this.negativePrefix);
364 else
365 dest.append(this.positivePrefix);
366
367 dest.append(symbols.getInfinity());
368
369 if (number < 0)
370 dest.append(this.negativeSuffix);
371 else
372 dest.append(this.positiveSuffix);
373
374 if ((fieldPos.getField() == INTEGER_FIELD ||
375 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
376 {
377 fieldPos.setBeginIndex(dest.length());
378 fieldPos.setEndIndex(0);
379 }
380 }
381 else
382 {
383 // get the number as a BigDecimal
384 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
385 formatInternal(bigDecimal, false, dest, fieldPos);
386 }
387
388 return dest;
389 }
390
391 /**
392 * Produce a formatted {@link String} representation of this long.
393 *
394 * @param number The long to format.
395 * @param dest The destination String; text will be appended to this String.
396 * @param fieldPos If used on input can be used to define an alignment
397 * field. If used on output defines the offsets of the alignment field.
398 * @return The String representation of this long.
399 */
400 public StringBuffer format(long number, StringBuffer dest,
401 FieldPosition fieldPos)
402 {
403 BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
404 formatInternal(bigDecimal, true, dest, fieldPos);
405 return dest;
406 }
407
408 /**
409 * Return an <code>AttributedCharacterIterator</code> as a result of
410 * the formatting of the passed {@link Object}.
411 *
412 * @return An {@link AttributedCharacterIterator}.
413 * @throws NullPointerException if value is <code>null</code>.
414 * @throws IllegalArgumentException if value is not an instance of
415 * {@link Number}.
416 */
417 public AttributedCharacterIterator formatToCharacterIterator(Object value)
418 {
419 /*
420 * This method implementation derives directly from the
421 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
422 */
423
424 if (value == null)
425 throw new NullPointerException("Passed Object is null");
426
427 if (!(value instanceof Number)) throw new
428 IllegalArgumentException("Cannot format given Object as a Number");
429
430 StringBuffer text = new StringBuffer();
431 attributes.clear();
432 super.format(value, text, new FieldPosition(0));
433
434 AttributedString as = new AttributedString(text.toString());
435
436 // add NumberFormat field attributes to the AttributedString
437 for (int i = 0; i < attributes.size(); i++)
438 {
439 FieldPosition pos = (FieldPosition) attributes.get(i);
440 Format.Field attribute = pos.getFieldAttribute();
441
442 as.addAttribute(attribute, attribute, pos.getBeginIndex(),
443 pos.getEndIndex());
444 }
445
446 // return the CharacterIterator from AttributedString
447 return as.getIterator();
448 }
449
450 /**
451 * Returns the currency corresponding to the currency symbol stored
452 * in the instance of <code>DecimalFormatSymbols</code> used by this
453 * <code>DecimalFormat</code>.
454 *
455 * @return A new instance of <code>Currency</code> if
456 * the currency code matches a known one, null otherwise.
457 */
458 public Currency getCurrency()
459 {
460 return symbols.getCurrency();
461 }
462
463 /**
464 * Returns a copy of the symbols used by this instance.
465 *
466 * @return A copy of the symbols.
467 */
468 public DecimalFormatSymbols getDecimalFormatSymbols()
469 {
470 return (DecimalFormatSymbols) symbols.clone();
471 }
472
473 /**
474 * Gets the interval used between a grouping separator and the next.
475 * For example, a grouping size of 3 means that the number 1234 is
476 * formatted as 1,234.
477 *
478 * The actual character used as grouping separator depends on the
479 * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()}
480 *
481 * @return The interval used between a grouping separator and the next.
482 */
483 public int getGroupingSize()
484 {
485 return groupingSize;
486 }
487
488 /**
489 * Gets the multiplier used in percent and similar formats.
490 *
491 * @return The multiplier used in percent and similar formats.
492 */
493 public int getMultiplier()
494 {
495 return multiplier;
496 }
497
498 /**
499 * Gets the negative prefix.
500 *
501 * @return The negative prefix.
502 */
503 public String getNegativePrefix()
504 {
505 return negativePrefix;
506 }
507
508 /**
509 * Gets the negative suffix.
510 *
511 * @return The negative suffix.
512 */
513 public String getNegativeSuffix()
514 {
515 return negativeSuffix;
516 }
517
518 /**
519 * Gets the positive prefix.
520 *
521 * @return The positive prefix.
522 */
523 public String getPositivePrefix()
524 {
525 return positivePrefix;
526 }
527
528 /**
529 * Gets the positive suffix.
530 *
531 * @return The positive suffix.
532 */
533 public String getPositiveSuffix()
534 {
535 return positiveSuffix;
536 }
537
538 public boolean isDecimalSeparatorAlwaysShown()
539 {
540 return decimalSeparatorAlwaysShown;
541 }
542
543 /**
544 * Define if <code>parse(java.lang.String, java.text.ParsePosition)</code>
545 * should return a {@link BigDecimal} or not.
546 *
547 * @param newValue
548 */
549 public void setParseBigDecimal(boolean newValue)
550 {
551 this.parseBigDecimal = newValue;
552 }
553
554 /**
555 * Returns <code>true</code> if
556 * <code>parse(java.lang.String, java.text.ParsePosition)</code> returns
557 * a <code>BigDecimal</code>, <code>false</code> otherwise.
558 * The default return value for this method is <code>false</code>.
559 *
560 * @return <code>true</code> if the parse method returns a {@link BigDecimal},
561 * <code>false</code> otherwise.
562 * @since 1.5
563 * @see #setParseBigDecimal(boolean)
564 */
565 public boolean isParseBigDecimal()
566 {
567 return this.parseBigDecimal;
568 }
569
570 /**
571 * This method parses the specified string into a <code>Number</code>.
572 *
573 * The parsing starts at <code>pos</code>, which is updated as the parser
574 * consume characters in the passed string.
575 * On error, the <code>Position</code> object index is not updated, while
576 * error position is set appropriately, an <code>null</code> is returned.
577 *
578 * @param str The string to parse.
579 * @param pos The desired <code>ParsePosition</code>.
580 *
581 * @return The parsed <code>Number</code>
582 */
583 public Number parse(String str, ParsePosition pos)
584 {
585 // a special values before anything else
586 // NaN
587 if (str.contains(this.symbols.getNaN()))
588 return Double.valueOf(Double.NaN);
589
590 // this will be our final number
591 StringBuffer number = new StringBuffer();
592
593 // special character
594 char minus = symbols.getMinusSign();
595
596 // starting parsing position
597 int start = pos.getIndex();
598
599 // validate the string, it have to be in the
600 // same form as the format string or parsing will fail
601 String _negativePrefix = (this.negativePrefix.compareTo("") == 0
602 ? minus + positivePrefix
603 : this.negativePrefix);
604
605 // we check both prefixes, because one might be empty.
606 // We want to pick the longest prefix that matches.
607 int positiveLen = positivePrefix.length();
608 int negativeLen = _negativePrefix.length();
609
610 boolean isNegative = str.startsWith(_negativePrefix);
611 boolean isPositive = str.startsWith(positivePrefix);
612
613 if (isPositive && isNegative)
614 {
615 // By checking this way, we preserve ambiguity in the case
616 // where the negative format differs only in suffix.
617 if (negativeLen > positiveLen)
618 {
619 start += _negativePrefix.length();
620 isNegative = true;
621 }
622 else
623 {
624 start += positivePrefix.length();
625 isPositive = true;
626 if (negativeLen < positiveLen)
627 isNegative = false;
628 }
629 }
630 else if (isNegative)
631 {
632 start += _negativePrefix.length();
633 isPositive = false;
634 }
635 else if (isPositive)
636 {
637 start += positivePrefix.length();
638 isNegative = false;
639 }
640 else
641 {
642 pos.setErrorIndex(start);
643 return null;
644 }
645
646 // other special characters used by the parser
647 char decimalSeparator = symbols.getDecimalSeparator();
648 char zero = symbols.getZeroDigit();
649 char exponent = symbols.getExponential();
650
651 // stop parsing position in the string
652 int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2;
653
654 if (useExponentialNotation)
655 stop += minExponentDigits + 1;
656
657 boolean inExponent = false;
658
659 // correct the size of the end parsing flag
660 int len = str.length();
661 if (len < stop) stop = len;
662 char groupingSeparator = symbols.getGroupingSeparator();
663
664 int i = start;
665 while (i < stop)
666 {
667 char ch = str.charAt(i);
668 i++;
669
670 if (ch >= zero && ch <= (zero + 9))
671 {
672 number.append(ch);
673 }
674 else if (this.parseIntegerOnly)
675 {
676 i--;
677 break;
678 }
679 else if (ch == decimalSeparator)
680 {
681 number.append('.');
682 }
683 else if (ch == exponent)
684 {
685 number.append(ch);
686 inExponent = !inExponent;
687 }
688 else if ((ch == '+' || ch == '-' || ch == minus))
689 {
690 if (inExponent)
691 number.append(ch);
692 else
693 {
694 i--;
695 break;
696 }
697 }
698 else
699 {
700 if (!groupingUsed || ch != groupingSeparator)
701 {
702 i--;
703 break;
704 }
705 }
706 }
707
708 // 2nd special case: infinity
709 // XXX: need to be tested
710 if (str.contains(symbols.getInfinity()))
711 {
712 int inf = str.indexOf(symbols.getInfinity());
713 pos.setIndex(inf);
714
715 // FIXME: ouch, this is really ugly and lazy code...
716 if (this.parseBigDecimal)
717 {
718 if (isNegative)
719 return new BigDecimal(Double.NEGATIVE_INFINITY);
720
721 return new BigDecimal(Double.POSITIVE_INFINITY);
722 }
723
724 if (isNegative)
725 return new Double(Double.NEGATIVE_INFINITY);
726
727 return new Double(Double.POSITIVE_INFINITY);
728 }
729
730 // no number...
731 if (i == start || number.length() == 0)
732 {
733 pos.setErrorIndex(i);
734 return null;
735 }
736
737 // now we have to check the suffix, done here after number parsing
738 // or the index will not be updated correctly...
739 boolean hasNegativeSuffix = str.endsWith(this.negativeSuffix);
740 boolean hasPositiveSuffix = str.endsWith(this.positiveSuffix);
741 boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix);
742
743 positiveLen = positiveSuffix.length();
744 negativeLen = negativeSuffix.length();
745
746 if (isNegative && !hasNegativeSuffix)
747 {
748 pos.setErrorIndex(i);
749 return null;
750 }
751 else if (hasNegativeSuffix &&
752 !positiveEqualsNegative &&
753 (negativeLen > positiveLen))
754 {
755 isNegative = true;
756 }
757 else if (!hasPositiveSuffix)
758 {
759 pos.setErrorIndex(i);
760 return null;
761 }
762
763 if (isNegative) number.insert(0, '-');
764
765 pos.setIndex(i);
766
767 // now we handle the return type
768 BigDecimal bigDecimal = new BigDecimal(number.toString());
769 if (this.parseBigDecimal)
770 return bigDecimal;
771
772 // want integer?
773 if (this.parseIntegerOnly)
774 return new Long(bigDecimal.longValue());
775
776 // 3th special case -0.0
777 if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0))
778 return new Double(-0.0);
779
780 try
781 {
782 BigDecimal integer
783 = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY);
784 return new Long(integer.longValue());
785 }
786 catch (ArithmeticException e)
787 {
788 return new Double(bigDecimal.doubleValue());
789 }
790 }
791
792 /**
793 * Sets the <code>Currency</code> on the
794 * <code>DecimalFormatSymbols</code> used, which also sets the
795 * currency symbols on those symbols.
796 *
797 * @param currency The new <code>Currency</code> on the
798 * <code>DecimalFormatSymbols</code>.
799 */
800 public void setCurrency(Currency currency)
801 {
802 symbols.setCurrency(currency);
803 }
804
805 /**
806 * Sets the symbols used by this instance. This method makes a copy of
807 * the supplied symbols.
808 *
809 * @param newSymbols the symbols (<code>null</code> not permitted).
810 */
811 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
812 {
813 symbols = (DecimalFormatSymbols) newSymbols.clone();
814 }
815
816 /**
817 * Define if the decimal separator should be always visible or only
818 * visible when needed. This method as effect only on integer values.
819 * Pass <code>true</code> if you want the decimal separator to be
820 * always shown, <code>false</code> otherwise.
821 *
822 * @param newValue true</code> if you want the decimal separator to be
823 * always shown, <code>false</code> otherwise.
824 */
825 public void setDecimalSeparatorAlwaysShown(boolean newValue)
826 {
827 decimalSeparatorAlwaysShown = newValue;
828 }
829
830 /**
831 * Sets the number of digits used to group portions of the integer part of
832 * the number. For example, the number <code>123456</code>, with a grouping
833 * size of 3, is rendered <code>123,456</code>.
834 *
835 * @param groupSize The number of digits used while grouping portions
836 * of the integer part of a number.
837 */
838 public void setGroupingSize(int groupSize)
839 {
840 groupingSize = (byte) groupSize;
841 }
842
843 /**
844 * Sets the maximum number of digits allowed in the integer
845 * portion of a number to the specified value.
846 * The new value will be the choosen as the minimum between
847 * <code>newvalue</code> and 309. Any value below zero will be
848 * replaced by zero.
849 *
850 * @param newValue The new maximum integer digits value.
851 */
852 public void setMaximumIntegerDigits(int newValue)
853 {
854 newValue = (newValue > 0) ? newValue : 0;
855 super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
856 }
857
858 /**
859 * Sets the minimum number of digits allowed in the integer
860 * portion of a number to the specified value.
861 * The new value will be the choosen as the minimum between
862 * <code>newvalue</code> and 309. Any value below zero will be
863 * replaced by zero.
864 *
865 * @param newValue The new minimum integer digits value.
866 */
867 public void setMinimumIntegerDigits(int newValue)
868 {
869 newValue = (newValue > 0) ? newValue : 0;
870 super.setMinimumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
871 }
872
873 /**
874 * Sets the maximum number of digits allowed in the fraction
875 * portion of a number to the specified value.
876 * The new value will be the choosen as the minimum between
877 * <code>newvalue</code> and 309. Any value below zero will be
878 * replaced by zero.
879 *
880 * @param newValue The new maximum fraction digits value.
881 */
882 public void setMaximumFractionDigits(int newValue)
883 {
884 newValue = (newValue > 0) ? newValue : 0;
885 super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
886 }
887
888 /**
889 * Sets the minimum number of digits allowed in the fraction
890 * portion of a number to the specified value.
891 * The new value will be the choosen as the minimum between
892 * <code>newvalue</code> and 309. Any value below zero will be
893 * replaced by zero.
894 *
895 * @param newValue The new minimum fraction digits value.
896 */
897 public void setMinimumFractionDigits(int newValue)
898 {
899 newValue = (newValue > 0) ? newValue : 0;
900 super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
901 }
902
903 /**
904 * Sets the multiplier for use in percent and similar formats.
905 * For example, for percent set the multiplier to 100, for permille, set the
906 * miltiplier to 1000.
907 *
908 * @param newValue the new value for multiplier.
909 */
910 public void setMultiplier(int newValue)
911 {
912 multiplier = newValue;
913 }
914
915 /**
916 * Sets the negative prefix.
917 *
918 * @param newValue The new negative prefix.
919 */
920 public void setNegativePrefix(String newValue)
921 {
922 negativePrefix = newValue;
923 }
924
925 /**
926 * Sets the negative suffix.
927 *
928 * @param newValue The new negative suffix.
929 */
930 public void setNegativeSuffix(String newValue)
931 {
932 negativeSuffix = newValue;
933 }
934
935 /**
936 * Sets the positive prefix.
937 *
938 * @param newValue The new positive prefix.
939 */
940 public void setPositivePrefix(String newValue)
941 {
942 positivePrefix = newValue;
943 }
944
945 /**
946 * Sets the new positive suffix.
947 *
948 * @param newValue The new positive suffix.
949 */
950 public void setPositiveSuffix(String newValue)
951 {
952 positiveSuffix = newValue;
953 }
954
955 /**
956 * This method returns a string with the formatting pattern being used
957 * by this object. The string is localized.
958 *
959 * @return A localized <code>String</code> with the formatting pattern.
960 * @see #toPattern()
961 */
962 public String toLocalizedPattern()
963 {
964 return computePattern(this.symbols);
965 }
966
967 /**
968 * This method returns a string with the formatting pattern being used
969 * by this object. The string is not localized.
970 *
971 * @return A <code>String</code> with the formatting pattern.
972 * @see #toLocalizedPattern()
973 */
974 public String toPattern()
975 {
976 return computePattern(nonLocalizedSymbols);
977 }
978
979 /* ***** private methods ***** */
980
981 /**
982 * This is an shortcut helper method used to test if two given strings are
983 * equals.
984 *
985 * @param s1 The first string to test for equality.
986 * @param s2 The second string to test for equality.
987 * @return <code>true</code> if the strings are both <code>null</code> or
988 * equals.
989 */
990 private boolean equals(String s1, String s2)
991 {
992 if (s1 == null || s2 == null)
993 return s1 == s2;
994 return s1.equals(s2);
995 }
996
997
998 /* ****** PATTERN ****** */
999
1000 /**
1001 * This helper function creates a string consisting of all the
1002 * characters which can appear in a pattern and must be quoted.
1003 */
1004 private String patternChars (DecimalFormatSymbols syms)
1005 {
1006 StringBuffer buf = new StringBuffer ();
1007
1008 buf.append(syms.getDecimalSeparator());
1009 buf.append(syms.getDigit());
1010 buf.append(syms.getExponential());
1011 buf.append(syms.getGroupingSeparator());
1012 buf.append(syms.getMinusSign());
1013 buf.append(syms.getPatternSeparator());
1014 buf.append(syms.getPercent());
1015 buf.append(syms.getPerMill());
1016 buf.append(syms.getZeroDigit());
1017 buf.append('\'');
1018 buf.append('\u00a4');
1019
1020 return buf.toString();
1021 }
1022
1023 /**
1024 * Quote special characters as defined by <code>patChars</code> in the
1025 * input string.
1026 *
1027 * @param text
1028 * @param patChars
1029 * @return A StringBuffer with special characters quoted.
1030 */
1031 private StringBuffer quoteFix(String text, String patChars)
1032 {
1033 StringBuffer buf = new StringBuffer();
1034
1035 int len = text.length();
1036 char ch;
1037 for (int index = 0; index < len; ++index)
1038 {
1039 ch = text.charAt(index);
1040 if (patChars.indexOf(ch) != -1)
1041 {
1042 buf.append('\'');
1043 buf.append(ch);
1044 if (ch != '\'') buf.append('\'');
1045 }
1046 else
1047 {
1048 buf.append(ch);
1049 }
1050 }
1051
1052 return buf;
1053 }
1054
1055 /**
1056 * Returns the format pattern, localized to follow the given
1057 * symbols.
1058 */
1059 private String computePattern(DecimalFormatSymbols symbols)
1060 {
1061 StringBuffer mainPattern = new StringBuffer();
1062
1063 // We have to at least emit a zero for the minimum number of
1064 // digits. Past that we need hash marks up to the grouping
1065 // separator (and one beyond).
1066 int _groupingSize = groupingUsed ? groupingSize + 1: groupingSize;
1067 int totalDigits = Math.max(minimumIntegerDigits, _groupingSize);
1068
1069 // if it is not in exponential notiation,
1070 // we always have a # prebended
1071 if (!useExponentialNotation) mainPattern.append(symbols.getDigit());
1072
1073 for (int i = 1; i < totalDigits - minimumIntegerDigits; i++)
1074 mainPattern.append(symbols.getDigit());
1075
1076 for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++)
1077 mainPattern.append(symbols.getZeroDigit());
1078
1079 if (groupingUsed)
1080 {
1081 mainPattern.insert(mainPattern.length() - groupingSize,
1082 symbols.getGroupingSeparator());
1083 }
1084
1085 // See if we need decimal info.
1086 if (minimumFractionDigits > 0 || maximumFractionDigits > 0 ||
1087 decimalSeparatorAlwaysShown)
1088 {
1089 mainPattern.append(symbols.getDecimalSeparator());
1090 }
1091
1092 for (int i = 0; i < minimumFractionDigits; ++i)
1093 mainPattern.append(symbols.getZeroDigit());
1094
1095 for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
1096 mainPattern.append(symbols.getDigit());
1097
1098 if (useExponentialNotation)
1099 {
1100 mainPattern.append(symbols.getExponential());
1101
1102 for (int i = 0; i < minExponentDigits; ++i)
1103 mainPattern.append(symbols.getZeroDigit());
1104
1105 if (minExponentDigits == 0)
1106 mainPattern.append(symbols.getDigit());
1107 }
1108
1109 // save the pattern
1110 String pattern = mainPattern.toString();
1111
1112 // so far we have the pattern itself, now we need to add
1113 // the positive and the optional negative prefixes and suffixes
1114 String patternChars = patternChars(symbols);
1115 mainPattern.insert(0, quoteFix(positivePrefix, patternChars));
1116 mainPattern.append(quoteFix(positiveSuffix, patternChars));
1117
1118 if (hasNegativePrefix)
1119 {
1120 mainPattern.append(symbols.getPatternSeparator());
1121 mainPattern.append(quoteFix(negativePrefix, patternChars));
1122 mainPattern.append(pattern);
1123 mainPattern.append(quoteFix(negativeSuffix, patternChars));
1124 }
1125
1126 // finally, return the pattern string
1127 return mainPattern.toString();
1128 }
1129
1130 /* ****** FORMAT PARSING ****** */
1131
1132 /**
1133 * Scan the input string and define a pattern suitable for use
1134 * with this decimal format.
1135 *
1136 * @param pattern
1137 * @param symbols
1138 */
1139 private void applyPatternWithSymbols(String pattern,
1140 DecimalFormatSymbols symbols)
1141 {
1142 // The pattern string is described by a BNF diagram.
1143 // we could use a recursive parser to read and prepare
1144 // the string, but this would be too slow and resource
1145 // intensive, while this code is quite critical as it is
1146 // called always when the class is instantiated and every
1147 // time a new pattern is given.
1148 // Our strategy is to divide the string into section as given by
1149 // the BNF diagram, iterating through the string and setting up
1150 // the parameters we need for formatting (which is basicly what
1151 // a descendent recursive parser would do - but without recursion).
1152 // I'm sure that there are smarter methods to do this.
1153
1154 // Restore default values. Most of these will be overwritten
1155 // but we want to be sure that nothing is left out.
1156 setDefaultValues();
1157
1158 int len = pattern.length();
1159 if (len == 0)
1160 {
1161 // this is another special case...
1162 this.minimumIntegerDigits = 1;
1163 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
1164 this.minimumFractionDigits = 0;
1165 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
1166
1167 // FIXME: ...and these values may not be valid in all locales
1168 this.minExponentDigits = 0;
1169 this.showDecimalSeparator = true;
1170 this.groupingUsed = true;
1171 this.groupingSize = 3;
1172
1173 return;
1174 }
1175
1176 int start = scanFix(pattern, symbols, 0, true);
1177 if (start < len) start = scanNumberInteger(pattern, symbols, start);
1178 if (start < len)
1179 {
1180 start = scanFractionalPortion(pattern, symbols, start);
1181 }
1182 else
1183 {
1184 // special case, pattern that ends here does not have a fractional
1185 // portion
1186 this.minimumFractionDigits = 0;
1187 this.maximumFractionDigits = 0;
1188 //this.decimalSeparatorAlwaysShown = false;
1189 //this.showDecimalSeparator = false;
1190 }
1191
1192 // XXX: this fixes a compatibility test with the RI.
1193 // If new uses cases fail, try removing this line first.
1194 //if (!this.hasIntegerPattern && !this.hasFractionalPattern)
1195 // throw new IllegalArgumentException("No valid pattern found!");
1196
1197 if (start < len) start = scanExponent(pattern, symbols, start);
1198 if (start < len) start = scanFix(pattern, symbols, start, false);
1199 if (start < len) scanNegativePattern(pattern, symbols, start);
1200
1201 if (useExponentialNotation &&
1202 (maxIntegerDigitsExponent > minimumIntegerDigits) &&
1203 (maxIntegerDigitsExponent > 1))
1204 {
1205 minimumIntegerDigits = 1;
1206 exponentRound = maxIntegerDigitsExponent;
1207 }
1208
1209 if (useExponentialNotation)
1210 maximumIntegerDigits = maxIntegerDigitsExponent;
1211
1212 if (!this.hasFractionalPattern && this.showDecimalSeparator == true)
1213 {
1214 this.decimalSeparatorAlwaysShown = true;
1215 }
1216 }
1217
1218 /**
1219 * Scans for the prefix or suffix portion of the pattern string.
1220 * This method handles the positive subpattern of the pattern string.
1221 *
1222 * @param pattern The pattern string to parse.
1223 * @return The position in the pattern string where parsing ended.
1224 */
1225 private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols,
1226 int start, boolean prefix)
1227 {
1228 StringBuffer buffer = new StringBuffer();
1229
1230 // the number portion is always delimited by one of those
1231 // characters
1232 char decimalSeparator = sourceSymbols.getDecimalSeparator();
1233 char patternSeparator = sourceSymbols.getPatternSeparator();
1234 char groupingSeparator = sourceSymbols.getGroupingSeparator();
1235 char digit = sourceSymbols.getDigit();
1236 char zero = sourceSymbols.getZeroDigit();
1237 char minus = sourceSymbols.getMinusSign();
1238
1239 // other special characters, cached here to avoid method calls later
1240 char percent = sourceSymbols.getPercent();
1241 char permille = sourceSymbols.getPerMill();
1242
1243 String currencySymbol = this.symbols.getCurrencySymbol();
1244
1245 boolean quote = false;
1246
1247 char ch = pattern.charAt(start);
1248 if (ch == patternSeparator)
1249 {
1250 // negative subpattern
1251 this.hasNegativePrefix = true;
1252 ++start;
1253 return start;
1254 }
1255
1256 int len = pattern.length();
1257 int i;
1258 for (i = start; i < len; i++)
1259 {
1260 ch = pattern.charAt(i);
1261
1262 // we are entering into the negative subpattern
1263 if (!quote && ch == patternSeparator)
1264 {
1265 if (this.hasNegativePrefix)
1266 {
1267 throw new IllegalArgumentException("Invalid pattern found: "
1268 + start);
1269 }
1270
1271 this.hasNegativePrefix = true;
1272 ++i;
1273 break;
1274 }
1275
1276 // this means we are inside the number portion
1277 if (!quote &&
1278 (ch == minus || ch == digit || ch == zero ||
1279 ch == groupingSeparator))
1280 break;
1281
1282 if (!quote && ch == decimalSeparator)
1283 {
1284 this.showDecimalSeparator = true;
1285 break;
1286 }
1287 else if (quote && ch != '\'')
1288 {
1289 buffer.append(ch);
1290 continue;
1291 }
1292
1293 if (ch == '\u00A4')
1294 {
1295 // CURRENCY
1296 currencySymbol = this.symbols.getCurrencySymbol();
1297
1298 // if \u00A4 is doubled, we use the international currency symbol
1299 if (i < len && pattern.charAt(i + 1) == '\u00A4')
1300 {
1301 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1302 i++;
1303 }
1304
1305 this.useCurrencySeparator = true;
1306 buffer.append(currencySymbol);
1307 }
1308 else if (ch == percent)
1309 {
1310 // PERCENT
1311 this.multiplier = 100;
1312 buffer.append(this.symbols.getPercent());
1313 }
1314 else if (ch == permille)
1315 {
1316 // PERMILLE
1317 this.multiplier = 1000;
1318 buffer.append(this.symbols.getPerMill());
1319 }
1320 else if (ch == '\'')
1321 {
1322 // QUOTE
1323 if (i < len && pattern.charAt(i + 1) == '\'')
1324 {
1325 // we need to add ' to the buffer
1326 buffer.append(ch);
1327 i++;
1328 }
1329 else
1330 {
1331 quote = !quote;
1332 continue;
1333 }
1334 }
1335 else
1336 {
1337 buffer.append(ch);
1338 }
1339 }
1340
1341 if (prefix)
1342 {
1343 this.positivePrefix = buffer.toString();
1344 this.negativePrefix = minus + "" + positivePrefix;
1345 }
1346 else
1347 {
1348 this.positiveSuffix = buffer.toString();
1349 }
1350
1351 return i;
1352 }
1353
1354 /**
1355 * Scan the given string for number patterns, starting
1356 * from <code>start</code>.
1357 * This method searches the integer part of the pattern only.
1358 *
1359 * @param pattern The pattern string to parse.
1360 * @param start The starting parse position in the string.
1361 * @return The position in the pattern string where parsing ended,
1362 * counted from the beginning of the string (that is, 0).
1363 */
1364 private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols,
1365 int start)
1366 {
1367 char digit = symbols.getDigit();
1368 char zero = symbols.getZeroDigit();
1369 char groupingSeparator = symbols.getGroupingSeparator();
1370 char decimalSeparator = symbols.getDecimalSeparator();
1371 char exponent = symbols.getExponential();
1372 char patternSeparator = symbols.getPatternSeparator();
1373
1374 // count the number of zeroes in the pattern
1375 // this number defines the minum digits in the integer portion
1376 int zeros = 0;
1377
1378 // count the number of digits used in grouping
1379 int _groupingSize = 0;
1380
1381 this.maxIntegerDigitsExponent = 0;
1382
1383 boolean intPartTouched = false;
1384
1385 char ch;
1386 int len = pattern.length();
1387 int i;
1388 for (i = start; i < len; i++)
1389 {
1390 ch = pattern.charAt(i);
1391
1392 // break on decimal separator or exponent or pattern separator
1393 if (ch == decimalSeparator || ch == exponent)
1394 break;
1395
1396 if (this.hasNegativePrefix && ch == patternSeparator)
1397 throw new IllegalArgumentException("Invalid pattern found: "
1398 + start);
1399
1400 if (ch == digit)
1401 {
1402 // in our implementation we could relax this strict
1403 // requirement, but this is used to keep compatibility with
1404 // the RI
1405 if (zeros > 0) throw new
1406 IllegalArgumentException("digit mark following zero in " +
1407 "positive subpattern, not allowed. Position: " + i);
1408
1409 _groupingSize++;
1410 intPartTouched = true;
1411 this.maxIntegerDigitsExponent++;
1412 }
1413 else if (ch == zero)
1414 {
1415 zeros++;
1416 _groupingSize++;
1417 this.maxIntegerDigitsExponent++;
1418 }
1419 else if (ch == groupingSeparator)
1420 {
1421 this.groupingSeparatorInPattern = true;
1422 this.groupingUsed = true;
1423 _groupingSize = 0;
1424 }
1425 else
1426 {
1427 // any other character not listed above
1428 // means we are in the suffix portion
1429 break;
1430 }
1431 }
1432
1433 if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize;
1434 this.minimumIntegerDigits = zeros;
1435
1436 // XXX: compatibility code with the RI: the number of minimum integer
1437 // digits is at least one when maximumIntegerDigits is more than zero
1438 if (intPartTouched && this.maximumIntegerDigits > 0 &&
1439 this.minimumIntegerDigits == 0)
1440 this.minimumIntegerDigits = 1;
1441
1442 return i;
1443 }
1444
1445 /**
1446 * Scan the given string for number patterns, starting
1447 * from <code>start</code>.
1448 * This method searches the fractional part of the pattern only.
1449 *
1450 * @param pattern The pattern string to parse.
1451 * @param start The starting parse position in the string.
1452 * @return The position in the pattern string where parsing ended,
1453 * counted from the beginning of the string (that is, 0).
1454 */
1455 private int scanFractionalPortion(String pattern,
1456 DecimalFormatSymbols symbols,
1457 int start)
1458 {
1459 char digit = symbols.getDigit();
1460 char zero = symbols.getZeroDigit();
1461 char groupingSeparator = symbols.getGroupingSeparator();
1462 char decimalSeparator = symbols.getDecimalSeparator();
1463 char exponent = symbols.getExponential();
1464 char patternSeparator = symbols.getPatternSeparator();
1465
1466 // first character needs to be '.' otherwise we are not parsing the
1467 // fractional portion
1468 char ch = pattern.charAt(start);
1469 if (ch != decimalSeparator)
1470 {
1471 this.minimumFractionDigits = 0;
1472 this.maximumFractionDigits = 0;
1473 return start;
1474 }
1475
1476 ++start;
1477
1478 this.hasFractionalPattern = true;
1479
1480 this.minimumFractionDigits = 0;
1481 int digits = 0;
1482
1483 int len = pattern.length();
1484 int i;
1485 for (i = start; i < len; i++)
1486 {
1487 ch = pattern.charAt(i);
1488
1489 // we hit the exponential or negative subpattern
1490 if (ch == exponent || ch == patternSeparator)
1491 break;
1492
1493 // pattern error
1494 if (ch == groupingSeparator || ch == decimalSeparator) throw new
1495 IllegalArgumentException("unexpected character '" + ch + "' " +
1496 "in fractional subpattern. Position: " + i);
1497
1498 if (ch == digit)
1499 {
1500 digits++;
1501 }
1502 else if (ch == zero)
1503 {
1504 if (digits > 0) throw new
1505 IllegalArgumentException("digit mark following zero in " +
1506 "positive subpattern, not allowed. Position: " + i);
1507
1508 this.minimumFractionDigits++;
1509 }
1510 else
1511 {
1512 // we are in the suffix section of pattern
1513 break;
1514 }
1515 }
1516
1517 if (i == start) this.hasFractionalPattern = false;
1518
1519 this.maximumFractionDigits = this.minimumFractionDigits + digits;
1520 this.showDecimalSeparator = true;
1521
1522 return i;
1523 }
1524
1525 /**
1526 * Scan the given string for number patterns, starting
1527 * from <code>start</code>.
1528 * This method searches the expoential part of the pattern only.
1529 *
1530 * @param pattern The pattern string to parse.
1531 * @param start The starting parse position in the string.
1532 * @return The position in the pattern string where parsing ended,
1533 * counted from the beginning of the string (that is, 0).
1534 */
1535 private int scanExponent(String pattern, DecimalFormatSymbols symbols,
1536 int start)
1537 {
1538 char digit = symbols.getDigit();
1539 char zero = symbols.getZeroDigit();
1540 char groupingSeparator = symbols.getGroupingSeparator();
1541 char decimalSeparator = symbols.getDecimalSeparator();
1542 char exponent = symbols.getExponential();
1543
1544 char ch = pattern.charAt(start);
1545
1546 if (ch == decimalSeparator)
1547 {
1548 // ignore dots
1549 ++start;
1550 }
1551
1552 if (ch != exponent)
1553 {
1554 this.useExponentialNotation = false;
1555 return start;
1556 }
1557
1558 ++start;
1559
1560 this.minExponentDigits = 0;
1561
1562 int len = pattern.length();
1563 int i;
1564 for (i = start; i < len; i++)
1565 {
1566 ch = pattern.charAt(i);
1567
1568 if (ch == groupingSeparator || ch == decimalSeparator ||
1569 ch == digit || ch == exponent) throw new
1570 IllegalArgumentException("unexpected character '" + ch + "' " +
1571 "in exponential subpattern. Position: " + i);
1572
1573 if (ch == zero)
1574 {
1575 this.minExponentDigits++;
1576 }
1577 else
1578 {
1579 // any character other than zero is an exit point
1580 break;
1581 }
1582 }
1583
1584 this.useExponentialNotation = true;
1585
1586 return i;
1587 }
1588
1589 /**
1590 * Scan the given string for number patterns, starting
1591 * from <code>start</code>.
1592 * This method searches the negative part of the pattern only and scan
1593 * throught the end of the string.
1594 *
1595 * @param pattern The pattern string to parse.
1596 * @param start The starting parse position in the string.
1597 */
1598 private void scanNegativePattern(String pattern,
1599 DecimalFormatSymbols sourceSymbols,
1600 int start)
1601 {
1602 StringBuffer buffer = new StringBuffer();
1603
1604 // the number portion is always delimited by one of those
1605 // characters
1606 char decimalSeparator = sourceSymbols.getDecimalSeparator();
1607 char patternSeparator = sourceSymbols.getPatternSeparator();
1608 char groupingSeparator = sourceSymbols.getGroupingSeparator();
1609 char digit = sourceSymbols.getDigit();
1610 char zero = sourceSymbols.getZeroDigit();
1611 char minus = sourceSymbols.getMinusSign();
1612
1613 // other special charcaters, cached here to avoid method calls later
1614 char percent = sourceSymbols.getPercent();
1615 char permille = sourceSymbols.getPerMill();
1616
1617 String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol();
1618 String currencySymbol = CURRENCY_SYMBOL;
1619
1620 boolean quote = false;
1621 boolean prefixDone = false;
1622
1623 int len = pattern.length();
1624 if (len > 0) this.hasNegativePrefix = true;
1625
1626 char ch = pattern.charAt(start);
1627 if (ch == patternSeparator)
1628 {
1629 // no pattern separator in the negative pattern
1630 if ((start + 1) > len) throw new
1631 IllegalArgumentException("unexpected character '" + ch + "' " +
1632 "in negative subpattern.");
1633 start++;
1634 }
1635
1636 int i;
1637 for (i = start; i < len; i++)
1638 {
1639 ch = pattern.charAt(i);
1640
1641 // this means we are inside the number portion
1642 if (!quote &&
1643 (ch == digit || ch == zero || ch == decimalSeparator ||
1644 ch == patternSeparator || ch == groupingSeparator))
1645 {
1646 if (!prefixDone)
1647 {
1648 this.negativePrefix = buffer.toString();
1649 buffer.delete(0, buffer.length());
1650 prefixDone = true;
1651 }
1652 }
1653 else if (ch == minus)
1654 {
1655 buffer.append(this.symbols.getMinusSign());
1656 }
1657 else if (quote && ch != '\'')
1658 {
1659 buffer.append(ch);
1660 }
1661 else if (ch == '\u00A4')
1662 {
1663 // CURRENCY
1664 currencySymbol = CURRENCY_SYMBOL;
1665
1666 // if \u00A4 is doubled, we use the international currency symbol
1667 if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
1668 {
1669 currencySymbol = this.symbols.getInternationalCurrencySymbol();
1670 i = i + 2;
1671 }
1672
1673 // FIXME: not sure about this, the specs says that we only have to
1674 // change prefix and suffix, so leave it as commented
1675 // unless in case of bug report/errors
1676 //this.useCurrencySeparator = true;
1677
1678 buffer.append(currencySymbol);
1679 }
1680 else if (ch == percent)
1681 {
1682 // PERCENT
1683 this.negativePatternMultiplier = 100;
1684 buffer.append(this.symbols.getPercent());
1685 }
1686 else if (ch == permille)
1687 {
1688 // PERMILLE
1689 this.negativePatternMultiplier = 1000;
1690 buffer.append(this.symbols.getPerMill());
1691 }
1692 else if (ch == '\'')
1693 {
1694 // QUOTE
1695 if (i < len && pattern.charAt(i + 1) == '\'')
1696 {
1697 // we need to add ' to the buffer
1698 buffer.append(ch);
1699 i++;
1700 }
1701 else
1702 {
1703 quote = !quote;
1704 }
1705 }
1706 else if (ch == patternSeparator)
1707 {
1708 // no pattern separator in the negative pattern
1709 throw new IllegalArgumentException("unexpected character '" + ch +
1710 "' in negative subpattern.");
1711 }
1712 else
1713 {
1714 buffer.append(ch);
1715 }
1716 }
1717
1718 if (prefixDone)
1719 this.negativeSuffix = buffer.toString();
1720 else
1721 this.negativePrefix = buffer.toString();
1722 }
1723
1724 /* ****** FORMATTING ****** */
1725
1726 /**
1727 * Handles the real formatting.
1728 *
1729 * We use a BigDecimal to format the number without precision loss.
1730 * All the rounding is done by methods in BigDecimal.
1731 * The <code>isLong</code> parameter is used to determine if we are
1732 * formatting a long or BigInteger. In this case, we avoid to format
1733 * the fractional part of the number (unless specified otherwise in the
1734 * format string) that would consist only of a 0 digit.
1735 *
1736 * @param number A BigDecimal representation fo the input number.
1737 * @param dest The destination buffer.
1738 * @param isLong A boolean that indicates if this BigDecimal is a real
1739 * decimal or an integer.
1740 * @param fieldPos Use to keep track of the formatting position.
1741 */
1742 private void formatInternal(BigDecimal number, boolean isLong,
1743 StringBuffer dest, FieldPosition fieldPos)
1744 {
1745 // The specs says that fieldPos should not be null, and that we
1746 // should throw a NPE, but it seems that in few classes that
1747 // reference this one, fieldPos is set to null.
1748 // This is even defined in the javadoc, see for example MessageFormat.
1749 // I think the best here is to check for fieldPos and build one if it is
1750 // null. If it cause harms or regressions, just remove this line and
1751 // fix the classes in the point of call, insted.
1752 if (fieldPos == null) fieldPos = new FieldPosition(0);
1753
1754 int _multiplier = this.multiplier;
1755
1756 // used to track attribute starting position for each attribute
1757 int attributeStart = -1;
1758
1759 // now get the sign this will be used by the special case Inifinity
1760 // and by the normal cases.
1761 boolean isNegative = (number.signum() < 0) ? true : false;
1762 if (isNegative)
1763 {
1764 attributeStart = dest.length();
1765
1766 // append the negative prefix to the string
1767 dest.append(negativePrefix);
1768
1769 // once got the negative prefix, we can use
1770 // the absolute value.
1771 number = number.abs();
1772
1773 _multiplier = negativePatternMultiplier;
1774
1775 addAttribute(Field.SIGN, attributeStart, dest.length());
1776 }
1777 else
1778 {
1779 // not negative, use the positive prefix
1780 dest.append(positivePrefix);
1781 }
1782
1783 // these are used ot update the field position
1784 int beginIndexInt = dest.length();
1785 int endIndexInt = 0;
1786 int beginIndexFract = 0;
1787 int endIndexFract = 0;
1788
1789 // compute the multiplier to use with percent and similar
1790 number = number.multiply(new BigDecimal(_multiplier));
1791
1792 // XXX: special case, not sure if it belongs here or if it is
1793 // correct at all. There may be other special cases as well
1794 // these should be handled in the format string parser.
1795 if (this.maximumIntegerDigits == 0 && this.maximumFractionDigits == 0)
1796 {
1797 number = BigDecimal.ZERO;
1798 this.maximumIntegerDigits = 1;
1799 this.minimumIntegerDigits = 1;
1800 }
1801
1802 // get the absolute number
1803 number = number.abs();
1804
1805 // the scaling to use while formatting this number
1806 int scale = this.maximumFractionDigits;
1807
1808 // this is the actual number we will use
1809 // it is corrected later on to handle exponential
1810 // notation, if needed
1811 long exponent = 0;
1812
1813 // are we using exponential notation?
1814 if (this.useExponentialNotation)
1815 {
1816 exponent = getExponent(number);
1817 number = number.movePointLeft((int) exponent);
1818
1819 // FIXME: this makes the test ##.###E0 to pass,
1820 // but all all the other tests to fail...
1821 // this should be really something like
1822 // min + max - what is already shown...
1823 //scale = this.minimumIntegerDigits + this.maximumFractionDigits;
1824 }
1825
1826 // round the number to the nearest neighbor
1827 number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
1828
1829 // now get the integer and fractional part of the string
1830 // that will be processed later
1831 String plain = number.toPlainString();
1832
1833 String intPart = null;
1834 String fractPart = null;
1835
1836 // remove - from the integer part, this is needed as
1837 // the Narrowing Primitive Conversions algorithm used may loose
1838 // information about the sign
1839 int minusIndex = plain.lastIndexOf('-', 0);
1840 if (minusIndex > -1) plain = plain.substring(minusIndex + 1);
1841
1842 // strip the decimal portion
1843 int dot = plain.indexOf('.');
1844 if (dot > -1)
1845 {
1846 intPart = plain.substring(0, dot);
1847 dot++;
1848
1849 if (useExponentialNotation)
1850 fractPart = plain.substring(dot, dot + scale);
1851 else
1852 fractPart = plain.substring(dot);
1853 }
1854 else
1855 {
1856 intPart = plain;
1857 }
1858
1859 // used in various places later on
1860 int intPartLen = intPart.length();
1861 endIndexInt = intPartLen;
1862
1863 // if the number of digits in our intPart is not greater than the
1864 // minimum we have to display, we append zero to the destination
1865 // buffer before adding the integer portion of the number.
1866 int zeroes = minimumIntegerDigits - intPartLen;
1867 if (zeroes > 0)
1868 {
1869 attributeStart = Math.max(dest.length() - 1, 0);
1870 appendZero(dest, zeroes, minimumIntegerDigits);
1871 }
1872
1873 if (this.useExponentialNotation)
1874 {
1875 // For exponential numbers, the significant in mantissa are
1876 // the sum of the minimum integer and maximum fraction
1877 // digits, and does not take into account the maximun integer
1878 // digits to display.
1879
1880 if (attributeStart < 0)
1881 attributeStart = Math.max(dest.length() - 1, 0);
1882 appendDigit(intPart, dest, this.groupingUsed);
1883 }
1884 else
1885 {
1886 // non exponential notation
1887 intPartLen = intPart.length();
1888 int canary = Math.min(intPartLen, this.maximumIntegerDigits);
1889
1890 // remove from the string the number in excess
1891 // use only latest digits
1892 intPart = intPart.substring(intPartLen - canary);
1893 endIndexInt = intPart.length() + 1;
1894
1895 // append it
1896 if (maximumIntegerDigits > 0 &&
1897 !(this.minimumIntegerDigits == 0 &&
1898 intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0))
1899 {
1900 if (attributeStart < 0)
1901 attributeStart = Math.max(dest.length() - 1, 0);
1902 appendDigit(intPart, dest, this.groupingUsed);
1903 }
1904 }
1905
1906 // add the INTEGER attribute
1907 addAttribute(Field.INTEGER, attributeStart, dest.length());
1908
1909 // ...update field position, if needed, and return...
1910 if ((fieldPos.getField() == INTEGER_FIELD ||
1911 fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
1912 {
1913 fieldPos.setBeginIndex(beginIndexInt);
1914 fieldPos.setEndIndex(endIndexInt);
1915 }
1916
1917 handleFractionalPart(dest, fractPart, fieldPos, isLong);
1918
1919 // and the exponent
1920 if (this.useExponentialNotation)
1921 {
1922 attributeStart = dest.length();
1923
1924 dest.append(symbols.getExponential());
1925
1926 addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
1927 attributeStart = dest.length();
1928
1929 if (exponent < 0)
1930 {
1931 dest.append(symbols.getMinusSign());
1932 exponent = -exponent;
1933
1934 addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
1935 }
1936
1937 attributeStart = dest.length();
1938
1939 String exponentString = String.valueOf(exponent);
1940 int exponentLength = exponentString.length();
1941
1942 for (int i = 0; i < minExponentDigits - exponentLength; i++)
1943 dest.append(symbols.getZeroDigit());
1944
1945 for (int i = 0; i < exponentLength; ++i)
1946 dest.append(exponentString.charAt(i));
1947
1948 addAttribute(Field.EXPONENT, attributeStart, dest.length());
1949 }
1950
1951 // now include the suffixes...
1952 if (isNegative)
1953 {
1954 dest.append(negativeSuffix);
1955 }
1956 else
1957 {
1958 dest.append(positiveSuffix);
1959 }
1960 }
1961
1962 /**
1963 * Add to the input buffer the result of formatting the fractional
1964 * portion of the number.
1965 *
1966 * @param dest
1967 * @param fractPart
1968 * @param fieldPos
1969 * @param isLong
1970 */
1971 private void handleFractionalPart(StringBuffer dest, String fractPart,
1972 FieldPosition fieldPos, boolean isLong)
1973 {
1974 int dotStart = 0;
1975 int dotEnd = 0;
1976 boolean addDecimal = false;
1977
1978 if (this.decimalSeparatorAlwaysShown ||
1979 ((!isLong || this.useExponentialNotation) &&
1980 this.showDecimalSeparator && this.maximumFractionDigits > 0) ||
1981 this.minimumFractionDigits > 0)
1982 {
1983 dotStart = dest.length();
1984
1985 if (this.useCurrencySeparator)
1986 dest.append(symbols.getMonetaryDecimalSeparator());
1987 else
1988 dest.append(symbols.getDecimalSeparator());
1989
1990 dotEnd = dest.length();
1991 addDecimal = true;
1992 }
1993
1994 // now handle the fraction portion of the number
1995 int fractStart = 0;
1996 int fractEnd = 0;
1997 boolean addFractional = false;
1998
1999 if ((!isLong || this.useExponentialNotation)
2000 && this.maximumFractionDigits > 0
2001 || this.minimumFractionDigits > 0)
2002 {
2003 fractStart = dest.length();
2004 fractEnd = fractStart;
2005
2006 int digits = this.minimumFractionDigits;
2007
2008 if (this.useExponentialNotation)
2009 {
2010 digits = (this.minimumIntegerDigits + this.minimumFractionDigits)
2011 - dest.length();
2012 if (digits < 0) digits = 0;
2013 }
2014
2015 fractPart = adjustTrailingZeros(fractPart, digits);
2016
2017 // FIXME: this code must be improved
2018 // now check if the factional part is just 0, in this case
2019 // we need to remove the '.' unless requested
2020 boolean allZeros = true;
2021 char fracts[] = fractPart.toCharArray();
2022 for (int i = 0; i < fracts.length; i++)
2023 {
2024 if (fracts[i] != '0')
2025 allZeros = false;
2026 }
2027
2028 if (!allZeros || (minimumFractionDigits > 0))
2029 {
2030 appendDigit(fractPart, dest, false);
2031 fractEnd = dest.length();
2032
2033 addDecimal = true;
2034 addFractional = true;
2035 }
2036 else if (!this.decimalSeparatorAlwaysShown)
2037 {
2038 dest.deleteCharAt(dest.length() - 1);
2039 addDecimal = false;
2040 }
2041 else
2042 {
2043 fractEnd = dest.length();
2044 addFractional = true;
2045 }
2046 }
2047
2048 if (addDecimal)
2049 addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
2050
2051 if (addFractional)
2052 addAttribute(Field.FRACTION, fractStart, fractEnd);
2053
2054 if ((fieldPos.getField() == FRACTION_FIELD ||
2055 fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION))
2056 {
2057 fieldPos.setBeginIndex(fractStart);
2058 fieldPos.setEndIndex(fractEnd);
2059 }
2060 }
2061
2062 /**
2063 * Append to <code>dest</code>the give number of zeros.
2064 * Grouping is added if needed.
2065 * The integer totalDigitCount defines the total number of digits
2066 * of the number to which we are appending zeroes.
2067 */
2068 private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount)
2069 {
2070 char ch = symbols.getZeroDigit();
2071 char gSeparator = symbols.getGroupingSeparator();
2072
2073 int i = 0;
2074 int gPos = totalDigitCount;
2075 for (i = 0; i < zeroes; i++, gPos--)
2076 {
2077 if (this.groupingSeparatorInPattern &&
2078 (this.groupingUsed && this.groupingSize != 0) &&
2079 (gPos % groupingSize == 0 && i > 0))
2080 dest.append(gSeparator);
2081
2082 dest.append(ch);
2083 }
2084
2085 // special case, that requires adding an additional separator
2086 if (this.groupingSeparatorInPattern &&
2087 (this.groupingUsed && this.groupingSize != 0) &&
2088 (gPos % groupingSize == 0))
2089 dest.append(gSeparator);
2090 }
2091
2092 /**
2093 * Append src to <code>dest</code>.
2094 *
2095 * Grouping is added if <code>groupingUsed</code> is set
2096 * to <code>true</code>.
2097 */
2098 private void appendDigit(String src, StringBuffer dest,
2099 boolean groupingUsed)
2100 {
2101 int zero = symbols.getZeroDigit() - '0';
2102
2103 int ch;
2104 char gSeparator = symbols.getGroupingSeparator();
2105
2106 int len = src.length();
2107 for (int i = 0, gPos = len; i < len; i++, gPos--)
2108 {
2109 ch = src.charAt(i);
2110 if (groupingUsed && this.groupingSize != 0 &&
2111 gPos % groupingSize == 0 && i > 0)
2112 dest.append(gSeparator);
2113
2114 dest.append((char) (zero + ch));
2115 }
2116 }
2117
2118 /**
2119 * Calculate the exponent to use if eponential notation is used.
2120 * The exponent is calculated as a power of ten.
2121 * <code>number</code> should be positive, if is zero, or less than zero,
2122 * zero is returned.
2123 */
2124 private long getExponent(BigDecimal number)
2125 {
2126 long exponent = 0;
2127
2128 if (number.signum() > 0)
2129 {
2130 double _number = number.doubleValue();
2131 exponent = (long) Math.floor (Math.log10(_number));
2132
2133 // get the right value for the exponent
2134 exponent = exponent - (exponent % this.exponentRound);
2135
2136 // if the minimumIntegerDigits is more than zero
2137 // we display minimumIntegerDigits of digits.
2138 // so, for example, if minimumIntegerDigits == 2
2139 // and the actual number is 0.123 it will be
2140 // formatted as 12.3E-2
2141 // this means that the exponent have to be shifted
2142 // to the correct value.
2143 if (minimumIntegerDigits > 0)
2144 exponent -= minimumIntegerDigits - 1;
2145 }
2146
2147 return exponent;
2148 }
2149
2150 /**
2151 * Remove contiguos zeros from the end of the <code>src</code> string,
2152 * if src contains more than <code>minimumDigits</code> digits.
2153 * if src contains less that <code>minimumDigits</code>,
2154 * then append zeros to the string.
2155 *
2156 * Only the first block of zero digits is removed from the string
2157 * and only if they fall in the src.length - minimumDigits
2158 * portion of the string.
2159 *
2160 * @param src The string with the correct number of zeros.
2161 */
2162 private String adjustTrailingZeros(String src, int minimumDigits)
2163 {
2164 int len = src.length();
2165 String result;
2166
2167 // remove all trailing zero
2168 if (len > minimumDigits)
2169 {
2170 int zeros = 0;
2171 for (int i = len - 1; i > minimumDigits; i--)
2172 {
2173 if (src.charAt(i) == '0')
2174 ++zeros;
2175 else
2176 break;
2177 }
2178 result = src.substring(0, len - zeros);
2179 }
2180 else
2181 {
2182 char zero = symbols.getZeroDigit();
2183 StringBuffer _result = new StringBuffer(src);
2184 for (int i = len; i < minimumDigits; i++)
2185 {
2186 _result.append(zero);
2187 }
2188 result = _result.toString();
2189 }
2190
2191 return result;
2192 }
2193
2194 /**
2195 * Adds an attribute to the attributes list.
2196 *
2197 * @param field
2198 * @param begin
2199 * @param end
2200 */
2201 private void addAttribute(Field field, int begin, int end)
2202 {
2203 /*
2204 * This method and its implementation derives directly from the
2205 * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
2206 */
2207
2208 FieldPosition pos = new FieldPosition(field);
2209 pos.setBeginIndex(begin);
2210 pos.setEndIndex(end);
2211 attributes.add(pos);
2212 }
2213
2214 /**
2215 * Sets the default values for the various properties in this DecimaFormat.
2216 */
2217 private void setDefaultValues()
2218 {
2219 // Maybe we should add these values to the message bundle and take
2220 // the most appropriate for them for any locale.
2221 // Anyway, these seem to be good values for a default in most languages.
2222 // Note that most of these will change based on the format string.
2223
2224 this.negativePrefix = String.valueOf(symbols.getMinusSign());
2225 this.negativeSuffix = "";
2226 this.positivePrefix = "";
2227 this.positiveSuffix = "";
2228
2229 this.multiplier = 1;
2230 this.negativePatternMultiplier = 1;
2231 this.exponentRound = 1;
2232
2233 this.hasNegativePrefix = false;
2234
2235 this.minimumIntegerDigits = 1;
2236 this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
2237 this.minimumFractionDigits = 0;
2238 this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
2239 this.minExponentDigits = 0;
2240
2241 this.groupingSize = 0;
2242
2243 this.decimalSeparatorAlwaysShown = false;
2244 this.showDecimalSeparator = false;
2245 this.useExponentialNotation = false;
2246 this.groupingUsed = false;
2247 this.groupingSeparatorInPattern = false;
2248
2249 this.useCurrencySeparator = false;
2250
2251 this.hasFractionalPattern = false;
2252 }
2253 }