Source for java.text.DecimalFormat

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