]> granicus.if.org Git - icu/commitdiff
ICU-13177 Small Java changes relating to Compact and Padding.
authorShane Carr <shane@unicode.org>
Sat, 16 Sep 2017 06:57:08 +0000 (06:57 +0000)
committerShane Carr <shane@unicode.org>
Sat, 16 Sep 2017 06:57:08 +0000 (06:57 +0000)
X-SVN-Rev: 40423

icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormat.java
icu4j/main/classes/core/src/newapi/CompactNotation.java
icu4j/main/classes/core/src/newapi/LocalizedNumberFormatter.java
icu4j/main/classes/core/src/newapi/NumberFormatter.java
icu4j/main/classes/core/src/newapi/NumberFormatterImpl.java
icu4j/main/classes/core/src/newapi/impl/CompactData.java
icu4j/main/classes/core/src/newapi/impl/Padder.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterTest.java

index 0a643cd7d96b052c12f8ae485244cb8e077443bc..7feeb33383c628e0d83738ccd580811dc82ce931 100644 (file)
@@ -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.
index 3df09789b7df15603d74d072863f46de49991c27..6f2415aae7181204831cb64880249ba4000922e7 100644 (file)
@@ -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);
index cd0956f13ba18d148b63cbb1f5720af9ef550fce..b71f1a815bb31a901d43fda6e788369d247ecd8c 100644 (file)
@@ -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()) {
index f08e56802576111eb89268bab0ade4168baa4029..dfa3f97cc49a531a702c461caba5b051fabcfa2c 100644 (file)
@@ -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;
     }
index 24a9b8e4f26e544b93ac773a5cf816f2ccdaa448..0e7fb71e862dbb6cca4d41004aec365805e0e3fc 100644 (file)
@@ -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);
         }
 
index 8b1c1df80111e1c421aa4faa56ba0eabc43b0aaf..5090f7091bb504dd5d5b0175e16e64483e5538b6 100644 (file)
@@ -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();
+    // 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 static final int COMPACT_MAX_DIGITS = 15;
+
+    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 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.
-        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);
+        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 (data.isEmpty() && compactStyle != CompactStyle.SHORT) {
-            resourceKey = getResourceBundleKey(nsName, CompactStyle.SHORT, compactType);
-            rb.getAllItemsWithFallbackNoFail(resourceKey, sink);
+        if (isEmpty && !compactIsShort) {
+            getResourceBundleKey(nsName, CompactStyle.SHORT, compactType, resourceKey);
+            rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink);
         }
-        if (data.isEmpty() && !nsName.equals("latn") && compactStyle != CompactStyle.SHORT) {
-            resourceKey = getResourceBundleKey("latn", CompactStyle.SHORT, compactType);
-            rb.getAllItemsWithFallbackNoFail(resourceKey, sink);
+        if (isEmpty && !nsIsLatn && !compactIsShort) {
+            getResourceBundleKey("latn", CompactStyle.SHORT, compactType, resourceKey);
+            rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink);
         }
 
-        // The last fallback is guaranteed to return data.
-        assert (!data.isEmpty());
-        return data;
+        // The last fallback should be guaranteed to return data.
+        if (isEmpty) {
+            throw new ICUException("Could not load compact decimal data for locale " + locale);
+        }
     }
 
-    /** Returns a string like "NumberElements/latn/patternsShort/decimalFormat". */
-    private static String getResourceBundleKey(String nsName, CompactStyle compactStyle, CompactType compactType) {
-        StringBuilder sb = new StringBuilder();
+    /** 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");
-        return sb.toString();
     }
 
     /** Java-only method used by CLDR tooling. */
-    public static CompactData getInstance(Map<String, Map<String, String>> powersToPluralsToPatterns) {
-        CompactData data = new CompactData();
+    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();
-                data.setPattern(patternString, magnitude, plural);
+                patterns[getIndex(magnitude, plural)] = patternString;
                 int numZeros = countZeros(patternString);
                 if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun"
-                    data.setMultiplier(magnitude, (byte) (numZeros - magnitude - 1));
+                    // Save the multiplier.
+                    multipliers[magnitude] = (byte) (numZeros - magnitude - 1);
+                    if (magnitude > largestMagnitude) {
+                        largestMagnitude = magnitude;
+                    }
+                    isEmpty = false;
                 }
             }
         }
-        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 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
@@ -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.
index 9f9a0a6cd92fd548e2ec1e1607d9ca911060fee2..6180fd58ddbcb1b7eef60ba08047672f549ac386 100644 (file)
@@ -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;
index 44b651fb3251856c06ecd35b8969a878c7058c10..8a6d196be4c6fd29cb8025652d73dcbe3183be5f 100644 (file)
@@ -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));
     }