mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
ICU-13060 Assorted test cases, @internal tags, and fixes for DecimalFormat.
X-SVN-Rev: 39908
This commit is contained in:
parent
e5ea539acf
commit
a47756d190
15 changed files with 545 additions and 224 deletions
|
@ -13,13 +13,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.TextTrieMap;
|
||||
import com.ibm.icu.impl.number.Parse.ParseMode;
|
||||
import com.ibm.icu.impl.number.formatters.BigDecimalMultiplier;
|
||||
import com.ibm.icu.impl.number.formatters.CurrencyFormat;
|
||||
import com.ibm.icu.impl.number.formatters.MagnitudeMultiplier;
|
||||
import com.ibm.icu.impl.number.formatters.PaddingFormat;
|
||||
import com.ibm.icu.impl.number.formatters.PositiveDecimalFormat;
|
||||
import com.ibm.icu.impl.number.formatters.PositiveNegativeAffixFormat;
|
||||
import com.ibm.icu.impl.number.formatters.ScientificFormat;
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.text.CurrencyPluralInfo;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
|
@ -102,7 +102,8 @@ public class Parse {
|
|||
CurrencyFormat.ICurrencyProperties,
|
||||
BigDecimalMultiplier.IProperties,
|
||||
MagnitudeMultiplier.IProperties,
|
||||
PositiveDecimalFormat.IProperties {
|
||||
PositiveDecimalFormat.IProperties,
|
||||
ScientificFormat.IProperties {
|
||||
|
||||
boolean DEFAULT_PARSE_INTEGER_ONLY = false;
|
||||
|
||||
|
@ -199,7 +200,7 @@ public class Parse {
|
|||
}
|
||||
|
||||
/**
|
||||
* @see #parse(String, ParsePosition, ParseMode, boolean, boolean, IProperties,
|
||||
* @see Parse#parse(String, ParsePosition, ParseMode, boolean, boolean, IProperties,
|
||||
* DecimalFormatSymbols)
|
||||
*/
|
||||
private static enum StateName {
|
||||
|
@ -303,6 +304,7 @@ public class Parse {
|
|||
boolean sawPrefix;
|
||||
boolean sawSuffix;
|
||||
boolean sawDecimalPoint;
|
||||
boolean sawExponentDigit;
|
||||
|
||||
// Data for intermediate parsing steps:
|
||||
StateName returnTo1;
|
||||
|
@ -310,6 +312,7 @@ public class Parse {
|
|||
// For string literals:
|
||||
CharSequence currentString;
|
||||
int currentOffset;
|
||||
boolean currentTrailing;
|
||||
// For affix patterns:
|
||||
CharSequence currentAffixPattern;
|
||||
long currentStepwiseParserTag;
|
||||
|
@ -349,12 +352,14 @@ public class Parse {
|
|||
sawPrefix = false;
|
||||
sawSuffix = false;
|
||||
sawDecimalPoint = false;
|
||||
sawExponentDigit = false;
|
||||
|
||||
// Data for intermediate parsing steps:
|
||||
returnTo1 = null;
|
||||
returnTo2 = null;
|
||||
currentString = null;
|
||||
currentOffset = 0;
|
||||
currentTrailing = false;
|
||||
currentAffixPattern = null;
|
||||
currentStepwiseParserTag = 0L;
|
||||
currentCurrencyTrieState = null;
|
||||
|
@ -404,12 +409,14 @@ public class Parse {
|
|||
sawPrefix = other.sawPrefix;
|
||||
sawSuffix = other.sawSuffix;
|
||||
sawDecimalPoint = other.sawDecimalPoint;
|
||||
sawExponentDigit = other.sawExponentDigit;
|
||||
|
||||
// Data for intermediate parsing steps:
|
||||
returnTo1 = other.returnTo1;
|
||||
returnTo2 = other.returnTo2;
|
||||
currentString = other.currentString;
|
||||
currentOffset = other.currentOffset;
|
||||
currentTrailing = other.currentTrailing;
|
||||
currentAffixPattern = other.currentAffixPattern;
|
||||
currentStepwiseParserTag = other.currentStepwiseParserTag;
|
||||
currentCurrencyTrieState = other.currentCurrencyTrieState;
|
||||
|
@ -427,6 +434,7 @@ public class Parse {
|
|||
*/
|
||||
void appendDigit(byte digit, DigitType type) {
|
||||
if (type == DigitType.EXPONENT) {
|
||||
sawExponentDigit = true;
|
||||
int newExponent = exponent * 10 + digit;
|
||||
if (newExponent < exponent) {
|
||||
// overflow
|
||||
|
@ -606,7 +614,11 @@ public class Parse {
|
|||
int groupingCp2;
|
||||
SeparatorType decimalType1;
|
||||
SeparatorType decimalType2;
|
||||
// TODO(sffc): Remove this field if it is not necessary.
|
||||
@SuppressWarnings("unused")
|
||||
SeparatorType groupingType1;
|
||||
// TODO(sffc): Remove this field if it is not necessary.
|
||||
@SuppressWarnings("unused")
|
||||
SeparatorType groupingType2;
|
||||
TextTrieMap<Byte> digitTrie;
|
||||
Set<AffixHolder> affixHolders = new HashSet<AffixHolder>();
|
||||
|
@ -1299,6 +1311,12 @@ public class Parse {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Check for scientific notation.
|
||||
if (properties.getMinimumExponentDigits() > 0 && !item.sawExponentDigit) {
|
||||
if (DEBUGGING) System.out.println("-> reject due to lack of exponent");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that grouping sizes are valid.
|
||||
int grouping1 = properties.getGroupingSize();
|
||||
int grouping2 = properties.getSecondaryGroupingSize();
|
||||
|
@ -1621,7 +1639,7 @@ public class Parse {
|
|||
|
||||
private static void acceptNan(int cp, StateName nextName, ParserState state, StateItem item) {
|
||||
CharSequence nan = state.symbols.getNaN();
|
||||
long added = acceptString(cp, nextName, null, state, item, nan, 0);
|
||||
long added = acceptString(cp, nextName, null, state, item, nan, 0, false);
|
||||
|
||||
// Set state in the items that were added by the function call
|
||||
for (int i = Long.numberOfTrailingZeros(added); (1L << i) <= added; i++) {
|
||||
|
@ -1634,7 +1652,7 @@ public class Parse {
|
|||
private static void acceptInfinity(
|
||||
int cp, StateName nextName, ParserState state, StateItem item) {
|
||||
CharSequence inf = state.symbols.getInfinity();
|
||||
long added = acceptString(cp, nextName, null, state, item, inf, 0);
|
||||
long added = acceptString(cp, nextName, null, state, item, inf, 0, false);
|
||||
|
||||
// Set state in the items that were added by the function call
|
||||
for (int i = Long.numberOfTrailingZeros(added); (1L << i) <= added; i++) {
|
||||
|
@ -1647,7 +1665,7 @@ public class Parse {
|
|||
private static void acceptExponentSeparator(
|
||||
int cp, StateName nextName, ParserState state, StateItem item) {
|
||||
CharSequence exp = state.symbols.getExponentSeparator();
|
||||
acceptString(cp, nextName, null, state, item, exp, 0);
|
||||
acceptString(cp, nextName, null, state, item, exp, 0, true);
|
||||
}
|
||||
|
||||
private static void acceptPrefix(int cp, StateName nextName, ParserState state, StateItem item) {
|
||||
|
@ -1676,7 +1694,7 @@ public class Parse {
|
|||
if (holder == null) return;
|
||||
String str = prefix ? holder.p : holder.s;
|
||||
if (holder.strings) {
|
||||
long added = acceptString(cp, nextName, null, state, item, str, 0);
|
||||
long added = acceptString(cp, nextName, null, state, item, str, 0, false);
|
||||
// At most one item can be added upon consuming a string.
|
||||
if (added != 0) {
|
||||
int i = state.lastInsertedIndex();
|
||||
|
@ -1705,7 +1723,14 @@ public class Parse {
|
|||
|
||||
private static void acceptStringOffset(int cp, ParserState state, StateItem item) {
|
||||
acceptString(
|
||||
cp, item.returnTo1, item.returnTo2, state, item, item.currentString, item.currentOffset);
|
||||
cp,
|
||||
item.returnTo1,
|
||||
item.returnTo2,
|
||||
state,
|
||||
item,
|
||||
item.currentString,
|
||||
item.currentOffset,
|
||||
item.currentTrailing);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1719,6 +1744,8 @@ public class Parse {
|
|||
* @param returnTo1 The state to return to after reaching the end of the string.
|
||||
* @param returnTo2 The state to save in <code>returnTo1</code> after reaching the end of the
|
||||
* string. Set to null if returning to the main state loop.
|
||||
* @param trailing true if this string should be ignored for the purposes of recording trailing
|
||||
* code points; false if it trailing count should be reset after reading the string.
|
||||
* @param state The current {@link ParserState}
|
||||
* @param item The current {@link StateItem}
|
||||
* @param str The string against which to check for a match.
|
||||
|
@ -1733,7 +1760,8 @@ public class Parse {
|
|||
ParserState state,
|
||||
StateItem item,
|
||||
CharSequence str,
|
||||
int offset) {
|
||||
int offset,
|
||||
boolean trailing) {
|
||||
if (str == null || str.length() == 0) return 0L;
|
||||
|
||||
// Fast path for fast mode
|
||||
|
@ -1771,10 +1799,11 @@ public class Parse {
|
|||
next.returnTo2 = returnTo2;
|
||||
next.currentString = str;
|
||||
next.currentOffset = offset;
|
||||
next.currentTrailing = trailing;
|
||||
} else {
|
||||
// We've reached the end of the string.
|
||||
next.name = returnTo1;
|
||||
next.trailingCount = 0;
|
||||
if (!trailing) next.trailingCount = 0;
|
||||
next.returnTo1 = returnTo2;
|
||||
next.returnTo2 = null;
|
||||
}
|
||||
|
@ -1832,9 +1861,11 @@ public class Parse {
|
|||
resolvedPlusSign = true;
|
||||
break;
|
||||
case AffixPatternUtils.TYPE_PERCENT:
|
||||
resolvedCp = '%'; // accept ASCII percent as well as locale percent
|
||||
resolvedStr = state.symbols.getPercentString();
|
||||
break;
|
||||
case AffixPatternUtils.TYPE_PERMILLE:
|
||||
resolvedCp = '‰'; // accept ASCII permille as well as locale permille
|
||||
resolvedStr = state.symbols.getPerMillString();
|
||||
break;
|
||||
case AffixPatternUtils.TYPE_CURRENCY_SINGLE:
|
||||
|
@ -1905,9 +1936,10 @@ public class Parse {
|
|||
// String symbol
|
||||
if (hasNext) {
|
||||
added |=
|
||||
acceptString(cp, StateName.INSIDE_AFFIX_PATTERN, returnTo, state, item, resolvedStr, 0);
|
||||
acceptString(
|
||||
cp, StateName.INSIDE_AFFIX_PATTERN, returnTo, state, item, resolvedStr, 0, false);
|
||||
} else {
|
||||
added |= acceptString(cp, returnTo, null, state, item, resolvedStr, 0);
|
||||
added |= acceptString(cp, returnTo, null, state, item, resolvedStr, 0, false);
|
||||
}
|
||||
}
|
||||
if (resolvedCurrency) {
|
||||
|
@ -1965,8 +1997,8 @@ public class Parse {
|
|||
str1 = state.symbols.getCurrencySymbol();
|
||||
str2 = state.symbols.getInternationalCurrencySymbol();
|
||||
}
|
||||
added |= acceptString(cp, returnTo1, returnTo2, state, item, str1, 0);
|
||||
added |= acceptString(cp, returnTo1, returnTo2, state, item, str2, 0);
|
||||
added |= acceptString(cp, returnTo1, returnTo2, state, item, str1, 0, false);
|
||||
added |= acceptString(cp, returnTo1, returnTo2, state, item, str2, 0, false);
|
||||
for (int i = Long.numberOfTrailingZeros(added); (1L << i) <= added; i++) {
|
||||
if (((1L << i) & added) != 0) {
|
||||
state.getItem(i).sawCurrency = true;
|
||||
|
|
|
@ -556,7 +556,6 @@ public class PatternString {
|
|||
int exponentDigits = 0;
|
||||
boolean hasPercentSign = false;
|
||||
boolean hasPerMilleSign = false;
|
||||
boolean hasCurrencySign = false;
|
||||
|
||||
StringBuilder padding = new StringBuilder();
|
||||
StringBuilder prefix = new StringBuilder();
|
||||
|
@ -690,7 +689,7 @@ public class PatternString {
|
|||
break;
|
||||
|
||||
case '¤':
|
||||
result.hasCurrencySign = true;
|
||||
// no need to record that we saw it
|
||||
break;
|
||||
}
|
||||
consumeLiteral(state, destination);
|
||||
|
|
|
@ -617,7 +617,7 @@ public class Properties
|
|||
int count = ois.readInt();
|
||||
|
||||
// 2) Read each field by its name and value
|
||||
for (int i=0; i<count; i++) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = (String) ois.readObject();
|
||||
Object value = ois.readObject();
|
||||
|
||||
|
@ -917,6 +917,16 @@ public class Properties
|
|||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("<Properties");
|
||||
toStringBare(result);
|
||||
result.append(">");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string containing properties that differ from the default, but without being
|
||||
* surrounded by <Properties>.
|
||||
*/
|
||||
public void toStringBare(StringBuilder result) {
|
||||
Field[] fields = Properties.class.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
Object myValue, defaultValue;
|
||||
|
@ -938,8 +948,6 @@ public class Properties
|
|||
result.append(" " + field.getName() + ":" + myValue);
|
||||
}
|
||||
}
|
||||
result.append(">");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -230,24 +230,6 @@ public abstract class Rounder extends Format.BeforeFormat {
|
|||
input.setIntegerFractionLength(minInt, maxInt, minFrac, maxFrac);
|
||||
}
|
||||
|
||||
private static final ThreadLocal<Properties> threadLocalProperties =
|
||||
new ThreadLocal<Properties>() {
|
||||
@Override
|
||||
protected Properties initialValue() {
|
||||
return new Properties();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a thread-local property bag that can be used to deliver properties to a constructor.
|
||||
* Rounders themselves are guaranteed to not internally use a copy of this property bag.
|
||||
*
|
||||
* @return A clean, thread-local property bag.
|
||||
*/
|
||||
public static Properties getThreadLocalProperties() {
|
||||
return threadLocalProperties.get().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before(FormatQuantity input, ModifierHolder mods) {
|
||||
apply(input);
|
||||
|
|
|
@ -4,6 +4,7 @@ package com.ibm.icu.impl.number.formatters;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import com.ibm.icu.impl.ICUData;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
|
@ -67,16 +68,45 @@ public class CompactDecimalFormat extends Format.BeforeFormat {
|
|||
return new CompactDecimalFormat(symbols, properties);
|
||||
}
|
||||
|
||||
private static final int DEFAULT_MIN_SIG = 1;
|
||||
private static final int DEFAULT_MAX_SIG = 2;
|
||||
private static final SignificantDigitsMode DEFAULT_SIG_MODE =
|
||||
SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION;
|
||||
|
||||
private static final ThreadLocal<Properties> threadLocalProperties =
|
||||
new ThreadLocal<Properties>() {
|
||||
@Override
|
||||
protected Properties initialValue() {
|
||||
return new Properties();
|
||||
}
|
||||
};
|
||||
|
||||
private static Rounder getRounder(IProperties properties) {
|
||||
// Use rounding settings if they were specified, or else use the default CDF rounder.
|
||||
Rounder rounder = RoundingFormat.getDefaultOrNull(properties);
|
||||
// TODO: Detecting and overriding significant digits here is a bit of a hack, since detection
|
||||
// is also performed in the "RoundingFormat.getDefaultOrNull" method.
|
||||
// It would be more elegant to call some sort of "fallback" copy method.
|
||||
Rounder rounder = null;
|
||||
if (!SignificantDigitsRounder.useSignificantDigits(properties)) {
|
||||
rounder = RoundingFormat.getDefaultOrNull(properties);
|
||||
}
|
||||
if (rounder == null) {
|
||||
rounder =
|
||||
SignificantDigitsRounder.getInstance(
|
||||
SignificantDigitsRounder.getThreadLocalProperties()
|
||||
.setMinimumSignificantDigits(1)
|
||||
.setMaximumSignificantDigits(2)
|
||||
.setSignificantDigitsMode(SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION));
|
||||
int _minSig = properties.getMinimumSignificantDigits();
|
||||
int _maxSig = properties.getMaximumSignificantDigits();
|
||||
SignificantDigitsMode _mode = properties.getSignificantDigitsMode();
|
||||
Properties rprops = threadLocalProperties.get().clear();
|
||||
// Settings needing possible override:
|
||||
rprops.setMinimumSignificantDigits(_minSig > 0 ? _minSig : DEFAULT_MIN_SIG);
|
||||
rprops.setMaximumSignificantDigits(_maxSig > 0 ? _maxSig : DEFAULT_MAX_SIG);
|
||||
rprops.setSignificantDigitsMode(_mode != null ? _mode : DEFAULT_SIG_MODE);
|
||||
// TODO: Should copyFrom() be used instead? It requires a cast.
|
||||
// Settings to copy verbatim:
|
||||
rprops.setRoundingMode(properties.getRoundingMode());
|
||||
rprops.setMinimumFractionDigits(properties.getMinimumFractionDigits());
|
||||
rprops.setMaximumFractionDigits(properties.getMaximumFractionDigits());
|
||||
rprops.setMinimumIntegerDigits(properties.getMinimumIntegerDigits());
|
||||
rprops.setMaximumIntegerDigits(properties.getMaximumIntegerDigits());
|
||||
rounder = SignificantDigitsRounder.getInstance(rprops);
|
||||
}
|
||||
return rounder;
|
||||
}
|
||||
|
@ -101,17 +131,31 @@ public class CompactDecimalFormat extends Format.BeforeFormat {
|
|||
ULocale ulocale = symbols.getULocale();
|
||||
CompactDecimalDataSink sink = new CompactDecimalDataSink(data, symbols, fingerprint);
|
||||
String nsName = NumberingSystem.getInstance(ulocale).getName();
|
||||
ICUResourceBundle r =
|
||||
ICUResourceBundle rb =
|
||||
(ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, ulocale);
|
||||
r.getAllItemsWithFallback("NumberElements/" + nsName, sink);
|
||||
internalPopulateData(nsName, rb, sink, data);
|
||||
if (data.isEmpty() && fingerprint.compactStyle == CompactStyle.LONG) {
|
||||
// No long data is available; load short data instead
|
||||
sink.compactStyle = CompactStyle.SHORT;
|
||||
internalPopulateData(nsName, rb, sink, data);
|
||||
}
|
||||
threadLocalDataCache.get().put(fingerprint, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static void internalPopulateData(
|
||||
String nsName, ICUResourceBundle rb, CompactDecimalDataSink sink, CompactDecimalData data) {
|
||||
try {
|
||||
rb.getAllItemsWithFallback("NumberElements/" + nsName, sink);
|
||||
} catch (MissingResourceException e) {
|
||||
// Fall back to latn
|
||||
}
|
||||
if (data.isEmpty() && !nsName.equals("latn")) {
|
||||
r.getAllItemsWithFallback("NumberElements/latn", sink);
|
||||
rb.getAllItemsWithFallback("NumberElements/latn", sink);
|
||||
}
|
||||
if (sink.exception != null) {
|
||||
throw sink.exception;
|
||||
}
|
||||
threadLocalDataCache.get().put(fingerprint, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static PositiveNegativeModifier getDefaultMod(
|
||||
|
@ -302,7 +346,7 @@ public class CompactDecimalFormat extends Format.BeforeFormat {
|
|||
currencySymbol = CurrencyFormat.getCurrencySymbol(symbols, properties);
|
||||
} else {
|
||||
compactType = CompactType.DECIMAL;
|
||||
currencySymbol = symbols.getCurrencySymbol(); // fallback; should remain unused
|
||||
currencySymbol = ""; // fallback; should remain unused
|
||||
}
|
||||
compactStyle = properties.getCompactStyle();
|
||||
uloc = symbols.getULocale();
|
||||
|
@ -337,12 +381,12 @@ public class CompactDecimalFormat extends Format.BeforeFormat {
|
|||
|
||||
private static final class CompactDecimalDataSink extends UResource.Sink {
|
||||
|
||||
final CompactDecimalData data;
|
||||
final DecimalFormatSymbols symbols;
|
||||
final CompactStyle compactStyle;
|
||||
final CompactType compactType;
|
||||
final String currencySymbol;
|
||||
final PNAffixGenerator pnag;
|
||||
CompactDecimalData data;
|
||||
DecimalFormatSymbols symbols;
|
||||
CompactStyle compactStyle;
|
||||
CompactType compactType;
|
||||
String currencySymbol;
|
||||
PNAffixGenerator pnag;
|
||||
IllegalArgumentException exception;
|
||||
|
||||
/*
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.ibm.icu.impl.number.Rounder;
|
|||
import com.ibm.icu.impl.number.modifiers.GeneralPluralModifier;
|
||||
import com.ibm.icu.impl.number.rounders.IncrementRounder;
|
||||
import com.ibm.icu.impl.number.rounders.MagnitudeRounder;
|
||||
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
|
||||
import com.ibm.icu.text.CurrencyPluralInfo;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.Currency;
|
||||
|
@ -288,6 +289,9 @@ public class CurrencyFormat {
|
|||
};
|
||||
|
||||
public static Rounder getCurrencyRounder(DecimalFormatSymbols symbols, IProperties properties) {
|
||||
if (SignificantDigitsRounder.useSignificantDigits(properties)) {
|
||||
return SignificantDigitsRounder.getInstance(properties);
|
||||
}
|
||||
Properties cprops = threadLocalProperties.get().clear();
|
||||
populateCurrencyRounderProperties(cprops, symbols, properties);
|
||||
if (cprops.getRoundingIncrement() != null) {
|
||||
|
|
|
@ -86,12 +86,13 @@ public class PositiveDecimalFormat implements Format.TargetFormat {
|
|||
}
|
||||
|
||||
public static boolean allowsDecimalPoint(IProperties properties) {
|
||||
return properties.getDecimalSeparatorAlwaysShown() || properties.getMaximumFractionDigits() != 0;
|
||||
return properties.getDecimalSeparatorAlwaysShown()
|
||||
|| properties.getMaximumFractionDigits() != 0;
|
||||
}
|
||||
|
||||
// Properties
|
||||
private final boolean alwaysShowDecimal;
|
||||
private final int groupingSize;
|
||||
private final int primaryGroupingSize;
|
||||
private final int secondaryGroupingSize;
|
||||
private final int minimumGroupingDigits;
|
||||
|
||||
|
@ -104,14 +105,10 @@ public class PositiveDecimalFormat implements Format.TargetFormat {
|
|||
private final int codePointZero;
|
||||
|
||||
public PositiveDecimalFormat(DecimalFormatSymbols symbols, IProperties properties) {
|
||||
groupingSize =
|
||||
(properties.getGroupingSize() < 0)
|
||||
? properties.getSecondaryGroupingSize()
|
||||
: properties.getGroupingSize();
|
||||
secondaryGroupingSize =
|
||||
(properties.getSecondaryGroupingSize() < 0)
|
||||
? properties.getGroupingSize()
|
||||
: properties.getSecondaryGroupingSize();
|
||||
int _primary = properties.getGroupingSize();
|
||||
int _secondary = properties.getSecondaryGroupingSize();
|
||||
primaryGroupingSize = _primary > 0 ? _primary : _secondary > 0 ? _secondary : 0;
|
||||
secondaryGroupingSize = _secondary > 0 ? _secondary : primaryGroupingSize;
|
||||
|
||||
minimumGroupingDigits = properties.getMinimumGroupingDigits();
|
||||
alwaysShowDecimal = properties.getDecimalSeparatorAlwaysShown();
|
||||
|
@ -167,7 +164,9 @@ public class PositiveDecimalFormat implements Format.TargetFormat {
|
|||
|
||||
// Add the decimal point
|
||||
if (input.getLowerDisplayMagnitude() < 0 || alwaysShowDecimal) {
|
||||
length += string.insert(startIndex + length, decimalSeparator, NumberFormat.Field.DECIMAL_SEPARATOR);
|
||||
length +=
|
||||
string.insert(
|
||||
startIndex + length, decimalSeparator, NumberFormat.Field.DECIMAL_SEPARATOR);
|
||||
}
|
||||
|
||||
// Add the fraction digits
|
||||
|
@ -182,12 +181,16 @@ public class PositiveDecimalFormat implements Format.TargetFormat {
|
|||
int integerCount = input.getUpperDisplayMagnitude() + 1;
|
||||
for (int i = 0; i < integerCount; i++) {
|
||||
// Add grouping separator
|
||||
if (groupingSize > 0 && i == groupingSize && integerCount - i >= minimumGroupingDigits) {
|
||||
length += string.insert(startIndex, groupingSeparator, NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
if (primaryGroupingSize > 0
|
||||
&& i == primaryGroupingSize
|
||||
&& integerCount - i >= minimumGroupingDigits) {
|
||||
length +=
|
||||
string.insert(startIndex, groupingSeparator, NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
} else if (secondaryGroupingSize > 0
|
||||
&& i > groupingSize
|
||||
&& (i - groupingSize) % secondaryGroupingSize == 0) {
|
||||
length += string.insert(startIndex, groupingSeparator, NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
&& i > primaryGroupingSize
|
||||
&& (i - primaryGroupingSize) % secondaryGroupingSize == 0) {
|
||||
length +=
|
||||
string.insert(startIndex, groupingSeparator, NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
}
|
||||
|
||||
// Get and append the next digit value
|
||||
|
@ -219,9 +222,13 @@ public class PositiveDecimalFormat implements Format.TargetFormat {
|
|||
|
||||
@Override
|
||||
public void export(Properties properties) {
|
||||
// For backwards compatibility, export 0 as secondary grouping if primary and secondary are the same
|
||||
int effectiveSecondaryGroupingSize =
|
||||
secondaryGroupingSize == primaryGroupingSize ? 0 : secondaryGroupingSize;
|
||||
|
||||
properties.setDecimalSeparatorAlwaysShown(alwaysShowDecimal);
|
||||
properties.setGroupingSize(groupingSize);
|
||||
properties.setSecondaryGroupingSize(secondaryGroupingSize);
|
||||
properties.setGroupingSize(primaryGroupingSize);
|
||||
properties.setSecondaryGroupingSize(effectiveSecondaryGroupingSize);
|
||||
properties.setMinimumGroupingDigits(minimumGroupingDigits);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,19 +9,11 @@
|
|||
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.NotSerializableException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.ibm.icu.impl.number.FormatQuantity4;
|
||||
import com.ibm.icu.impl.number.Properties;
|
||||
import com.ibm.icu.util.CurrencyAmount;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
|
@ -55,7 +47,7 @@ import com.ibm.icu.util.ULocale;
|
|||
*/
|
||||
public class CompactDecimalFormat extends DecimalFormat {
|
||||
|
||||
private static final long serialVersionUID = 4716293295276629682L;
|
||||
private static final long serialVersionUID = 4716293295276629682L;
|
||||
|
||||
/**
|
||||
* Style parameter for CompactDecimalFormat.
|
||||
|
@ -122,109 +114,6 @@ public class CompactDecimalFormat extends DecimalFormat {
|
|||
refreshFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
formatter.format(fq, toAppendTo, pos);
|
||||
fq.populateUFieldPosition(pos);
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 50
|
||||
*/
|
||||
@Override
|
||||
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
|
||||
if (!(obj instanceof Number)) throw new IllegalArgumentException();
|
||||
Number number = (Number) obj;
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
AttributedCharacterIterator result = formatter.formatToCharacterIterator(fq);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
formatter.format(fq, toAppendTo, pos);
|
||||
fq.populateUFieldPosition(pos);
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
formatter.format(fq, toAppendTo, pos);
|
||||
fq.populateUFieldPosition(pos);
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
formatter.format(fq, toAppendTo, pos);
|
||||
fq.populateUFieldPosition(pos);
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(
|
||||
com.ibm.icu.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number.toBigDecimal());
|
||||
formatter.format(fq, toAppendTo, pos);
|
||||
fq.populateUFieldPosition(pos);
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * {@inheritDoc}
|
||||
// *
|
||||
// * @internal ICU 57 technology preview
|
||||
// * @deprecated This API might change or be removed in a future release.
|
||||
// */
|
||||
// @Override
|
||||
// @Deprecated
|
||||
// public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
// // TODO(sffc)
|
||||
// throw new UnsupportedOperationException();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Parsing is currently unsupported, and throws an UnsupportedOperationException.
|
||||
*
|
||||
|
@ -235,13 +124,13 @@ public class CompactDecimalFormat extends DecimalFormat {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// DISALLOW Serialization, at least while draft
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
throw new NotSerializableException();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException {
|
||||
throw new NotSerializableException();
|
||||
/**
|
||||
* Parsing is currently unsupported, and throws an UnsupportedOperationException.
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public CurrencyAmount parseCurrency(CharSequence text, ParsePosition parsePosition) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -418,7 +418,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
// FORMAT AND PARSE APIS //
|
||||
//=====================================================================================//
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
|
@ -427,7 +431,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
|
@ -436,7 +444,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number);
|
||||
|
@ -445,7 +457,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(
|
||||
java.math.BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
|
@ -455,7 +471,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
|
||||
FormatQuantity4 fq = new FormatQuantity4(number.toBigDecimal());
|
||||
|
@ -464,7 +484,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** @stable ICU 3.6 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 3.6
|
||||
*/
|
||||
@Override
|
||||
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
|
||||
if (!(obj instanceof Number)) throw new IllegalArgumentException();
|
||||
|
@ -474,7 +498,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected static final ThreadLocal<Properties> threadLocalCurrencyProperties =
|
||||
private static final ThreadLocal<Properties> threadLocalCurrencyProperties =
|
||||
new ThreadLocal<Properties>() {
|
||||
@Override
|
||||
protected Properties initialValue() {
|
||||
|
@ -482,24 +506,42 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
@Override
|
||||
public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
// TODO: This is ugly (although not as ugly as it was in ICU 58).
|
||||
// Currency should be a free parameter, not in property bag. Fix in ICU 60.
|
||||
Properties cprops = threadLocalCurrencyProperties.get();
|
||||
SingularFormat fmt = null;
|
||||
synchronized (this) {
|
||||
cprops.copyFrom(properties);
|
||||
// Use the pre-compiled formatter if possible. Otherwise, copy the properties
|
||||
// and build our own formatter.
|
||||
// TODO: Consider using a static format path here.
|
||||
if (currAmt.getCurrency().equals(properties.getCurrency())) {
|
||||
fmt = formatter;
|
||||
} else {
|
||||
cprops.copyFrom(properties);
|
||||
}
|
||||
}
|
||||
if (fmt == null) {
|
||||
cprops.setCurrency(currAmt.getCurrency());
|
||||
fmt = Endpoint.fromBTA(cprops, symbols);
|
||||
}
|
||||
cprops.setCurrency(currAmt.getCurrency());
|
||||
FormatQuantity4 fq = new FormatQuantity4(currAmt.getNumber());
|
||||
// TODO: Use a static format path here
|
||||
SingularFormat fmt = Endpoint.fromBTA(cprops, symbols);
|
||||
fmt.format(fq, toAppendTo, pos);
|
||||
fq.populateUFieldPosition(pos);
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public Number parse(String text, ParsePosition parsePosition) {
|
||||
// Backwards compatibility: use currency parse mode if this is a currency instance
|
||||
|
@ -511,7 +553,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return result;
|
||||
}
|
||||
|
||||
/** @stable ICU 49 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 49
|
||||
*/
|
||||
@Override
|
||||
public CurrencyAmount parseCurrency(CharSequence text, ParsePosition parsePosition) {
|
||||
try {
|
||||
|
@ -1922,7 +1968,11 @@ public class DecimalFormat extends NumberFormat {
|
|||
return properties.equals(other.properties) && symbols.equals(other.symbols);
|
||||
}
|
||||
|
||||
/** @stable ICU 2.0 */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return properties.hashCode();
|
||||
|
@ -1936,9 +1986,26 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the default value of toString() with extra DecimalFormat-specific information appended
|
||||
* to the end of the string. This extra information is intended for debugging purposes, and the
|
||||
* format is not guaranteed to be stable.
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return "<DecimalFormat " + symbols.toString() + " " + properties.toString() + ">";
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(getClass().getName());
|
||||
result.append("@");
|
||||
result.append(Integer.toHexString(hashCode()));
|
||||
result.append(" { symbols@");
|
||||
result.append(Integer.toHexString(symbols.hashCode()));
|
||||
synchronized (this) {
|
||||
properties.toStringBare(result);
|
||||
}
|
||||
result.append(" }");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2027,7 +2094,17 @@ public class DecimalFormat extends NumberFormat {
|
|||
refreshFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static interface PropertySetter {
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public void set(Properties props);
|
||||
}
|
||||
|
||||
|
|
|
@ -1044,7 +1044,8 @@ public abstract class NumberFormat extends UFormat {
|
|||
// ===== End of factory stuff =====
|
||||
|
||||
/**
|
||||
* Overrides hashCode.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
@Override
|
||||
|
|
|
@ -418,18 +418,73 @@ public class PluralRules implements Serializable {
|
|||
*/
|
||||
@Deprecated
|
||||
public static enum Operand {
|
||||
/** The double value of the entire number. */
|
||||
/**
|
||||
* The double value of the entire number.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
n,
|
||||
/** The integer value, with the fraction digits truncated off. */
|
||||
|
||||
/**
|
||||
* The integer value, with the fraction digits truncated off.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
i,
|
||||
/** All visible fraction digits as an integer, including trailing zeros. */
|
||||
|
||||
/**
|
||||
* All visible fraction digits as an integer, including trailing zeros.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
f,
|
||||
/** Visible fraction digits, not including trailing zeros. */
|
||||
|
||||
/**
|
||||
* Visible fraction digits as an integer, not including trailing zeros.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
t,
|
||||
/** Number of visible fraction digits. */
|
||||
|
||||
/**
|
||||
* Number of visible fraction digits.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
v,
|
||||
|
||||
/**
|
||||
* Number of visible fraction digits, not including trailing zeros.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
w,
|
||||
/* deprecated */
|
||||
|
||||
/**
|
||||
* THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC.
|
||||
*
|
||||
* <p>Returns the integer value, but will fail if the number has fraction digits.
|
||||
* That is, using "j" instead of "i" is like implicitly adding "v is 0".
|
||||
*
|
||||
* <p>For example, "j is 3" is equivalent to "i is 3 and v is 0": it matches
|
||||
* "3" but not "3.1" or "3.0".
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
j;
|
||||
}
|
||||
|
||||
|
@ -439,8 +494,28 @@ public class PluralRules implements Serializable {
|
|||
*/
|
||||
@Deprecated
|
||||
public static interface IFixedDecimal {
|
||||
/**
|
||||
* Returns the value corresponding to the specified operand (n, i, f, t, v, or w).
|
||||
* If the operand is 'n', returns a double; otherwise, returns an integer.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public double getPluralOperand(Operand operand);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isNaN();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isInfinite();
|
||||
}
|
||||
|
||||
|
@ -744,6 +819,8 @@ public class PluralRules implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
|
@ -751,12 +828,13 @@ public class PluralRules implements Serializable {
|
|||
@Deprecated
|
||||
public double getPluralOperand(Operand operand) {
|
||||
switch(operand) {
|
||||
default: return source;
|
||||
case n: return source;
|
||||
case i: return integerValue;
|
||||
case f: return decimalDigits;
|
||||
case t: return decimalDigitsWithoutTrailingZeros;
|
||||
case v: return visibleDecimalDigitCount;
|
||||
case w: return visibleDecimalDigitCountWithoutTrailingZeros;
|
||||
default: return source;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -904,17 +982,25 @@ public class PluralRules implements Serializable {
|
|||
throw new NotSerializableException();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.PluralRules.IFixedDecimal#isNaN()
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean isNaN() {
|
||||
return Double.isNaN(source);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.PluralRules.IFixedDecimal#isInfinite()
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean isInfinite() {
|
||||
return Double.isInfinite(source);
|
||||
|
|
|
@ -762,15 +762,30 @@ public class Currency extends MeasureUnit {
|
|||
private String isoCode;
|
||||
private String currencyString;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public CurrencyStringInfo(String isoCode, String currencyString) {
|
||||
this.isoCode = isoCode;
|
||||
this.currencyString = currencyString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public String getISOCode() {
|
||||
return isoCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unused")
|
||||
public String getCurrencyString() {
|
||||
return currencyString;
|
||||
|
|
|
@ -850,6 +850,17 @@ parse output breaks
|
|||
// JDK parses as -1945
|
||||
(1,945d1) fail K
|
||||
|
||||
test parse strict scientific
|
||||
set locale en
|
||||
set pattern #E0
|
||||
set lenient 0
|
||||
begin
|
||||
parse output breaks
|
||||
123 fail JK
|
||||
123E1 1230
|
||||
123E0 123
|
||||
123E fail JK
|
||||
|
||||
test parse strict without prefix/suffix
|
||||
set locale en
|
||||
set pattern #
|
||||
|
|
|
@ -553,6 +553,66 @@ public class CompactDecimalFormatTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestLongShortFallback() {
|
||||
// smn, dz have long but not short
|
||||
// es_US, es_GT, es_419, ee have short but not long
|
||||
ULocale[] locs = new ULocale[] {
|
||||
new ULocale("smn"),
|
||||
new ULocale("es_US"),
|
||||
new ULocale("es_GT"),
|
||||
new ULocale("es_419"),
|
||||
new ULocale("ee"),
|
||||
};
|
||||
double number = 12345.0;
|
||||
// These expected values are the same in both ICU 58 and 59.
|
||||
String[][] expectedShortLong = new String[][] {
|
||||
{ "12K", "12 tuhháát" },
|
||||
{ "12k", "12 mil" },
|
||||
{ "12k", "12 mil" },
|
||||
{ "12k", "12 mil" },
|
||||
{ "12K", "12K" },
|
||||
};
|
||||
|
||||
for (int i=0; i<locs.length; i++) {
|
||||
ULocale loc = locs[i];
|
||||
String expectedShort = expectedShortLong[i][0];
|
||||
String expectedLong = expectedShortLong[i][1];
|
||||
CompactDecimalFormat cdfShort = CompactDecimalFormat.getInstance(loc, CompactStyle.SHORT);
|
||||
CompactDecimalFormat cdfLong = CompactDecimalFormat.getInstance(loc, CompactStyle.LONG);
|
||||
String actualShort = cdfShort.format(number);
|
||||
String actualLong = cdfLong.format(number);
|
||||
assertEquals("Short, locale " + loc, expectedShort, actualShort);
|
||||
assertEquals("Long, locale " + loc, expectedLong, actualLong);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestLocales() {
|
||||
// Run a CDF over all locales to make sure there are no unexpected exceptions.
|
||||
ULocale[] locs = ULocale.getAvailableLocales();
|
||||
for (ULocale loc : locs) {
|
||||
CompactDecimalFormat cdfShort = CompactDecimalFormat.getInstance(loc, CompactStyle.SHORT);
|
||||
CompactDecimalFormat cdfLong = CompactDecimalFormat.getInstance(loc, CompactStyle.LONG);
|
||||
for (double d = 12345.0; d > 0.01; d /= 10) {
|
||||
String s1 = cdfShort.format(d);
|
||||
String s2 = cdfLong.format(d);
|
||||
assertNotNull("Short " + loc, s1);
|
||||
assertNotNull("Long " + loc, s2);
|
||||
assertNotEquals("Short " + loc, 0, s1.length());
|
||||
assertNotEquals("Long " + loc, 0, s2.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestDigitDisplay() {
|
||||
CompactDecimalFormat cdf = CompactDecimalFormat.getInstance(ULocale.US, CompactStyle.SHORT);
|
||||
cdf.setMinimumSignificantDigits(2);
|
||||
String actual = cdf.format(70123.45678);
|
||||
assertEquals("Should not display any extra fraction digits", "70K", actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestBug12422() {
|
||||
CompactDecimalFormat cdf;
|
||||
|
|
|
@ -4683,7 +4683,9 @@ public class NumberFormatTest extends TestFmwk {
|
|||
NumberFormat fmt = NumberFormat.getInstance(new ULocale("en"));
|
||||
fmt.setMinimumIntegerDigits(10);
|
||||
FieldPosition pos = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
|
||||
fmt.format(1234, new StringBuffer(), pos);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
fmt.format(1234567, sb, pos);
|
||||
assertEquals("Should have multiple grouping separators", "0,001,234,567", sb.toString());
|
||||
assertEquals("FieldPosition should report the first occurence", 1, pos.getBeginIndex());
|
||||
assertEquals("FieldPosition should report the first occurence", 2, pos.getEndIndex());
|
||||
}
|
||||
|
@ -4940,6 +4942,27 @@ public class NumberFormatTest extends TestFmwk {
|
|||
assertNotEquals("df2 != df1", df2, df1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Test13055() {
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance();
|
||||
df.setMaximumFractionDigits(0);
|
||||
df.setRoundingMode(BigDecimal.ROUND_HALF_EVEN);
|
||||
assertEquals("Should round percent toward even number", "216%", df.format(2.155));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Test13056() {
|
||||
DecimalFormat df = new DecimalFormat("#,##0");
|
||||
assertEquals("Primary grouping should return 3", 3, df.getGroupingSize());
|
||||
assertEquals("Secondary grouping should return 0", 0, df.getSecondaryGroupingSize());
|
||||
df.setSecondaryGroupingSize(3);
|
||||
assertEquals("Primary grouping should still return 3", 3, df.getGroupingSize());
|
||||
assertEquals("Secondary grouping should still return 0", 0, df.getSecondaryGroupingSize());
|
||||
df.setGroupingSize(4);
|
||||
assertEquals("Primary grouping should return 4", 4, df.getGroupingSize());
|
||||
assertEquals("Secondary should remember explicit setting and return 3", 3, df.getSecondaryGroupingSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPercentZero() {
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance();
|
||||
|
@ -5017,6 +5040,89 @@ public class NumberFormatTest extends TestFmwk {
|
|||
assertEquals("Rounding mode ordinal from java.math.RoundingMode should be the same", df1, df2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrencySignificantDigits() {
|
||||
ULocale locale = new ULocale("en-US");
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
|
||||
df.setMaximumSignificantDigits(2);
|
||||
String result = df.format(1234);
|
||||
assertEquals("Currency rounding should obey significant digits", "$1,200", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseStrictScientific() {
|
||||
// See ticket #13057
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getScientificInstance();
|
||||
df.setParseStrict(true);
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result0 = df.parse("123E4", ppos);
|
||||
assertEquals("Should accept number with exponent", 1230000L, result0);
|
||||
assertEquals("Should consume the whole number", 5, ppos.getIndex());
|
||||
ppos.setIndex(0);
|
||||
result0 = df.parse("123", ppos);
|
||||
assertNull("Should reject number without exponent", result0);
|
||||
ppos.setIndex(0);
|
||||
CurrencyAmount result1 = df.parseCurrency("USD123", ppos);
|
||||
assertNull("Should reject currency without exponent", result1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLenientScientific() {
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getScientificInstance();
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result0 = df.parse("123E", ppos);
|
||||
assertEquals("Should parse the number in lenient mode", 123L, result0);
|
||||
assertEquals("Should stop before the E", 3, ppos.getIndex());
|
||||
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
|
||||
dfs.setExponentSeparator("EE");
|
||||
df.setDecimalFormatSymbols(dfs);
|
||||
ppos.setIndex(0);
|
||||
result0 = df.parse("123EE", ppos);
|
||||
assertEquals("Should parse the number in lenient mode", 123L, result0);
|
||||
assertEquals("Should stop before the EE", 3, ppos.getIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAcceptAsciiPercentPermilleFallback() {
|
||||
ULocale loc = new ULocale("ar");
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance(loc);
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result = df.parse("42%", ppos);
|
||||
assertEquals("Should parse as 0.42 even in ar", new BigDecimal("0.42"), result);
|
||||
assertEquals("Should consume the entire string even in ar", 3, ppos.getIndex());
|
||||
// TODO: Is there a better way to make a localized permille formatter?
|
||||
df.applyPattern(df.toPattern().replace("%", "‰"));
|
||||
ppos.setIndex(0);
|
||||
result = df.parse("42‰", ppos);
|
||||
assertEquals("Should parse as 0.042 even in ar", new BigDecimal("0.042"), result);
|
||||
assertEquals("Should consume the entire string even in ar", 3, ppos.getIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSubtraction() {
|
||||
// TODO: Is this a case we need to support? It prevents us from automatically parsing
|
||||
// minus signs that appear after the number, like in "12-" vs "-12".
|
||||
DecimalFormat df = new DecimalFormat();
|
||||
String str = "12 - 5";
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number n1 = df.parse(str, ppos);
|
||||
Number n2 = df.parse(str, ppos);
|
||||
assertEquals("Should parse 12 and -5", 7, n1.intValue() + n2.intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiCodePointPaddingInPattern() {
|
||||
DecimalFormat df = new DecimalFormat("a*'நி'###0b");
|
||||
String result = df.format(12);
|
||||
assertEquals("Multi-codepoint padding should not be split", "aநிநி12b", result);
|
||||
df = new DecimalFormat("a*😁###0b");
|
||||
result = df.format(12);
|
||||
assertEquals("Single-codepoint padding should not be split", "a😁😁12b", result);
|
||||
df = new DecimalFormat("a*''###0b");
|
||||
result = df.format(12);
|
||||
assertEquals("Quote should be escapable in padding syntax", "a''12b", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignificantDigitsMode() {
|
||||
String[][] allExpected = {
|
||||
|
|
Loading…
Add table
Reference in a new issue