< prev index next >

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

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 46,58 **** import java.math.RoundingMode; import java.text.spi.NumberFormatProvider; import java.util.ArrayList; import java.util.Currency; import java.util.Locale; - import java.util.ResourceBundle; - import java.util.concurrent.ConcurrentHashMap; - import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.ResourceBundleBasedAdapter; --- 46,55 ----
*** 155,165 **** * 1,0000,0000. If you supply a pattern with multiple grouping characters, the * interval between the last one and the end of the integer is the one that is * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> == * <code>"##,####,####"</code>. * ! * <h4>Special Pattern Characters</h4> * * <p>Many characters in a pattern are taken literally; they are matched during * parsing and output unchanged during formatting. Special characters, on the * other hand, stand for other characters, strings, or classes of characters. * They must be quoted, unless noted otherwise, if they are to appear in the --- 152,162 ---- * 1,0000,0000. If you supply a pattern with multiple grouping characters, the * interval between the last one and the end of the integer is the one that is * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> == * <code>"##,####,####"</code>. * ! * <h4><a id="special_pattern_character">Special Pattern Characters</a></h4> * * <p>Many characters in a pattern are taken literally; they are matched during * parsing and output unchanged during formatting. Special characters, on the * other hand, stand for other characters, strings, or classes of characters. * They must be quoted, unless noted otherwise, if they are to appear in the
*** 570,587 **** * @param delegate notified of locations of sub fields * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @return The formatted number string */ ! private StringBuffer format(double number, StringBuffer result, FieldDelegate delegate) { ! if (Double.isNaN(number) || ! (Double.isInfinite(number) && multiplier == 0)) { ! int iFieldStart = result.length(); ! result.append(symbols.getNaN()); ! delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER, ! iFieldStart, result.length(), result); return result; } /* Detecting whether a double is negative is easy with the exception of * the value -0.0. This is a double which has a zero mantissa (and --- 567,581 ---- * @param delegate notified of locations of sub fields * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @return The formatted number string */ ! StringBuffer format(double number, StringBuffer result, FieldDelegate delegate) { ! ! boolean nanOrInfinity = handleNaN(number, result, delegate); ! if (nanOrInfinity) { return result; } /* Detecting whether a double is negative is easy with the exception of * the value -0.0. This is a double which has a zero mantissa (and
*** 597,606 **** --- 591,650 ---- if (multiplier != 1) { number *= multiplier; } + nanOrInfinity = handleInfinity(number, result, delegate, isNegative); + if (nanOrInfinity) { + return result; + } + + if (isNegative) { + number = -number; + } + + // at this point we are guaranteed a nonnegative finite number. + assert (number >= 0 && !Double.isInfinite(number)); + return doubleSubformat(number, result, delegate, isNegative); + } + + /** + * Checks if the given {@code number} is {@code Double.NaN}. if yes; + * appends the NaN symbol to the result string. The NaN string is + * determined by the DecimalFormatSymbols object. + * @param number the double number to format + * @param result where the text is to be appended + * @param delegate notified of locations of sub fields + * @return true, if number is a NaN; false otherwise + */ + boolean handleNaN(double number, StringBuffer result, + FieldDelegate delegate) { + if (Double.isNaN(number) + || (Double.isInfinite(number) && multiplier == 0)) { + int iFieldStart = result.length(); + result.append(symbols.getNaN()); + delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER, + iFieldStart, result.length(), result); + return true; + } + return false; + } + + /** + * Checks if the given {@code number} is {@code Double.NEGATIVE_INFINITY} + * or {@code Double.POSITIVE_INFINITY}. if yes; + * appends the infinity string to the result string. The infinity string is + * determined by the DecimalFormatSymbols object. + * @param number the double number to format + * @param result where the text is to be appended + * @param delegate notified of locations of sub fields + * @param isNegative whether the given {@code number} is negative + * @return true, if number is a {@code Double.NEGATIVE_INFINITY} or + * {@code Double.POSITIVE_INFINITY}; false otherwise + */ + boolean handleInfinity(double number, StringBuffer result, + FieldDelegate delegate, boolean isNegative) { if (Double.isInfinite(number)) { if (isNegative) { append(result, negativePrefix, delegate, getNegativePrefixFieldPositions(), Field.SIGN); } else {
*** 619,646 **** } else { append(result, positiveSuffix, delegate, getPositiveSuffixFieldPositions(), Field.SIGN); } ! return result; } ! ! if (isNegative) { ! number = -number; } ! // at this point we are guaranteed a nonnegative finite number. ! assert(number >= 0 && !Double.isInfinite(number)); ! ! synchronized(digitList) { int maxIntDigits = super.getMaximumIntegerDigits(); int minIntDigits = super.getMinimumIntegerDigits(); int maxFraDigits = super.getMaximumFractionDigits(); int minFraDigits = super.getMinimumFractionDigits(); ! digitList.set(isNegative, number, useExponentialNotation ? ! maxIntDigits + maxFraDigits : maxFraDigits, !useExponentialNotation); return subformat(result, delegate, isNegative, false, maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); } } --- 663,687 ---- } else { append(result, positiveSuffix, delegate, getPositiveSuffixFieldPositions(), Field.SIGN); } ! return true; } ! return false; } ! StringBuffer doubleSubformat(double number, StringBuffer result, ! FieldDelegate delegate, boolean isNegative) { ! synchronized (digitList) { int maxIntDigits = super.getMaximumIntegerDigits(); int minIntDigits = super.getMinimumIntegerDigits(); int maxFraDigits = super.getMaximumFractionDigits(); int minFraDigits = super.getMinimumFractionDigits(); ! digitList.set(isNegative, number, useExponentialNotation ! ? maxIntDigits + maxFraDigits : maxFraDigits, !useExponentialNotation); return subformat(result, delegate, isNegative, false, maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); } }
*** 681,691 **** * @return The formatted number string * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ ! private StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) { boolean isNegative = (number < 0); if (isNegative) { number = -number; } --- 722,732 ---- * @return The formatted number string * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ ! StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) { boolean isNegative = (number < 0); if (isNegative) { number = -number; }
*** 772,782 **** * @param delegate notified of locations of sub fields * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @return The formatted number string */ ! private StringBuffer format(BigDecimal number, StringBuffer result, FieldDelegate delegate) { if (multiplier != 1) { number = number.multiply(getBigDecimalMultiplier()); } boolean isNegative = number.signum() == -1; --- 813,823 ---- * @param delegate notified of locations of sub fields * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @return The formatted number string */ ! StringBuffer format(BigDecimal number, StringBuffer result, FieldDelegate delegate) { if (multiplier != 1) { number = number.multiply(getBigDecimalMultiplier()); } boolean isNegative = number.signum() == -1;
*** 833,843 **** * @return The formatted number string * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ ! private StringBuffer format(BigInteger number, StringBuffer result, FieldDelegate delegate, boolean formatLong) { if (multiplier != 1) { number = number.multiply(getBigIntegerMultiplier()); } boolean isNegative = number.signum() == -1; --- 874,884 ---- * @return The formatted number string * @exception ArithmeticException if rounding is needed with rounding * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ ! StringBuffer format(BigInteger number, StringBuffer result, FieldDelegate delegate, boolean formatLong) { if (multiplier != 1) { number = number.multiply(getBigIntegerMultiplier()); } boolean isNegative = number.signum() == -1;
*** 915,925 **** "Cannot format given Object as a Number"); } return delegate.getIterator(sb.toString()); } ! // ==== Begin fast-path formating logic for double ========================= /* Fast-path formatting will be used for format(double ...) methods iff a * number of conditions are met (see checkAndSetFastPathStatus()): * - Only if instance properties meet the right predefined conditions. * - The abs value of the double to format is <= Integer.MAX_VALUE. --- 956,966 ---- "Cannot format given Object as a Number"); } return delegate.getIterator(sb.toString()); } ! // ==== Begin fast-path formatting logic for double ========================= /* Fast-path formatting will be used for format(double ...) methods iff a * number of conditions are met (see checkAndSetFastPathStatus()): * - Only if instance properties meet the right predefined conditions. * - The abs value of the double to format is <= Integer.MAX_VALUE.
*** 1660,1696 **** fastPathData.firstUsedIndex, fastPathData.lastFreeIndex - fastPathData.firstUsedIndex); } // ======== End fast-path formating logic for double ========================= /** * Complete the formatting of a finite number. On entry, the digitList must * be filled in with the correct digits. */ private StringBuffer subformat(StringBuffer result, FieldDelegate delegate, boolean isNegative, boolean isInteger, int maxIntDigits, int minIntDigits, int maxFraDigits, int minFraDigits) { - // NOTE: This isn't required anymore because DigitList takes care of this. - // - // // The negative of the exponent represents the number of leading - // // zeros between the decimal and the first non-zero digit, for - // // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this - // // is more than the maximum fraction digits, then we have an underflow - // // for the printed representation. We recognize this here and set - // // the DigitList representation to zero in this situation. - // - // if (-digitList.decimalAt >= getMaximumFractionDigits()) - // { - // digitList.count = 0; - // } char zero = symbols.getZeroDigit(); int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero ! char grouping = symbols.getGroupingSeparator(); char decimal = isCurrencyFormat ? symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator(); /* Per bug 4147706, DecimalFormat must respect the sign of numbers which --- 1701,1787 ---- fastPathData.firstUsedIndex, fastPathData.lastFreeIndex - fastPathData.firstUsedIndex); } + /** + * Sets the {@code DigitList} used by this {@code DecimalFormat} + * instance. + * @param number the number to format + * @param isNegative true, if the number is negative; false otherwise + * @param maxDigits the max digits + */ + void setDigitList(Number number, boolean isNegative, int maxDigits) { + + if (number instanceof Double) { + digitList.set(isNegative, (Double) number, maxDigits, true); + } else if (number instanceof BigDecimal) { + digitList.set(isNegative, (BigDecimal) number, maxDigits, true); + } else if (number instanceof Long) { + digitList.set(isNegative, (Long) number, maxDigits); + } else if (number instanceof BigInteger) { + digitList.set(isNegative, (BigInteger) number, maxDigits); + } + } + // ======== End fast-path formating logic for double ========================= /** * Complete the formatting of a finite number. On entry, the digitList must * be filled in with the correct digits. */ private StringBuffer subformat(StringBuffer result, FieldDelegate delegate, boolean isNegative, boolean isInteger, int maxIntDigits, int minIntDigits, int maxFraDigits, int minFraDigits) { + // Process prefix + if (isNegative) { + append(result, negativePrefix, delegate, + getNegativePrefixFieldPositions(), Field.SIGN); + } else { + append(result, positivePrefix, delegate, + getPositivePrefixFieldPositions(), Field.SIGN); + } + + // Process number + subformatNumber(result, delegate, isNegative, isInteger, + maxIntDigits, minIntDigits, maxFraDigits, minFraDigits); + + // Process suffix + if (isNegative) { + append(result, negativeSuffix, delegate, + getNegativeSuffixFieldPositions(), Field.SIGN); + } else { + append(result, positiveSuffix, delegate, + getPositiveSuffixFieldPositions(), Field.SIGN); + } + + return result; + } + + /** + * Subformats number part using the {@code DigitList} of this + * {@code DecimalFormat} instance. + * @param result where the text is to be appended + * @param delegate notified of the location of sub fields + * @param isNegative true, if the number is negative; false otherwise + * @param isInteger true, if the number is an integer; false otherwise + * @param maxIntDigits maximum integer digits + * @param minIntDigits minimum integer digits + * @param maxFraDigits maximum fraction digits + * @param minFraDigits minimum fraction digits + */ + void subformatNumber(StringBuffer result, FieldDelegate delegate, + boolean isNegative, boolean isInteger, + int maxIntDigits, int minIntDigits, + int maxFraDigits, int minFraDigits) { + + char grouping = symbols.getGroupingSeparator(); char zero = symbols.getZeroDigit(); int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero ! char decimal = isCurrencyFormat ? symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator(); /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
*** 1701,1727 **** */ if (digitList.isZero()) { digitList.decimalAt = 0; // Normalize } - if (isNegative) { - append(result, negativePrefix, delegate, - getNegativePrefixFieldPositions(), Field.SIGN); - } else { - append(result, positivePrefix, delegate, - getPositivePrefixFieldPositions(), Field.SIGN); - } - if (useExponentialNotation) { int iFieldStart = result.length(); int iFieldEnd = -1; int fFieldStart = -1; // Minimum integer digits are handled in exponential format by // adjusting the exponent. For example, 0.01234 with 3 minimum // integer digits is "123.4E-4". - // Maximum integer digits are interpreted as indicating the // repeating range. This is useful for engineering notation, in // which the exponent is restricted to a multiple of 3. For // example, 0.01234 with 3 maximum integer digits is "12.34e-3". // If maximum integer digits are > 1 and are larger than --- 1792,1809 ----
*** 1957,1976 **** // Record field information for caller. delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION, fFieldStart, result.length(), result); } - - if (isNegative) { - append(result, negativeSuffix, delegate, - getNegativeSuffixFieldPositions(), Field.SIGN); - } else { - append(result, positiveSuffix, delegate, - getPositiveSuffixFieldPositions(), Field.SIGN); - } - - return result; } /** * Appends the String <code>string</code> to <code>result</code>. * <code>delegate</code> is notified of all the --- 2039,2048 ----
*** 2212,2222 **** String positivePrefix, String negativePrefix, DigitList digits, boolean isExponent, boolean status[]) { int position = parsePosition.index; int oldStart = parsePosition.index; - int backup; boolean gotPositive, gotNegative; // check for positivePrefix; take longest gotPositive = text.regionMatches(position, positivePrefix, 0, positivePrefix.length()); --- 2284,2293 ----
*** 2238,2247 **** --- 2309,2383 ---- } else { parsePosition.errorIndex = position; return false; } + position = subparseNumber(text, position, digits, true, isExponent, status); + if (position == -1) { + parsePosition.index = oldStart; + parsePosition.errorIndex = oldStart; + return false; + } + + // Check for suffix + if (!isExponent) { + if (gotPositive) { + gotPositive = text.regionMatches(position,positiveSuffix,0, + positiveSuffix.length()); + } + if (gotNegative) { + gotNegative = text.regionMatches(position,negativeSuffix,0, + negativeSuffix.length()); + } + + // If both match, take longest + if (gotPositive && gotNegative) { + if (positiveSuffix.length() > negativeSuffix.length()) { + gotNegative = false; + } else if (positiveSuffix.length() < negativeSuffix.length()) { + gotPositive = false; + } + } + + // Fail if neither or both + if (gotPositive == gotNegative) { + parsePosition.errorIndex = position; + return false; + } + + parsePosition.index = position + + (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success! + } else { + parsePosition.index = position; + } + + status[STATUS_POSITIVE] = gotPositive; + if (parsePosition.index == oldStart) { + parsePosition.errorIndex = position; + return false; + } + return true; + } + + /** + * Parses a number from the given {@code text}. The text is parsed + * beginning at position, until an unparseable character is seen. + * + * @param text the string to parse + * @param position the position at which parsing begins + * @param digits the DigitList to set to the parsed value + * @param checkExponent whether to check for exponential number + * @param isExponent if the exponential part is encountered + * @param status upon return contains boolean status flags indicating + * whether the value is infinite and whether it is + * positive + * @return returns the position of the first unparseable character or + * -1 in case of no valid number parsed + */ + int subparseNumber(String text, int position, + DigitList digits, boolean checkExponent, + boolean isExponent, boolean status[]) { // process digits or Inf, find decimal position status[STATUS_INFINITE] = false; if (!isExponent && text.regionMatches(position,symbols.getInfinity(),0, symbols.getInfinity().length())) { position += symbols.getInfinity().length();
*** 2268,2278 **** // We have to track digitCount ourselves, because digits.count will // pin when the maximum allowable digits is reached. int digitCount = 0; ! backup = -1; for (; position < text.length(); ++position) { char ch = text.charAt(position); /* We recognize all digit ranges, not only the Latin digit range * '0'..'9'. We do so by using the Character.digit() method, --- 2404,2414 ---- // We have to track digitCount ourselves, because digits.count will // pin when the maximum allowable digits is reached. int digitCount = 0; ! int backup = -1; for (; position < text.length(); ++position) { char ch = text.charAt(position); /* We recognize all digit ranges, not only the Latin digit range * '0'..'9'. We do so by using the Character.digit() method,
*** 2332,2342 **** } // Ignore grouping characters, if we are using them, but // require that they be followed by a digit. Otherwise // we backup and reprocess them. backup = position; ! } else if (!isExponent && text.regionMatches(position, exponentString, 0, exponentString.length()) && !sawExponent) { // Process the exponent by recursively calling this method. ParsePosition pos = new ParsePosition(position + exponentString.length()); boolean[] stat = new boolean[STATUS_LENGTH]; DigitList exponentDigits = new DigitList(); --- 2468,2478 ---- } // Ignore grouping characters, if we are using them, but // require that they be followed by a digit. Otherwise // we backup and reprocess them. backup = position; ! } else if (checkExponent && !isExponent && text.regionMatches(position, exponentString, 0, exponentString.length()) && !sawExponent) { // Process the exponent by recursively calling this method. ParsePosition pos = new ParsePosition(position + exponentString.length()); boolean[] stat = new boolean[STATUS_LENGTH]; DigitList exponentDigits = new DigitList();
*** 2371,2424 **** // If none of the text string was recognized. For example, parse // "x" with pattern "#0.00" (return index and error index both 0) // parse "$" with pattern "$#0.00". (return index 0 and error // index 1). if (!sawDigit && digitCount == 0) { ! parsePosition.index = oldStart; ! parsePosition.errorIndex = oldStart; ! return false; ! } ! } ! ! // check for suffix ! if (!isExponent) { ! if (gotPositive) { ! gotPositive = text.regionMatches(position,positiveSuffix,0, ! positiveSuffix.length()); ! } ! if (gotNegative) { ! gotNegative = text.regionMatches(position,negativeSuffix,0, ! negativeSuffix.length()); ! } ! ! // if both match, take longest ! if (gotPositive && gotNegative) { ! if (positiveSuffix.length() > negativeSuffix.length()) { ! gotNegative = false; ! } else if (positiveSuffix.length() < negativeSuffix.length()) { ! gotPositive = false; } } - // fail if neither or both - if (gotPositive == gotNegative) { - parsePosition.errorIndex = position; - return false; - } - - parsePosition.index = position + - (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success! - } else { - parsePosition.index = position; - } - - status[STATUS_POSITIVE] = gotPositive; - if (parsePosition.index == oldStart) { - parsePosition.errorIndex = position; - return false; - } - return true; } /** * Returns a copy of the decimal format symbols, which is generally not * changed by the programmer or user. --- 2507,2521 ---- // If none of the text string was recognized. For example, parse // "x" with pattern "#0.00" (return index and error index both 0) // parse "$" with pattern "$#0.00". (return index 0 and error // index 1). if (!sawDigit && digitCount == 0) { ! return -1; } } + return position; } /** * Returns a copy of the decimal format symbols, which is generally not * changed by the programmer or user.
< prev index next >