diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java index ce3f667926..da5da542da 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java @@ -221,8 +221,14 @@ public class StringSegment implements CharSequence { return Utility.charSequenceHashCode(this); } + /** Returns a string representation useful for debugging. */ @Override public String toString() { return str.substring(0, start) + "[" + str.substring(start, end) + "]" + str.substring(end); } + + /** Returns a String that is equivalent to the CharSequence representation. */ + public String asString() { + return str.substring(start, end); + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java index a6878a1fd9..492f6b5893 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java @@ -53,6 +53,7 @@ class NumberSkeletonImpl { STATE_INCREMENT_PRECISION, STATE_MEASURE_UNIT, STATE_PER_MEASURE_UNIT, + STATE_IDENTIFIER_UNIT, STATE_CURRENCY_UNIT, STATE_INTEGER_WIDTH, STATE_NUMBERING_SYSTEM, @@ -76,6 +77,7 @@ class NumberSkeletonImpl { STEM_BASE_UNIT, STEM_PERCENT, STEM_PERMILLE, + STEM_PERCENT_100, // concise-only STEM_PRECISION_INTEGER, STEM_PRECISION_UNLIMITED, STEM_PRECISION_CURRENCY_STANDARD, @@ -113,6 +115,7 @@ class NumberSkeletonImpl { STEM_PRECISION_INCREMENT, STEM_MEASURE_UNIT, STEM_PER_MEASURE_UNIT, + STEM_UNIT, STEM_CURRENCY, STEM_INTEGER_WIDTH, STEM_NUMBERING_SYSTEM, @@ -174,11 +177,27 @@ class NumberSkeletonImpl { b.add("precision-increment", StemEnum.STEM_PRECISION_INCREMENT.ordinal()); b.add("measure-unit", StemEnum.STEM_MEASURE_UNIT.ordinal()); b.add("per-measure-unit", StemEnum.STEM_PER_MEASURE_UNIT.ordinal()); + b.add("unit", StemEnum.STEM_UNIT.ordinal()); b.add("currency", StemEnum.STEM_CURRENCY.ordinal()); b.add("integer-width", StemEnum.STEM_INTEGER_WIDTH.ordinal()); b.add("numbering-system", StemEnum.STEM_NUMBERING_SYSTEM.ordinal()); b.add("scale", StemEnum.STEM_SCALE.ordinal()); + // Section 3 (concise tokens): + b.add("K", StemEnum.STEM_COMPACT_SHORT.ordinal()); + b.add("KK", StemEnum.STEM_COMPACT_LONG.ordinal()); + b.add("%", StemEnum.STEM_PERCENT.ordinal()); + b.add("%x100", StemEnum.STEM_PERCENT_100.ordinal()); + b.add(",_", StemEnum.STEM_GROUP_OFF.ordinal()); + b.add(",?", StemEnum.STEM_GROUP_MIN2.ordinal()); + b.add(",!", StemEnum.STEM_GROUP_ON_ALIGNED.ordinal()); + b.add("+!", StemEnum.STEM_SIGN_ALWAYS.ordinal()); + b.add("+_", StemEnum.STEM_SIGN_NEVER.ordinal()); + b.add("()", StemEnum.STEM_SIGN_ACCOUNTING.ordinal()); + b.add("()!", StemEnum.STEM_SIGN_ACCOUNTING_ALWAYS.ordinal()); + b.add("+?", StemEnum.STEM_SIGN_EXCEPT_ZERO.ordinal()); + b.add("()?", StemEnum.STEM_SIGN_ACCOUNTING_EXCEPT_ZERO.ordinal()); + // Build the CharsTrie // TODO: Use SLOW or FAST here? return b.buildCharSequence(StringTrieBuilder.Option.FAST).toString(); @@ -604,6 +623,14 @@ class NumberSkeletonImpl { checkNull(macros.precision, segment); BlueprintHelpers.parseDigitsStem(segment, macros); return ParseState.STATE_NULL; + case 'E': + checkNull(macros.notation, segment); + BlueprintHelpers.parseScientificStem(segment, macros); + return ParseState.STATE_NULL; + case '0': + checkNull(macros.notation, segment); + BlueprintHelpers.parseIntegerStem(segment, macros); + return ParseState.STATE_NULL; } // Now look at the stemsTrie, which is already be pointing at our stem. @@ -641,6 +668,13 @@ class NumberSkeletonImpl { macros.unit = StemToObject.unit(stem); return ParseState.STATE_NULL; + case STEM_PERCENT_100: + checkNull(macros.scale, segment); + checkNull(macros.unit, segment); + macros.scale = Scale.powerOfTen(2); + macros.unit = NoUnit.PERCENT; + return ParseState.STATE_NULL; + case STEM_PRECISION_INTEGER: case STEM_PRECISION_UNLIMITED: case STEM_PRECISION_CURRENCY_STANDARD: @@ -720,6 +754,11 @@ class NumberSkeletonImpl { checkNull(macros.perUnit, segment); return ParseState.STATE_PER_MEASURE_UNIT; + case STEM_UNIT: + checkNull(macros.unit, segment); + checkNull(macros.perUnit, segment); + return ParseState.STATE_IDENTIFIER_UNIT; + case STEM_CURRENCY: checkNull(macros.unit, segment); return ParseState.STATE_CURRENCY_UNIT; @@ -761,6 +800,9 @@ class NumberSkeletonImpl { case STATE_PER_MEASURE_UNIT: BlueprintHelpers.parseMeasurePerUnitOption(segment, macros); return ParseState.STATE_NULL; + case STATE_IDENTIFIER_UNIT: + BlueprintHelpers.parseIdentifierUnitOption(segment, macros); + return ParseState.STATE_NULL; case STATE_INCREMENT_PRECISION: BlueprintHelpers.parseIncrementOption(segment, macros); return ParseState.STATE_NULL; @@ -970,7 +1012,7 @@ class NumberSkeletonImpl { } private static void parseMeasurePerUnitOption(StringSegment segment, MacroProps macros) { - // A little bit of a hack: safe the current unit (numerator), call the main measure unit + // A little bit of a hack: save the current unit (numerator), call the main measure unit // parsing code, put back the numerator unit, and put the new unit into per-unit. MeasureUnit numerator = macros.unit; parseMeasureUnitOption(segment, macros); @@ -978,6 +1020,17 @@ class NumberSkeletonImpl { macros.unit = numerator; } + private static void parseIdentifierUnitOption(StringSegment segment, MacroProps macros) { + MeasureUnit[] units = MeasureUnit.parseCoreUnitIdentifier(segment.asString()); + if (units == null) { + throw new SkeletonSyntaxException("Invalid core unit identifier", segment); + } + macros.unit = units[0]; + if (units.length == 2) { + macros.perUnit = units[1]; + } + } + private static void parseFractionStem(StringSegment segment, MacroProps macros) { assert segment.charAt(0) == '.'; int offset = 1; @@ -1012,7 +1065,11 @@ class NumberSkeletonImpl { } // Use the public APIs to enforce bounds checking if (maxFrac == -1) { - macros.precision = Precision.minFraction(minFrac); + if (minFrac == 0) { + macros.precision = Precision.unlimited(); + } else { + macros.precision = Precision.minFraction(minFrac); + } } else { macros.precision = Precision.minMaxFraction(minFrac, maxFrac); } @@ -1081,6 +1138,71 @@ class NumberSkeletonImpl { } } + private static void parseScientificStem(StringSegment segment, MacroProps macros) { + assert(segment.charAt(0) == 'E'); + block: + { + int offset = 1; + if (segment.length() == offset) { + break block; + } + boolean isEngineering = false; + if (segment.charAt(offset) == 'E') { + isEngineering = true; + offset++; + if (segment.length() == offset) { + break block; + } + } + SignDisplay signDisplay = SignDisplay.AUTO; + if (segment.charAt(offset) == '+') { + offset++; + if (segment.length() == offset) { + break block; + } + if (segment.charAt(offset) == '!') { + signDisplay = SignDisplay.ALWAYS; + } else if (segment.charAt(offset) == '?') { + signDisplay = SignDisplay.EXCEPT_ZERO; + } else { + break block; + } + offset++; + if (segment.length() == offset) { + break block; + } + } + int minDigits = 0; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) != '0') { + break block; + } + minDigits++; + } + macros.notation = (isEngineering ? Notation.engineering() : Notation.scientific()) + .withExponentSignDisplay(signDisplay) + .withMinExponentDigits(minDigits); + return; + } + throw new SkeletonSyntaxException("Invalid scientific stem", segment); + } + + private static void parseIntegerStem(StringSegment segment, MacroProps macros) { + assert(segment.charAt(0) == '0'); + int offset = 1; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) != '0') { + offset--; + break; + } + } + if (offset < segment.length()) { + throw new SkeletonSyntaxException("Invalid integer stem", segment); + } + macros.integerWidth = IntegerWidth.zeroFillTo(offset); + return; + } + /** @return Whether we successfully found and parsed a frac-sig option. */ private static boolean parseFracSigOption(StringSegment segment, MacroProps macros) { if (segment.charAt(0) != '@') { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java b/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java index eaf01d511a..357fe9612e 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java @@ -205,6 +205,47 @@ public class MeasureUnit implements Serializable { return MeasureUnit.addUnit(type, subType, factory); } + private static MeasureUnit findBySubType(String subType) { + populateCache(); + for (Map unitsForType : cache.values()) { + if (unitsForType.containsKey(subType)) { + return unitsForType.get(subType); + } + } + return null; + } + + /** + * For ICU use only. + * @internal + * @deprecated This API is ICU internal only. + */ + @Deprecated + public static MeasureUnit[] parseCoreUnitIdentifier(String coreUnitIdentifier) { + // First search for the whole code unit identifier as a subType + MeasureUnit whole = findBySubType(coreUnitIdentifier); + if (whole != null) { + return new MeasureUnit[] { whole }; // found a numerator but not denominator + } + + // If not found, try breaking apart numerator and denominator + int perIdx = coreUnitIdentifier.indexOf("-per-"); + if (perIdx == -1) { + // String does not contain "-per-" + return null; + } + String numeratorStr = coreUnitIdentifier.substring(0, perIdx); + String denominatorStr = coreUnitIdentifier.substring(perIdx + 5); + MeasureUnit numerator = findBySubType(numeratorStr); + MeasureUnit denominator = findBySubType(denominatorStr); + if (numerator != null && denominator != null) { + return new MeasureUnit[] { numerator, denominator }; // found both a numerator and denominator + } + + // The numerator or denominator were invalid + return null; + } + /** * For ICU use only. * @internal diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java index a12468f2c1..4ba49dcdaf 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java @@ -72,6 +72,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Basic", "", + "", NumberFormatter.with(), ULocale.ENGLISH, "87,650", @@ -87,6 +88,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Big Simple", "notation-simple", + "", NumberFormatter.with().notation(Notation.simple()), ULocale.ENGLISH, "87,650,000", @@ -102,6 +104,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Basic with Negative Sign", "", + "", NumberFormatter.with(), ULocale.ENGLISH, -9876543.21, @@ -113,6 +116,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Scientific", "scientific", + "E0", NumberFormatter.with().notation(Notation.scientific()), ULocale.ENGLISH, "8.765E4", @@ -128,6 +132,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Engineering", "engineering", + "EE0", NumberFormatter.with().notation(Notation.engineering()), ULocale.ENGLISH, "87.65E3", @@ -143,6 +148,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Scientific sign always shown", "scientific/sign-always", + "E+!0", NumberFormatter.with().notation(Notation.scientific().withExponentSignDisplay(SignDisplay.ALWAYS)), ULocale.ENGLISH, "8.765E+4", @@ -158,6 +164,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Scientific min exponent digits", "scientific/+ee", + "E00", NumberFormatter.with().notation(Notation.scientific().withMinExponentDigits(2)), ULocale.ENGLISH, "8.765E04", @@ -173,6 +180,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Scientific Negative", "scientific", + "E0", NumberFormatter.with().notation(Notation.scientific()), ULocale.ENGLISH, -1000000, @@ -181,6 +189,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Scientific Infinity", "scientific", + "E0", NumberFormatter.with().notation(Notation.scientific()), ULocale.ENGLISH, Double.NEGATIVE_INFINITY, @@ -189,6 +198,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Scientific NaN", "scientific", + "E0", NumberFormatter.with().notation(Notation.scientific()), ULocale.ENGLISH, Double.NaN, @@ -200,6 +210,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Compact Short", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, "88M", @@ -215,6 +226,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Compact Long", "compact-long", + "KK", NumberFormatter.with().notation(Notation.compactLong()), ULocale.ENGLISH, "88 million", @@ -230,6 +242,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Short Currency", "compact-short currency/USD", + "K currency/USD", NumberFormatter.with().notation(Notation.compactShort()).unit(USD), ULocale.ENGLISH, "$88K", @@ -245,6 +258,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Short with ISO Currency", "compact-short currency/USD unit-width-iso-code", + "K currency/USD unit-width-iso-code", NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.ISO_CODE), ULocale.ENGLISH, "USD 88K", @@ -260,6 +274,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Short with Long Name Currency", "compact-short currency/USD unit-width-full-name", + "K currency/USD unit-width-full-name", NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, "88K US dollars", @@ -277,6 +292,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Long Currency", "compact-long currency/USD", + "KK currency/USD", NumberFormatter.with().notation(Notation.compactLong()).unit(USD), ULocale.ENGLISH, "$88K", // should be something like "$88 thousand" @@ -294,6 +310,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Long with ISO Currency", "compact-long currency/USD unit-width-iso-code", + "KK currency/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" @@ -310,6 +327,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Long with Long Name Currency", "compact-long currency/USD unit-width-full-name", + "KK currency/USD unit-width-full-name", NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, "88 thousand US dollars", @@ -325,6 +343,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Plural One", "compact-long", + "KK", NumberFormatter.with().notation(Notation.compactLong()), ULocale.forLanguageTag("es"), 1000000, @@ -333,6 +352,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Plural Other", "compact-long", + "KK", NumberFormatter.with().notation(Notation.compactLong()), ULocale.forLanguageTag("es"), 2000000, @@ -341,6 +361,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact with Negative Sign", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, -9876543.21, @@ -349,6 +370,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Rounding", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, 990000, @@ -357,6 +379,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Rounding", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, 999000, @@ -365,6 +388,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Rounding", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, 999900, @@ -373,6 +397,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Rounding", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, 9900000, @@ -381,6 +406,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Rounding", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, 9990000, @@ -389,6 +415,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact in zh-Hant-HK", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), new ULocale("zh-Hant-HK"), 1e7, @@ -397,6 +424,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact in zh-Hant", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), new ULocale("zh-Hant"), 1e7, @@ -405,6 +433,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Infinity", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, Double.NEGATIVE_INFINITY, @@ -413,6 +442,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact NaN", "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.ENGLISH, Double.NaN, @@ -426,6 +456,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Compact Somali No Figure", null, // feature not supported in skeleton + null, NumberFormatter.with().notation(CompactNotation.forCustomData(compactCustomData)), ULocale.ENGLISH, 1000, @@ -437,6 +468,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Meters Short", "measure-unit/length-meter", + "unit/meter", NumberFormatter.with().unit(MeasureUnit.METER), ULocale.ENGLISH, "87,650 m", @@ -452,6 +484,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Meters Long", "measure-unit/length-meter unit-width-full-name", + "unit/meter unit-width-full-name", NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, "87,650 meters", @@ -467,6 +500,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Compact Meters Long", "compact-long measure-unit/length-meter unit-width-full-name", + "KK unit/meter unit-width-full-name", NumberFormatter.with().notation(Notation.compactLong()).unit(MeasureUnit.METER) .unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, @@ -483,6 +517,7 @@ public class NumberFormatterApiTest { assertFormatSingleMeasure( "Meters with Measure Input", "unit-width-full-name", + "unit-width-full-name", NumberFormatter.with().unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, new Measure(5.43, MeasureUnit.METER), @@ -491,6 +526,7 @@ public class NumberFormatterApiTest { assertFormatSingleMeasure( "Measure format method takes precedence over fluent chain", "measure-unit/length-meter", + "unit/meter", NumberFormatter.with().unit(MeasureUnit.METER), ULocale.ENGLISH, new Measure(5.43, USD), @@ -499,6 +535,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Meters with Negative Sign", "measure-unit/length-meter", + "unit/meter", NumberFormatter.with().unit(MeasureUnit.METER), ULocale.ENGLISH, -9876543.21, @@ -508,6 +545,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Interesting Data Fallback 1", "measure-unit/duration-day unit-width-full-name", + "unit/day unit-width-full-name", NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.FULL_NAME), ULocale.forLanguageTag("brx"), 5.43, @@ -517,6 +555,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Interesting Data Fallback 2", "measure-unit/duration-day unit-width-narrow", + "unit/day unit-width-narrow", NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.NARROW), ULocale.forLanguageTag("brx"), 5.43, @@ -527,6 +566,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Interesting Data Fallback 3", "measure-unit/area-square-meter unit-width-narrow", + "unit/square-meter unit-width-narrow", NumberFormatter.with().unit(MeasureUnit.SQUARE_METER).unitWidth(UnitWidth.NARROW), ULocale.forLanguageTag("en-GB"), 5.43, @@ -536,6 +576,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Interesting Data Fallback 4", "measure-unit/area-square-meter unit-width-narrow", + "unit/square-meter unit-width-narrow", NumberFormatter.with().unit(MeasureUnit.SQUARE_METER).unitWidth(UnitWidth.NARROW), ULocale.forLanguageTag("root"), 5.43, @@ -546,6 +587,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "MeasureUnit Difference between Narrow and Short (Narrow Version)", "measure-unit/temperature-fahrenheit unit-width-narrow", + "unit/fahrenheit unit-width-narrow", NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.NARROW), ULocale.forLanguageTag("es-US"), 5.43, @@ -554,6 +596,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "MeasureUnit Difference between Narrow and Short (Short Version)", "measure-unit/temperature-fahrenheit unit-width-short", + "unit/fahrenheit unit-width-short", NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.SHORT), ULocale.forLanguageTag("es-US"), 5.43, @@ -562,6 +605,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "MeasureUnit form without {0} in CLDR pattern", "measure-unit/temperature-kelvin unit-width-full-name", + "unit/kelvin unit-width-full-name", NumberFormatter.with().unit(MeasureUnit.KELVIN).unitWidth(UnitWidth.FULL_NAME), ULocale.forLanguageTag("es-MX"), 1, @@ -570,6 +614,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "MeasureUnit form without {0} in CLDR pattern and wide base form", "measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name", + "unit/kelvin .00000000000000000000 unit-width-full-name", NumberFormatter.with() .precision(Precision.fixedFraction(20)) .unit(MeasureUnit.KELVIN) @@ -584,6 +629,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Meters Per Second Short (unit that simplifies) and perUnit method", "measure-unit/length-meter per-measure-unit/duration-second", + "~unit/meter-per-second", // does not round-trip to the full skeleton above NumberFormatter.with().unit(MeasureUnit.METER).perUnit(MeasureUnit.SECOND), ULocale.ENGLISH, "87,650 m/s", @@ -599,6 +645,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Pounds Per Square Mile Short (secondary unit has per-format)", "measure-unit/mass-pound per-measure-unit/area-square-mile", + "unit/pound-per-square-mile", NumberFormatter.with().unit(MeasureUnit.POUND).perUnit(MeasureUnit.SQUARE_MILE), ULocale.ENGLISH, "87,650 lb/mi²", @@ -614,6 +661,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Joules Per Furlong Short (unit with no simplifications or special patterns)", "measure-unit/energy-joule per-measure-unit/length-furlong", + "unit/joule-per-furlong", NumberFormatter.with().unit(MeasureUnit.JOULE).perUnit(MeasureUnit.FURLONG), ULocale.ENGLISH, "87,650 J/fur", @@ -632,6 +680,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency", "currency/GBP", + "currency/GBP", NumberFormatter.with().unit(GBP), ULocale.ENGLISH, "£87,650.00", @@ -647,6 +696,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency ISO", "currency/GBP unit-width-iso-code", + "currency/GBP unit-width-iso-code", NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.ISO_CODE), ULocale.ENGLISH, "GBP 87,650.00", @@ -662,6 +712,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency Long Name", "currency/GBP unit-width-full-name", + "currency/GBP unit-width-full-name", NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, "87,650.00 British pounds", @@ -677,6 +728,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency Hidden", "currency/GBP unit-width-hidden", + "currency/GBP unit-width-hidden", NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.HIDDEN), ULocale.ENGLISH, "87,650.00", @@ -692,6 +744,7 @@ public class NumberFormatterApiTest { assertFormatSingleMeasure( "Currency with CurrencyAmount Input", "", + "", NumberFormatter.with(), ULocale.ENGLISH, new CurrencyAmount(5.43, GBP), @@ -700,6 +753,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency Long Name from Pattern Syntax", null, + null, NumberFormatter.fromDecimalFormat( PatternStringParser.parseToProperties("0 ¤¤¤"), DecimalFormatSymbols.getInstance(ULocale.ENGLISH), @@ -711,6 +765,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency with Negative Sign", "currency/GBP", + "currency/GBP", NumberFormatter.with().unit(GBP), ULocale.ENGLISH, -9876543.21, @@ -721,6 +776,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency Difference between Narrow and Short (Narrow Version)", "currency/USD unit-width-narrow", + "currency/USD unit-width-narrow", NumberFormatter.with().unit(USD).unitWidth(UnitWidth.NARROW), ULocale.forLanguageTag("en-CA"), 5.43, @@ -729,6 +785,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency Difference between Narrow and Short (Short Version)", "currency/USD unit-width-short", + "currency/USD unit-width-short", NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT), ULocale.forLanguageTag("en-CA"), 5.43, @@ -737,6 +794,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency-dependent format (Control)", "currency/USD unit-width-short", + "currency/USD unit-width-short", NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT), ULocale.forLanguageTag("ca"), 444444.55, @@ -745,6 +803,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency-dependent format (Test)", "currency/ESP unit-width-short", + "currency/ESP unit-width-short", NumberFormatter.with().unit(ESP).unitWidth(UnitWidth.SHORT), ULocale.forLanguageTag("ca"), 444444.55, @@ -753,6 +812,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency-dependent symbols (Control)", "currency/USD unit-width-short", + "currency/USD unit-width-short", NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT), ULocale.forLanguageTag("pt-PT"), 444444.55, @@ -763,6 +823,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency-dependent symbols (Test Short)", "currency/PTE unit-width-short", + "currency/PTE unit-width-short", NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.SHORT), ULocale.forLanguageTag("pt-PT"), 444444.55, @@ -771,6 +832,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency-dependent symbols (Test Narrow)", "currency/PTE unit-width-narrow", + "currency/PTE unit-width-narrow", NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.NARROW), ULocale.forLanguageTag("pt-PT"), 444444.55, @@ -779,6 +841,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency-dependent symbols (Test ISO Code)", "currency/PTE unit-width-iso-code", + "currency/PTE unit-width-iso-code", NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.ISO_CODE), ULocale.forLanguageTag("pt-PT"), 444444.55, @@ -787,6 +850,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Plural form depending on visible digits (ICU-20499)", "currency/RON unit-width-full-name", + "currency/RON unit-width-full-name", NumberFormatter.with().unit(RON).unitWidth(UnitWidth.FULL_NAME), ULocale.forLanguageTag("ro-RO"), 24, @@ -798,6 +862,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Percent", "percent", + "%", NumberFormatter.with().unit(NoUnit.PERCENT), ULocale.ENGLISH, "87,650%", @@ -813,6 +878,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Permille", "permille", + "permille", NumberFormatter.with().unit(NoUnit.PERMILLE), ULocale.ENGLISH, "87,650‰", @@ -828,6 +894,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "NoUnit Base", "base-unit", + "", NumberFormatter.with().unit(NoUnit.BASE), ULocale.ENGLISH, 51423, @@ -836,6 +903,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Percent with Negative Sign", "percent", + "%", NumberFormatter.with().unit(NoUnit.PERCENT), ULocale.ENGLISH, -98.7654321, @@ -847,6 +915,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer", "precision-integer", + ".", NumberFormatter.with().precision(Precision.integer()), ULocale.ENGLISH, "87,650", @@ -862,6 +931,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Fixed Fraction", ".000", + ".000", NumberFormatter.with().precision(Precision.fixedFraction(3)), ULocale.ENGLISH, "87,650.000", @@ -877,6 +947,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Min Fraction", ".0+", + ".0+", NumberFormatter.with().precision(Precision.minFraction(1)), ULocale.ENGLISH, "87,650.0", @@ -892,6 +963,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Max Fraction", ".#", + ".#", NumberFormatter.with().precision(Precision.maxFraction(1)), ULocale.ENGLISH, "87,650", @@ -907,6 +979,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Min/Max Fraction", ".0##", + ".0##", NumberFormatter.with().precision(Precision.minMaxFraction(1, 3)), ULocale.ENGLISH, "87,650.0", @@ -925,6 +998,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Fixed Significant", "@@@", + "@@@", NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)), ULocale.ENGLISH, -98, @@ -933,6 +1007,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Fixed Significant Rounding", "@@@", + "@@@", NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)), ULocale.ENGLISH, -98.7654321, @@ -941,6 +1016,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Fixed Significant Zero", "@@@", + "@@@", NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)), ULocale.ENGLISH, 0, @@ -949,6 +1025,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Min Significant", "@@+", + "@@+", NumberFormatter.with().precision(Precision.minSignificantDigits(2)), ULocale.ENGLISH, -9, @@ -957,6 +1034,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Max Significant", "@###", + "@###", NumberFormatter.with().precision(Precision.maxSignificantDigits(4)), ULocale.ENGLISH, 98.7654321, @@ -965,6 +1043,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Min/Max Significant", "@@@#", + "@@@#", NumberFormatter.with().precision(Precision.minMaxSignificantDigits(3, 4)), ULocale.ENGLISH, 9.99999, @@ -973,6 +1052,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Fixed Significant on zero with zero integer width", "@ integer-width/+", + "@ integer-width/+", NumberFormatter.with().precision(Precision.fixedSignificantDigits(1)).integerWidth(IntegerWidth.zeroFillTo(0)), ULocale.ENGLISH, 0, @@ -981,6 +1061,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Fixed Significant on zero with lots of integer width", "@ integer-width/+000", + "@ 000", NumberFormatter.with().precision(Precision.fixedSignificantDigits(1)).integerWidth(IntegerWidth.zeroFillTo(3)), ULocale.ENGLISH, 0, @@ -992,6 +1073,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Basic Significant", // for comparison "@#", + "@#", NumberFormatter.with().precision(Precision.maxSignificantDigits(2)), ULocale.ENGLISH, "88,000", @@ -1007,6 +1089,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "FracSig minMaxFrac minSig", ".0#/@@@+", + ".0#/@@@+", NumberFormatter.with().precision(Precision.minMaxFraction(1, 2).withMinDigits(3)), ULocale.ENGLISH, "87,650.0", @@ -1022,6 +1105,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "FracSig minMaxFrac maxSig A", ".0##/@#", + ".0##/@#", NumberFormatter.with().precision(Precision.minMaxFraction(1, 3).withMaxDigits(2)), ULocale.ENGLISH, "88,000.0", // maxSig beats maxFrac @@ -1037,6 +1121,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "FracSig minMaxFrac maxSig B", ".00/@#", + ".00/@#", NumberFormatter.with().precision(Precision.fixedFraction(2).withMaxDigits(2)), ULocale.ENGLISH, "88,000.00", // maxSig beats maxFrac @@ -1052,6 +1137,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "FracSig minFrac maxSig", ".0+/@#", + ".0+/@#", NumberFormatter.with().precision(Precision.minFraction(1).withMaxDigits(2)), ULocale.ENGLISH, "88,000.0", @@ -1067,6 +1153,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "FracSig with trailing zeros A", ".00/@@@+", + ".00/@@@+", NumberFormatter.with().precision(Precision.fixedFraction(2).withMinDigits(3)), ULocale.ENGLISH, 0.1, @@ -1075,6 +1162,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "FracSig with trailing zeros B", ".00/@@@+", + ".00/@@@+", NumberFormatter.with().precision(Precision.fixedFraction(2).withMinDigits(3)), ULocale.ENGLISH, 0.0999999, @@ -1086,6 +1174,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Rounding None", "precision-unlimited", + ".+", NumberFormatter.with().precision(Precision.unlimited()), ULocale.ENGLISH, "87,650", @@ -1101,6 +1190,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Increment", "precision-increment/0.5", + "precision-increment/0.5", NumberFormatter.with().precision(Precision.increment(BigDecimal.valueOf(0.5))), ULocale.ENGLISH, "87,650.0", @@ -1116,6 +1206,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Increment with Min Fraction", "precision-increment/0.50", + "precision-increment/0.50", NumberFormatter.with().precision(Precision.increment(new BigDecimal("0.50"))), ULocale.ENGLISH, "87,650.00", @@ -1131,6 +1222,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Strange Increment", "precision-increment/3.140", + "precision-increment/3.140", NumberFormatter.with().precision(Precision.increment(new BigDecimal("3.140"))), ULocale.ENGLISH, "87,649.960", @@ -1146,6 +1238,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Increment Resolving to Power of 10", "precision-increment/0.010", + "precision-increment/0.010", NumberFormatter.with().precision(Precision.increment(new BigDecimal("0.010"))), ULocale.ENGLISH, "87,650.000", @@ -1161,6 +1254,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency Standard", "currency/CZK precision-currency-standard", + "currency/CZK precision-currency-standard", NumberFormatter.with().precision(Precision.currency(CurrencyUsage.STANDARD)).unit(CZK), ULocale.ENGLISH, "CZK 87,650.00", @@ -1176,6 +1270,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency Cash", "currency/CZK precision-currency-cash", + "currency/CZK precision-currency-cash", NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH)).unit(CZK), ULocale.ENGLISH, "CZK 87,650", @@ -1191,6 +1286,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency Cash with Nickel Rounding", "currency/CAD precision-currency-cash", + "currency/CAD precision-currency-cash", NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH)).unit(CAD), ULocale.ENGLISH, "CA$87,650.00", @@ -1206,6 +1302,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Currency not in top-level fluent chain", "precision-integer", // calling .withCurrency() applies currency rounding rules immediately + ".", NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH).withCurrency(CZK)), ULocale.ENGLISH, "87,650", @@ -1222,6 +1319,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Rounding Mode CEILING", "precision-integer rounding-mode-ceiling", + ". rounding-mode-ceiling", NumberFormatter.with().precision(Precision.integer()).roundingMode(RoundingMode.CEILING), ULocale.ENGLISH, "87,650", @@ -1240,6 +1338,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Western Grouping", "group-auto", + "", NumberFormatter.with().grouping(GroupingStrategy.AUTO), ULocale.ENGLISH, "87,650,000", @@ -1255,6 +1354,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Indic Grouping", "group-auto", + "", NumberFormatter.with().grouping(GroupingStrategy.AUTO), new ULocale("en-IN"), "8,76,50,000", @@ -1270,6 +1370,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Western Grouping, Min 2", "group-min2", + ",?", NumberFormatter.with().grouping(GroupingStrategy.MIN2), ULocale.ENGLISH, "87,650,000", @@ -1285,6 +1386,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Indic Grouping, Min 2", "group-min2", + ",?", NumberFormatter.with().grouping(GroupingStrategy.MIN2), new ULocale("en-IN"), "8,76,50,000", @@ -1300,6 +1402,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "No Grouping", "group-off", + ",_", NumberFormatter.with().grouping(GroupingStrategy.OFF), new ULocale("en-IN"), "87650000", @@ -1315,6 +1418,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Indic locale with THOUSANDS grouping", "group-thousands", + "group-thousands", NumberFormatter.with().grouping(GroupingStrategy.THOUSANDS), new ULocale("en-IN"), "87,650,000", @@ -1333,6 +1437,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Polish Grouping", "group-auto", + "", NumberFormatter.with().grouping(GroupingStrategy.AUTO), new ULocale("pl"), "87 650 000", @@ -1348,6 +1453,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Polish Grouping, Min 2", "group-min2", + ",?", NumberFormatter.with().grouping(GroupingStrategy.MIN2), new ULocale("pl"), "87 650 000", @@ -1363,6 +1469,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Polish Grouping, Always", "group-on-aligned", + ",!", NumberFormatter.with().grouping(GroupingStrategy.ON_ALIGNED), new ULocale("pl"), "87 650 000", @@ -1380,6 +1487,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Bulgarian Currency Grouping", "currency/USD group-auto", + "currency/USD", NumberFormatter.with().grouping(GroupingStrategy.AUTO).unit(USD), new ULocale("bg"), "87650000,00 щ.д.", @@ -1395,6 +1503,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Bulgarian Currency Grouping, Always", "currency/USD group-on-aligned", + "currency/USD ,!", NumberFormatter.with().grouping(GroupingStrategy.ON_ALIGNED).unit(USD), new ULocale("bg"), "87 650 000,00 щ.д.", @@ -1412,6 +1521,7 @@ public class NumberFormatterApiTest { assertFormatDescendingBig( "Custom Grouping via Internal API", null, + null, NumberFormatter.with().macros(macros), ULocale.ENGLISH, "8,7,6,5,0000", @@ -1430,6 +1540,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Padding", null, + null, NumberFormatter.with().padding(Padder.none()), ULocale.ENGLISH, "87,650", @@ -1445,6 +1556,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Padding", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)), ULocale.ENGLISH, "**87,650", @@ -1460,6 +1572,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Padding with code points", null, + null, NumberFormatter.with().padding(Padder.codePoints(0x101E4, 8, PadPosition.AFTER_PREFIX)), ULocale.ENGLISH, "𐇤𐇤87,650", @@ -1475,6 +1588,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Padding with wide digits", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)) .symbols(NumberingSystem.getInstanceByName("mathsanb")), ULocale.ENGLISH, @@ -1491,6 +1605,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Padding with currency spacing", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 10, PadPosition.AFTER_PREFIX)).unit(GBP) .unitWidth(UnitWidth.ISO_CODE), ULocale.ENGLISH, @@ -1507,6 +1622,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Pad Before Prefix", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_PREFIX)), ULocale.ENGLISH, -88.88, @@ -1515,6 +1631,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Pad After Prefix", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)), ULocale.ENGLISH, -88.88, @@ -1523,6 +1640,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Pad Before Suffix", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_SUFFIX)) .unit(NoUnit.PERCENT), ULocale.ENGLISH, @@ -1532,6 +1650,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Pad After Suffix", null, + null, NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_SUFFIX)) .unit(NoUnit.PERCENT), ULocale.ENGLISH, @@ -1541,6 +1660,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency Spacing with Zero Digit Padding Broken", null, + null, NumberFormatter.with().padding(Padder.codePoints('0', 12, PadPosition.AFTER_PREFIX)).unit(GBP) .unitWidth(UnitWidth.ISO_CODE), ULocale.ENGLISH, @@ -1553,6 +1673,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Default", "integer-width/+0", + "0", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(1)), ULocale.ENGLISH, "87,650", @@ -1568,6 +1689,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Zero Fill 0", "integer-width/+", + "integer-width/+", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(0)), ULocale.ENGLISH, "87,650", @@ -1583,6 +1705,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Zero Fill 3", "integer-width/+000", + "000", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(3)), ULocale.ENGLISH, "87,650", @@ -1598,6 +1721,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Max 3", "integer-width/##0", + "integer-width/##0", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(1).truncateAt(3)), ULocale.ENGLISH, "650", @@ -1613,6 +1737,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Fixed 2", "integer-width/00", + "integer-width/00", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)), ULocale.ENGLISH, "50", @@ -1628,6 +1753,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Compact", "compact-short integer-width/000", + "K integer-width/000", NumberFormatter.with() .notation(Notation.compactShort()) .integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)), @@ -1645,6 +1771,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Scientific", "scientific integer-width/000", + "E0 integer-width/000", NumberFormatter.with() .notation(Notation.scientific()) .integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)), @@ -1662,6 +1789,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Integer Width Engineering", "engineering integer-width/000", + "EE0 integer-width/000", NumberFormatter.with() .notation(Notation.engineering()) .integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)), @@ -1679,6 +1807,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Integer Width Remove All A", "integer-width/00", + "integer-width/00", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)), ULocale.ENGLISH, 2500, @@ -1687,6 +1816,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Integer Width Remove All B", "integer-width/00", + "integer-width/00", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)), ULocale.ENGLISH, 25000, @@ -1695,6 +1825,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Integer Width Remove All B, Bytes Mode", "integer-width/00", + "integer-width/00", NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)), ULocale.ENGLISH, // Note: this double produces all 17 significant digits @@ -1707,6 +1838,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "French Symbols with Japanese Data 1", null, + null, NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(ULocale.FRENCH)), ULocale.JAPAN, "87\u202F650", @@ -1722,6 +1854,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "French Symbols with Japanese Data 2", null, + null, NumberFormatter.with().notation(Notation.compactShort()) .symbols(DecimalFormatSymbols.getInstance(ULocale.FRENCH)), ULocale.JAPAN, @@ -1731,6 +1864,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Latin Numbering System with Arabic Data", "currency/USD latin", + "currency/USD latin", NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD), new ULocale("ar"), "US$ 87,650.00", @@ -1746,6 +1880,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Math Numbering System with French Data", "numbering-system/mathsanb", + "numbering-system/mathsanb", NumberFormatter.with().symbols(NumberingSystem.getInstanceByName("mathsanb")), ULocale.FRENCH, "𝟴𝟳\u202f𝟲𝟱𝟬", @@ -1761,6 +1896,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Swiss Symbols (used in documentation)", null, + null, NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(new ULocale("de-CH"))), ULocale.ENGLISH, 12345.67, @@ -1769,6 +1905,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Myanmar Symbols (used in documentation)", null, + null, NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(new ULocale("my_MY"))), ULocale.ENGLISH, 12345.67, @@ -1779,6 +1916,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency symbol should precede number in ar with NS latn", "currency/USD latin", + "currency/USD latin", NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD), new ULocale("ar"), 12345.67, @@ -1787,6 +1925,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency symbol should precede number in ar@numbers=latn", "currency/USD", + "currency/USD", NumberFormatter.with().unit(USD), new ULocale("ar@numbers=latn"), 12345.67, @@ -1795,6 +1934,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency symbol should follow number in ar-EG with NS arab", "currency/USD", + "currency/USD", NumberFormatter.with().unit(USD), new ULocale("ar-EG"), 12345.67, @@ -1803,6 +1943,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Currency symbol should follow number in ar@numbers=arab", "currency/USD", + "currency/USD", NumberFormatter.with().unit(USD), new ULocale("ar@numbers=arab"), 12345.67, @@ -1811,6 +1952,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "NumberingSystem in API should win over @numbers keyword", "currency/USD latin", + "currency/USD latin", NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD), new ULocale("ar@numbers=arab"), 12345.67, @@ -1830,6 +1972,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Symbols object should be copied", null, + null, f, ULocale.ENGLISH, 12345.67, @@ -1838,6 +1981,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "The last symbols setter wins", "latin", + "latin", NumberFormatter.with().symbols(symbols).symbols(NumberingSystem.LATIN), ULocale.ENGLISH, 12345.67, @@ -1846,6 +1990,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "The last symbols setter wins", null, + null, NumberFormatter.with().symbols(NumberingSystem.LATIN).symbols(symbols), ULocale.ENGLISH, 12345.67, @@ -1861,6 +2006,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Custom Short Currency Symbol", "$XXX", + "$XXX", NumberFormatter.with().unit(Currency.getInstance("XXX")).symbols(dfs), ULocale.ENGLISH, 12.3, @@ -1872,6 +2018,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Auto Positive", "sign-auto", + "", NumberFormatter.with().sign(SignDisplay.AUTO), ULocale.ENGLISH, 444444, @@ -1880,6 +2027,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Auto Negative", "sign-auto", + "", NumberFormatter.with().sign(SignDisplay.AUTO), ULocale.ENGLISH, -444444, @@ -1888,6 +2036,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Auto Zero", "sign-auto", + "", NumberFormatter.with().sign(SignDisplay.AUTO), ULocale.ENGLISH, 0, @@ -1896,6 +2045,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Always Positive", "sign-always", + "+!", NumberFormatter.with().sign(SignDisplay.ALWAYS), ULocale.ENGLISH, 444444, @@ -1904,6 +2054,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Always Negative", "sign-always", + "+!", NumberFormatter.with().sign(SignDisplay.ALWAYS), ULocale.ENGLISH, -444444, @@ -1912,6 +2063,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Always Zero", "sign-always", + "+!", NumberFormatter.with().sign(SignDisplay.ALWAYS), ULocale.ENGLISH, 0, @@ -1920,6 +2072,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Never Positive", "sign-never", + "+_", NumberFormatter.with().sign(SignDisplay.NEVER), ULocale.ENGLISH, 444444, @@ -1928,6 +2081,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Never Negative", "sign-never", + "+_", NumberFormatter.with().sign(SignDisplay.NEVER), ULocale.ENGLISH, -444444, @@ -1936,6 +2090,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Never Zero", "sign-never", + "+_", NumberFormatter.with().sign(SignDisplay.NEVER), ULocale.ENGLISH, 0, @@ -1944,6 +2099,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Positive", "currency/USD sign-accounting", + "currency/USD ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD), ULocale.ENGLISH, 444444, @@ -1952,6 +2108,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Negative", "currency/USD sign-accounting", + "currency/USD ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD), ULocale.ENGLISH, -444444, @@ -1960,6 +2117,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Zero", "currency/USD sign-accounting", + "currency/USD ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD), ULocale.ENGLISH, 0, @@ -1968,6 +2126,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting-Always Positive", "currency/USD sign-accounting-always", + "currency/USD ()!", NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD), ULocale.ENGLISH, 444444, @@ -1976,6 +2135,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting-Always Negative", "currency/USD sign-accounting-always", + "currency/USD ()!", NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD), ULocale.ENGLISH, -444444, @@ -1984,6 +2144,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting-Always Zero", "currency/USD sign-accounting-always", + "currency/USD ()!", NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD), ULocale.ENGLISH, 0, @@ -1992,6 +2153,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Except-Zero Positive", "sign-except-zero", + "+?", NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO), ULocale.ENGLISH, 444444, @@ -2000,6 +2162,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Except-Zero Negative", "sign-except-zero", + "+?", NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO), ULocale.ENGLISH, -444444, @@ -2008,6 +2171,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Except-Zero Zero", "sign-except-zero", + "+?", NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO), ULocale.ENGLISH, 0, @@ -2016,6 +2180,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting-Except-Zero Positive", "currency/USD sign-accounting-except-zero", + "currency/USD ()?", NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD), ULocale.ENGLISH, 444444, @@ -2024,6 +2189,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting-Except-Zero Negative", "currency/USD sign-accounting-except-zero", + "currency/USD ()?", NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD), ULocale.ENGLISH, -444444, @@ -2032,6 +2198,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting-Except-Zero Zero", "currency/USD sign-accounting-except-zero", + "currency/USD ()?", NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD), ULocale.ENGLISH, 0, @@ -2040,6 +2207,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Negative Hidden", "currency/USD unit-width-hidden sign-accounting", + "currency/USD unit-width-hidden ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.HIDDEN), ULocale.ENGLISH, -444444, @@ -2048,6 +2216,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Negative Narrow", "currency/USD unit-width-narrow sign-accounting", + "currency/USD unit-width-narrow ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.NARROW), ULocale.CANADA, -444444, @@ -2056,6 +2225,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Negative Short", "currency/USD sign-accounting", + "currency/USD ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.SHORT), ULocale.CANADA, -444444, @@ -2064,6 +2234,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Negative Iso Code", "currency/USD unit-width-iso-code sign-accounting", + "currency/USD unit-width-iso-code ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.ISO_CODE), ULocale.CANADA, -444444, @@ -2074,6 +2245,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Sign Accounting Negative Full Name", "currency/USD unit-width-full-name sign-accounting", + "currency/USD unit-width-full-name ()", NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.FULL_NAME), ULocale.CANADA, -444444, @@ -2154,6 +2326,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Decimal Default", "decimal-auto", + "", NumberFormatter.with().decimal(DecimalSeparatorDisplay.AUTO), ULocale.ENGLISH, "87,650", @@ -2169,6 +2342,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Decimal Always Shown", "decimal-always", + "decimal-always", NumberFormatter.with().decimal(DecimalSeparatorDisplay.ALWAYS), ULocale.ENGLISH, "87,650.", @@ -2187,6 +2361,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Multiplier None", "scale/1", + "", NumberFormatter.with().scale(Scale.none()), ULocale.ENGLISH, "87,650", @@ -2202,6 +2377,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Multiplier Power of Ten", "scale/1000000", + "scale/1000000", NumberFormatter.with().scale(Scale.powerOfTen(6)), ULocale.ENGLISH, "87,650,000,000", @@ -2217,6 +2393,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Multiplier Arbitrary Double", "scale/5.2", + "scale/5.2", NumberFormatter.with().scale(Scale.byDouble(5.2)), ULocale.ENGLISH, "455,780", @@ -2232,6 +2409,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Multiplier Arbitrary BigDecimal", "scale/5.2", + "scale/5.2", NumberFormatter.with().scale(Scale.byBigDecimal(new BigDecimal("5.2"))), ULocale.ENGLISH, "455,780", @@ -2247,6 +2425,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Multiplier Arbitrary Double And Power Of Ten", "scale/5200", + "scale/5200", NumberFormatter.with().scale(Scale.byDoubleAndPowerOfTen(5.2, 3)), ULocale.ENGLISH, "455,780,000", @@ -2262,6 +2441,7 @@ public class NumberFormatterApiTest { assertFormatDescending( "Multiplier Zero", "scale/0", + "scale/0", NumberFormatter.with().scale(Scale.byDouble(0)), ULocale.ENGLISH, "0", @@ -2277,6 +2457,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Multiplier Skeleton Scientific Notation and Percent", "percent scale/1E2", + "%x100", NumberFormatter.with().unit(NoUnit.PERCENT).scale(Scale.powerOfTen(2)), ULocale.ENGLISH, 0.5, @@ -2285,6 +2466,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Negative Multiplier", "scale/-5.2", + "scale/-5.2", NumberFormatter.with().scale(Scale.byDouble(-5.2)), ULocale.ENGLISH, 2, @@ -2293,6 +2475,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Negative One Multiplier", "scale/-1", + "scale/-1", NumberFormatter.with().scale(Scale.byDouble(-1)), ULocale.ENGLISH, 444444, @@ -2301,6 +2484,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Two-Type Multiplier with Overlap", "scale/10000", + "scale/10000", NumberFormatter.with().scale(Scale.byDoubleAndPowerOfTen(100, 2)), ULocale.ENGLISH, 2, @@ -2346,6 +2530,7 @@ public class NumberFormatterApiTest { FormattedNumber fmtd = assertFormatSingle( message, "", + "", NumberFormatter.with(), ULocale.ENGLISH, -9876543210.12, @@ -2399,6 +2584,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "measure-unit/temperature-fahrenheit", + "unit/fahrenheit", NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT), ULocale.ENGLISH, 68, @@ -2418,6 +2604,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "measure-unit/temperature-fahrenheit per-measure-unit/duration-day", + "unit/fahrenheit-per-day", NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).perUnit(MeasureUnit.DAY), ULocale.ENGLISH, 68, @@ -2437,6 +2624,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "measure-unit/length-meter unit-width-full-name", + "unit/meter unit-width-full-name", NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, 68, @@ -2457,6 +2645,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name", + "~unit/meter-per-second unit-width-full-name", // does not round-trip to the full skeleton above NumberFormatter.with().unit(MeasureUnit.METER).perUnit(MeasureUnit.SECOND).unitWidth(UnitWidth.FULL_NAME), new ULocale("ky"), // locale with the interesting data 68, @@ -2477,6 +2666,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "measure-unit/temperature-fahrenheit unit-width-full-name", + "unit/fahrenheit unit-width-full-name", NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.FULL_NAME), new ULocale("vi"), // locale with the interesting data 68, @@ -2500,6 +2690,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "measure-unit/temperature-kelvin", + "unit/kelvin", NumberFormatter.with().unit(MeasureUnit.KELVIN), new ULocale("fa"), // locale with the interesting data 68, @@ -2519,6 +2710,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "compact-short", + "K", NumberFormatter.with().notation(Notation.compactShort()), ULocale.US, 65000, @@ -2538,6 +2730,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "compact-long", + "KK", NumberFormatter.with().notation(Notation.compactLong()), ULocale.US, 65000, @@ -2557,6 +2750,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "compact-long", + "KK", NumberFormatter.with().notation(Notation.compactLong()), new ULocale("fil"), // locale with interesting data 6000, @@ -2576,6 +2770,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "compact-long", + "KK", NumberFormatter.with().notation(Notation.compactLong()), new ULocale("he"), // locale with interesting data 6000, @@ -2595,6 +2790,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "compact-short currency/USD", + "K currency/USD", NumberFormatter.with().notation(Notation.compactShort()).unit(USD), new ULocale("sr_Latn"), // locale with interesting data 65000, @@ -2615,6 +2811,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "currency/USD unit-width-full-name", + "currency/USD unit-width-full-name", NumberFormatter.with().unit(USD) .unitWidth(UnitWidth.FULL_NAME), ULocale.ENGLISH, @@ -2638,6 +2835,7 @@ public class NumberFormatterApiTest { FormattedNumber result = assertFormatSingle( message, "compact-long measure-unit/length-meter unit-width-full-name", + "KK unit/meter unit-width-full-name", NumberFormatter.with().notation(Notation.compactLong()) .unit(MeasureUnit.METER) .unitWidth(UnitWidth.FULL_NAME), @@ -2704,6 +2902,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Plural 1", "currency/USD precision-integer unit-width-full-name", + "currency/USD . unit-width-full-name", NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).precision(Precision.fixedFraction(0)), ULocale.ENGLISH, 1, @@ -2712,6 +2911,7 @@ public class NumberFormatterApiTest { assertFormatSingle( "Plural 1.00", "currency/USD .00 unit-width-full-name", + "currency/USD .00 unit-width-full-name", NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).precision(Precision.fixedFraction(2)), ULocale.ENGLISH, 1, @@ -2835,26 +3035,29 @@ public class NumberFormatterApiTest { static void assertFormatDescending( String message, String skeleton, + String conciseSkeleton, UnlocalizedNumberFormatter f, ULocale locale, String... expected) { final double[] inputs = new double[] { 87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0 }; - assertFormatDescending(message, skeleton, f, locale, inputs, expected); + assertFormatDescending(message, skeleton, conciseSkeleton, f, locale, inputs, expected); } static void assertFormatDescendingBig( String message, String skeleton, + String conciseSkeleton, UnlocalizedNumberFormatter f, ULocale locale, String... expected) { final double[] inputs = new double[] { 87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0 }; - assertFormatDescending(message, skeleton, f, locale, inputs, expected); + assertFormatDescending(message, skeleton, conciseSkeleton, f, locale, inputs, expected); } static void assertFormatDescending( String message, String skeleton, + String conciseSkeleton, UnlocalizedNumberFormatter f, ULocale locale, double[] inputs, @@ -2880,6 +3083,22 @@ public class NumberFormatterApiTest { String actual3 = l3.format(d).toString(); assertEquals(message + ": Skeleton Path: " + d, expected[i], actual3); } + // Concise skeletons should have same output, and usually round-trip to the normalized skeleton. + // If the concise skeleton starts with '~', disable the round-trip check. + boolean shouldRoundTrip = true; + if (conciseSkeleton.length() > 0 && conciseSkeleton.charAt(0) == '~') { + conciseSkeleton = conciseSkeleton.substring(1); + shouldRoundTrip = false; + } + LocalizedNumberFormatter l4 = NumberFormatter.forSkeleton(conciseSkeleton).locale(locale); + if (shouldRoundTrip) { + assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton()); + } + for (int i = 0; i < 9; i++) { + double d = inputs[i]; + String actual4 = l4.format(d).toString(); + assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expected[i], actual4); + } } else { assertUndefinedSkeleton(f); } @@ -2888,6 +3107,7 @@ public class NumberFormatterApiTest { static FormattedNumber assertFormatSingle( String message, String skeleton, + String conciseSkeleton, UnlocalizedNumberFormatter f, ULocale locale, Number input, @@ -2907,6 +3127,19 @@ public class NumberFormatterApiTest { LocalizedNumberFormatter l3 = NumberFormatter.forSkeleton(normalized).locale(locale); String actual3 = l3.format(input).toString(); assertEquals(message + ": Skeleton Path: " + input, expected, actual3); + // Concise skeletons should have same output, and usually round-trip to the normalized skeleton. + // If the concise skeleton starts with '~', disable the round-trip check. + boolean shouldRoundTrip = true; + if (conciseSkeleton.length() > 0 && conciseSkeleton.charAt(0) == '~') { + conciseSkeleton = conciseSkeleton.substring(1); + shouldRoundTrip = false; + } + LocalizedNumberFormatter l4 = NumberFormatter.forSkeleton(conciseSkeleton).locale(locale); + if (shouldRoundTrip) { + assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton()); + } + String actual4 = l4.format(input).toString(); + assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4); } else { assertUndefinedSkeleton(f); } @@ -2916,6 +3149,7 @@ public class NumberFormatterApiTest { static void assertFormatSingleMeasure( String message, String skeleton, + String conciseSkeleton, UnlocalizedNumberFormatter f, ULocale locale, Measure input, @@ -2934,6 +3168,19 @@ public class NumberFormatterApiTest { LocalizedNumberFormatter l3 = NumberFormatter.forSkeleton(normalized).locale(locale); String actual3 = l3.format(input).toString(); assertEquals(message + ": Skeleton Path: " + input, expected, actual3); + // Concise skeletons should have same output, and usually round-trip to the normalized skeleton. + // If the concise skeleton starts with '~', disable the round-trip check. + boolean shouldRoundTrip = true; + if (conciseSkeleton.length() > 0 && conciseSkeleton.charAt(0) == '~') { + conciseSkeleton = conciseSkeleton.substring(1); + shouldRoundTrip = false; + } + LocalizedNumberFormatter l4 = NumberFormatter.forSkeleton(conciseSkeleton).locale(locale); + if (shouldRoundTrip) { + assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton()); + } + String actual4 = l4.format(input).toString(); + assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4); } else { assertUndefinedSkeleton(f); } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java index 8d5f83ba65..a2c9553301 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java @@ -57,6 +57,7 @@ public class NumberSkeletonTest { "measure-unit/length-meter", "measure-unit/area-square-meter", "measure-unit/energy-joule per-measure-unit/length-meter", + "unit/square-meter-per-square-meter", "currency/XXX", "currency/ZZZ", "currency/usd", @@ -92,7 +93,20 @@ public class NumberSkeletonTest { "numbering-system/latn", "precision-integer/@##", "precision-integer rounding-mode-ceiling", - "precision-currency-cash rounding-mode-ceiling" }; + "precision-currency-cash rounding-mode-ceiling", + "0", + "00", + "000", + "E0", + "E00", + "E000", + "EE0", + "EE00", + "EE+?0", + "EE+?00", + "EE+!0", + "EE+!00", + }; for (String cas : cases) { try { @@ -137,7 +151,20 @@ public class NumberSkeletonTest { "integer-width/+0#", "integer-width/+#", "integer-width/+#0", - "scientific/foo" }; + "scientific/foo", + "E", + "E1", + "E+", + "E+?", + "E+!", + "E+0", + "EE", + "EE+", + "EEE", + "EEE0", + "001", + "00+", + }; for (String cas : cases) { try {