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: package java.text;
  39: 
  40: import gnu.java.text.AttributedFormatBuffer;
  41: import gnu.java.text.FormatBuffer;
  42: import gnu.java.text.FormatCharacterIterator;
  43: import gnu.java.text.StringFormatBuffer;
  44: 
  45: import java.io.IOException;
  46: import java.io.ObjectInputStream;
  47: import java.util.Currency;
  48: import java.util.HashMap;
  49: import java.util.Locale;
  50: 
  51: /**
  52:  * @author Tom Tromey (tromey@cygnus.com)
  53:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
  54:  * @date March 4, 1999
  55:  */
  56: /* Written using "Java Class Libraries", 2nd edition, plus online
  57:  * API docs for JDK 1.2 from http://www.javasoft.com.
  58:  * Status:  Believed complete and correct to 1.2.
  59:  * Note however that the docs are very unclear about how format parsing
  60:  * should work.  No doubt there are problems here.
  61:  */
  62: public class DecimalFormat extends NumberFormat
  63: {
  64:   // This is a helper for applyPatternWithSymbols.  It reads a prefix
  65:   // or a suffix.  It can cause some side-effects.
  66:   private int scanFix (String pattern, int index, FormatBuffer buf,
  67:                        String patChars, DecimalFormatSymbols syms,
  68:                        boolean is_suffix)
  69:     {
  70:     int len = pattern.length();
  71:     boolean quoteStarted = false;
  72:     buf.clear();
  73:     
  74:     boolean multiplierSet = false;
  75:     while (index < len)
  76:       {
  77:     char c = pattern.charAt(index);
  78: 
  79:     if (quoteStarted)
  80:       {
  81:         if (c == '\'')
  82:           quoteStarted = false;
  83:         else
  84:           buf.append(c);
  85:         index++;
  86:         continue;
  87:       }
  88: 
  89:     if (c == '\'' && index + 1 < len
  90:         && pattern.charAt(index + 1) == '\'')
  91:       {
  92:         buf.append(c);
  93:         index++;
  94:       }
  95:     else if (c == '\'')
  96:       {
  97:         quoteStarted = true;
  98:       }
  99:     else if (c == '\u00a4')
 100:       {
 101:             /* Currency interpreted later */
 102:             buf.append(c);
 103:         }
 104:     else if (c == syms.getPercent())
 105:       {
 106:         if (multiplierSet)
 107:           throw new IllegalArgumentException ("multiplier already set " +
 108:                           "- index: " + index);
 109:         multiplierSet = true;
 110:         multiplier = 100;
 111:         buf.append(c, NumberFormat.Field.PERCENT);
 112:       }
 113:     else if (c == syms.getPerMill())
 114:       {
 115:         if (multiplierSet)
 116:           throw new IllegalArgumentException ("multiplier already set " +
 117:                           "- index: " + index);
 118:         multiplierSet = true;
 119:         multiplier = 1000;
 120:         buf.append(c, NumberFormat.Field.PERMILLE);
 121:       }
 122:     else if (patChars.indexOf(c) != -1)
 123:       {
 124:         // This is a pattern character.
 125:         break;
 126:       }
 127:     else
 128:         {
 129:             buf.append(c);
 130:         }
 131:     index++;
 132:       }
 133: 
 134:     if (quoteStarted)
 135:       throw new IllegalArgumentException ("pattern is lacking a closing quote");
 136: 
 137:     return index;
 138:   }
 139: 
 140:   // A helper which reads a number format.
 141:   private int scanFormat (String pattern, int index, String patChars,
 142:                           DecimalFormatSymbols syms, boolean is_positive)
 143:   {
 144:     int max = pattern.length();
 145: 
 146:     int countSinceGroup = 0;
 147:     int zeroCount = 0;
 148:     boolean saw_group = false;
 149: 
 150:     //
 151:     // Scan integer part.
 152:     //
 153:     while (index < max)
 154:       {
 155:     char c = pattern.charAt(index);
 156: 
 157:     if (c == syms.getDigit())
 158:       {
 159:         if (zeroCount > 0)
 160:           throw new IllegalArgumentException ("digit mark following " +
 161:                           "zero - index: " + index);
 162:         ++countSinceGroup;
 163:       }
 164:     else if (c == syms.getZeroDigit())
 165:       {
 166:         ++zeroCount;
 167:         ++countSinceGroup;
 168:       }
 169:     else if (c == syms.getGroupingSeparator())
 170:       {
 171:         countSinceGroup = 0;
 172:         saw_group = true;
 173:       }
 174:     else
 175:       break;
 176: 
 177:     ++index;
 178:       }
 179: 
 180:     // We can only side-effect when parsing the positive format.
 181:     if (is_positive)
 182:       {
 183:     groupingUsed = saw_group;
 184:     groupingSize = (byte) countSinceGroup;
 185:     // Checking "zeroCount > 0" avoids 0 being formatted into "" with "#".
 186:     if (zeroCount > 0)
 187:       minimumIntegerDigits = zeroCount;
 188:       }
 189: 
 190:     // Early termination.
 191:     if (index == max || pattern.charAt(index) == syms.getGroupingSeparator())
 192:       {
 193:     if (is_positive)
 194:       decimalSeparatorAlwaysShown = false;
 195:     return index;
 196:       }
 197: 
 198:     if (pattern.charAt(index) == syms.getDecimalSeparator())
 199:       {
 200:     ++index;
 201: 
 202:     //
 203:     // Scan fractional part.
 204:     //
 205:     int hashCount = 0;
 206:     zeroCount = 0;
 207:     while (index < max)
 208:       {
 209:         char c = pattern.charAt(index);
 210:         if (c == syms.getZeroDigit())
 211:           {
 212:         if (hashCount > 0)
 213:           throw new IllegalArgumentException ("zero mark " +
 214:                               "following digit - index: " + index);
 215:         ++zeroCount;
 216:           }
 217:         else if (c == syms.getDigit())
 218:           {
 219:         ++hashCount;
 220:           }
 221:         else if (c != syms.getExponential()
 222:              && c != syms.getPatternSeparator()
 223:              && c != syms.getPercent()
 224:              && c != syms.getPerMill()
 225:              && patChars.indexOf(c) != -1)
 226:           throw new IllegalArgumentException ("unexpected special " +
 227:                           "character - index: " + index);
 228:         else
 229:           break;
 230: 
 231:         ++index;
 232:       }
 233: 
 234:     if (is_positive)
 235:       {
 236:         maximumFractionDigits = hashCount + zeroCount;
 237:         minimumFractionDigits = zeroCount;
 238:       }
 239: 
 240:     if (index == max)
 241:       return index;
 242:       }
 243: 
 244:     if (pattern.charAt(index) == syms.getExponential())
 245:       {
 246:     //
 247:     // Scan exponential format.
 248:     //
 249:     zeroCount = 0;
 250:     ++index;
 251:     while (index < max)
 252:       {
 253:         char c = pattern.charAt(index);
 254:         if (c == syms.getZeroDigit())
 255:           ++zeroCount;
 256:         else if (c == syms.getDigit())
 257:           {
 258:         if (zeroCount > 0)
 259:           throw new
 260:             IllegalArgumentException ("digit mark following zero " +
 261:                           "in exponent - index: " +
 262:                           index);
 263:           }
 264:         else if (patChars.indexOf(c) != -1)
 265:           throw new IllegalArgumentException ("unexpected special " +
 266:                           "character - index: " +
 267:                           index);
 268:         else
 269:           break;
 270: 
 271:         ++index;
 272:       }
 273: 
 274:     if (is_positive)
 275:       {
 276:         useExponentialNotation = true;
 277:         minExponentDigits = (byte) zeroCount;
 278:       }
 279: 
 280:     maximumIntegerDigits = groupingSize;
 281:     groupingSize = 0;
 282:     if (maximumIntegerDigits > minimumIntegerDigits && maximumIntegerDigits > 0)
 283:       {
 284:         minimumIntegerDigits = 1;
 285:         exponentRound = maximumIntegerDigits;
 286:       }
 287:     else
 288:       exponentRound = 1;
 289:       }
 290: 
 291:     return index;
 292:   }
 293: 
 294:   // This helper function creates a string consisting of all the
 295:   // characters which can appear in a pattern and must be quoted.
 296:   private String patternChars (DecimalFormatSymbols syms)
 297:   {
 298:     StringBuffer buf = new StringBuffer ();
 299:     buf.append(syms.getDecimalSeparator());
 300:     buf.append(syms.getDigit());
 301:     buf.append(syms.getExponential());
 302:     buf.append(syms.getGroupingSeparator());
 303:     // Adding this one causes pattern application to fail.
 304:     // Of course, omitting is causes toPattern to fail.
 305:     // ... but we already have bugs there.  FIXME.
 306:     // buf.append(syms.getMinusSign());
 307:     buf.append(syms.getPatternSeparator());
 308:     buf.append(syms.getPercent());
 309:     buf.append(syms.getPerMill());
 310:     buf.append(syms.getZeroDigit());
 311:     buf.append('\u00a4');
 312:     return buf.toString();
 313:   }
 314: 
 315:   private void applyPatternWithSymbols(String pattern, DecimalFormatSymbols syms)
 316:   {
 317:     // Initialize to the state the parser expects.
 318:     negativePrefix = "";
 319:     negativeSuffix = "";
 320:     positivePrefix = "";
 321:     positiveSuffix = "";
 322:     decimalSeparatorAlwaysShown = false;
 323:     groupingSize = 0;
 324:     minExponentDigits = 0;
 325:     multiplier = 1;
 326:     useExponentialNotation = false;
 327:     groupingUsed = false;
 328:     maximumFractionDigits = 0;
 329:     maximumIntegerDigits = MAXIMUM_INTEGER_DIGITS;
 330:     minimumFractionDigits = 0;
 331:     minimumIntegerDigits = 1;
 332: 
 333:     AttributedFormatBuffer buf = new AttributedFormatBuffer ();
 334:     String patChars = patternChars (syms);
 335: 
 336:     int max = pattern.length();
 337:     int index = scanFix (pattern, 0, buf, patChars, syms, false);
 338:     buf.sync();
 339:     positivePrefix = buf.getBuffer().toString();
 340:     positivePrefixRanges = buf.getRanges();
 341:     positivePrefixAttrs = buf.getAttributes();
 342: 
 343:     index = scanFormat (pattern, index, patChars, syms, true);
 344: 
 345:     index = scanFix (pattern, index, buf, patChars, syms, true);
 346:     buf.sync();
 347:     positiveSuffix = buf.getBuffer().toString();
 348:     positiveSuffixRanges = buf.getRanges();
 349:     positiveSuffixAttrs = buf.getAttributes();
 350: 
 351:     if (index == pattern.length())
 352:       {
 353:     // No negative info.
 354:     negativePrefix = null;
 355:     negativeSuffix = null;
 356:       }
 357:     else
 358:       {
 359:     if (pattern.charAt(index) != syms.getPatternSeparator())
 360:       throw new IllegalArgumentException ("separator character " +
 361:                           "expected - index: " + index);
 362: 
 363:     index = scanFix (pattern, index + 1, buf, patChars, syms, false);
 364:     buf.sync();
 365:     negativePrefix = buf.getBuffer().toString();
 366:     negativePrefixRanges = buf.getRanges();
 367:     negativePrefixAttrs = buf.getAttributes();
 368: 
 369:     // We parse the negative format for errors but we don't let
 370:     // it side-effect this object.
 371:     index = scanFormat (pattern, index, patChars, syms, false);
 372: 
 373:     index = scanFix (pattern, index, buf, patChars, syms, true);
 374:     buf.sync();
 375:     negativeSuffix = buf.getBuffer().toString();
 376:     negativeSuffixRanges = buf.getRanges();
 377:     negativeSuffixAttrs = buf.getAttributes();
 378: 
 379:     if (index != pattern.length())
 380:       throw new IllegalArgumentException ("end of pattern expected " +
 381:                           "- index: " + index);
 382:       }
 383:   }
 384: 
 385:   public void applyLocalizedPattern (String pattern)
 386:   {
 387:     // JCL p. 638 claims this throws a ParseException but p. 629
 388:     // contradicts this.  Empirical tests with patterns of "0,###.0"
 389:     // and "#.#.#" corroborate the p. 629 statement that an
 390:     // IllegalArgumentException is thrown.
 391:     applyPatternWithSymbols (pattern, symbols);
 392:   }
 393: 
 394:   public void applyPattern (String pattern)
 395:   {
 396:     // JCL p. 638 claims this throws a ParseException but p. 629
 397:     // contradicts this.  Empirical tests with patterns of "0,###.0"
 398:     // and "#.#.#" corroborate the p. 629 statement that an
 399:     // IllegalArgumentException is thrown.
 400:     applyPatternWithSymbols (pattern, nonLocalizedSymbols);
 401:   }
 402: 
 403:   public Object clone ()
 404:   {
 405:     DecimalFormat c = (DecimalFormat) super.clone ();
 406:     c.symbols = (DecimalFormatSymbols) symbols.clone ();
 407:     return c;
 408:   }
 409: 
 410:   /**
 411:    * Constructs a <code>DecimalFormat</code> which uses the default
 412:    * pattern and symbols.
 413:    */
 414:   public DecimalFormat ()
 415:   {
 416:     this ("#,##0.###");
 417:   }
 418: 
 419:   /**
 420:    * Constructs a <code>DecimalFormat</code> which uses the given
 421:    * pattern and the default symbols for formatting and parsing.
 422:    *
 423:    * @param pattern the non-localized pattern to use.
 424:    * @throws NullPointerException if any argument is null.
 425:    * @throws IllegalArgumentException if the pattern is invalid.
 426:    */
 427:   public DecimalFormat (String pattern)
 428:   {
 429:     this (pattern, new DecimalFormatSymbols ());
 430:   }
 431: 
 432:   /**
 433:    * Constructs a <code>DecimalFormat</code> using the given pattern
 434:    * and formatting symbols.  This construction method is used to give
 435:    * complete control over the formatting process.  
 436:    *
 437:    * @param pattern the non-localized pattern to use.
 438:    * @param symbols the set of symbols used for parsing and formatting.
 439:    * @throws NullPointerException if any argument is null.
 440:    * @throws IllegalArgumentException if the pattern is invalid.
 441:    */
 442:   public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
 443:   {
 444:     this.symbols = (DecimalFormatSymbols) symbols.clone();
 445:     applyPattern(pattern);
 446:   }
 447: 
 448:   private boolean equals(String s1, String s2)
 449:   {
 450:     if (s1 == null || s2 == null)
 451:       return s1 == s2;
 452:     return s1.equals(s2);
 453:   }
 454: 
 455:   /**
 456:    * Tests this instance for equality with an arbitrary object.  This method
 457:    * returns <code>true</code> if:
 458:    * <ul>
 459:    * <li><code>obj</code> is not <code>null</code>;</li>
 460:    * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
 461:    * <li>this instance and <code>obj</code> have the same attributes;</li>
 462:    * </ul>
 463:    * 
 464:    * @param obj  the object (<code>null</code> permitted).
 465:    * 
 466:    * @return A boolean.
 467:    */
 468:   public boolean equals(Object obj)
 469:   {
 470:     if (! (obj instanceof DecimalFormat))
 471:       return false;
 472:     DecimalFormat dup = (DecimalFormat) obj;
 473:     return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown 
 474:            && groupingUsed == dup.groupingUsed 
 475:            && groupingSize == dup.groupingSize 
 476:            && multiplier == dup.multiplier
 477:            && useExponentialNotation == dup.useExponentialNotation
 478:            && minExponentDigits == dup.minExponentDigits
 479:            && minimumIntegerDigits == dup.minimumIntegerDigits
 480:            && maximumIntegerDigits == dup.maximumIntegerDigits
 481:            && minimumFractionDigits == dup.minimumFractionDigits
 482:            && maximumFractionDigits == dup.maximumFractionDigits
 483:            && equals(negativePrefix, dup.negativePrefix)
 484:            && equals(negativeSuffix, dup.negativeSuffix)
 485:            && equals(positivePrefix, dup.positivePrefix)
 486:            && equals(positiveSuffix, dup.positiveSuffix)
 487:            && symbols.equals(dup.symbols));
 488:   }
 489: 
 490:   private void formatInternal (double number, FormatBuffer dest,
 491:                    FieldPosition fieldPos)
 492:   {
 493:     // A very special case.
 494:     if (Double.isNaN(number))
 495:       {
 496:     dest.append(symbols.getNaN());
 497:     if (fieldPos != null && 
 498:         (fieldPos.getField() == INTEGER_FIELD ||
 499:          fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
 500:       {
 501:         int index = dest.length();
 502:         fieldPos.setBeginIndex(index - symbols.getNaN().length());
 503:         fieldPos.setEndIndex(index);
 504:       }
 505:     return;
 506:       }
 507:         
 508:     boolean is_neg = number < 0;
 509:     if (is_neg)
 510:       {
 511:     if (negativePrefix != null)
 512:       {
 513:         dest.append(substituteCurrency(negativePrefix, number),
 514:             negativePrefixRanges, negativePrefixAttrs);
 515:       }
 516:     else
 517:       {
 518:         dest.append(symbols.getMinusSign(), NumberFormat.Field.SIGN);
 519:         dest.append(substituteCurrency(positivePrefix, number),
 520:             positivePrefixRanges, positivePrefixAttrs);
 521:       }
 522:     number = - number;
 523:       }
 524:     else
 525:       {
 526:     dest.append(substituteCurrency(positivePrefix, number),
 527:             positivePrefixRanges, positivePrefixAttrs);
 528:       }
 529:     int integerBeginIndex = dest.length();
 530:     int integerEndIndex = 0;
 531:     int zeroStart = symbols.getZeroDigit() - '0';
 532:         
 533:     if (Double.isInfinite (number))
 534:       {
 535:     dest.append(symbols.getInfinity());
 536:     integerEndIndex = dest.length();
 537:       }
 538:     else
 539:       {
 540:     number *= multiplier;
 541:     
 542:     // Compute exponent.
 543:     long exponent = 0;
 544:     double baseNumber;
 545:     if (useExponentialNotation && number > 0)
 546:       {
 547:         exponent = (long) Math.floor (Math.log10(number));
 548:         exponent = exponent - (exponent % exponentRound);
 549:         if (minimumIntegerDigits > 0)
 550:           exponent -= minimumIntegerDigits - 1;
 551:         baseNumber = (number / Math.pow(10.0, exponent));
 552:       }
 553:     else
 554:       baseNumber = number;
 555: 
 556:     // Round to the correct number of digits.
 557:     baseNumber += 5 * Math.pow(10.0, - maximumFractionDigits - 1);
 558: 
 559:     int index = dest.length();
 560:     //double intPart = Math.floor(baseNumber);
 561:     String intPart = Long.toString((long)Math.floor(baseNumber));
 562:     int count, groupPosition = intPart.length();
 563: 
 564:     dest.setDefaultAttribute(NumberFormat.Field.INTEGER);
 565: 
 566:     for (count = 0; count < minimumIntegerDigits-intPart.length(); count++)
 567:       dest.append(symbols.getZeroDigit());
 568: 
 569:     for (count = 0;
 570:          count < maximumIntegerDigits && count < intPart.length();
 571:          count++)
 572:       {
 573:         int dig = intPart.charAt(count);
 574: 
 575:         // Append group separator if required.
 576:         if (groupingUsed && count > 0 && groupingSize != 0 && groupPosition % groupingSize == 0)
 577:           {
 578:         dest.append(symbols.getGroupingSeparator(), NumberFormat.Field.GROUPING_SEPARATOR);
 579:         dest.setDefaultAttribute(NumberFormat.Field.INTEGER);
 580:           }
 581:         dest.append((char) (zeroStart + dig));
 582: 
 583:         groupPosition--;
 584:       }
 585:     dest.setDefaultAttribute(null);
 586: 
 587:     integerEndIndex = dest.length();
 588:        
 589:     int decimal_index = integerEndIndex;
 590:     int consecutive_zeros = 0;
 591:     int total_digits = 0;
 592: 
 593:     int localMaximumFractionDigits = maximumFractionDigits;
 594: 
 595:     if (useExponentialNotation)
 596:       localMaximumFractionDigits += minimumIntegerDigits - count;
 597: 
 598:     // Strip integer part from NUMBER.
 599:     double fracPart = baseNumber - Math.floor(baseNumber);
 600:     
 601:     if ( ((fracPart != 0 || minimumFractionDigits > 0) && localMaximumFractionDigits > 0)
 602:          || decimalSeparatorAlwaysShown)
 603:       {
 604:         dest.append (symbols.getDecimalSeparator(), NumberFormat.Field.DECIMAL_SEPARATOR);
 605:       }
 606: 
 607:     int fraction_begin = dest.length();
 608:     dest.setDefaultAttribute(NumberFormat.Field.FRACTION);
 609:     for (count = 0;
 610:          count < localMaximumFractionDigits
 611:            && (fracPart != 0 || count < minimumFractionDigits);
 612:          ++count)
 613:       {
 614:         ++total_digits;
 615:         fracPart *= 10;
 616:         long dig = (long) fracPart;
 617:         if (dig == 0)
 618:           ++consecutive_zeros;
 619:         else
 620:           consecutive_zeros = 0;
 621:         dest.append((char) (symbols.getZeroDigit() + dig));
 622: 
 623:         // Strip integer part from FRACPART.
 624:         fracPart = fracPart - Math.floor (fracPart);
 625:       }
 626: 
 627:     // Strip extraneous trailing `0's.  We can't always detect
 628:     // these in the loop.
 629:     int extra_zeros = Math.min (consecutive_zeros,
 630:                     total_digits - minimumFractionDigits);
 631:     if (extra_zeros > 0)
 632:       {
 633:         dest.cutTail(extra_zeros);
 634:         total_digits -= extra_zeros;
 635:         if (total_digits == 0 && !decimalSeparatorAlwaysShown)
 636:           dest.cutTail(1);
 637:       }
 638: 
 639:     if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
 640:       {
 641:         fieldPos.setBeginIndex(fraction_begin);
 642:         fieldPos.setEndIndex(dest.length());
 643:       }
 644: 
 645:     // Finally, print the exponent.
 646:     if (useExponentialNotation)
 647:       {
 648:         dest.append(symbols.getExponential(), NumberFormat.Field.EXPONENT_SYMBOL);        
 649:         if (exponent < 0)
 650:           {
 651:         dest.append (symbols.getMinusSign (), NumberFormat.Field.EXPONENT_SIGN);
 652:         exponent = - exponent;
 653:           }
 654:         index = dest.length();
 655:         dest.setDefaultAttribute(NumberFormat.Field.EXPONENT);
 656:         String exponentString = Long.toString ((long) exponent);
 657: 
 658:         for (count = 0; count < minExponentDigits-exponentString.length();
 659:          count++)
 660:           dest.append((char) symbols.getZeroDigit());
 661: 
 662:         for (count = 0;
 663:          count < exponentString.length();
 664:          ++count)
 665:           {
 666:         int dig = exponentString.charAt(count);
 667:         dest.append((char) (zeroStart + dig));
 668:           }
 669:       }
 670:       }
 671: 
 672:     if (fieldPos != null && 
 673:     (fieldPos.getField() == INTEGER_FIELD ||
 674:      fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
 675:       {
 676:     fieldPos.setBeginIndex(integerBeginIndex);
 677:     fieldPos.setEndIndex(integerEndIndex);
 678:       }
 679:         
 680:     if (is_neg && negativeSuffix != null)
 681:       {
 682:     dest.append(substituteCurrency(negativeSuffix, number),
 683:             negativeSuffixRanges, negativeSuffixAttrs);
 684:       }
 685:     else
 686:       {
 687:     dest.append(substituteCurrency(positiveSuffix, number),
 688:             positiveSuffixRanges, positiveSuffixAttrs);
 689:       }
 690:   }
 691: 
 692:   public StringBuffer format (double number, StringBuffer dest,
 693:                   FieldPosition fieldPos)
 694:   {
 695:     formatInternal (number, new StringFormatBuffer(dest), fieldPos);
 696:     return dest;
 697:   }
 698: 
 699:   public AttributedCharacterIterator formatToCharacterIterator (Object value)
 700:   {
 701:     AttributedFormatBuffer sbuf = new AttributedFormatBuffer();
 702: 
 703:     if (value instanceof Number)
 704:       formatInternal(((Number) value).doubleValue(), sbuf, null);
 705:     else
 706:       throw new IllegalArgumentException 
 707:     ("Cannot format given Object as a Number");
 708:     
 709:     sbuf.sync();
 710:     return new FormatCharacterIterator(sbuf.getBuffer().toString(), 
 711:                        sbuf.getRanges(), 
 712:                        sbuf.getAttributes());
 713:   }
 714: 
 715:   public StringBuffer format (long number, StringBuffer dest,
 716:                   FieldPosition fieldPos)
 717:   {
 718:     // If using exponential notation, we just format as a double.
 719:     if (useExponentialNotation)
 720:        return format ((double) number, dest, fieldPos);
 721: 
 722:     boolean is_neg = number < 0;
 723:     if (is_neg)
 724:       {
 725:     if (negativePrefix != null)
 726:       dest.append(substituteCurrency(negativePrefix, number));
 727:     else
 728:       {
 729:         dest.append(symbols.getMinusSign());
 730:         dest.append(substituteCurrency(positivePrefix, number));
 731:       }
 732:     number = - number;
 733:       }
 734:     else
 735:       dest.append(substituteCurrency(positivePrefix, number));
 736: 
 737:     int integerBeginIndex = dest.length();
 738:     int index = dest.length();
 739:     int count = 0;
 740: 
 741:     /* Handle percentages, etc. */
 742:     number *= multiplier;
 743:     while (count < maximumIntegerDigits
 744:        && (number > 0 || count < minimumIntegerDigits))
 745:       {
 746:     long dig = number % 10;
 747:     number /= 10;
 748:     // NUMBER and DIG will be less than 0 if the original number
 749:     // was the most negative long.
 750:     if (dig < 0)
 751:       {
 752:         dig = - dig;
 753:         number = - number;
 754:       }
 755: 
 756:     // Append group separator if required.
 757:     if (groupingUsed && count > 0 && groupingSize != 0 && count % groupingSize == 0)
 758:       dest.insert(index, symbols.getGroupingSeparator());
 759: 
 760:     dest.insert(index, (char) (symbols.getZeroDigit() + dig));
 761: 
 762:     ++count;
 763:       }
 764: 
 765:     if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD)
 766:       {
 767:     fieldPos.setBeginIndex(integerBeginIndex);
 768:     fieldPos.setEndIndex(dest.length());
 769:       }
 770: 
 771:     if (decimalSeparatorAlwaysShown || minimumFractionDigits > 0)
 772:       {
 773:     dest.append(symbols.getDecimalSeparator());
 774:     if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD)
 775:       {
 776:         fieldPos.setBeginIndex(dest.length());
 777:         fieldPos.setEndIndex(dest.length() + minimumFractionDigits);
 778:       }
 779:       }
 780: 
 781:     for (count = 0; count < minimumFractionDigits; ++count)
 782:       dest.append(symbols.getZeroDigit());
 783: 
 784:     dest.append((is_neg && negativeSuffix != null)
 785:         ? substituteCurrency(negativeSuffix, number)
 786:         : substituteCurrency(positiveSuffix, number));
 787:     return dest;
 788:   }
 789: 
 790:   /**
 791:    * Returns the currency corresponding to the currency symbol stored
 792:    * in the instance of <code>DecimalFormatSymbols</code> used by this
 793:    * <code>DecimalFormat</code>.
 794:    *
 795:    * @return A new instance of <code>Currency</code> if
 796:    * the currency code matches a known one, null otherwise.
 797:    */
 798:   public Currency getCurrency()
 799:   {
 800:     return symbols.getCurrency();
 801:   }
 802: 
 803:   /**
 804:    * Returns a copy of the symbols used by this instance.
 805:    * 
 806:    * @return A copy of the symbols.
 807:    */
 808:   public DecimalFormatSymbols getDecimalFormatSymbols()
 809:   {
 810:     return (DecimalFormatSymbols) symbols.clone();
 811:   }
 812: 
 813:   public int getGroupingSize ()
 814:   {
 815:     return groupingSize;
 816:   }
 817: 
 818:   public int getMultiplier ()
 819:   {
 820:     return multiplier;
 821:   }
 822: 
 823:   public String getNegativePrefix ()
 824:   {
 825:     return negativePrefix;
 826:   }
 827: 
 828:   public String getNegativeSuffix ()
 829:   {
 830:     return negativeSuffix;
 831:   }
 832: 
 833:   public String getPositivePrefix ()
 834:   {
 835:     return positivePrefix;
 836:   }
 837: 
 838:   public String getPositiveSuffix ()
 839:   {
 840:     return positiveSuffix;
 841:   }
 842: 
 843:   /**
 844:    * Returns a hash code for this object.
 845:    *
 846:    * @return A hash code.
 847:    */
 848:   public int hashCode()
 849:   {
 850:     return toPattern().hashCode();
 851:   }
 852: 
 853:   public boolean isDecimalSeparatorAlwaysShown ()
 854:   {
 855:     return decimalSeparatorAlwaysShown;
 856:   }
 857: 
 858:   public Number parse (String str, ParsePosition pos)
 859:   {
 860:     /*
 861:      * Our strategy is simple: copy the text into separate buffers: one for the int part,
 862:      * one for the fraction part and for the exponential part.
 863:      * We translate or omit locale-specific information.  
 864:      * If exponential is sufficiently big we merge the fraction and int part and
 865:      * remove the '.' and then we use Long to convert the number. In the other
 866:      * case, we use Double to convert the full number.
 867:      */
 868: 
 869:     boolean is_neg = false;
 870:     int index = pos.getIndex();
 871:     StringBuffer int_buf = new StringBuffer ();
 872:         
 873:     // We have to check both prefixes, because one might be empty.  We
 874:     // want to pick the longest prefix that matches.
 875:     boolean got_pos = str.startsWith(positivePrefix, index);
 876:     String np = (negativePrefix != null
 877:          ? negativePrefix
 878:          : positivePrefix + symbols.getMinusSign());
 879:     boolean got_neg = str.startsWith(np, index);
 880: 
 881:     if (got_pos && got_neg)
 882:       {
 883:     // By checking this way, we preserve ambiguity in the case
 884:     // where the negative format differs only in suffix.  We
 885:     // check this again later.
 886:     if (np.length() > positivePrefix.length())
 887:       {
 888:         is_neg = true;
 889:         index += np.length();
 890:       }
 891:     else
 892:       index += positivePrefix.length();
 893:       }
 894:     else if (got_neg)
 895:       {
 896:     is_neg = true;
 897:     index += np.length();
 898:       }
 899:     else if (got_pos)
 900:       index += positivePrefix.length();
 901:     else
 902:       {
 903:     pos.setErrorIndex (index);
 904:     return null;
 905:       }
 906: 
 907:     // FIXME: handle Inf and NaN.
 908: 
 909:     // FIXME: do we have to respect minimum digits?
 910:     // What about multiplier?
 911: 
 912:     StringBuffer buf = int_buf;
 913:     StringBuffer frac_buf = null;
 914:     StringBuffer exp_buf = null;
 915:     int start_index = index;
 916:     int max = str.length();
 917:     int exp_index = -1;
 918:     int last = index + maximumIntegerDigits; 
 919: 
 920:     if (maximumFractionDigits > 0)
 921:       last += maximumFractionDigits + 1;
 922:     
 923:     if (useExponentialNotation)
 924:       last += minExponentDigits + 1;
 925: 
 926:     if (last > 0 && max > last)
 927:       max = last;
 928: 
 929:     char zero = symbols.getZeroDigit();
 930:     int last_group = -1;
 931:     boolean int_part = true;
 932:     boolean exp_part = false;
 933:     for (; index < max; ++index)
 934:       {
 935:     char c = str.charAt(index);
 936: 
 937:     // FIXME: what about grouping size?
 938:     if (groupingUsed && c == symbols.getGroupingSeparator())
 939:       {
 940:         if (last_group != -1 
 941:         && groupingSize != 0  
 942:         && (index - last_group) % groupingSize != 0)
 943:           {
 944:         pos.setErrorIndex(index);
 945:         return null;
 946:           }
 947:         last_group = index+1;
 948:       }
 949:     else if (c >= zero && c <= zero + 9)
 950:       {
 951:         buf.append((char) (c - zero + '0'));
 952:       }
 953:     else if (parseIntegerOnly)
 954:       break;
 955:     else if (c == symbols.getDecimalSeparator())
 956:       {
 957:         if (last_group != -1 
 958:         && groupingSize != 0 
 959:         && (index - last_group) % groupingSize != 0)
 960:           {
 961:         pos.setErrorIndex(index);
 962:         return null;
 963:           }
 964:         buf = frac_buf = new StringBuffer();
 965:         frac_buf.append('.');
 966:         int_part = false;
 967:       }
 968:     else if (c == symbols.getExponential())
 969:       {
 970:         buf = exp_buf = new StringBuffer();
 971:         int_part = false;
 972:         exp_part = true;
 973:         exp_index = index+1;
 974:       }
 975:     else if (exp_part
 976:          && (c == '+' || c == '-' || c == symbols.getMinusSign()))
 977:       {
 978:         // For exponential notation.
 979:         buf.append(c);
 980:       }
 981:     else
 982:       break;
 983:       }
 984: 
 985:     if (index == start_index)
 986:       {
 987:     // Didn't see any digits.
 988:     pos.setErrorIndex(index);
 989:     return null;
 990:       }
 991: 
 992:     // Check the suffix.  We must do this before converting the
 993:     // buffer to a number to handle the case of a number which is
 994:     // the most negative Long.
 995:     boolean got_pos_suf = str.startsWith(positiveSuffix, index);
 996:     String ns = (negativePrefix == null ? positiveSuffix : negativeSuffix);
 997:     boolean got_neg_suf = str.startsWith(ns, index);
 998:     if (is_neg)
 999:       {
1000:     if (! got_neg_suf)
1001:       {
1002:         pos.setErrorIndex(index);
1003:         return null;
1004:       }
1005:       }
1006:     else if (got_pos && got_neg && got_neg_suf)
1007:       {
1008:     is_neg = true;
1009:       }
1010:     else if (got_pos != got_pos_suf && got_neg != got_neg_suf)
1011:       {
1012:     pos.setErrorIndex(index);
1013:     return null;
1014:       }
1015:     else if (! got_pos_suf)
1016:       {
1017:        pos.setErrorIndex(index);
1018:        return null;
1019:       }
1020: 
1021:     String suffix = is_neg ? ns : positiveSuffix;
1022:     long parsedMultiplier = 1;
1023:     boolean use_long;
1024: 
1025:     if (is_neg)
1026:       int_buf.insert(0, '-');
1027: 
1028:     // Now handle the exponential part if there is one.
1029:     if (exp_buf != null)
1030:       {
1031:     int exponent_value;
1032: 
1033:     try
1034:       {
1035:         exponent_value = Integer.parseInt(exp_buf.toString());
1036:       }
1037:     catch (NumberFormatException x1)
1038:       {
1039:         pos.setErrorIndex(exp_index);
1040:         return null;
1041:       }
1042: 
1043:     if (frac_buf == null)
1044:       {
1045:         // We only have to add some zeros to the int part.
1046:         // Build a multiplier.
1047:         for (int i = 0; i < exponent_value; i++)
1048:           int_buf.append('0');
1049:         
1050:         use_long = true;
1051:       }
1052:     else
1053:       {
1054:         boolean long_sufficient;
1055: 
1056:         if (exponent_value < frac_buf.length()-1)
1057:           {
1058:         int lastNonNull = -1;
1059:         /* We have to check the fraction buffer: it may only be full of '0'
1060:          * or be sufficiently filled with it to convert the number into Long.
1061:          */
1062:         for (int i = 1; i < frac_buf.length(); i++)
1063:           if (frac_buf.charAt(i) != '0')
1064:             lastNonNull = i;
1065: 
1066:         long_sufficient = (lastNonNull < 0 || lastNonNull <= exponent_value);
1067:           }
1068:         else
1069:           long_sufficient = true;
1070:         
1071:         if (long_sufficient)
1072:           {
1073:         for (int i = 1; i < frac_buf.length() && i < exponent_value; i++)
1074:           int_buf.append(frac_buf.charAt(i));
1075:         for (int i = frac_buf.length()-1; i < exponent_value; i++)
1076:           int_buf.append('0');
1077:         use_long = true;
1078:           }
1079:         else
1080:           {
1081:         /*
1082:          * A long type is not sufficient, we build the full buffer to
1083:          * be parsed by Double.
1084:          */
1085:         int_buf.append(frac_buf);
1086:         int_buf.append('E');
1087:         int_buf.append(exp_buf);
1088:         use_long = false;
1089:           }
1090:       }
1091:       }
1092:     else
1093:       {
1094:     if (frac_buf != null)
1095:       {
1096:         /* Check whether the fraction buffer contains only '0' */
1097:         int i;
1098:         for (i = 1; i < frac_buf.length(); i++)
1099:           if (frac_buf.charAt(i) != '0')
1100:         break;
1101:        
1102:         if (i != frac_buf.length())
1103:           {
1104:         use_long = false;
1105:         int_buf.append(frac_buf);
1106:           }
1107:         else
1108:           use_long = true;
1109:       }
1110:     else
1111:       use_long = true;
1112:       }
1113: 
1114:     String t = int_buf.toString();
1115:     Number result = null;
1116:     if (use_long)
1117:       {
1118:     try
1119:       {
1120:         result = new Long (t);
1121:       }
1122:     catch (NumberFormatException x1)
1123:       {
1124:       }
1125:       }
1126:     else
1127:       {
1128:     try
1129:       {
1130:         result = new Double (t);
1131:       }
1132:     catch (NumberFormatException x2)
1133:       {
1134:       }
1135:       }
1136:     if (result == null)
1137:       {
1138:     pos.setErrorIndex(index);
1139:     return null;
1140:       }
1141: 
1142:     pos.setIndex(index + suffix.length());
1143: 
1144:     return result;
1145:   }
1146: 
1147:   /**
1148:    * Sets the <code>Currency</code> on the
1149:    * <code>DecimalFormatSymbols</code> used, which also sets the
1150:    * currency symbols on those symbols.
1151:    */
1152:   public void setCurrency(Currency currency)
1153:   {
1154:     symbols.setCurrency(currency);
1155:   }
1156: 
1157:   /**
1158:    * Sets the symbols used by this instance.  This method makes a copy of 
1159:    * the supplied symbols.
1160:    * 
1161:    * @param newSymbols  the symbols (<code>null</code> not permitted).
1162:    */
1163:   public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
1164:   {
1165:     symbols = (DecimalFormatSymbols) newSymbols.clone();
1166:   }
1167: 
1168:   public void setDecimalSeparatorAlwaysShown (boolean newValue)
1169:   {
1170:     decimalSeparatorAlwaysShown = newValue;
1171:   }
1172: 
1173:   public void setGroupingSize (int groupSize)
1174:   {
1175:     groupingSize = (byte) groupSize;
1176:   }
1177: 
1178:   public void setMaximumFractionDigits (int newValue)
1179:   {
1180:     super.setMaximumFractionDigits(Math.min(newValue, 340));
1181:   }
1182: 
1183:   public void setMaximumIntegerDigits (int newValue)
1184:   {
1185:     super.setMaximumIntegerDigits(Math.min(newValue, 309));
1186:   }
1187: 
1188:   public void setMinimumFractionDigits (int newValue)
1189:   {
1190:     super.setMinimumFractionDigits(Math.min(newValue, 340));
1191:   }
1192: 
1193:   public void setMinimumIntegerDigits (int newValue)
1194:   {
1195:     super.setMinimumIntegerDigits(Math.min(newValue, 309));
1196:   }
1197: 
1198:   public void setMultiplier (int newValue)
1199:   {
1200:     multiplier = newValue;
1201:   }
1202: 
1203:   public void setNegativePrefix (String newValue)
1204:   {
1205:     negativePrefix = newValue;
1206:   }
1207: 
1208:   public void setNegativeSuffix (String newValue)
1209:   {
1210:     negativeSuffix = newValue;
1211:   }
1212: 
1213:   public void setPositivePrefix (String newValue)
1214:   {
1215:     positivePrefix = newValue;
1216:   }
1217: 
1218:   public void setPositiveSuffix (String newValue)
1219:   {
1220:     positiveSuffix = newValue;
1221:   }
1222: 
1223:   private void quoteFix(StringBuffer buf, String text, String patChars)
1224:   {
1225:     int len = text.length();
1226:     for (int index = 0; index < len; ++index)
1227:       {
1228:     char c = text.charAt(index);
1229:     if (patChars.indexOf(c) != -1)
1230:       {
1231:         buf.append('\'');
1232:         buf.append(c);
1233:         buf.append('\'');
1234:       }
1235:     else
1236:       buf.append(c);
1237:       }
1238:   }
1239: 
1240:   private String computePattern(DecimalFormatSymbols syms)
1241:   {
1242:     StringBuffer mainPattern = new StringBuffer ();
1243:     // We have to at least emit a zero for the minimum number of
1244:     // digits.  Past that we need hash marks up to the grouping
1245:     // separator (and one beyond).
1246:     int total_digits = Math.max(minimumIntegerDigits,
1247:                 groupingUsed ? groupingSize + 1: groupingSize);
1248:     for (int i = 0; i < total_digits - minimumIntegerDigits; ++i)
1249:       mainPattern.append(syms.getDigit());
1250:     for (int i = total_digits - minimumIntegerDigits; i < total_digits; ++i)
1251:       mainPattern.append(syms.getZeroDigit());
1252:     // Inserting the gropuing operator afterwards is easier.
1253:     if (groupingUsed)
1254:       mainPattern.insert(mainPattern.length() - groupingSize,
1255:              syms.getGroupingSeparator());
1256:     // See if we need decimal info.
1257:     if (minimumFractionDigits > 0 || maximumFractionDigits > 0
1258:     || decimalSeparatorAlwaysShown)
1259:       mainPattern.append(syms.getDecimalSeparator());
1260:     for (int i = 0; i < minimumFractionDigits; ++i)
1261:       mainPattern.append(syms.getZeroDigit());
1262:     for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
1263:       mainPattern.append(syms.getDigit());
1264:     if (useExponentialNotation)
1265:       {
1266:     mainPattern.append(syms.getExponential());
1267:     for (int i = 0; i < minExponentDigits; ++i)
1268:       mainPattern.append(syms.getZeroDigit());
1269:     if (minExponentDigits == 0)
1270:       mainPattern.append(syms.getDigit());
1271:       }
1272: 
1273:     String main = mainPattern.toString();
1274:     String patChars = patternChars (syms);
1275:     mainPattern.setLength(0);
1276: 
1277:     quoteFix (mainPattern, positivePrefix, patChars);
1278:     mainPattern.append(main);
1279:     quoteFix (mainPattern, positiveSuffix, patChars);
1280: 
1281:     if (negativePrefix != null)
1282:       {
1283:     quoteFix (mainPattern, negativePrefix, patChars);
1284:     mainPattern.append(main);
1285:     quoteFix (mainPattern, negativeSuffix, patChars);
1286:       }
1287: 
1288:     return mainPattern.toString();
1289:   }
1290: 
1291:   public String toLocalizedPattern ()
1292:   {
1293:     return computePattern (symbols);
1294:   }
1295: 
1296:   public String toPattern ()
1297:   {
1298:     return computePattern (nonLocalizedSymbols);
1299:   }
1300: 
1301:   private static final int MAXIMUM_INTEGER_DIGITS = 309; 
1302: 
1303:   // These names are fixed by the serialization spec.
1304:   private boolean decimalSeparatorAlwaysShown;
1305:   private byte groupingSize;
1306:   private byte minExponentDigits;
1307:   private int exponentRound;
1308:   private int multiplier;
1309:   private String negativePrefix;
1310:   private String negativeSuffix;
1311:   private String positivePrefix;
1312:   private String positiveSuffix;
1313:   private int[] negativePrefixRanges, positivePrefixRanges;
1314:   private HashMap[] negativePrefixAttrs, positivePrefixAttrs;
1315:   private int[] negativeSuffixRanges, positiveSuffixRanges;
1316:   private HashMap[] negativeSuffixAttrs, positiveSuffixAttrs;
1317:   private int serialVersionOnStream = 1;
1318:   private DecimalFormatSymbols symbols;
1319:   private boolean useExponentialNotation;
1320:   private static final long serialVersionUID = 864413376551465018L;
1321: 
1322:   private void readObject(ObjectInputStream stream)
1323:     throws IOException, ClassNotFoundException
1324:   {
1325:     stream.defaultReadObject();
1326:     if (serialVersionOnStream < 1)
1327:       {
1328:         useExponentialNotation = false;
1329:     serialVersionOnStream = 1;
1330:       }
1331:   }
1332: 
1333:   // The locale-independent pattern symbols happen to be the same as
1334:   // the US symbols.
1335:   private static final DecimalFormatSymbols nonLocalizedSymbols
1336:     = new DecimalFormatSymbols (Locale.US);
1337: 
1338:   /**
1339:    * <p>
1340:    * Substitutes the currency symbol into the given string,
1341:    * based on the value used.  Currency symbols can either
1342:    * be a simple series of characters (e.g. '$'), which are
1343:    * simply used as is, or they can be of a more complex
1344:    * form:
1345:    * </p>
1346:    * <p>
1347:    * (lower bound)|(mid value)|(upper bound)
1348:    * </p>
1349:    * <p>
1350:    * where each bound has the syntax '(value)(# or <)(symbol)',
1351:    * to indicate the bounding value and the symbol used.
1352:    * </p>
1353:    * <p>
1354:    * The currency symbol replaces the currency specifier, '\u00a4',
1355:    * an unlocalised character, which thus is used as such in all formats.
1356:    * If this symbol occurs twice, the international currency code is used
1357:    * instead.
1358:    * </p>
1359:    *
1360:    * @param string The string containing the currency specifier, '\u00a4'.
1361:    * @param number the number being formatted.
1362:    * @return a string formatted for the correct currency.
1363:    */
1364:   private String substituteCurrency(String string, double number)
1365:   {
1366:     int index;
1367:     int length;
1368:     char currentChar;
1369:     StringBuffer buf;
1370:     
1371:     index = 0;
1372:     length = string.length();
1373:     buf = new StringBuffer();
1374:     
1375:     while (index < length)
1376:       {
1377:     currentChar = string.charAt(index);
1378:     if (string.charAt(index) == '\u00a4')
1379:       {
1380:         if ((index + 1) < length && string.charAt(index + 1) == '\u00a4')
1381:           {
1382:         buf.append(symbols.getInternationalCurrencySymbol());
1383:         index += 2;
1384:           }
1385:         else
1386:           {
1387:         String symbol;
1388:         
1389:         symbol = symbols.getCurrencySymbol();
1390:         if (symbol.startsWith("="))
1391:           {
1392:             String[] bounds;
1393:             int[] boundValues;
1394:             String[] boundSymbols;
1395:             
1396:             bounds = symbol.substring(1).split("\\|");
1397:             boundValues = new int[3];
1398:             boundSymbols = new String[3];
1399:             for (int a = 0; a < 3; ++a)
1400:               {
1401:             String[] bound;
1402:             
1403:             bound = bounds[a].split("[#<]");
1404:             boundValues[a] = Integer.parseInt(bound[0]);
1405:             boundSymbols[a] = bound[1];
1406:               }
1407:             if (number <= boundValues[0])
1408:               {
1409:             buf.append(boundSymbols[0]);
1410:               }
1411:             else if (number >= boundValues[2])
1412:               {
1413:             buf.append(boundSymbols[2]);
1414:               }
1415:             else 
1416:               {
1417:             buf.append(boundSymbols[1]);
1418:               }
1419:             ++index;
1420:           }
1421:         else
1422:           {
1423:             buf.append(symbol);
1424:             ++index;
1425:           }
1426:           }
1427:       }
1428:     else
1429:       {
1430:         buf.append(string.charAt(index));
1431:         ++index;
1432:       }
1433:       }
1434:     return buf.toString();
1435:   }
1436:   
1437: }