mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-21 12:40:02 +00:00
ICU-13177 Small Java changes relating to Compact and Padding.
X-SVN-Rev: 40423
This commit is contained in:
parent
c842f7426d
commit
6a1bbcaa58
8 changed files with 158 additions and 171 deletions
|
@ -69,26 +69,6 @@ public class CompactDecimalFormat extends DecimalFormat {
|
|||
LONG
|
||||
}
|
||||
|
||||
/**
|
||||
* Type parameter for CompactDecimalFormat.
|
||||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
public enum CompactType {
|
||||
/**
|
||||
* Standard compact format, like "1.2T"
|
||||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
DECIMAL,
|
||||
/**
|
||||
* Compact format with currency, like "$1.2T"
|
||||
*
|
||||
* @draft ICU 60
|
||||
*/
|
||||
CURRENCY
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CompactDecimalFormat appropriate for a locale. The result may be affected by the
|
||||
* number system in the locale, such as ar-u-nu-latn.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package newapi;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -11,7 +12,6 @@ import com.ibm.icu.impl.number.DecimalQuantity;
|
|||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactType;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
|
@ -26,6 +26,10 @@ public class CompactNotation extends Notation {
|
|||
final CompactStyle compactStyle;
|
||||
final Map<String, Map<String, String>> compactCustomData;
|
||||
|
||||
public enum CompactType {
|
||||
DECIMAL, CURRENCY
|
||||
}
|
||||
|
||||
public CompactNotation(CompactStyle compactStyle) {
|
||||
compactCustomData = null;
|
||||
this.compactStyle = compactStyle;
|
||||
|
@ -36,18 +40,13 @@ public class CompactNotation extends Notation {
|
|||
this.compactCustomData = compactCustomData;
|
||||
}
|
||||
|
||||
/* package-private */ MicroPropsGenerator withLocaleData(ULocale dataLocale, CompactType compactType, PluralRules rules,
|
||||
MutablePatternModifier buildReference, MicroPropsGenerator parent) {
|
||||
CompactData data;
|
||||
if (compactStyle != null) {
|
||||
data = CompactData.getInstance(dataLocale, compactType, compactStyle);
|
||||
} else {
|
||||
data = CompactData.getInstance(compactCustomData);
|
||||
}
|
||||
return new CompactImpl(data, rules, buildReference, parent);
|
||||
/* package-private */ MicroPropsGenerator withLocaleData(ULocale locale, String nsName, CompactType compactType,
|
||||
PluralRules rules, MutablePatternModifier buildReference, MicroPropsGenerator parent) {
|
||||
// TODO: Add a data cache? It would be keyed by locale, nsName, compact type, and compact style.
|
||||
return new CompactHandler(this, locale, nsName, compactType, rules, buildReference, parent);
|
||||
}
|
||||
|
||||
private static class CompactImpl implements MicroPropsGenerator {
|
||||
private static class CompactHandler implements MicroPropsGenerator {
|
||||
|
||||
private static class CompactModInfo {
|
||||
public ImmutablePatternModifier mod;
|
||||
|
@ -55,28 +54,35 @@ public class CompactNotation extends Notation {
|
|||
}
|
||||
|
||||
final PluralRules rules;
|
||||
final CompactData data;
|
||||
final Map<String, CompactModInfo> precomputedMods;
|
||||
final MicroPropsGenerator parent;
|
||||
final Map<String, CompactModInfo> precomputedMods;
|
||||
final CompactData data;
|
||||
|
||||
private CompactImpl(CompactData data, PluralRules rules, MutablePatternModifier buildReference, MicroPropsGenerator parent) {
|
||||
this.data = data;
|
||||
private CompactHandler(CompactNotation notation, ULocale locale, String nsName, CompactType compactType,
|
||||
PluralRules rules, MutablePatternModifier buildReference, MicroPropsGenerator parent) {
|
||||
this.rules = rules;
|
||||
this.parent = parent;
|
||||
this.data = new CompactData();
|
||||
if (notation.compactStyle != null) {
|
||||
data.populate(locale, nsName, notation.compactStyle, compactType);
|
||||
} else {
|
||||
data.populate(notation.compactCustomData);
|
||||
}
|
||||
if (buildReference != null) {
|
||||
// Safe code path
|
||||
precomputedMods = precomputeAllModifiers(data, buildReference);
|
||||
precomputedMods = new HashMap<String, CompactModInfo>();
|
||||
precomputeAllModifiers(buildReference);
|
||||
} else {
|
||||
// Unsafe code path
|
||||
precomputedMods = null;
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/** Used by the safe code path */
|
||||
private static Map<String, CompactModInfo> precomputeAllModifiers(CompactData data,
|
||||
MutablePatternModifier buildReference) {
|
||||
Map<String, CompactModInfo> precomputedMods = new HashMap<String, CompactModInfo>();
|
||||
Set<String> allPatterns = data.getAllPatterns();
|
||||
private void precomputeAllModifiers(MutablePatternModifier buildReference) {
|
||||
Set<String> allPatterns = new HashSet<String>();
|
||||
data.getUniquePatterns(allPatterns);
|
||||
|
||||
for (String patternString : allPatterns) {
|
||||
CompactModInfo info = new CompactModInfo();
|
||||
ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(patternString);
|
||||
|
@ -85,27 +91,26 @@ public class CompactNotation extends Notation {
|
|||
info.numDigits = patternInfo.positive.integerTotal;
|
||||
precomputedMods.put(patternString, info);
|
||||
}
|
||||
return precomputedMods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MicroProps processQuantity(DecimalQuantity input) {
|
||||
MicroProps micros = parent.processQuantity(input);
|
||||
public MicroProps processQuantity(DecimalQuantity quantity) {
|
||||
MicroProps micros = parent.processQuantity(quantity);
|
||||
assert micros.rounding != null;
|
||||
|
||||
// Treat zero as if it had magnitude 0
|
||||
int magnitude;
|
||||
if (input.isZero()) {
|
||||
if (quantity.isZero()) {
|
||||
magnitude = 0;
|
||||
micros.rounding.apply(input);
|
||||
micros.rounding.apply(quantity);
|
||||
} else {
|
||||
// TODO: Revisit chooseMultiplierAndApply
|
||||
int multiplier = micros.rounding.chooseMultiplierAndApply(input, data);
|
||||
magnitude = input.isZero() ? 0 : input.getMagnitude();
|
||||
int multiplier = micros.rounding.chooseMultiplierAndApply(quantity, data);
|
||||
magnitude = quantity.isZero() ? 0 : quantity.getMagnitude();
|
||||
magnitude -= multiplier;
|
||||
}
|
||||
|
||||
StandardPlural plural = input.getStandardPlural(rules);
|
||||
StandardPlural plural = quantity.getStandardPlural(rules);
|
||||
String patternString = data.getPattern(magnitude, plural);
|
||||
int numDigits = -1;
|
||||
if (patternString == null) {
|
||||
|
@ -113,12 +118,13 @@ public class CompactNotation extends Notation {
|
|||
// No need to take any action.
|
||||
} else if (precomputedMods != null) {
|
||||
// Safe code path.
|
||||
// Java uses a hash set here for O(1) lookup. C++ uses a linear search.
|
||||
CompactModInfo info = precomputedMods.get(patternString);
|
||||
info.mod.applyToMicros(micros, input);
|
||||
info.mod.applyToMicros(micros, quantity);
|
||||
numDigits = info.numDigits;
|
||||
} else {
|
||||
// Unsafe code path.
|
||||
// Overwrite the PatternInfo in the existing modMiddle
|
||||
// Overwrite the PatternInfo in the existing modMiddle.
|
||||
assert micros.modMiddle instanceof MutablePatternModifier;
|
||||
ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(patternString);
|
||||
((MutablePatternModifier) micros.modMiddle).setPatternInfo(patternInfo);
|
||||
|
|
|
@ -71,6 +71,7 @@ public class LocalizedNumberFormatter extends NumberFormatterSettings<LocalizedN
|
|||
public FormattedNumber format(DecimalQuantity fq) {
|
||||
MacroProps macros = resolve();
|
||||
NumberStringBuilder string = new NumberStringBuilder();
|
||||
// TODO: Make this more like C++, where we get and then conditionally atomic-increment?
|
||||
long currentCount = callCount.incrementAndGet(this);
|
||||
MicroProps micros;
|
||||
if (currentCount == macros.threshold.longValue()) {
|
||||
|
|
|
@ -31,6 +31,12 @@ public final class NumberFormatter {
|
|||
AUTO, ALWAYS, NEVER, ACCOUNTING, ACCOUNTING_ALWAYS,
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a default threshold of 3. This means that the third time .format() is called, the data structures get built
|
||||
* using the "safe" code path. The first two calls to .format() will trigger the unsafe code path.
|
||||
*/
|
||||
static final long DEFAULT_THRESHOLD = 3;
|
||||
|
||||
public static UnlocalizedNumberFormatter with() {
|
||||
return BASE;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import com.ibm.icu.impl.number.NumberStringBuilder;
|
|||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
|
||||
import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactType;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
import com.ibm.icu.text.NumberingSystem;
|
||||
|
@ -17,6 +16,7 @@ import com.ibm.icu.util.Currency.CurrencyUsage;
|
|||
import com.ibm.icu.util.NoUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
import newapi.CompactNotation.CompactType;
|
||||
import newapi.NumberFormatter.DecimalMarkDisplay;
|
||||
import newapi.NumberFormatter.SignDisplay;
|
||||
import newapi.NumberFormatter.UnitWidth;
|
||||
|
@ -90,6 +90,9 @@ class NumberFormatterImpl {
|
|||
boolean perMille = false;
|
||||
PluralRules rules = macros.rules;
|
||||
|
||||
// FIXME
|
||||
String nsName = NumberingSystem.getInstance(macros.loc).getName();
|
||||
|
||||
MicroProps micros = new MicroProps(safe);
|
||||
MicroPropsGenerator chain = micros;
|
||||
|
||||
|
@ -242,7 +245,7 @@ class NumberFormatterImpl {
|
|||
CompactType compactType = (macros.unit instanceof Currency && macros.unitWidth != UnitWidth.FULL_NAME)
|
||||
? CompactType.CURRENCY
|
||||
: CompactType.DECIMAL;
|
||||
chain = ((CompactNotation) macros.notation).withLocaleData(macros.loc, compactType, rules,
|
||||
chain = ((CompactNotation) macros.notation).withLocaleData(macros.loc, nsName, compactType, rules,
|
||||
safe ? patternMod : null, chain);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package newapi.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -12,89 +11,92 @@ import com.ibm.icu.impl.ICUResourceBundle;
|
|||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.UResource;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactType;
|
||||
import com.ibm.icu.text.NumberingSystem;
|
||||
import com.ibm.icu.util.ICUException;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
import newapi.CompactNotation.CompactType;
|
||||
|
||||
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);
|
||||
String nsName = NumberingSystem.getInstance(locale).getName();
|
||||
ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
// 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 byte largestMagnitude;
|
||||
private boolean isEmpty;
|
||||
private int largestMagnitude;
|
||||
|
||||
private static final int MAX_DIGITS = 15;
|
||||
private static final int COMPACT_MAX_DIGITS = 15;
|
||||
|
||||
private CompactData() {
|
||||
patterns = new String[(CompactData.MAX_DIGITS + 1) * StandardPlural.COUNT];
|
||||
multipliers = new byte[CompactData.MAX_DIGITS + 1];
|
||||
isEmpty = true;
|
||||
public CompactData() {
|
||||
patterns = new String[(CompactData.COMPACT_MAX_DIGITS + 1) * StandardPlural.COUNT];
|
||||
multipliers = new byte[CompactData.COMPACT_MAX_DIGITS + 1];
|
||||
largestMagnitude = 0;
|
||||
isEmpty = true;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return isEmpty;
|
||||
public void populate(ULocale locale, String nsName, CompactStyle compactStyle, CompactType compactType) {
|
||||
assert isEmpty;
|
||||
CompactDataSink sink = new CompactDataSink(this);
|
||||
ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
|
||||
|
||||
boolean nsIsLatn = nsName.equals("latn");
|
||||
boolean compactIsShort = compactStyle == CompactStyle.SHORT;
|
||||
|
||||
// Fall back to latn numbering system and/or short compact style.
|
||||
StringBuilder resourceKey = new StringBuilder();
|
||||
getResourceBundleKey(nsName, compactStyle, compactType, resourceKey);
|
||||
rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink);
|
||||
if (isEmpty && !nsIsLatn) {
|
||||
getResourceBundleKey("latn", compactStyle, compactType, resourceKey);
|
||||
rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink);
|
||||
}
|
||||
if (isEmpty && !compactIsShort) {
|
||||
getResourceBundleKey(nsName, CompactStyle.SHORT, compactType, resourceKey);
|
||||
rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink);
|
||||
}
|
||||
if (isEmpty && !nsIsLatn && !compactIsShort) {
|
||||
getResourceBundleKey("latn", CompactStyle.SHORT, compactType, resourceKey);
|
||||
rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink);
|
||||
}
|
||||
|
||||
// The last fallback should be guaranteed to return data.
|
||||
if (isEmpty) {
|
||||
throw new ICUException("Could not load compact decimal data for locale " + locale);
|
||||
}
|
||||
}
|
||||
|
||||
/** Produces a string like "NumberElements/latn/patternsShort/decimalFormat". */
|
||||
private static void getResourceBundleKey(String nsName, CompactStyle compactStyle, CompactType compactType, StringBuilder sb) {
|
||||
sb.setLength(0);
|
||||
sb.append("NumberElements/");
|
||||
sb.append(nsName);
|
||||
sb.append(compactStyle == CompactStyle.SHORT ? "/patternsShort" : "/patternsLong");
|
||||
sb.append(compactType == CompactType.DECIMAL ? "/decimalFormat" : "/currencyFormat");
|
||||
}
|
||||
|
||||
/** Java-only method used by CLDR tooling. */
|
||||
public void populate(Map<String, Map<String, String>> powersToPluralsToPatterns) {
|
||||
assert isEmpty;
|
||||
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();
|
||||
patterns[getIndex(magnitude, plural)] = patternString;
|
||||
int numZeros = countZeros(patternString);
|
||||
if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
|
||||
// Save the multiplier.
|
||||
multipliers[magnitude] = (byte) (numZeros - magnitude - 1);
|
||||
if (magnitude > largestMagnitude) {
|
||||
largestMagnitude = magnitude;
|
||||
}
|
||||
isEmpty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,23 +110,6 @@ public class CompactData implements MultiplierProducer {
|
|||
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;
|
||||
|
@ -144,32 +129,13 @@ public class CompactData implements MultiplierProducer {
|
|||
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();
|
||||
public void getUniquePatterns(Set<String> output) {
|
||||
assert output.isEmpty();
|
||||
// NOTE: In C++, this is done more manually with a UVector.
|
||||
// In Java, we can take advantage of JDK HashSet.
|
||||
output.addAll(Arrays.asList(patterns));
|
||||
output.remove(USE_FALLBACK);
|
||||
output.remove(null);
|
||||
}
|
||||
|
||||
private static final class CompactDataSink extends UResource.Sink {
|
||||
|
@ -189,16 +155,17 @@ public class CompactData implements MultiplierProducer {
|
|||
// 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;
|
||||
byte multiplier = data.multipliers[magnitude];
|
||||
assert magnitude < COMPACT_MAX_DIGITS;
|
||||
|
||||
// Iterate over the plural variants ("one", "other", etc)
|
||||
UResource.Table pluralVariantsTable = value.getTable();
|
||||
for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) {
|
||||
|
||||
// Skip this magnitude/plural if we already have it from a child locale.
|
||||
// Note: This also skips USE_FALLBACK entries.
|
||||
StandardPlural plural = StandardPlural.fromString(key.toString());
|
||||
if (data.has(magnitude, plural)) {
|
||||
if (data.patterns[getIndex(magnitude, plural)] != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -206,12 +173,11 @@ public class CompactData implements MultiplierProducer {
|
|||
// to parent locales. Example locale where this is relevant: 'it'.
|
||||
String patternString = value.toString();
|
||||
if (patternString.equals("0")) {
|
||||
data.setNoFallback(magnitude, plural);
|
||||
continue;
|
||||
patternString = USE_FALLBACK;
|
||||
}
|
||||
|
||||
// Save the pattern string. We will parse it lazily.
|
||||
data.setPattern(patternString, magnitude, plural);
|
||||
data.patterns[getIndex(magnitude, plural)] = patternString;
|
||||
|
||||
// If necessary, compute the multiplier: the difference between the magnitude
|
||||
// and the number of zeros in the pattern.
|
||||
|
@ -223,11 +189,24 @@ public class CompactData implements MultiplierProducer {
|
|||
}
|
||||
}
|
||||
|
||||
data.setMultiplier(magnitude, multiplier);
|
||||
// Save the multiplier.
|
||||
if (data.multipliers[magnitude] == 0) {
|
||||
data.multipliers[magnitude] = multiplier;
|
||||
if (magnitude > data.largestMagnitude) {
|
||||
data.largestMagnitude = magnitude;
|
||||
}
|
||||
data.isEmpty = false;
|
||||
} else {
|
||||
assert data.multipliers[magnitude] == multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int getIndex(int magnitude, StandardPlural plural) {
|
||||
return magnitude * StandardPlural.COUNT + plural.ordinal();
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -117,7 +117,7 @@ public class Padder {
|
|||
// Should not happen since currency spacing is always on the inside.
|
||||
throw new AssertionError();
|
||||
}
|
||||
length += string.insert(insertIndex, paddingString, null);
|
||||
addPaddingHelper(paddingString, requiredPadding, string, insertIndex);
|
||||
}
|
||||
|
||||
return length;
|
||||
|
@ -126,6 +126,7 @@ public class Padder {
|
|||
private static int addPaddingHelper(String paddingString, int requiredPadding, NumberStringBuilder string,
|
||||
int index) {
|
||||
for (int i = 0; i < requiredPadding; i++) {
|
||||
// TODO: If appending to the end, this will cause actual insertion operations. Improve.
|
||||
string.insert(index, paddingString, null);
|
||||
}
|
||||
return paddingString.length() * requiredPadding;
|
||||
|
|
|
@ -1015,6 +1015,15 @@ public class NumberFormatterTest {
|
|||
ULocale.ENGLISH,
|
||||
0.8888,
|
||||
"88.88%**");
|
||||
|
||||
assertFormatSingle(
|
||||
"Currency Spacing with Zero Digit Padding Broken",
|
||||
"$GBP unit-width=ISO_CODE",
|
||||
NumberFormatter.with().padding(Padder.codePoints('0', 12, PadPosition.AFTER_PREFIX)).unit(GBP)
|
||||
.unitWidth(UnitWidth.ISO_CODE),
|
||||
ULocale.ENGLISH,
|
||||
514.23,
|
||||
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1330,6 +1339,8 @@ public class NumberFormatterTest {
|
|||
public void locale() {
|
||||
// Coverage for the locale setters.
|
||||
assertEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.with().locale(Locale.ENGLISH));
|
||||
assertEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.withLocale(ULocale.ENGLISH));
|
||||
assertEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.withLocale(Locale.ENGLISH));
|
||||
assertNotEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.with().locale(Locale.FRENCH));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue