< prev index next >

src/java.base/share/classes/java/text/DecimalFormat.java

Print this page


   1 /*
   2  * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  31  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  32  * materials are provided under terms of a License Agreement between Taligent
  33  * and Sun. This technology is protected by multiple US and International
  34  * patents. This notice and attribution to Taligent may not be removed.
  35  *   Taligent is a registered trademark of Taligent, Inc.
  36  *
  37  */
  38 
  39 package java.text;
  40 
  41 import java.io.IOException;
  42 import java.io.InvalidObjectException;
  43 import java.io.ObjectInputStream;
  44 import java.math.BigDecimal;
  45 import java.math.BigInteger;
  46 import java.math.RoundingMode;
  47 import java.text.spi.NumberFormatProvider;
  48 import java.util.ArrayList;
  49 import java.util.Currency;
  50 import java.util.Locale;
  51 import java.util.ResourceBundle;
  52 import java.util.concurrent.ConcurrentHashMap;
  53 import java.util.concurrent.ConcurrentMap;
  54 import java.util.concurrent.atomic.AtomicInteger;
  55 import java.util.concurrent.atomic.AtomicLong;
  56 import sun.util.locale.provider.LocaleProviderAdapter;
  57 import sun.util.locale.provider.ResourceBundleBasedAdapter;
  58 
  59 /**
  60  * <code>DecimalFormat</code> is a concrete subclass of
  61  * <code>NumberFormat</code> that formats decimal numbers. It has a variety of
  62  * features designed to make it possible to parse and format numbers in any
  63  * locale, including support for Western, Arabic, and Indic digits.  It also
  64  * supports different kinds of numbers, including integers (123), fixed-point
  65  * numbers (123.4), scientific notation (1.23E4), percentages (12%), and
  66  * currency amounts ($123).  All of these can be localized.
  67  *
  68  * <p>To obtain a <code>NumberFormat</code> for a specific locale, including the
  69  * default locale, call one of <code>NumberFormat</code>'s factory methods, such
  70  * as <code>getInstance()</code>.  In general, do not call the
  71  * <code>DecimalFormat</code> constructors directly, since the
  72  * <code>NumberFormat</code> factory methods may return subclasses other than
  73  * <code>DecimalFormat</code>. If you need to customize the format object, do


 140  *
 141  * <p>The prefixes, suffixes, and various symbols used for infinity, digits,
 142  * thousands separators, decimal separators, etc. may be set to arbitrary
 143  * values, and they will appear properly during formatting.  However, care must
 144  * be taken that the symbols and strings do not conflict, or parsing will be
 145  * unreliable.  For example, either the positive and negative prefixes or the
 146  * suffixes must be distinct for <code>DecimalFormat.parse()</code> to be able
 147  * to distinguish positive from negative values.  (If they are identical, then
 148  * <code>DecimalFormat</code> will behave as if no negative subpattern was
 149  * specified.)  Another example is that the decimal separator and thousands
 150  * separator should be distinct characters, or parsing will be impossible.
 151  *
 152  * <p>The grouping separator is commonly used for thousands, but in some
 153  * countries it separates ten-thousands. The grouping size is a constant number
 154  * of digits between the grouping characters, such as 3 for 100,000,000 or 4 for
 155  * 1,0000,0000.  If you supply a pattern with multiple grouping characters, the
 156  * interval between the last one and the end of the integer is the one that is
 157  * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> ==
 158  * <code>"##,####,####"</code>.
 159  *
 160  * <h4>Special Pattern Characters</h4>
 161  *
 162  * <p>Many characters in a pattern are taken literally; they are matched during
 163  * parsing and output unchanged during formatting.  Special characters, on the
 164  * other hand, stand for other characters, strings, or classes of characters.
 165  * They must be quoted, unless noted otherwise, if they are to appear in the
 166  * prefix or suffix as literals.
 167  *
 168  * <p>The characters listed here are used in non-localized patterns.  Localized
 169  * patterns use the corresponding characters taken from this formatter's
 170  * <code>DecimalFormatSymbols</code> object instead, and these characters lose
 171  * their special status.  Two exceptions are the currency sign and quote, which
 172  * are not localized.
 173  *
 174  * <blockquote>
 175  * <table class="striped">
 176  * <caption style="display:none">Chart showing symbol, location, localized, and meaning.</caption>
 177  * <thead>
 178  *     <tr>
 179  *          <th scope="col" style="text-align:left">Symbol
 180  *          <th scope="col" style="text-align:left">Location


 555             String tempResult = fastFormat(number);
 556             if (tempResult != null) {
 557                 result.append(tempResult);
 558                 return result;
 559             }
 560         }
 561 
 562         // if fast-path could not work, we fallback to standard code.
 563         return format(number, result, fieldPosition.getFieldDelegate());
 564     }
 565 
 566     /**
 567      * Formats a double to produce a string.
 568      * @param number    The double to format
 569      * @param result    where the text is to be appended
 570      * @param delegate notified of locations of sub fields
 571      * @exception       ArithmeticException if rounding is needed with rounding
 572      *                  mode being set to RoundingMode.UNNECESSARY
 573      * @return The formatted number string
 574      */
 575     private StringBuffer format(double number, StringBuffer result,
 576                                 FieldDelegate delegate) {
 577         if (Double.isNaN(number) ||
 578            (Double.isInfinite(number) && multiplier == 0)) {
 579             int iFieldStart = result.length();
 580             result.append(symbols.getNaN());
 581             delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
 582                                iFieldStart, result.length(), result);
 583             return result;
 584         }
 585 
 586         /* Detecting whether a double is negative is easy with the exception of
 587          * the value -0.0.  This is a double which has a zero mantissa (and
 588          * exponent), but a negative sign bit.  It is semantically distinct from
 589          * a zero with a positive sign bit, and this distinction is important
 590          * to certain kinds of computations.  However, it's a little tricky to
 591          * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0).  How then, you may
 592          * ask, does it behave distinctly from +0.0?  Well, 1/(-0.0) ==
 593          * -Infinity.  Proper detection of -0.0 is needed to deal with the
 594          * issues raised by bugs 4106658, 4106667, and 4147706.  Liu 7/6/98.
 595          */
 596         boolean isNegative = ((number < 0.0) || (number == 0.0 && 1/number < 0.0)) ^ (multiplier < 0);
 597 
 598         if (multiplier != 1) {
 599             number *= multiplier;
 600         }
 601 


















































 602         if (Double.isInfinite(number)) {
 603             if (isNegative) {
 604                 append(result, negativePrefix, delegate,
 605                        getNegativePrefixFieldPositions(), Field.SIGN);
 606             } else {
 607                 append(result, positivePrefix, delegate,
 608                        getPositivePrefixFieldPositions(), Field.SIGN);
 609             }
 610 
 611             int iFieldStart = result.length();
 612             result.append(symbols.getInfinity());
 613             delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
 614                                iFieldStart, result.length(), result);
 615 
 616             if (isNegative) {
 617                 append(result, negativeSuffix, delegate,
 618                        getNegativeSuffixFieldPositions(), Field.SIGN);
 619             } else {
 620                 append(result, positiveSuffix, delegate,
 621                        getPositiveSuffixFieldPositions(), Field.SIGN);
 622             }
 623 
 624             return result;
 625         }
 626 
 627         if (isNegative) {
 628             number = -number;
 629         }
 630 
 631         // at this point we are guaranteed a nonnegative finite number.
 632         assert(number >= 0 && !Double.isInfinite(number));
 633 
 634         synchronized(digitList) {
 635             int maxIntDigits = super.getMaximumIntegerDigits();
 636             int minIntDigits = super.getMinimumIntegerDigits();
 637             int maxFraDigits = super.getMaximumFractionDigits();
 638             int minFraDigits = super.getMinimumFractionDigits();
 639 
 640             digitList.set(isNegative, number, useExponentialNotation ?
 641                           maxIntDigits + maxFraDigits : maxFraDigits,
 642                           !useExponentialNotation);
 643             return subformat(result, delegate, isNegative, false,
 644                        maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
 645         }
 646     }
 647 
 648     /**
 649      * Format a long to produce a string.
 650      * @param number    The long to format
 651      * @param result    where the text is to be appended
 652      * @param fieldPosition    keeps track on the position of the field within
 653      *                         the returned string. For example, for formatting
 654      *                         a number {@code 123456789} in {@code Locale.US}
 655      *                         locale, if the given {@code fieldPosition} is
 656      *                         {@link NumberFormat#INTEGER_FIELD}, the begin index
 657      *                         and end index of {@code fieldPosition} will be set
 658      *                         to 0 and 11, respectively for the output string
 659      *                         {@code 123,456,789}.
 660      * @exception       NullPointerException if {@code result} or
 661      *                  {@code fieldPosition} is {@code null}


 666      */
 667     @Override
 668     public StringBuffer format(long number, StringBuffer result,
 669                                FieldPosition fieldPosition) {
 670         fieldPosition.setBeginIndex(0);
 671         fieldPosition.setEndIndex(0);
 672 
 673         return format(number, result, fieldPosition.getFieldDelegate());
 674     }
 675 
 676     /**
 677      * Format a long to produce a string.
 678      * @param number    The long to format
 679      * @param result    where the text is to be appended
 680      * @param delegate notified of locations of sub fields
 681      * @return The formatted number string
 682      * @exception        ArithmeticException if rounding is needed with rounding
 683      *                   mode being set to RoundingMode.UNNECESSARY
 684      * @see java.text.FieldPosition
 685      */
 686     private StringBuffer format(long number, StringBuffer result,
 687                                FieldDelegate delegate) {
 688         boolean isNegative = (number < 0);
 689         if (isNegative) {
 690             number = -number;
 691         }
 692 
 693         // In general, long values always represent real finite numbers, so
 694         // we don't have to check for +/- Infinity or NaN.  However, there
 695         // is one case we have to be careful of:  The multiplier can push
 696         // a number near MIN_VALUE or MAX_VALUE outside the legal range.  We
 697         // check for this before multiplying, and if it happens we use
 698         // BigInteger instead.
 699         boolean useBigInteger = false;
 700         if (number < 0) { // This can only happen if number == Long.MIN_VALUE.
 701             if (multiplier != 0) {
 702                 useBigInteger = true;
 703             }
 704         } else if (multiplier != 1 && multiplier != 0) {
 705             long cutoff = Long.MAX_VALUE / multiplier;
 706             if (cutoff < 0) {


 757      * @exception        ArithmeticException if rounding is needed with rounding
 758      *                   mode being set to RoundingMode.UNNECESSARY
 759      * @see java.text.FieldPosition
 760      */
 761     private StringBuffer format(BigDecimal number, StringBuffer result,
 762                                 FieldPosition fieldPosition) {
 763         fieldPosition.setBeginIndex(0);
 764         fieldPosition.setEndIndex(0);
 765         return format(number, result, fieldPosition.getFieldDelegate());
 766     }
 767 
 768     /**
 769      * Formats a BigDecimal to produce a string.
 770      * @param number    The BigDecimal to format
 771      * @param result    where the text is to be appended
 772      * @param delegate notified of locations of sub fields
 773      * @exception        ArithmeticException if rounding is needed with rounding
 774      *                   mode being set to RoundingMode.UNNECESSARY
 775      * @return The formatted number string
 776      */
 777     private StringBuffer format(BigDecimal number, StringBuffer result,
 778                                 FieldDelegate delegate) {
 779         if (multiplier != 1) {
 780             number = number.multiply(getBigDecimalMultiplier());
 781         }
 782         boolean isNegative = number.signum() == -1;
 783         if (isNegative) {
 784             number = number.negate();
 785         }
 786 
 787         synchronized(digitList) {
 788             int maxIntDigits = getMaximumIntegerDigits();
 789             int minIntDigits = getMinimumIntegerDigits();
 790             int maxFraDigits = getMaximumFractionDigits();
 791             int minFraDigits = getMinimumFractionDigits();
 792             int maximumDigits = maxIntDigits + maxFraDigits;
 793 
 794             digitList.set(isNegative, number, useExponentialNotation ?
 795                 ((maximumDigits < 0) ? Integer.MAX_VALUE : maximumDigits) :
 796                 maxFraDigits, !useExponentialNotation);
 797 


 818      * @see java.text.FieldPosition
 819      */
 820     private StringBuffer format(BigInteger number, StringBuffer result,
 821                                FieldPosition fieldPosition) {
 822         fieldPosition.setBeginIndex(0);
 823         fieldPosition.setEndIndex(0);
 824 
 825         return format(number, result, fieldPosition.getFieldDelegate(), false);
 826     }
 827 
 828     /**
 829      * Format a BigInteger to produce a string.
 830      * @param number    The BigInteger to format
 831      * @param result    where the text is to be appended
 832      * @param delegate notified of locations of sub fields
 833      * @return The formatted number string
 834      * @exception        ArithmeticException if rounding is needed with rounding
 835      *                   mode being set to RoundingMode.UNNECESSARY
 836      * @see java.text.FieldPosition
 837      */
 838     private StringBuffer format(BigInteger number, StringBuffer result,
 839                                FieldDelegate delegate, boolean formatLong) {
 840         if (multiplier != 1) {
 841             number = number.multiply(getBigIntegerMultiplier());
 842         }
 843         boolean isNegative = number.signum() == -1;
 844         if (isNegative) {
 845             number = number.negate();
 846         }
 847 
 848         synchronized(digitList) {
 849             int maxIntDigits, minIntDigits, maxFraDigits, minFraDigits, maximumDigits;
 850             if (formatLong) {
 851                 maxIntDigits = super.getMaximumIntegerDigits();
 852                 minIntDigits = super.getMinimumIntegerDigits();
 853                 maxFraDigits = super.getMaximumFractionDigits();
 854                 minFraDigits = super.getMinimumFractionDigits();
 855                 maximumDigits = maxIntDigits + maxFraDigits;
 856             } else {
 857                 maxIntDigits = getMaximumIntegerDigits();
 858                 minIntDigits = getMinimumIntegerDigits();


 900         if (obj instanceof Double || obj instanceof Float) {
 901             format(((Number)obj).doubleValue(), sb, delegate);
 902         } else if (obj instanceof Long || obj instanceof Integer ||
 903                    obj instanceof Short || obj instanceof Byte ||
 904                    obj instanceof AtomicInteger || obj instanceof AtomicLong) {
 905             format(((Number)obj).longValue(), sb, delegate);
 906         } else if (obj instanceof BigDecimal) {
 907             format((BigDecimal)obj, sb, delegate);
 908         } else if (obj instanceof BigInteger) {
 909             format((BigInteger)obj, sb, delegate, false);
 910         } else if (obj == null) {
 911             throw new NullPointerException(
 912                 "formatToCharacterIterator must be passed non-null object");
 913         } else {
 914             throw new IllegalArgumentException(
 915                 "Cannot format given Object as a Number");
 916         }
 917         return delegate.getIterator(sb.toString());
 918     }
 919 
 920     // ==== Begin fast-path formating logic for double =========================
 921 
 922     /* Fast-path formatting will be used for format(double ...) methods iff a
 923      * number of conditions are met (see checkAndSetFastPathStatus()):
 924      * - Only if instance properties meet the right predefined conditions.
 925      * - The abs value of the double to format is <= Integer.MAX_VALUE.
 926      *
 927      * The basic approach is to split the binary to decimal conversion of a
 928      * double value into two phases:
 929      * * The conversion of the integer portion of the double.
 930      * * The conversion of the fractional portion of the double
 931      *   (limited to two or three digits).
 932      *
 933      * The isolation and conversion of the integer portion of the double is
 934      * straightforward. The conversion of the fraction is more subtle and relies
 935      * on some rounding properties of double to the decimal precisions in
 936      * question.  Using the terminology of BigDecimal, this fast-path algorithm
 937      * is applied when a double value has a magnitude less than Integer.MAX_VALUE
 938      * and rounding is to nearest even and the destination format has two or
 939      * three digits of *scale* (digits after the decimal point).
 940      *


1645                 /*
1646                  * If the fast path data is not set through
1647                  * checkAndSetFastPathStatus() and fulfil the
1648                  * fast path conditions then reset the data
1649                  * directly through resetFastPathData()
1650                  */
1651                 resetFastPathData(isFastPath);
1652             }
1653             fastDoubleFormat(d, negative);
1654 
1655         }
1656 
1657 
1658         // Returns a new string from updated fastPathContainer.
1659         return new String(fastPathData.fastPathContainer,
1660                           fastPathData.firstUsedIndex,
1661                           fastPathData.lastFreeIndex - fastPathData.firstUsedIndex);
1662 
1663     }
1664 




















1665     // ======== End fast-path formating logic for double =========================
1666 
1667     /**
1668      * Complete the formatting of a finite number.  On entry, the digitList must
1669      * be filled in with the correct digits.
1670      */
1671     private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
1672                                    boolean isNegative, boolean isInteger,
1673                                    int maxIntDigits, int minIntDigits,
1674                                    int maxFraDigits, int minFraDigits) {
1675         // NOTE: This isn't required anymore because DigitList takes care of this.
1676         //
1677         //  // The negative of the exponent represents the number of leading
1678         //  // zeros between the decimal and the first non-zero digit, for
1679         //  // a value < 0.1 (e.g., for 0.00123, -fExponent == 2).  If this
1680         //  // is more than the maximum fraction digits, then we have an underflow
1681         //  // for the printed representation.  We recognize this here and set
1682         //  // the DigitList representation to zero in this situation.
1683         //
1684         //  if (-digitList.decimalAt >= getMaximumFractionDigits())
1685         //  {
1686         //      digitList.count = 0;
1687         //  }
1688 











































1689         char zero = symbols.getZeroDigit();
1690         int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
1691         char grouping = symbols.getGroupingSeparator();
1692         char decimal = isCurrencyFormat ?
1693             symbols.getMonetaryDecimalSeparator() :
1694             symbols.getDecimalSeparator();
1695 
1696         /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
1697          * format as zero.  This allows sensible computations and preserves
1698          * relations such as signum(1/x) = signum(x), where x is +Infinity or
1699          * -Infinity.  Prior to this fix, we always formatted zero values as if
1700          * they were positive.  Liu 7/6/98.
1701          */
1702         if (digitList.isZero()) {
1703             digitList.decimalAt = 0; // Normalize
1704         }
1705 
1706         if (isNegative) {
1707             append(result, negativePrefix, delegate,
1708                    getNegativePrefixFieldPositions(), Field.SIGN);
1709         } else {
1710             append(result, positivePrefix, delegate,
1711                    getPositivePrefixFieldPositions(), Field.SIGN);
1712         }
1713 
1714         if (useExponentialNotation) {
1715             int iFieldStart = result.length();
1716             int iFieldEnd = -1;
1717             int fFieldStart = -1;
1718 
1719             // Minimum integer digits are handled in exponential format by
1720             // adjusting the exponent.  For example, 0.01234 with 3 minimum
1721             // integer digits is "123.4E-4".
1722 
1723             // Maximum integer digits are interpreted as indicating the
1724             // repeating range.  This is useful for engineering notation, in
1725             // which the exponent is restricted to a multiple of 3.  For
1726             // example, 0.01234 with 3 maximum integer digits is "12.34e-3".
1727             // If maximum integer digits are > 1 and are larger than
1728             // minimum integer digits, then minimum integer digits are
1729             // ignored.
1730             int exponent = digitList.decimalAt;
1731             int repeat = maxIntDigits;
1732             int minimumIntegerDigits = minIntDigits;
1733             if (repeat > 1 && repeat > minIntDigits) {
1734                 // A repeating range is defined; adjust to it as follows.
1735                 // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
1736                 // -3,-4,-5=>-6, etc. This takes into account that the
1737                 // exponent we have here is off by one from what we expect;
1738                 // it is for the format 0.MMMMMx10^n.
1739                 if (exponent >= 1) {
1740                     exponent = ((exponent - 1) / repeat) * repeat;
1741                 } else {
1742                     // integer division rounds towards 0


1942                 // after the decimal but before any significant digits. These
1943                 // are only output if abs(number being formatted) < 1.0.
1944                 if (-1-i > (digitList.decimalAt-1)) {
1945                     result.append(zero);
1946                     continue;
1947                 }
1948 
1949                 // Output a digit, if we have any precision left, or a
1950                 // zero if we don't.  We don't want to output noise digits.
1951                 if (!isInteger && digitIndex < digitList.count) {
1952                     result.append((char)(digitList.digits[digitIndex++] + zeroDelta));
1953                 } else {
1954                     result.append(zero);
1955                 }
1956             }
1957 
1958             // Record field information for caller.
1959             delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
1960                                fFieldStart, result.length(), result);
1961         }
1962 
1963         if (isNegative) {
1964             append(result, negativeSuffix, delegate,
1965                    getNegativeSuffixFieldPositions(), Field.SIGN);
1966         } else {
1967             append(result, positiveSuffix, delegate,
1968                    getPositiveSuffixFieldPositions(), Field.SIGN);
1969         }
1970 
1971         return result;
1972     }
1973 
1974     /**
1975      * Appends the String <code>string</code> to <code>result</code>.
1976      * <code>delegate</code> is notified of all  the
1977      * <code>FieldPosition</code>s in <code>positions</code>.
1978      * <p>
1979      * If one of the <code>FieldPosition</code>s in <code>positions</code>
1980      * identifies a <code>SIGN</code> attribute, it is mapped to
1981      * <code>signAttribute</code>. This is used
1982      * to map the <code>SIGN</code> attribute to the <code>EXPONENT</code>
1983      * attribute as necessary.
1984      * <p>
1985      * This is used by <code>subformat</code> to add the prefix/suffix.
1986      */
1987     private void append(StringBuffer result, String string,
1988                         FieldDelegate delegate,
1989                         FieldPosition[] positions,
1990                         Format.Field signAttribute) {
1991         int start = result.length();


   1 /*
   2  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  31  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  32  * materials are provided under terms of a License Agreement between Taligent
  33  * and Sun. This technology is protected by multiple US and International
  34  * patents. This notice and attribution to Taligent may not be removed.
  35  *   Taligent is a registered trademark of Taligent, Inc.
  36  *
  37  */
  38 
  39 package java.text;
  40 
  41 import java.io.IOException;
  42 import java.io.InvalidObjectException;
  43 import java.io.ObjectInputStream;
  44 import java.math.BigDecimal;
  45 import java.math.BigInteger;
  46 import java.math.RoundingMode;
  47 import java.text.spi.NumberFormatProvider;
  48 import java.util.ArrayList;
  49 import java.util.Currency;
  50 import java.util.Locale;



  51 import java.util.concurrent.atomic.AtomicInteger;
  52 import java.util.concurrent.atomic.AtomicLong;
  53 import sun.util.locale.provider.LocaleProviderAdapter;
  54 import sun.util.locale.provider.ResourceBundleBasedAdapter;
  55 
  56 /**
  57  * <code>DecimalFormat</code> is a concrete subclass of
  58  * <code>NumberFormat</code> that formats decimal numbers. It has a variety of
  59  * features designed to make it possible to parse and format numbers in any
  60  * locale, including support for Western, Arabic, and Indic digits.  It also
  61  * supports different kinds of numbers, including integers (123), fixed-point
  62  * numbers (123.4), scientific notation (1.23E4), percentages (12%), and
  63  * currency amounts ($123).  All of these can be localized.
  64  *
  65  * <p>To obtain a <code>NumberFormat</code> for a specific locale, including the
  66  * default locale, call one of <code>NumberFormat</code>'s factory methods, such
  67  * as <code>getInstance()</code>.  In general, do not call the
  68  * <code>DecimalFormat</code> constructors directly, since the
  69  * <code>NumberFormat</code> factory methods may return subclasses other than
  70  * <code>DecimalFormat</code>. If you need to customize the format object, do


 137  *
 138  * <p>The prefixes, suffixes, and various symbols used for infinity, digits,
 139  * thousands separators, decimal separators, etc. may be set to arbitrary
 140  * values, and they will appear properly during formatting.  However, care must
 141  * be taken that the symbols and strings do not conflict, or parsing will be
 142  * unreliable.  For example, either the positive and negative prefixes or the
 143  * suffixes must be distinct for <code>DecimalFormat.parse()</code> to be able
 144  * to distinguish positive from negative values.  (If they are identical, then
 145  * <code>DecimalFormat</code> will behave as if no negative subpattern was
 146  * specified.)  Another example is that the decimal separator and thousands
 147  * separator should be distinct characters, or parsing will be impossible.
 148  *
 149  * <p>The grouping separator is commonly used for thousands, but in some
 150  * countries it separates ten-thousands. The grouping size is a constant number
 151  * of digits between the grouping characters, such as 3 for 100,000,000 or 4 for
 152  * 1,0000,0000.  If you supply a pattern with multiple grouping characters, the
 153  * interval between the last one and the end of the integer is the one that is
 154  * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> ==
 155  * <code>"##,####,####"</code>.
 156  *
 157  * <h4><a id="special_pattern_character">Special Pattern Characters</a></h4>
 158  *
 159  * <p>Many characters in a pattern are taken literally; they are matched during
 160  * parsing and output unchanged during formatting.  Special characters, on the
 161  * other hand, stand for other characters, strings, or classes of characters.
 162  * They must be quoted, unless noted otherwise, if they are to appear in the
 163  * prefix or suffix as literals.
 164  *
 165  * <p>The characters listed here are used in non-localized patterns.  Localized
 166  * patterns use the corresponding characters taken from this formatter's
 167  * <code>DecimalFormatSymbols</code> object instead, and these characters lose
 168  * their special status.  Two exceptions are the currency sign and quote, which
 169  * are not localized.
 170  *
 171  * <blockquote>
 172  * <table class="striped">
 173  * <caption style="display:none">Chart showing symbol, location, localized, and meaning.</caption>
 174  * <thead>
 175  *     <tr>
 176  *          <th scope="col" style="text-align:left">Symbol
 177  *          <th scope="col" style="text-align:left">Location


 552             String tempResult = fastFormat(number);
 553             if (tempResult != null) {
 554                 result.append(tempResult);
 555                 return result;
 556             }
 557         }
 558 
 559         // if fast-path could not work, we fallback to standard code.
 560         return format(number, result, fieldPosition.getFieldDelegate());
 561     }
 562 
 563     /**
 564      * Formats a double to produce a string.
 565      * @param number    The double to format
 566      * @param result    where the text is to be appended
 567      * @param delegate notified of locations of sub fields
 568      * @exception       ArithmeticException if rounding is needed with rounding
 569      *                  mode being set to RoundingMode.UNNECESSARY
 570      * @return The formatted number string
 571      */
 572     StringBuffer format(double number, StringBuffer result,
 573                                 FieldDelegate delegate) {
 574 
 575         boolean nanOrInfinity = handleNaN(number, result, delegate);
 576         if (nanOrInfinity) {



 577             return result;
 578         }
 579 
 580         /* Detecting whether a double is negative is easy with the exception of
 581          * the value -0.0.  This is a double which has a zero mantissa (and
 582          * exponent), but a negative sign bit.  It is semantically distinct from
 583          * a zero with a positive sign bit, and this distinction is important
 584          * to certain kinds of computations.  However, it's a little tricky to
 585          * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0).  How then, you may
 586          * ask, does it behave distinctly from +0.0?  Well, 1/(-0.0) ==
 587          * -Infinity.  Proper detection of -0.0 is needed to deal with the
 588          * issues raised by bugs 4106658, 4106667, and 4147706.  Liu 7/6/98.
 589          */
 590         boolean isNegative = ((number < 0.0) || (number == 0.0 && 1/number < 0.0)) ^ (multiplier < 0);
 591 
 592         if (multiplier != 1) {
 593             number *= multiplier;
 594         }
 595 
 596         nanOrInfinity = handleInfinity(number, result, delegate, isNegative);
 597         if (nanOrInfinity) {
 598             return result;
 599         }
 600 
 601         if (isNegative) {
 602             number = -number;
 603         }
 604 
 605         // at this point we are guaranteed a nonnegative finite number.
 606         assert (number >= 0 && !Double.isInfinite(number));
 607         return doubleSubformat(number, result, delegate, isNegative);
 608     }
 609 
 610     /**
 611      * Checks if the given {@code number} is {@code Double.NaN}. if yes;
 612      * appends the NaN symbol to the result string. The NaN string is
 613      * determined by the DecimalFormatSymbols object.
 614      * @param number the double number to format
 615      * @param result where the text is to be appended
 616      * @param delegate notified of locations of sub fields
 617      * @return true, if number is a NaN; false otherwise
 618      */
 619     boolean handleNaN(double number, StringBuffer result,
 620             FieldDelegate delegate) {
 621         if (Double.isNaN(number)
 622                 || (Double.isInfinite(number) && multiplier == 0)) {
 623             int iFieldStart = result.length();
 624             result.append(symbols.getNaN());
 625             delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
 626                     iFieldStart, result.length(), result);
 627             return true;
 628         }
 629         return false;
 630     }
 631 
 632     /**
 633      * Checks if the given {@code number} is {@code Double.NEGATIVE_INFINITY}
 634      * or {@code Double.POSITIVE_INFINITY}. if yes;
 635      * appends the infinity string to the result string. The infinity string is
 636      * determined by the DecimalFormatSymbols object.
 637      * @param number the double number to format
 638      * @param result where the text is to be appended
 639      * @param delegate notified of locations of sub fields
 640      * @param isNegative whether the given {@code number} is negative
 641      * @return true, if number is a {@code Double.NEGATIVE_INFINITY} or
 642      *         {@code Double.POSITIVE_INFINITY}; false otherwise
 643      */
 644     boolean handleInfinity(double number, StringBuffer result,
 645             FieldDelegate delegate, boolean isNegative) {
 646         if (Double.isInfinite(number)) {
 647             if (isNegative) {
 648                 append(result, negativePrefix, delegate,
 649                        getNegativePrefixFieldPositions(), Field.SIGN);
 650             } else {
 651                 append(result, positivePrefix, delegate,
 652                        getPositivePrefixFieldPositions(), Field.SIGN);
 653             }
 654 
 655             int iFieldStart = result.length();
 656             result.append(symbols.getInfinity());
 657             delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
 658                                iFieldStart, result.length(), result);
 659 
 660             if (isNegative) {
 661                 append(result, negativeSuffix, delegate,
 662                        getNegativeSuffixFieldPositions(), Field.SIGN);
 663             } else {
 664                 append(result, positiveSuffix, delegate,
 665                        getPositiveSuffixFieldPositions(), Field.SIGN);
 666             }
 667 
 668             return true;
 669         }
 670         return false;


 671     }
 672 
 673     StringBuffer doubleSubformat(double number, StringBuffer result,
 674             FieldDelegate delegate, boolean isNegative) {
 675         synchronized (digitList) {

 676             int maxIntDigits = super.getMaximumIntegerDigits();
 677             int minIntDigits = super.getMinimumIntegerDigits();
 678             int maxFraDigits = super.getMaximumFractionDigits();
 679             int minFraDigits = super.getMinimumFractionDigits();
 680 
 681             digitList.set(isNegative, number, useExponentialNotation
 682                     ? maxIntDigits + maxFraDigits : maxFraDigits,
 683                     !useExponentialNotation);
 684             return subformat(result, delegate, isNegative, false,
 685                     maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
 686         }
 687     }
 688 
 689     /**
 690      * Format a long to produce a string.
 691      * @param number    The long to format
 692      * @param result    where the text is to be appended
 693      * @param fieldPosition    keeps track on the position of the field within
 694      *                         the returned string. For example, for formatting
 695      *                         a number {@code 123456789} in {@code Locale.US}
 696      *                         locale, if the given {@code fieldPosition} is
 697      *                         {@link NumberFormat#INTEGER_FIELD}, the begin index
 698      *                         and end index of {@code fieldPosition} will be set
 699      *                         to 0 and 11, respectively for the output string
 700      *                         {@code 123,456,789}.
 701      * @exception       NullPointerException if {@code result} or
 702      *                  {@code fieldPosition} is {@code null}


 707      */
 708     @Override
 709     public StringBuffer format(long number, StringBuffer result,
 710                                FieldPosition fieldPosition) {
 711         fieldPosition.setBeginIndex(0);
 712         fieldPosition.setEndIndex(0);
 713 
 714         return format(number, result, fieldPosition.getFieldDelegate());
 715     }
 716 
 717     /**
 718      * Format a long to produce a string.
 719      * @param number    The long to format
 720      * @param result    where the text is to be appended
 721      * @param delegate notified of locations of sub fields
 722      * @return The formatted number string
 723      * @exception        ArithmeticException if rounding is needed with rounding
 724      *                   mode being set to RoundingMode.UNNECESSARY
 725      * @see java.text.FieldPosition
 726      */
 727     StringBuffer format(long number, StringBuffer result,
 728                                FieldDelegate delegate) {
 729         boolean isNegative = (number < 0);
 730         if (isNegative) {
 731             number = -number;
 732         }
 733 
 734         // In general, long values always represent real finite numbers, so
 735         // we don't have to check for +/- Infinity or NaN.  However, there
 736         // is one case we have to be careful of:  The multiplier can push
 737         // a number near MIN_VALUE or MAX_VALUE outside the legal range.  We
 738         // check for this before multiplying, and if it happens we use
 739         // BigInteger instead.
 740         boolean useBigInteger = false;
 741         if (number < 0) { // This can only happen if number == Long.MIN_VALUE.
 742             if (multiplier != 0) {
 743                 useBigInteger = true;
 744             }
 745         } else if (multiplier != 1 && multiplier != 0) {
 746             long cutoff = Long.MAX_VALUE / multiplier;
 747             if (cutoff < 0) {


 798      * @exception        ArithmeticException if rounding is needed with rounding
 799      *                   mode being set to RoundingMode.UNNECESSARY
 800      * @see java.text.FieldPosition
 801      */
 802     private StringBuffer format(BigDecimal number, StringBuffer result,
 803                                 FieldPosition fieldPosition) {
 804         fieldPosition.setBeginIndex(0);
 805         fieldPosition.setEndIndex(0);
 806         return format(number, result, fieldPosition.getFieldDelegate());
 807     }
 808 
 809     /**
 810      * Formats a BigDecimal to produce a string.
 811      * @param number    The BigDecimal to format
 812      * @param result    where the text is to be appended
 813      * @param delegate notified of locations of sub fields
 814      * @exception        ArithmeticException if rounding is needed with rounding
 815      *                   mode being set to RoundingMode.UNNECESSARY
 816      * @return The formatted number string
 817      */
 818     StringBuffer format(BigDecimal number, StringBuffer result,
 819                                 FieldDelegate delegate) {
 820         if (multiplier != 1) {
 821             number = number.multiply(getBigDecimalMultiplier());
 822         }
 823         boolean isNegative = number.signum() == -1;
 824         if (isNegative) {
 825             number = number.negate();
 826         }
 827 
 828         synchronized(digitList) {
 829             int maxIntDigits = getMaximumIntegerDigits();
 830             int minIntDigits = getMinimumIntegerDigits();
 831             int maxFraDigits = getMaximumFractionDigits();
 832             int minFraDigits = getMinimumFractionDigits();
 833             int maximumDigits = maxIntDigits + maxFraDigits;
 834 
 835             digitList.set(isNegative, number, useExponentialNotation ?
 836                 ((maximumDigits < 0) ? Integer.MAX_VALUE : maximumDigits) :
 837                 maxFraDigits, !useExponentialNotation);
 838 


 859      * @see java.text.FieldPosition
 860      */
 861     private StringBuffer format(BigInteger number, StringBuffer result,
 862                                FieldPosition fieldPosition) {
 863         fieldPosition.setBeginIndex(0);
 864         fieldPosition.setEndIndex(0);
 865 
 866         return format(number, result, fieldPosition.getFieldDelegate(), false);
 867     }
 868 
 869     /**
 870      * Format a BigInteger to produce a string.
 871      * @param number    The BigInteger to format
 872      * @param result    where the text is to be appended
 873      * @param delegate notified of locations of sub fields
 874      * @return The formatted number string
 875      * @exception        ArithmeticException if rounding is needed with rounding
 876      *                   mode being set to RoundingMode.UNNECESSARY
 877      * @see java.text.FieldPosition
 878      */
 879     StringBuffer format(BigInteger number, StringBuffer result,
 880                                FieldDelegate delegate, boolean formatLong) {
 881         if (multiplier != 1) {
 882             number = number.multiply(getBigIntegerMultiplier());
 883         }
 884         boolean isNegative = number.signum() == -1;
 885         if (isNegative) {
 886             number = number.negate();
 887         }
 888 
 889         synchronized(digitList) {
 890             int maxIntDigits, minIntDigits, maxFraDigits, minFraDigits, maximumDigits;
 891             if (formatLong) {
 892                 maxIntDigits = super.getMaximumIntegerDigits();
 893                 minIntDigits = super.getMinimumIntegerDigits();
 894                 maxFraDigits = super.getMaximumFractionDigits();
 895                 minFraDigits = super.getMinimumFractionDigits();
 896                 maximumDigits = maxIntDigits + maxFraDigits;
 897             } else {
 898                 maxIntDigits = getMaximumIntegerDigits();
 899                 minIntDigits = getMinimumIntegerDigits();


 941         if (obj instanceof Double || obj instanceof Float) {
 942             format(((Number)obj).doubleValue(), sb, delegate);
 943         } else if (obj instanceof Long || obj instanceof Integer ||
 944                    obj instanceof Short || obj instanceof Byte ||
 945                    obj instanceof AtomicInteger || obj instanceof AtomicLong) {
 946             format(((Number)obj).longValue(), sb, delegate);
 947         } else if (obj instanceof BigDecimal) {
 948             format((BigDecimal)obj, sb, delegate);
 949         } else if (obj instanceof BigInteger) {
 950             format((BigInteger)obj, sb, delegate, false);
 951         } else if (obj == null) {
 952             throw new NullPointerException(
 953                 "formatToCharacterIterator must be passed non-null object");
 954         } else {
 955             throw new IllegalArgumentException(
 956                 "Cannot format given Object as a Number");
 957         }
 958         return delegate.getIterator(sb.toString());
 959     }
 960 
 961     // ==== Begin fast-path formatting logic for double =========================
 962 
 963     /* Fast-path formatting will be used for format(double ...) methods iff a
 964      * number of conditions are met (see checkAndSetFastPathStatus()):
 965      * - Only if instance properties meet the right predefined conditions.
 966      * - The abs value of the double to format is <= Integer.MAX_VALUE.
 967      *
 968      * The basic approach is to split the binary to decimal conversion of a
 969      * double value into two phases:
 970      * * The conversion of the integer portion of the double.
 971      * * The conversion of the fractional portion of the double
 972      *   (limited to two or three digits).
 973      *
 974      * The isolation and conversion of the integer portion of the double is
 975      * straightforward. The conversion of the fraction is more subtle and relies
 976      * on some rounding properties of double to the decimal precisions in
 977      * question.  Using the terminology of BigDecimal, this fast-path algorithm
 978      * is applied when a double value has a magnitude less than Integer.MAX_VALUE
 979      * and rounding is to nearest even and the destination format has two or
 980      * three digits of *scale* (digits after the decimal point).
 981      *


1686                 /*
1687                  * If the fast path data is not set through
1688                  * checkAndSetFastPathStatus() and fulfil the
1689                  * fast path conditions then reset the data
1690                  * directly through resetFastPathData()
1691                  */
1692                 resetFastPathData(isFastPath);
1693             }
1694             fastDoubleFormat(d, negative);
1695 
1696         }
1697 
1698 
1699         // Returns a new string from updated fastPathContainer.
1700         return new String(fastPathData.fastPathContainer,
1701                           fastPathData.firstUsedIndex,
1702                           fastPathData.lastFreeIndex - fastPathData.firstUsedIndex);
1703 
1704     }
1705 
1706     /**
1707      * Sets the {@code DigitList} used by this {@code DecimalFormat}
1708      * instance.
1709      * @param number the number to format
1710      * @param isNegative true, if the number is negative; false otherwise
1711      * @param maxDigits the max digits
1712      */
1713     void setDigitList(Number number, boolean isNegative, int maxDigits) {
1714 
1715         if (number instanceof Double) {
1716             digitList.set(isNegative, (Double) number, maxDigits, true);
1717         } else if (number instanceof BigDecimal) {
1718             digitList.set(isNegative, (BigDecimal) number, maxDigits, true);
1719         } else if (number instanceof Long) {
1720             digitList.set(isNegative, (Long) number, maxDigits);
1721         } else if (number instanceof BigInteger) {
1722             digitList.set(isNegative, (BigInteger) number, maxDigits);
1723         }
1724     }
1725 
1726     // ======== End fast-path formating logic for double =========================
1727 
1728     /**
1729      * Complete the formatting of a finite number.  On entry, the digitList must
1730      * be filled in with the correct digits.
1731      */
1732     private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
1733             boolean isNegative, boolean isInteger,
1734             int maxIntDigits, int minIntDigits,
1735             int maxFraDigits, int minFraDigits) {













1736 
1737         // process prefix
1738         if (isNegative) {
1739             append(result, negativePrefix, delegate,
1740                     getNegativePrefixFieldPositions(), Field.SIGN);
1741         } else {
1742             append(result, positivePrefix, delegate,
1743                     getPositivePrefixFieldPositions(), Field.SIGN);
1744         }
1745 
1746         // process number
1747         subformatNumber(result, delegate, isNegative, isInteger,
1748                 maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
1749 
1750         // process suffix
1751         if (isNegative) {
1752             append(result, negativeSuffix, delegate,
1753                     getNegativeSuffixFieldPositions(), Field.SIGN);
1754         } else {
1755             append(result, positiveSuffix, delegate,
1756                     getPositiveSuffixFieldPositions(), Field.SIGN);
1757         }
1758 
1759         return result;
1760     }
1761 
1762     /**
1763      * Subformats number part using the {@code DigitList} of this
1764      * {@code DecimalFormat} instance.
1765      * @param result where the text is to be appended
1766      * @param delegate notified of the location of sub fields
1767      * @param isNegative true, if the number is negative; false otherwise
1768      * @param isInteger true, if the number is an integer; false otherwise
1769      * @param maxIntDigits maximum integer digits
1770      * @param minIntDigits minimum integer digits
1771      * @param maxFraDigits maximum fraction digits
1772      * @param minFraDigits minimum fraction digits
1773      */
1774     void subformatNumber(StringBuffer result, FieldDelegate delegate,
1775             boolean isNegative, boolean isInteger,
1776             int maxIntDigits, int minIntDigits,
1777             int maxFraDigits, int minFraDigits) {
1778 
1779         char grouping = symbols.getGroupingSeparator();
1780         char zero = symbols.getZeroDigit();
1781         int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
1782 
1783         char decimal = isCurrencyFormat ?
1784                 symbols.getMonetaryDecimalSeparator() :
1785                 symbols.getDecimalSeparator();
1786 
1787         /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
1788          * format as zero.  This allows sensible computations and preserves
1789          * relations such as signum(1/x) = signum(x), where x is +Infinity or
1790          * -Infinity.  Prior to this fix, we always formatted zero values as if
1791          * they were positive.  Liu 7/6/98.
1792          */
1793         if (digitList.isZero()) {
1794             digitList.decimalAt = 0; // Normalize
1795         }
1796 








1797         if (useExponentialNotation) {
1798             int iFieldStart = result.length();
1799             int iFieldEnd = -1;
1800             int fFieldStart = -1;
1801 
1802             // Minimum integer digits are handled in exponential format by
1803             // adjusting the exponent.  For example, 0.01234 with 3 minimum
1804             // integer digits is "123.4E-4".

1805             // Maximum integer digits are interpreted as indicating the
1806             // repeating range.  This is useful for engineering notation, in
1807             // which the exponent is restricted to a multiple of 3.  For
1808             // example, 0.01234 with 3 maximum integer digits is "12.34e-3".
1809             // If maximum integer digits are > 1 and are larger than
1810             // minimum integer digits, then minimum integer digits are
1811             // ignored.
1812             int exponent = digitList.decimalAt;
1813             int repeat = maxIntDigits;
1814             int minimumIntegerDigits = minIntDigits;
1815             if (repeat > 1 && repeat > minIntDigits) {
1816                 // A repeating range is defined; adjust to it as follows.
1817                 // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
1818                 // -3,-4,-5=>-6, etc. This takes into account that the
1819                 // exponent we have here is off by one from what we expect;
1820                 // it is for the format 0.MMMMMx10^n.
1821                 if (exponent >= 1) {
1822                     exponent = ((exponent - 1) / repeat) * repeat;
1823                 } else {
1824                     // integer division rounds towards 0


2024                 // after the decimal but before any significant digits. These
2025                 // are only output if abs(number being formatted) < 1.0.
2026                 if (-1-i > (digitList.decimalAt-1)) {
2027                     result.append(zero);
2028                     continue;
2029                 }
2030 
2031                 // Output a digit, if we have any precision left, or a
2032                 // zero if we don't.  We don't want to output noise digits.
2033                 if (!isInteger && digitIndex < digitList.count) {
2034                     result.append((char)(digitList.digits[digitIndex++] + zeroDelta));
2035                 } else {
2036                     result.append(zero);
2037                 }
2038             }
2039 
2040             // Record field information for caller.
2041             delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
2042                     fFieldStart, result.length(), result);
2043         }










2044     }
2045 
2046     /**
2047      * Appends the String <code>string</code> to <code>result</code>.
2048      * <code>delegate</code> is notified of all  the
2049      * <code>FieldPosition</code>s in <code>positions</code>.
2050      * <p>
2051      * If one of the <code>FieldPosition</code>s in <code>positions</code>
2052      * identifies a <code>SIGN</code> attribute, it is mapped to
2053      * <code>signAttribute</code>. This is used
2054      * to map the <code>SIGN</code> attribute to the <code>EXPONENT</code>
2055      * attribute as necessary.
2056      * <p>
2057      * This is used by <code>subformat</code> to add the prefix/suffix.
2058      */
2059     private void append(StringBuffer result, String string,
2060                         FieldDelegate delegate,
2061                         FieldPosition[] positions,
2062                         Format.Field signAttribute) {
2063         int start = result.length();


< prev index next >