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