< prev index next >
src/java.base/share/classes/java/text/DecimalFormat.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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,13 +46,10 @@
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;
@@ -155,11 +152,11 @@
* 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>
+ * <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,18 +567,15 @@
* @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,
+ 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);
+
+ 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,10 +591,60 @@
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,28 +663,25 @@
} else {
append(result, positiveSuffix, delegate,
getPositiveSuffixFieldPositions(), Field.SIGN);
}
- return result;
+ return true;
}
-
- if (isNegative) {
- number = -number;
+ return false;
}
- // at this point we are guaranteed a nonnegative finite number.
- assert(number >= 0 && !Double.isInfinite(number));
-
- synchronized(digitList) {
+ 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,
+ digitList.set(isNegative, number, useExponentialNotation
+ ? maxIntDigits + maxFraDigits : maxFraDigits,
!useExponentialNotation);
return subformat(result, delegate, isNegative, false,
maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
}
}
@@ -681,11 +722,11 @@
* @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,
+ StringBuffer format(long number, StringBuffer result,
FieldDelegate delegate) {
boolean isNegative = (number < 0);
if (isNegative) {
number = -number;
}
@@ -772,11 +813,11 @@
* @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,
+ StringBuffer format(BigDecimal number, StringBuffer result,
FieldDelegate delegate) {
if (multiplier != 1) {
number = number.multiply(getBigDecimalMultiplier());
}
boolean isNegative = number.signum() == -1;
@@ -833,11 +874,11 @@
* @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,
+ StringBuffer format(BigInteger number, StringBuffer result,
FieldDelegate delegate, boolean formatLong) {
if (multiplier != 1) {
number = number.multiply(getBigIntegerMultiplier());
}
boolean isNegative = number.signum() == -1;
@@ -915,11 +956,11 @@
"Cannot format given Object as a Number");
}
return delegate.getIterator(sb.toString());
}
- // ==== Begin fast-path formating logic for double =========================
+ // ==== 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,37 +1701,87 @@
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) {
- // 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;
- // }
+ // 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 grouping = symbols.getGroupingSeparator();
+
char decimal = isCurrencyFormat ?
symbols.getMonetaryDecimalSeparator() :
symbols.getDecimalSeparator();
/* Per bug 4147706, DecimalFormat must respect the sign of numbers which
@@ -1701,27 +1792,18 @@
*/
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
@@ -1957,20 +2039,10 @@
// 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
@@ -2212,11 +2284,10 @@
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());
@@ -2238,10 +2309,75 @@
} 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,11 +2404,11 @@
// We have to track digitCount ourselves, because digits.count will
// pin when the maximum allowable digits is reached.
int digitCount = 0;
- backup = -1;
+ 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,11 +2468,11 @@
}
// 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())
+ } 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,54 +2507,15 @@
// 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;
+ return -1;
}
-
- parsePosition.index = position +
- (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success!
- } else {
- parsePosition.index = position;
}
+ return 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.
< prev index next >