001/* DecimalFormat.java -- Formats and parses numbers
002   Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception 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
044package java.text;
045
046import gnu.java.lang.CPStringBuilder;
047
048import java.math.BigDecimal;
049import java.math.BigInteger;
050
051import java.util.ArrayList;
052import java.util.Currency;
053import 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 */
080public 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}