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