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