ICU-13177 Cleaning up Java compact and long names in preparation for C++.

X-SVN-Rev: 40418
This commit is contained in:
Shane Carr 2017-09-14 23:02:22 +00:00
parent 33d0dbbb73
commit c842f7426d
6 changed files with 346 additions and 271 deletions

View File

@ -364,6 +364,14 @@ public class ICUResourceBundle extends UResourceBundle {
return result;
}
public void getAllItemsWithFallbackNoFail(String path, UResource.Sink sink) {
try {
getAllItemsWithFallback(path, sink);
} catch (MissingResourceException e) {
// Quietly ignore the exception.
}
}
public void getAllItemsWithFallback(String path, UResource.Sink sink)
throws MissingResourceException {
// Collect existing and parsed key objects into an array of keys,

View File

@ -140,7 +140,8 @@ class NumberFormatterImpl {
ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(innerPattern);
// Symbols
// NOTE: C++ has a special class, SymbolsWrapper, in MacroProps. Java has all the resolution logic here directly.
// NOTE: C++ has a special class, SymbolsWrapper, in MacroProps. Java has all the resolution logic here
// directly.
if (macros.symbols == null) {
micros.symbols = DecimalFormatSymbols.getInstance(macros.loc);
} else if (macros.symbols instanceof DecimalFormatSymbols) {
@ -238,7 +239,9 @@ class NumberFormatterImpl {
// Lazily create PluralRules
rules = PluralRules.forLocale(macros.loc);
}
CompactType compactType = (macros.unit instanceof Currency) ? CompactType.CURRENCY : CompactType.DECIMAL;
CompactType compactType = (macros.unit instanceof Currency && macros.unitWidth != UnitWidth.FULL_NAME)
? CompactType.CURRENCY
: CompactType.DECIMAL;
chain = ((CompactNotation) macros.notation).withLocaleData(macros.loc, compactType, rules,
safe ? patternMod : null, chain);
}

View File

@ -58,25 +58,39 @@ public class ScientificNotation extends Notation implements Cloneable {
/* package-private */ MicroPropsGenerator withLocaleData(DecimalFormatSymbols symbols, boolean build,
MicroPropsGenerator parent) {
return new MurkyScientificHandler(symbols, build, parent);
return new ScientificHandler(this, symbols, build, parent);
}
private class MurkyScientificHandler implements MicroPropsGenerator, MultiplierProducer, Modifier {
// NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
//
// During formatting, we need to provide an object with state (the exponent) as the inner modifier.
//
// In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
// ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
// instances. This scheme reduces the number of object creations by 1 in both safe and unsafe.
//
// In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
// the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
private static class ScientificHandler implements MicroPropsGenerator, MultiplierProducer, Modifier {
final ScientificNotation notation;
final DecimalFormatSymbols symbols;
final ImmutableScientificModifier[] precomputedMods;
final ScientificModifier[] precomputedMods;
final MicroPropsGenerator parent;
/* unsafe */ int exponent;
private MurkyScientificHandler(DecimalFormatSymbols symbols, boolean safe, MicroPropsGenerator parent) {
private ScientificHandler(ScientificNotation notation, DecimalFormatSymbols symbols, boolean safe,
MicroPropsGenerator parent) {
this.notation = notation;
this.symbols = symbols;
this.parent = parent;
if (safe) {
// Pre-build the modifiers for exponents -12 through 12
precomputedMods = new ImmutableScientificModifier[25];
precomputedMods = new ScientificModifier[25];
for (int i = -12; i <= 12; i++) {
precomputedMods[i + 12] = new ImmutableScientificModifier(i);
precomputedMods[i + 12] = new ScientificModifier(i, this);
}
} else {
precomputedMods = null;
@ -91,9 +105,9 @@ public class ScientificNotation extends Notation implements Cloneable {
// Treat zero as if it had magnitude 0
int exponent;
if (quantity.isZero()) {
if (requireMinInt && micros.rounding instanceof SignificantRounderImpl) {
if (notation.requireMinInt && micros.rounding instanceof SignificantRounderImpl) {
// Show "00.000E0" on pattern "00.000E0"
((SignificantRounderImpl) micros.rounding).apply(quantity, engineeringInterval);
((SignificantRounderImpl) micros.rounding).apply(quantity, notation.engineeringInterval);
exponent = 0;
} else {
micros.rounding.apply(quantity);
@ -109,7 +123,7 @@ public class ScientificNotation extends Notation implements Cloneable {
micros.modInner = precomputedMods[exponent + 12];
} else if (precomputedMods != null) {
// Safe code path B
micros.modInner = new ImmutableScientificModifier(exponent);
micros.modInner = new ScientificModifier(exponent, this);
} else {
// Unsafe code path: mutates the object and re-uses it as a Modifier!
this.exponent = exponent;
@ -124,9 +138,9 @@ public class ScientificNotation extends Notation implements Cloneable {
@Override
public int getMultiplier(int magnitude) {
int interval = engineeringInterval;
int interval = notation.engineeringInterval;
int digitsShown;
if (requireMinInt) {
if (notation.requireMinInt) {
// For patterns like "000.00E0" and ".00E0"
digitsShown = interval;
} else if (interval <= 1) {
@ -141,7 +155,7 @@ public class ScientificNotation extends Notation implements Cloneable {
@Override
public int getPrefixLength() {
// FIXME: Localized exponent separator location.
// TODO: Localized exponent separator location.
return 0;
}
@ -153,6 +167,7 @@ public class ScientificNotation extends Notation implements Cloneable {
@Override
public boolean isStrong() {
// Scientific is always strong
return true;
}
@ -166,49 +181,52 @@ public class ScientificNotation extends Notation implements Cloneable {
int i = rightIndex;
// Append the exponent separator and sign
i += output.insert(i, symbols.getExponentSeparator(), NumberFormat.Field.EXPONENT_SYMBOL);
if (exponent < 0 && exponentSignDisplay != SignDisplay.NEVER) {
if (exponent < 0 && notation.exponentSignDisplay != SignDisplay.NEVER) {
i += output.insert(i, symbols.getMinusSignString(), NumberFormat.Field.EXPONENT_SIGN);
} else if (exponentSignDisplay == SignDisplay.ALWAYS) {
} else if (notation.exponentSignDisplay == SignDisplay.ALWAYS) {
i += output.insert(i, symbols.getPlusSignString(), NumberFormat.Field.EXPONENT_SIGN);
}
// Append the exponent digits (using a simple inline algorithm)
int disp = Math.abs(exponent);
for (int j = 0; j < minExponentDigits || disp > 0; j++, disp /= 10) {
for (int j = 0; j < notation.minExponentDigits || disp > 0; j++, disp /= 10) {
int d = disp % 10;
String digitString = symbols.getDigitStringsLocal()[d];
i += output.insert(i - j, digitString, NumberFormat.Field.EXPONENT);
}
return i - rightIndex;
}
}
private class ImmutableScientificModifier implements Modifier {
final int exponent;
private static class ScientificModifier implements Modifier {
final int exponent;
final ScientificHandler handler;
ImmutableScientificModifier(int exponent) {
this.exponent = exponent;
}
ScientificModifier(int exponent, ScientificHandler handler) {
this.exponent = exponent;
this.handler = handler;
}
@Override
public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
return doApply(exponent, output, rightIndex);
}
@Override
public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
return handler.doApply(exponent, output, rightIndex);
}
@Override
public int getPrefixLength() {
// FIXME: Localized exponent separator location.
return 0;
}
@Override
public int getPrefixLength() {
// TODO: Localized exponent separator location.
return 0;
}
@Override
public int getCodePointCount() {
// This method is not used for strong modifiers.
throw new AssertionError();
}
@Override
public int getCodePointCount() {
// This method is not used for strong modifiers.
throw new AssertionError();
}
@Override
public boolean isStrong() {
return true;
}
@Override
public boolean isStrong() {
// Scientific is always strong
return true;
}
}
}

View File

@ -5,7 +5,6 @@ package newapi.impl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import com.ibm.icu.impl.ICUData;
@ -20,257 +19,226 @@ import com.ibm.icu.util.UResourceBundle;
public class CompactData implements MultiplierProducer {
public static CompactData getInstance(
ULocale locale, CompactType compactType, CompactStyle compactStyle) {
// TODO: Add a data cache? It would be keyed by locale, compact type, and compact style.
CompactData data = new CompactData();
CompactDataSink sink = new CompactDataSink(data, compactType, compactStyle);
String nsName = NumberingSystem.getInstance(locale).getName();
ICUResourceBundle rb =
(ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
CompactData.internalPopulateData(nsName, rb, sink, data);
if (data.isEmpty() && compactStyle == CompactStyle.LONG) {
// No long data is available; load short data instead
sink.compactStyle = CompactStyle.SHORT;
CompactData.internalPopulateData(nsName, rb, sink, data);
}
return data;
}
public static CompactData getInstance(ULocale locale, CompactType compactType, CompactStyle compactStyle) {
// TODO: Add a data cache? It would be keyed by locale, compact type, and compact style.
CompactData data = new CompactData();
CompactDataSink sink = new CompactDataSink(data);
String nsName = NumberingSystem.getInstance(locale).getName();
ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
public static CompactData getInstance(
Map<String, Map<String, String>> powersToPluralsToPatterns) {
CompactData data = new CompactData();
for (Map.Entry<String, Map<String, String>> magnitudeEntry :
powersToPluralsToPatterns.entrySet()) {
byte magnitude = (byte) (magnitudeEntry.getKey().length() - 1);
for (Map.Entry<String, String> pluralEntry : magnitudeEntry.getValue().entrySet()) {
StandardPlural plural = StandardPlural.fromString(pluralEntry.getKey().toString());
String patternString = pluralEntry.getValue().toString();
data.setPattern(patternString, magnitude, plural);
int numZeros = countZeros(patternString);
if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
data.setMultiplier(magnitude, (byte) (numZeros - magnitude - 1));
// Fall back to latn numbering system and/or short compact style.
String resourceKey = getResourceBundleKey(nsName, compactStyle, compactType);
rb.getAllItemsWithFallbackNoFail(resourceKey, sink);
if (data.isEmpty() && !nsName.equals("latn")) {
resourceKey = getResourceBundleKey("latn", compactStyle, compactType);
rb.getAllItemsWithFallbackNoFail(resourceKey, sink);
}
}
if (data.isEmpty() && compactStyle != CompactStyle.SHORT) {
resourceKey = getResourceBundleKey(nsName, CompactStyle.SHORT, compactType);
rb.getAllItemsWithFallbackNoFail(resourceKey, sink);
}
if (data.isEmpty() && !nsName.equals("latn") && compactStyle != CompactStyle.SHORT) {
resourceKey = getResourceBundleKey("latn", CompactStyle.SHORT, compactType);
rb.getAllItemsWithFallbackNoFail(resourceKey, sink);
}
// The last fallback is guaranteed to return data.
assert (!data.isEmpty());
return data;
}
return data;
}
private static void internalPopulateData(
String nsName, ICUResourceBundle rb, CompactDataSink sink, CompactData data) {
try {
rb.getAllItemsWithFallback("NumberElements/" + nsName, sink);
} catch (MissingResourceException e) {
// Fall back to latn
/** Returns a string like "NumberElements/latn/patternsShort/decimalFormat". */
private static String getResourceBundleKey(String nsName, CompactStyle compactStyle, CompactType compactType) {
StringBuilder sb = new StringBuilder();
sb.append("NumberElements/");
sb.append(nsName);
sb.append(compactStyle == CompactStyle.SHORT ? "/patternsShort" : "/patternsLong");
sb.append(compactType == CompactType.DECIMAL ? "/decimalFormat" : "/currencyFormat");
return sb.toString();
}
if (data.isEmpty() && !nsName.equals("latn")) {
rb.getAllItemsWithFallback("NumberElements/latn", sink);
/** Java-only method used by CLDR tooling. */
public static CompactData getInstance(Map<String, Map<String, String>> powersToPluralsToPatterns) {
CompactData data = new CompactData();
for (Map.Entry<String, Map<String, String>> magnitudeEntry : powersToPluralsToPatterns.entrySet()) {
byte magnitude = (byte) (magnitudeEntry.getKey().length() - 1);
for (Map.Entry<String, String> pluralEntry : magnitudeEntry.getValue().entrySet()) {
StandardPlural plural = StandardPlural.fromString(pluralEntry.getKey().toString());
String patternString = pluralEntry.getValue().toString();
data.setPattern(patternString, magnitude, plural);
int numZeros = countZeros(patternString);
if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
data.setMultiplier(magnitude, (byte) (numZeros - magnitude - 1));
}
}
}
return data;
}
if (sink.exception != null) {
throw sink.exception;
// A dummy object used when a "0" compact decimal entry is encountered. This is necessary
// in order to prevent falling back to root. Object equality ("==") is intended.
private static final String USE_FALLBACK = "<USE FALLBACK>";
private final String[] patterns;
private final byte[] multipliers;
private boolean isEmpty;
private int largestMagnitude;
private static final int MAX_DIGITS = 15;
private CompactData() {
patterns = new String[(CompactData.MAX_DIGITS + 1) * StandardPlural.COUNT];
multipliers = new byte[CompactData.MAX_DIGITS + 1];
isEmpty = true;
largestMagnitude = 0;
}
}
// A dummy object used when a "0" compact decimal entry is encountered. This is necessary
// in order to prevent falling back to root. Object equality ("==") is intended.
private static final String USE_FALLBACK = "<USE FALLBACK>";
private final String[] patterns;
private final byte[] multipliers;
private boolean isEmpty;
private int largestMagnitude;
private static final int MAX_DIGITS = 15;
private CompactData() {
patterns = new String[(CompactData.MAX_DIGITS + 1) * StandardPlural.COUNT];
multipliers = new byte[CompactData.MAX_DIGITS + 1];
isEmpty = true;
largestMagnitude = 0;
}
public boolean isEmpty() {
return isEmpty;
}
@Override
public int getMultiplier(int magnitude) {
if (magnitude < 0) {
return 0;
}
if (magnitude > largestMagnitude) {
magnitude = largestMagnitude;
}
return multipliers[magnitude];
}
/** Returns the multiplier from the array directly without bounds checking. */
public int getMultiplierDirect(int magnitude) {
return multipliers[magnitude];
}
private void setMultiplier(int magnitude, byte multiplier) {
if (multipliers[magnitude] != 0) {
assert multipliers[magnitude] == multiplier;
return;
}
multipliers[magnitude] = multiplier;
isEmpty = false;
if (magnitude > largestMagnitude) largestMagnitude = magnitude;
}
public String getPattern(int magnitude, StandardPlural plural) {
if (magnitude < 0) {
return null;
}
if (magnitude > largestMagnitude) {
magnitude = largestMagnitude;
}
String patternString = patterns[getIndex(magnitude, plural)];
if (patternString == null && plural != StandardPlural.OTHER) {
// Fall back to "other" plural variant
patternString = patterns[getIndex(magnitude, StandardPlural.OTHER)];
}
if (patternString == USE_FALLBACK) {
// Return null if USE_FALLBACK is present
patternString = null;
}
return patternString;
}
public Set<String> getAllPatterns() {
Set<String> result = new HashSet<String>();
result.addAll(Arrays.asList(patterns));
result.remove(USE_FALLBACK);
result.remove(null);
return result;
}
private boolean has(int magnitude, StandardPlural plural) {
// Return true if USE_FALLBACK is present
return patterns[getIndex(magnitude, plural)] != null;
}
private void setPattern(String patternString, int magnitude, StandardPlural plural) {
patterns[getIndex(magnitude, plural)] = patternString;
isEmpty = false;
if (magnitude > largestMagnitude) largestMagnitude = magnitude;
}
private void setNoFallback(int magnitude, StandardPlural plural) {
setPattern(USE_FALLBACK, magnitude, plural);
}
private static final int getIndex(int magnitude, StandardPlural plural) {
return magnitude * StandardPlural.COUNT + plural.ordinal();
}
private static final class CompactDataSink extends UResource.Sink {
CompactData data;
CompactStyle compactStyle;
CompactType compactType;
IllegalArgumentException exception;
/*
* NumberElements{ <-- top (numbering system table)
* latn{ <-- patternsTable (one per numbering system)
* patternsLong{ <-- formatsTable (one per pattern)
* decimalFormat{ <-- powersOfTenTable (one per format)
* 1000{ <-- pluralVariantsTable (one per power of ten)
* one{"0 thousand"} <-- plural variant and template
*/
public CompactDataSink(CompactData data, CompactType compactType, CompactStyle compactStyle) {
this.data = data;
this.compactType = compactType;
this.compactStyle = compactStyle;
public boolean isEmpty() {
return isEmpty;
}
@Override
public void put(UResource.Key key, UResource.Value value, boolean isRoot) {
UResource.Table patternsTable = value.getTable();
for (int i1 = 0; patternsTable.getKeyAndValue(i1, key, value); ++i1) {
if (key.contentEquals("patternsShort") && compactStyle == CompactStyle.SHORT) {
} else if (key.contentEquals("patternsLong") && compactStyle == CompactStyle.LONG) {
} else {
continue;
public int getMultiplier(int magnitude) {
if (magnitude < 0) {
return 0;
}
if (magnitude > largestMagnitude) {
magnitude = largestMagnitude;
}
return multipliers[magnitude];
}
/** Returns the multiplier from the array directly without bounds checking. */
public int getMultiplierDirect(int magnitude) {
return multipliers[magnitude];
}
private void setMultiplier(int magnitude, byte multiplier) {
if (multipliers[magnitude] != 0) {
assert multipliers[magnitude] == multiplier;
return;
}
multipliers[magnitude] = multiplier;
isEmpty = false;
if (magnitude > largestMagnitude) {
largestMagnitude = magnitude;
}
}
public String getPattern(int magnitude, StandardPlural plural) {
if (magnitude < 0) {
return null;
}
if (magnitude > largestMagnitude) {
magnitude = largestMagnitude;
}
String patternString = patterns[getIndex(magnitude, plural)];
if (patternString == null && plural != StandardPlural.OTHER) {
// Fall back to "other" plural variant
patternString = patterns[getIndex(magnitude, StandardPlural.OTHER)];
}
if (patternString == USE_FALLBACK) { // == is intended
// Return null if USE_FALLBACK is present
patternString = null;
}
return patternString;
}
public Set<String> getAllPatterns() {
Set<String> result = new HashSet<String>();
result.addAll(Arrays.asList(patterns));
result.remove(USE_FALLBACK);
result.remove(null);
return result;
}
private boolean has(int magnitude, StandardPlural plural) {
// Return true if USE_FALLBACK is present
return patterns[getIndex(magnitude, plural)] != null;
}
private void setPattern(String patternString, int magnitude, StandardPlural plural) {
patterns[getIndex(magnitude, plural)] = patternString;
isEmpty = false;
if (magnitude > largestMagnitude)
largestMagnitude = magnitude;
}
private void setNoFallback(int magnitude, StandardPlural plural) {
setPattern(USE_FALLBACK, magnitude, plural);
}
private static final int getIndex(int magnitude, StandardPlural plural) {
return magnitude * StandardPlural.COUNT + plural.ordinal();
}
private static final class CompactDataSink extends UResource.Sink {
CompactData data;
public CompactDataSink(CompactData data) {
this.data = data;
}
// traverse into the table of formats
UResource.Table formatsTable = value.getTable();
for (int i2 = 0; formatsTable.getKeyAndValue(i2, key, value); ++i2) {
if (key.contentEquals("decimalFormat") && compactType == CompactType.DECIMAL) {
} else if (key.contentEquals("currencyFormat") && compactType == CompactType.CURRENCY) {
} else {
continue;
}
@Override
public void put(UResource.Key key, UResource.Value value, boolean isRoot) {
// traverse into the table of powers of ten
UResource.Table powersOfTenTable = value.getTable();
for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) {
// traverse into the table of powers of ten
UResource.Table powersOfTenTable = value.getTable();
for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) {
// Assumes that the keys are always of the form "10000" where the magnitude is the
// length of the key minus one. We expect magnitudes to be less than MAX_DIGITS.
byte magnitude = (byte) (key.length() - 1);
byte multiplier = (byte) data.getMultiplierDirect(magnitude);
assert magnitude < MAX_DIGITS;
// Assumes that the keys are always of the form "10000" where the magnitude is the
// length of the key minus one
byte magnitude = (byte) (key.length() - 1);
byte multiplier = (byte) data.getMultiplierDirect(magnitude);
// Iterate over the plural variants ("one", "other", etc)
UResource.Table pluralVariantsTable = value.getTable();
for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) {
// Silently ignore divisors that are too big.
if (magnitude >= CompactData.MAX_DIGITS) continue;
// Skip this magnitude/plural if we already have it from a child locale.
StandardPlural plural = StandardPlural.fromString(key.toString());
if (data.has(magnitude, plural)) {
continue;
}
// Iterate over the plural variants ("one", "other", etc)
UResource.Table pluralVariantsTable = value.getTable();
for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) {
// The value "0" means that we need to use the default pattern and not fall back
// to parent locales. Example locale where this is relevant: 'it'.
String patternString = value.toString();
if (patternString.equals("0")) {
data.setNoFallback(magnitude, plural);
continue;
}
// Skip this magnitude/plural if we already have it from a child locale.
StandardPlural plural = StandardPlural.fromString(key.toString());
if (data.has(magnitude, plural)) {
continue;
}
// Save the pattern string. We will parse it lazily.
data.setPattern(patternString, magnitude, plural);
// The value "0" means that we need to use the default pattern and not fall back
// to parent locales. Example locale where this is relevant: 'it'.
String patternString = value.toString();
if (patternString.equals("0")) {
data.setNoFallback(magnitude, plural);
continue;
}
// Save the pattern string. We will parse it lazily.
data.setPattern(patternString, magnitude, plural);
// If necessary, compute the multiplier: the difference between the magnitude
// and the number of zeros in the pattern.
if (multiplier == 0) {
int numZeros = countZeros(patternString);
if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
multiplier = (byte) (numZeros - magnitude - 1);
// If necessary, compute the multiplier: the difference between the magnitude
// and the number of zeros in the pattern.
if (multiplier == 0) {
int numZeros = countZeros(patternString);
if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
multiplier = (byte) (numZeros - magnitude - 1);
}
}
}
}
data.setMultiplier(magnitude, multiplier);
}
data.setMultiplier(magnitude, multiplier);
}
// We want only one table of compact decimal formats, so if we get here, stop consuming.
// The data.isEmpty() check will prevent further bundles from being traversed.
return;
}
}
}
}
private static final int countZeros(String patternString) {
// NOTE: This strategy for computing the number of zeros is a hack for efficiency.
// It could break if there are any 0s that aren't part of the main pattern.
int numZeros = 0;
for (int i = 0; i < patternString.length(); i++) {
if (patternString.charAt(i) == '0') {
numZeros++;
} else if (numZeros > 0) {
break; // zeros should always be contiguous
}
private static final int countZeros(String patternString) {
// NOTE: This strategy for computing the number of zeros is a hack for efficiency.
// It could break if there are any 0s that aren't part of the main pattern.
int numZeros = 0;
for (int i = 0; i < patternString.length(); i++) {
if (patternString.charAt(i) == '0') {
numZeros++;
} else if (numZeros > 0) {
break; // zeros should always be contiguous
}
}
return numZeros;
}
return numZeros;
}
}

View File

@ -46,7 +46,9 @@ public class LongNameHandler implements MicroPropsGenerator {
String pluralKeyword = e.getKey();
StandardPlural plural = StandardPlural.fromString(e.getKey());
String longName = currency.getName(loc, Currency.PLURAL_LONG_NAME, pluralKeyword, null);
String simpleFormat = e.getValue(); // e.g., "{0} {1}"
String simpleFormat = e.getValue();
// Example pattern from data: "{0} {1}"
// Example output after find-and-replace: "{0} US dollars"
simpleFormat = simpleFormat.replace("{1}", longName);
String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY, false);

View File

@ -186,13 +186,50 @@ public class NumberFormatterTest {
"$0.0088",
"$0");
assertFormatDescending(
"Compact Short with ISO Currency",
"C $USD unit-width=ISO_CODE",
NumberFormatter.with()
.notation(Notation.compactShort())
.unit(USD)
.unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K",
"USD 8.8K",
"USD 876",
"USD 88",
"USD 8.8",
"USD 0.88",
"USD 0.088",
"USD 0.0088",
"USD 0");
assertFormatDescending(
"Compact Short with Long Name Currency",
"C $USD unit-width=FULL_NAME",
NumberFormatter.with()
.notation(Notation.compactShort())
.unit(USD)
.unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88K US dollars",
"8.8K US dollars",
"876 US dollars",
"88 US dollars",
"8.8 US dollars",
"0.88 US dollars",
"0.088 US dollars",
"0.0088 US dollars",
"0 US dollars");
// Note: Most locales don't have compact long currency, so this currently falls back to short.
// This test case should be fixed when proper compact long currency patterns are added.
assertFormatDescending(
"Compact Long Currency",
"CC $USD",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD),
ULocale.ENGLISH,
"$88K",
"$88K", // should be something like "$88 thousand"
"$8.8K",
"$876",
"$88",
@ -202,6 +239,45 @@ public class NumberFormatterTest {
"$0.0088",
"$0");
// Note: Most locales don't have compact long currency, so this currently falls back to short.
// This test case should be fixed when proper compact long currency patterns are added.
assertFormatDescending(
"Compact Long with ISO Currency",
"CC $USD unit-width=ISO_CODE",
NumberFormatter.with()
.notation(Notation.compactLong())
.unit(USD)
.unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K", // should be something like "USD 88 thousand"
"USD 8.8K",
"USD 876",
"USD 88",
"USD 8.8",
"USD 0.88",
"USD 0.088",
"USD 0.0088",
"USD 0");
// TODO: This behavior could be improved and should be revisited.
assertFormatDescending(
"Compact Long with Long Name Currency",
"CC $USD unit-width=FULL_NAME",
NumberFormatter.with()
.notation(Notation.compactLong())
.unit(USD)
.unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88 thousand US dollars",
"8.8 thousand US dollars",
"876 US dollars",
"88 US dollars",
"8.8 US dollars",
"0.88 US dollars",
"0.088 US dollars",
"0.0088 US dollars",
"0 US dollars");
assertFormatSingle(
"Compact Plural One",
"CC",