mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8177552: Compact Number Formatting support
Reviewed-by: naoto, rriggs
This commit is contained in:
parent
f3646ad79b
commit
4a28e27fd2
28 changed files with 5157 additions and 196 deletions
2130
src/java.base/share/classes/java/text/CompactNumberFormat.java
Normal file
2130
src/java.base/share/classes/java/text/CompactNumberFormat.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -48,9 +48,6 @@ 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;
|
||||
|
@ -157,7 +154,7 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
|
|||
* 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
|
||||
|
@ -572,14 +569,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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;
|
||||
}
|
||||
|
||||
|
@ -599,6 +593,56 @@ public class DecimalFormat extends NumberFormat {
|
|||
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,
|
||||
|
@ -621,27 +665,24 @@ public class DecimalFormat extends NumberFormat {
|
|||
getPositiveSuffixFieldPositions(), Field.SIGN);
|
||||
}
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNegative) {
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// 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,
|
||||
!useExponentialNotation);
|
||||
digitList.set(isNegative, number, useExponentialNotation
|
||||
? maxIntDigits + maxFraDigits : maxFraDigits,
|
||||
!useExponentialNotation);
|
||||
return subformat(result, delegate, isNegative, false,
|
||||
maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
|
||||
maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,7 +724,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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) {
|
||||
|
@ -774,7 +815,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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());
|
||||
|
@ -835,7 +876,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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());
|
||||
|
@ -917,7 +958,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
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()):
|
||||
|
@ -1662,6 +1703,26 @@ public class DecimalFormat extends NumberFormat {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 =========================
|
||||
|
||||
/**
|
||||
|
@ -1669,29 +1730,59 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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;
|
||||
// }
|
||||
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 grouping = symbols.getGroupingSeparator();
|
||||
|
||||
char decimal = isCurrencyFormat ?
|
||||
symbols.getMonetaryDecimalSeparator() :
|
||||
symbols.getDecimalSeparator();
|
||||
symbols.getMonetaryDecimalSeparator() :
|
||||
symbols.getDecimalSeparator();
|
||||
|
||||
/* Per bug 4147706, DecimalFormat must respect the sign of numbers which
|
||||
* format as zero. This allows sensible computations and preserves
|
||||
|
@ -1703,14 +1794,6 @@ public class DecimalFormat extends NumberFormat {
|
|||
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;
|
||||
|
@ -1719,7 +1802,6 @@ public class DecimalFormat extends NumberFormat {
|
|||
// 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
|
||||
|
@ -1782,8 +1864,8 @@ public class DecimalFormat extends NumberFormat {
|
|||
fFieldStart = result.length();
|
||||
}
|
||||
result.append((i < digitList.count) ?
|
||||
(char)(digitList.digits[i] + zeroDelta) :
|
||||
zero);
|
||||
(char)(digitList.digits[i] + zeroDelta) :
|
||||
zero);
|
||||
}
|
||||
|
||||
if (decimalSeparatorAlwaysShown && totalDigits == integerDigits) {
|
||||
|
@ -1802,17 +1884,17 @@ public class DecimalFormat extends NumberFormat {
|
|||
iFieldEnd = result.length();
|
||||
}
|
||||
delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
|
||||
iFieldStart, iFieldEnd, result);
|
||||
iFieldStart, iFieldEnd, result);
|
||||
if (addedDecimalSeparator) {
|
||||
delegate.formatted(Field.DECIMAL_SEPARATOR,
|
||||
Field.DECIMAL_SEPARATOR,
|
||||
iFieldEnd, fFieldStart, result);
|
||||
Field.DECIMAL_SEPARATOR,
|
||||
iFieldEnd, fFieldStart, result);
|
||||
}
|
||||
if (fFieldStart == -1) {
|
||||
fFieldStart = result.length();
|
||||
}
|
||||
delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
|
||||
fFieldStart, result.length(), result);
|
||||
fFieldStart, result.length(), result);
|
||||
|
||||
// The exponent is output using the pattern-specified minimum
|
||||
// exponent digits. There is no maximum limit to the exponent
|
||||
|
@ -1823,7 +1905,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
result.append(symbols.getExponentSeparator());
|
||||
|
||||
delegate.formatted(Field.EXPONENT_SYMBOL, Field.EXPONENT_SYMBOL,
|
||||
fieldStart, result.length(), result);
|
||||
fieldStart, result.length(), result);
|
||||
|
||||
// For zero values, we force the exponent to zero. We
|
||||
// must do this here, and not earlier, because the value
|
||||
|
@ -1838,7 +1920,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
fieldStart = result.length();
|
||||
result.append(symbols.getMinusSign());
|
||||
delegate.formatted(Field.EXPONENT_SIGN, Field.EXPONENT_SIGN,
|
||||
fieldStart, result.length(), result);
|
||||
fieldStart, result.length(), result);
|
||||
}
|
||||
digitList.set(negativeExponent, exponent);
|
||||
|
||||
|
@ -1849,10 +1931,10 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
for (int i=0; i<digitList.decimalAt; ++i) {
|
||||
result.append((i < digitList.count) ?
|
||||
(char)(digitList.digits[i] + zeroDelta) : zero);
|
||||
(char)(digitList.digits[i] + zeroDelta) : zero);
|
||||
}
|
||||
delegate.formatted(Field.EXPONENT, Field.EXPONENT, eFieldStart,
|
||||
result.length(), result);
|
||||
result.length(), result);
|
||||
} else {
|
||||
int iFieldStart = result.length();
|
||||
|
||||
|
@ -1889,19 +1971,19 @@ public class DecimalFormat extends NumberFormat {
|
|||
// grouping separator if i==0 though; that's at the end of
|
||||
// the integer part.
|
||||
if (isGroupingUsed() && i>0 && (groupingSize != 0) &&
|
||||
(i % groupingSize == 0)) {
|
||||
(i % groupingSize == 0)) {
|
||||
int gStart = result.length();
|
||||
result.append(grouping);
|
||||
delegate.formatted(Field.GROUPING_SEPARATOR,
|
||||
Field.GROUPING_SEPARATOR, gStart,
|
||||
result.length(), result);
|
||||
Field.GROUPING_SEPARATOR, gStart,
|
||||
result.length(), result);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether or not there are any printable fractional
|
||||
// digits. If we've used up the digits we know there aren't.
|
||||
boolean fractionPresent = (minFraDigits > 0) ||
|
||||
(!isInteger && digitIndex < digitList.count);
|
||||
(!isInteger && digitIndex < digitList.count);
|
||||
|
||||
// If there is no fraction present, and we haven't printed any
|
||||
// integer digits, then print a zero. Otherwise we won't print
|
||||
|
@ -1911,7 +1993,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
|
||||
delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
|
||||
iFieldStart, result.length(), result);
|
||||
iFieldStart, result.length(), result);
|
||||
|
||||
// Output the decimal separator if we always do so.
|
||||
int sStart = result.length();
|
||||
|
@ -1921,8 +2003,8 @@ public class DecimalFormat extends NumberFormat {
|
|||
|
||||
if (sStart != result.length()) {
|
||||
delegate.formatted(Field.DECIMAL_SEPARATOR,
|
||||
Field.DECIMAL_SEPARATOR,
|
||||
sStart, result.length(), result);
|
||||
Field.DECIMAL_SEPARATOR,
|
||||
sStart, result.length(), result);
|
||||
}
|
||||
int fFieldStart = result.length();
|
||||
|
||||
|
@ -1934,7 +2016,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
// we have an integer, so there is no fractional stuff to
|
||||
// display, or we're out of significant digits.
|
||||
if (i >= minFraDigits &&
|
||||
(isInteger || digitIndex >= digitList.count)) {
|
||||
(isInteger || digitIndex >= digitList.count)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1957,18 +2039,8 @@ public class DecimalFormat extends NumberFormat {
|
|||
|
||||
// Record field information for caller.
|
||||
delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
|
||||
fFieldStart, result.length(), result);
|
||||
fFieldStart, result.length(), result);
|
||||
}
|
||||
|
||||
if (isNegative) {
|
||||
append(result, negativeSuffix, delegate,
|
||||
getNegativeSuffixFieldPositions(), Field.SIGN);
|
||||
} else {
|
||||
append(result, positiveSuffix, delegate,
|
||||
getPositiveSuffixFieldPositions(), Field.SIGN);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2209,19 +2281,18 @@ public class DecimalFormat extends NumberFormat {
|
|||
* whether the value was infinite and whether it was positive.
|
||||
*/
|
||||
private final boolean subparse(String text, ParsePosition parsePosition,
|
||||
String positivePrefix, String negativePrefix,
|
||||
DigitList digits, boolean isExponent,
|
||||
boolean status[]) {
|
||||
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());
|
||||
positivePrefix.length());
|
||||
gotNegative = text.regionMatches(position, negativePrefix, 0,
|
||||
negativePrefix.length());
|
||||
negativePrefix.length());
|
||||
|
||||
if (gotPositive && gotNegative) {
|
||||
if (positivePrefix.length() > negativePrefix.length()) {
|
||||
|
@ -2240,10 +2311,75 @@ public class DecimalFormat extends NumberFormat {
|
|||
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())) {
|
||||
symbols.getInfinity().length())) {
|
||||
position += symbols.getInfinity().length();
|
||||
status[STATUS_INFINITE] = true;
|
||||
} else {
|
||||
|
@ -2257,8 +2393,8 @@ public class DecimalFormat extends NumberFormat {
|
|||
digits.decimalAt = digits.count = 0;
|
||||
char zero = symbols.getZeroDigit();
|
||||
char decimal = isCurrencyFormat ?
|
||||
symbols.getMonetaryDecimalSeparator() :
|
||||
symbols.getDecimalSeparator();
|
||||
symbols.getMonetaryDecimalSeparator() :
|
||||
symbols.getDecimalSeparator();
|
||||
char grouping = symbols.getGroupingSeparator();
|
||||
String exponentString = symbols.getExponentSeparator();
|
||||
boolean sawDecimal = false;
|
||||
|
@ -2270,7 +2406,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
// 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);
|
||||
|
||||
|
@ -2334,15 +2470,15 @@ public class DecimalFormat extends NumberFormat {
|
|||
// 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) {
|
||||
} 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());
|
||||
ParsePosition pos = new ParsePosition(position + exponentString.length());
|
||||
boolean[] stat = new boolean[STATUS_LENGTH];
|
||||
DigitList exponentDigits = new DigitList();
|
||||
|
||||
if (subparse(text, pos, "", Character.toString(symbols.getMinusSign()), exponentDigits, true, stat) &&
|
||||
exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
|
||||
exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
|
||||
position = pos.index; // Advance past the exponent
|
||||
exponent = (int)exponentDigits.getLong();
|
||||
if (!stat[STATUS_POSITIVE]) {
|
||||
|
@ -2373,50 +2509,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
// parse "$" with pattern "$#0.00". (return index 0 and error
|
||||
// index 1).
|
||||
if (!sawDigit && digitCount == 0) {
|
||||
parsePosition.index = oldStart;
|
||||
parsePosition.errorIndex = oldStart;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -47,13 +47,11 @@ import java.math.RoundingMode;
|
|||
import java.text.spi.NumberFormatProvider;
|
||||
import java.util.Currency;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.spi.LocaleServiceProvider;
|
||||
import sun.util.locale.provider.LocaleProviderAdapter;
|
||||
import sun.util.locale.provider.LocaleServiceProviderPool;
|
||||
|
||||
|
@ -112,9 +110,12 @@ import sun.util.locale.provider.LocaleServiceProviderPool;
|
|||
* Use <code>getInstance</code> or <code>getNumberInstance</code> to get the
|
||||
* normal number format. Use <code>getIntegerInstance</code> to get an
|
||||
* integer number format. Use <code>getCurrencyInstance</code> to get the
|
||||
* currency number format. And use <code>getPercentInstance</code> to get a
|
||||
* format for displaying percentages. With this format, a fraction like
|
||||
* 0.53 is displayed as 53%.
|
||||
* currency number format. Use {@code getCompactNumberInstance} to get the
|
||||
* compact number format to format a number in shorter form. For example,
|
||||
* {@code 2000} can be formatted as {@code "2K"} in
|
||||
* {@link java.util.Locale#US US locale}. Use <code>getPercentInstance</code>
|
||||
* to get a format for displaying percentages. With this format, a fraction
|
||||
* like 0.53 is displayed as 53%.
|
||||
*
|
||||
* <p>
|
||||
* You can also control the display of numbers with such methods as
|
||||
|
@ -122,9 +123,10 @@ import sun.util.locale.provider.LocaleServiceProviderPool;
|
|||
* If you want even more control over the format or parsing,
|
||||
* or want to give your users more control,
|
||||
* you can try casting the <code>NumberFormat</code> you get from the factory methods
|
||||
* to a <code>DecimalFormat</code>. This will work for the vast majority
|
||||
* of locales; just remember to put it in a <code>try</code> block in case you
|
||||
* encounter an unusual one.
|
||||
* to a {@code DecimalFormat} or {@code CompactNumberFormat} depending on
|
||||
* the factory method used. This will work for the vast majority of locales;
|
||||
* just remember to put it in a <code>try</code> block in case you encounter
|
||||
* an unusual one.
|
||||
*
|
||||
* <p>
|
||||
* NumberFormat and DecimalFormat are designed such that some controls
|
||||
|
@ -201,6 +203,7 @@ import sun.util.locale.provider.LocaleServiceProviderPool;
|
|||
*
|
||||
* @see DecimalFormat
|
||||
* @see ChoiceFormat
|
||||
* @see CompactNumberFormat
|
||||
* @author Mark Davis
|
||||
* @author Helena Shih
|
||||
* @since 1.1
|
||||
|
@ -472,7 +475,7 @@ public abstract class NumberFormat extends Format {
|
|||
* formatting
|
||||
*/
|
||||
public static final NumberFormat getInstance() {
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, NUMBERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -485,7 +488,7 @@ public abstract class NumberFormat extends Format {
|
|||
* formatting
|
||||
*/
|
||||
public static NumberFormat getInstance(Locale inLocale) {
|
||||
return getInstance(inLocale, NUMBERSTYLE);
|
||||
return getInstance(inLocale, null, NUMBERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -501,7 +504,7 @@ public abstract class NumberFormat extends Format {
|
|||
* @see java.util.Locale.Category#FORMAT
|
||||
*/
|
||||
public static final NumberFormat getNumberInstance() {
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, NUMBERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -512,7 +515,7 @@ public abstract class NumberFormat extends Format {
|
|||
* formatting
|
||||
*/
|
||||
public static NumberFormat getNumberInstance(Locale inLocale) {
|
||||
return getInstance(inLocale, NUMBERSTYLE);
|
||||
return getInstance(inLocale, null, NUMBERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -534,7 +537,7 @@ public abstract class NumberFormat extends Format {
|
|||
* @since 1.4
|
||||
*/
|
||||
public static final NumberFormat getIntegerInstance() {
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), INTEGERSTYLE);
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, INTEGERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -551,7 +554,7 @@ public abstract class NumberFormat extends Format {
|
|||
* @since 1.4
|
||||
*/
|
||||
public static NumberFormat getIntegerInstance(Locale inLocale) {
|
||||
return getInstance(inLocale, INTEGERSTYLE);
|
||||
return getInstance(inLocale, null, INTEGERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -566,7 +569,7 @@ public abstract class NumberFormat extends Format {
|
|||
* @see java.util.Locale.Category#FORMAT
|
||||
*/
|
||||
public static final NumberFormat getCurrencyInstance() {
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), CURRENCYSTYLE);
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, CURRENCYSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -576,7 +579,7 @@ public abstract class NumberFormat extends Format {
|
|||
* @return the {@code NumberFormat} instance for currency formatting
|
||||
*/
|
||||
public static NumberFormat getCurrencyInstance(Locale inLocale) {
|
||||
return getInstance(inLocale, CURRENCYSTYLE);
|
||||
return getInstance(inLocale, null, CURRENCYSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -591,7 +594,7 @@ public abstract class NumberFormat extends Format {
|
|||
* @see java.util.Locale.Category#FORMAT
|
||||
*/
|
||||
public static final NumberFormat getPercentInstance() {
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), PERCENTSTYLE);
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, PERCENTSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -601,14 +604,14 @@ public abstract class NumberFormat extends Format {
|
|||
* @return the {@code NumberFormat} instance for percentage formatting
|
||||
*/
|
||||
public static NumberFormat getPercentInstance(Locale inLocale) {
|
||||
return getInstance(inLocale, PERCENTSTYLE);
|
||||
return getInstance(inLocale, null, PERCENTSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a scientific format for the current default locale.
|
||||
*/
|
||||
/*public*/ final static NumberFormat getScientificInstance() {
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), SCIENTIFICSTYLE);
|
||||
return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, SCIENTIFICSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -617,7 +620,50 @@ public abstract class NumberFormat extends Format {
|
|||
* @param inLocale the desired locale
|
||||
*/
|
||||
/*public*/ static NumberFormat getScientificInstance(Locale inLocale) {
|
||||
return getInstance(inLocale, SCIENTIFICSTYLE);
|
||||
return getInstance(inLocale, null, SCIENTIFICSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a compact number format for the default
|
||||
* {@link java.util.Locale.Category#FORMAT FORMAT} locale with
|
||||
* {@link NumberFormat.Style#SHORT "SHORT"} format style.
|
||||
*
|
||||
* @return A {@code NumberFormat} instance for compact number
|
||||
* formatting
|
||||
*
|
||||
* @see CompactNumberFormat
|
||||
* @see NumberFormat.Style
|
||||
* @see java.util.Locale#getDefault(java.util.Locale.Category)
|
||||
* @see java.util.Locale.Category#FORMAT
|
||||
* @since 12
|
||||
*/
|
||||
public static NumberFormat getCompactNumberInstance() {
|
||||
return getInstance(Locale.getDefault(
|
||||
Locale.Category.FORMAT), NumberFormat.Style.SHORT, COMPACTSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a compact number format for the specified {@link java.util.Locale locale}
|
||||
* and {@link NumberFormat.Style formatStyle}.
|
||||
*
|
||||
* @param locale the desired locale
|
||||
* @param formatStyle the style for formatting a number
|
||||
* @return A {@code NumberFormat} instance for compact number
|
||||
* formatting
|
||||
* @throws NullPointerException if {@code locale} or {@code formatStyle}
|
||||
* is {@code null}
|
||||
*
|
||||
* @see CompactNumberFormat
|
||||
* @see NumberFormat.Style
|
||||
* @see java.util.Locale
|
||||
* @since 12
|
||||
*/
|
||||
public static NumberFormat getCompactNumberInstance(Locale locale,
|
||||
NumberFormat.Style formatStyle) {
|
||||
|
||||
Objects.requireNonNull(locale);
|
||||
Objects.requireNonNull(formatStyle);
|
||||
return getInstance(locale, formatStyle, COMPACTSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -900,20 +946,22 @@ public abstract class NumberFormat extends Format {
|
|||
// =======================privates===============================
|
||||
|
||||
private static NumberFormat getInstance(Locale desiredLocale,
|
||||
int choice) {
|
||||
Style formatStyle, int choice) {
|
||||
LocaleProviderAdapter adapter;
|
||||
adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class,
|
||||
desiredLocale);
|
||||
NumberFormat numberFormat = getInstance(adapter, desiredLocale, choice);
|
||||
desiredLocale);
|
||||
NumberFormat numberFormat = getInstance(adapter, desiredLocale,
|
||||
formatStyle, choice);
|
||||
if (numberFormat == null) {
|
||||
numberFormat = getInstance(LocaleProviderAdapter.forJRE(),
|
||||
desiredLocale, choice);
|
||||
desiredLocale, formatStyle, choice);
|
||||
}
|
||||
return numberFormat;
|
||||
}
|
||||
|
||||
private static NumberFormat getInstance(LocaleProviderAdapter adapter,
|
||||
Locale locale, int choice) {
|
||||
Locale locale, Style formatStyle,
|
||||
int choice) {
|
||||
NumberFormatProvider provider = adapter.getNumberFormatProvider();
|
||||
NumberFormat numberFormat = null;
|
||||
switch (choice) {
|
||||
|
@ -929,6 +977,9 @@ public abstract class NumberFormat extends Format {
|
|||
case INTEGERSTYLE:
|
||||
numberFormat = provider.getIntegerInstance(locale);
|
||||
break;
|
||||
case COMPACTSTYLE:
|
||||
numberFormat = provider.getCompactNumberInstance(locale, formatStyle);
|
||||
break;
|
||||
}
|
||||
return numberFormat;
|
||||
}
|
||||
|
@ -1001,6 +1052,7 @@ public abstract class NumberFormat extends Format {
|
|||
private static final int PERCENTSTYLE = 2;
|
||||
private static final int SCIENTIFICSTYLE = 3;
|
||||
private static final int INTEGERSTYLE = 4;
|
||||
private static final int COMPACTSTYLE = 5;
|
||||
|
||||
/**
|
||||
* True if the grouping (i.e. thousands) separator is used when
|
||||
|
@ -1276,5 +1328,43 @@ public abstract class NumberFormat extends Format {
|
|||
* Constant identifying the exponent sign field.
|
||||
*/
|
||||
public static final Field EXPONENT_SIGN = new Field("exponent sign");
|
||||
|
||||
/**
|
||||
* Constant identifying the prefix field.
|
||||
*
|
||||
* @since 12
|
||||
*/
|
||||
public static final Field PREFIX = new Field("prefix");
|
||||
|
||||
/**
|
||||
* Constant identifying the suffix field.
|
||||
*
|
||||
* @since 12
|
||||
*/
|
||||
public static final Field SUFFIX = new Field("suffix");
|
||||
}
|
||||
|
||||
/**
|
||||
* A number format style.
|
||||
* <p>
|
||||
* {@code Style} is an enum which represents the style for formatting
|
||||
* a number within a given {@code NumberFormat} instance.
|
||||
*
|
||||
* @see CompactNumberFormat
|
||||
* @see NumberFormat#getCompactNumberInstance(Locale, Style)
|
||||
* @since 12
|
||||
*/
|
||||
public enum Style {
|
||||
|
||||
/**
|
||||
* The {@code SHORT} number format style.
|
||||
*/
|
||||
SHORT,
|
||||
|
||||
/**
|
||||
* The {@code LONG} number format style.
|
||||
*/
|
||||
LONG
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 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
|
||||
|
@ -110,4 +110,37 @@ public abstract class NumberFormatProvider extends LocaleServiceProvider {
|
|||
* @see java.text.NumberFormat#getPercentInstance(java.util.Locale)
|
||||
*/
|
||||
public abstract NumberFormat getPercentInstance(Locale locale);
|
||||
|
||||
/**
|
||||
* Returns a new {@code NumberFormat} instance which formats
|
||||
* a number in its compact form for the specified
|
||||
* {@code locale} and {@code formatStyle}.
|
||||
*
|
||||
* @implSpec The default implementation of this method throws
|
||||
* {@code UnSupportedOperationException}. Overriding the implementation
|
||||
* of this method returns the compact number formatter instance
|
||||
* of the given {@code locale} with specified {@code formatStyle}.
|
||||
*
|
||||
* @param locale the desired locale
|
||||
* @param formatStyle the style for formatting a number
|
||||
* @throws NullPointerException if {@code locale} or {@code formatStyle}
|
||||
* is {@code null}
|
||||
* @throws IllegalArgumentException if {@code locale} is not
|
||||
* one of the locales returned from
|
||||
* {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
|
||||
* getAvailableLocales()}.
|
||||
* @return a compact number formatter
|
||||
*
|
||||
* @see java.text.NumberFormat#getCompactNumberInstance(Locale,
|
||||
* NumberFormat.Style)
|
||||
* @since 12
|
||||
*/
|
||||
public NumberFormat getCompactNumberInstance(Locale locale,
|
||||
NumberFormat.Style formatStyle) {
|
||||
throw new UnsupportedOperationException(
|
||||
"The " + this.getClass().getName() + " should override this"
|
||||
+ " method to return compact number format instance of "
|
||||
+ locale + " locale and " + formatStyle + " style.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue