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