diff --git a/icu4c/source/i18n/number_affixutils.cpp b/icu4c/source/i18n/number_affixutils.cpp index 8da29a03d52..0da353bb7c1 100644 --- a/icu4c/source/i18n/number_affixutils.cpp +++ b/icu4c/source/i18n/number_affixutils.cpp @@ -158,7 +158,7 @@ Field AffixUtils::getFieldForType(AffixPatternType type) { int32_t AffixUtils::unescape(const UnicodeString &affixPattern, NumberStringBuilder &output, int32_t position, - const SymbolProvider &provider, UErrorCode &status) { + const SymbolProvider &provider, Field field, UErrorCode &status) { int32_t length = 0; AffixTag tag; while (hasNext(tag, affixPattern)) { @@ -171,7 +171,7 @@ AffixUtils::unescape(const UnicodeString &affixPattern, NumberStringBuilder &out length += output.insert( position + length, provider.getSymbol(tag.type), getFieldForType(tag.type), status); } else { - length += output.insertCodePoint(position + length, tag.codePoint, UNUM_FIELD_COUNT, status); + length += output.insertCodePoint(position + length, tag.codePoint, field, status); } } return length; diff --git a/icu4c/source/i18n/number_affixutils.h b/icu4c/source/i18n/number_affixutils.h index 1d7e1a115e0..f011a54b316 100644 --- a/icu4c/source/i18n/number_affixutils.h +++ b/icu4c/source/i18n/number_affixutils.h @@ -144,7 +144,8 @@ class U_I18N_API AffixUtils { * @param provider An object to generate locale symbols. */ static int32_t unescape(const UnicodeString& affixPattern, NumberStringBuilder& output, - int32_t position, const SymbolProvider& provider, UErrorCode& status); + int32_t position, const SymbolProvider& provider, Field field, + UErrorCode& status); /** * Sames as {@link #unescape}, but only calculates the code point count. More efficient than {@link #unescape} diff --git a/icu4c/source/i18n/number_compact.cpp b/icu4c/source/i18n/number_compact.cpp index 10942c35f53..cbcfb679ebf 100644 --- a/icu4c/source/i18n/number_compact.cpp +++ b/icu4c/source/i18n/number_compact.cpp @@ -260,7 +260,7 @@ void CompactHandler::precomputeAllModifiers(MutablePatternModifier &buildReferen ParsedPatternInfo patternInfo; PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); if (U_FAILURE(status)) { return; } - buildReference.setPatternInfo(&patternInfo); + buildReference.setPatternInfo(&patternInfo, UNUM_COMPACT_FIELD); info.mod = buildReference.createImmutable(status); if (U_FAILURE(status)) { return; } info.patternString = patternString; @@ -310,7 +310,7 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr ParsedPatternInfo &patternInfo = const_cast(this)->unsafePatternInfo; PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); static_cast(const_cast(micros.modMiddle)) - ->setPatternInfo(&patternInfo); + ->setPatternInfo(&patternInfo, UNUM_COMPACT_FIELD); } // We already performed rounding. Do not perform it again. diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp index 60c18ee284e..c64703699cd 100644 --- a/icu4c/source/i18n/number_formatimpl.cpp +++ b/icu4c/source/i18n/number_formatimpl.cpp @@ -344,7 +344,8 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, fPatternModifier.adoptInstead(patternModifier); patternModifier->setPatternInfo( macros.affixProvider != nullptr ? macros.affixProvider - : static_cast(fPatternInfo.getAlias())); + : static_cast(fPatternInfo.getAlias()), + UNUM_FIELD_COUNT); patternModifier->setPatternAttributes(fMicros.sign, isPermille); if (patternModifier->needsPlurals()) { patternModifier->setSymbols( diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp index 4c61a0d35bc..0146ee99056 100644 --- a/icu4c/source/i18n/number_patternmodifier.cpp +++ b/icu4c/source/i18n/number_patternmodifier.cpp @@ -23,13 +23,14 @@ AffixPatternProvider::~AffixPatternProvider() = default; MutablePatternModifier::MutablePatternModifier(bool isStrong) : fStrong(isStrong) {} -void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo) { +void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) { fPatternInfo = patternInfo; + fField = field; } void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) { fSignDisplay = signDisplay; - this->perMilleReplacesPercent = perMille; + fPerMilleReplacesPercent = perMille; } void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols, @@ -255,20 +256,20 @@ bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& sb, int position, UErrorCode& status) { prepareAffix(true); - int length = AffixUtils::unescape(currentAffix, sb, position, *this, status); + int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); return length; } int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& sb, int position, UErrorCode& status) { prepareAffix(false); - int length = AffixUtils::unescape(currentAffix, sb, position, *this, status); + int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); return length; } /** This method contains the heart of the logic for rendering LDML affix strings. */ void MutablePatternModifier::prepareAffix(bool isPrefix) { PatternStringUtils::patternInfoToStringBuilder( - *fPatternInfo, isPrefix, fSignum, fSignDisplay, fPlural, perMilleReplacesPercent, currentAffix); + *fPatternInfo, isPrefix, fSignum, fSignDisplay, fPlural, fPerMilleReplacesPercent, currentAffix); } UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { diff --git a/icu4c/source/i18n/number_patternmodifier.h b/icu4c/source/i18n/number_patternmodifier.h index ea80d6305e7..9e6e33fccae 100644 --- a/icu4c/source/i18n/number_patternmodifier.h +++ b/icu4c/source/i18n/number_patternmodifier.h @@ -95,8 +95,11 @@ class U_I18N_API MutablePatternModifier * Sets a reference to the parsed decimal format pattern, usually obtained from * {@link PatternStringParser#parseToPatternInfo(String)}, but any implementation of {@link AffixPatternProvider} is * accepted. + * + * @param field + * Which field to use for literal characters in the pattern. */ - void setPatternInfo(const AffixPatternProvider *patternInfo); + void setPatternInfo(const AffixPatternProvider *patternInfo, Field field); /** * Sets attributes that imply changes to the literal interpretation of the pattern string affixes. @@ -203,8 +206,9 @@ class U_I18N_API MutablePatternModifier // Pattern details (initialized in setPatternInfo and setPatternAttributes) const AffixPatternProvider *fPatternInfo; + Field fField; UNumberSignDisplay fSignDisplay; - bool perMilleReplacesPercent; + bool fPerMilleReplacesPercent; // Symbol details (initialized in setSymbols) const DecimalFormatSymbols *fSymbols; diff --git a/icu4c/source/i18n/unicode/numfmt.h b/icu4c/source/i18n/unicode/numfmt.h index 30979b61e35..af124c6e6be 100644 --- a/icu4c/source/i18n/unicode/numfmt.h +++ b/icu4c/source/i18n/unicode/numfmt.h @@ -239,6 +239,8 @@ public: kSignField = UNUM_SIGN_FIELD, /** @draft ICU 64 */ kMeasureUnitField = UNUM_MEASURE_UNIT_FIELD, + /** @draft ICU 64 */ + kCompactField = UNUM_COMPACT_FIELD, /** * These constants are provided for backwards compatibility only. diff --git a/icu4c/source/i18n/unicode/unum.h b/icu4c/source/i18n/unicode/unum.h index 3f07f9459eb..cdc6d056bc2 100644 --- a/icu4c/source/i18n/unicode/unum.h +++ b/icu4c/source/i18n/unicode/unum.h @@ -377,6 +377,9 @@ typedef enum UNumberFormatFields { UNUM_SIGN_FIELD, /** @draft ICU 64 */ UNUM_MEASURE_UNIT_FIELD, + /** @draft ICU 64 */ + UNUM_COMPACT_FIELD, + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UNumberFormatFields value. diff --git a/icu4c/source/test/intltest/numbertest_affixutils.cpp b/icu4c/source/test/intltest/numbertest_affixutils.cpp index d72991a042f..f2029667e12 100644 --- a/icu4c/source/test/intltest/numbertest_affixutils.cpp +++ b/icu4c/source/test/intltest/numbertest_affixutils.cpp @@ -223,7 +223,7 @@ void AffixUtilsTest::testUnescapeWithSymbolProvider() { UnicodeString input(cas[0]); UnicodeString expected(cas[1]); sb.clear(); - AffixUtils::unescape(input, sb, 0, provider, status); + AffixUtils::unescape(input, sb, 0, provider, UNUM_FIELD_COUNT, status); assertSuccess("Spot 1", status); assertEquals(input, expected, sb.toUnicodeString()); assertEquals(input, expected, sb.toTempUnicodeString()); @@ -233,7 +233,7 @@ void AffixUtilsTest::testUnescapeWithSymbolProvider() { sb.clear(); sb.append(u"abcdefg", UNUM_FIELD_COUNT, status); assertSuccess("Spot 2", status); - AffixUtils::unescape(u"-+%", sb, 4, provider, status); + AffixUtils::unescape(u"-+%", sb, 4, provider, UNUM_FIELD_COUNT, status); assertSuccess("Spot 3", status); assertEquals(u"Symbol provider into middle", u"abcd123efg", sb.toUnicodeString()); } @@ -241,7 +241,7 @@ void AffixUtilsTest::testUnescapeWithSymbolProvider() { UnicodeString AffixUtilsTest::unescapeWithDefaults(const SymbolProvider &defaultProvider, UnicodeString input, UErrorCode &status) { NumberStringBuilder nsb; - int32_t length = AffixUtils::unescape(input, nsb, 0, defaultProvider, status); + int32_t length = AffixUtils::unescape(input, nsb, 0, defaultProvider, UNUM_FIELD_COUNT, status); assertEquals("Return value of unescape", nsb.length(), length); return nsb.toUnicodeString(); } diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index c7d59accc13..0ae6d83a057 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp @@ -2349,6 +2349,130 @@ void NumberFormatterApiTest::fieldPositionCoverage() { expectedFieldPositions, sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); } + + { + const char16_t* message = u"Compact field basic"; + FormattedNumber result = assertFormatSingle( + message, + u"compact-short", + NumberFormatter::with().notation(Notation::compactShort()), + Locale::getUS(), + 65000, + u"65K"); + static const UFieldPosition expectedFieldPositions[] = { + // field, begin index, end index + {UNUM_INTEGER_FIELD, 0, 2}, + {UNUM_COMPACT_FIELD, 2, 3}}; + assertFieldPositions( + message, + result, + expectedFieldPositions, + sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); + } + + { + const char16_t* message = u"Compact field with spaces"; + FormattedNumber result = assertFormatSingle( + message, + u"compact-long", + NumberFormatter::with().notation(Notation::compactLong()), + Locale::getUS(), + 65000, + u"65 thousand"); + static const UFieldPosition expectedFieldPositions[] = { + // field, begin index, end index + {UNUM_INTEGER_FIELD, 0, 2}, + {UNUM_COMPACT_FIELD, 3, 11}}; + assertFieldPositions( + message, + result, + expectedFieldPositions, + sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); + } + + { + const char16_t* message = u"Compact field with inner space"; + FormattedNumber result = assertFormatSingle( + message, + u"compact-long", + NumberFormatter::with().notation(Notation::compactLong()), + "fil", // locale with interesting data + 6000, + u"6 na libo"); + static const UFieldPosition expectedFieldPositions[] = { + // field, begin index, end index + {UNUM_INTEGER_FIELD, 0, 1}, + {UNUM_COMPACT_FIELD, 2, 9}}; + assertFieldPositions( + message, + result, + expectedFieldPositions, + sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); + } + + { + const char16_t* message = u"Compact field with bidi mark"; + FormattedNumber result = assertFormatSingle( + message, + u"compact-long", + NumberFormatter::with().notation(Notation::compactLong()), + "he", // locale with interesting data + 6000, + u"\u200F6 אלף"); + static const UFieldPosition expectedFieldPositions[] = { + // field, begin index, end index + {UNUM_INTEGER_FIELD, 1, 2}, + {UNUM_COMPACT_FIELD, 3, 6}}; + assertFieldPositions( + message, + result, + expectedFieldPositions, + sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); + } + + { + const char16_t* message = u"Compact with currency fields"; + FormattedNumber result = assertFormatSingle( + message, + u"compact-short currency/USD", + NumberFormatter::with().notation(Notation::compactShort()).unit(USD), + "sr_Latn", // locale with interesting data + 65000, + u"65 hilj. US$"); + static const UFieldPosition expectedFieldPositions[] = { + // field, begin index, end index + {UNUM_INTEGER_FIELD, 0, 2}, + {UNUM_COMPACT_FIELD, 3, 8}, + {UNUM_CURRENCY_FIELD, 9, 12}}; + assertFieldPositions( + message, + result, + expectedFieldPositions, + sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); + } + + { + const char16_t* message = u"Compact with measure unit fields"; + FormattedNumber result = assertFormatSingle( + message, + u"compact-long measure-unit/length-meter unit-width-full-name", + NumberFormatter::with().notation(Notation::compactLong()) + .unit(METER) + .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), + Locale::getUS(), + 65000, + u"65 thousand meters"); + static const UFieldPosition expectedFieldPositions[] = { + // field, begin index, end index + {UNUM_INTEGER_FIELD, 0, 2}, + {UNUM_COMPACT_FIELD, 3, 11}, + {UNUM_MEASURE_UNIT_FIELD, 12, 18}}; + assertFieldPositions( + message, + result, + expectedFieldPositions, + sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions)); + } } void NumberFormatterApiTest::toFormat() { diff --git a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp index 3a0fda82675..2156c9b435e 100644 --- a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp +++ b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp @@ -26,7 +26,7 @@ void PatternModifierTest::testBasic() { ParsedPatternInfo patternInfo; PatternParser::parseToPatternInfo(u"a0b", patternInfo, status); assertSuccess("Spot 1", status); - mod.setPatternInfo(&patternInfo); + mod.setPatternInfo(&patternInfo, UNUM_FIELD_COUNT); mod.setPatternAttributes(UNUM_SIGN_AUTO, false); DecimalFormatSymbols symbols(Locale::getEnglish(), status); CurrencySymbols currencySymbols({u"USD", status}, "en", status); @@ -58,7 +58,7 @@ void PatternModifierTest::testBasic() { ParsedPatternInfo patternInfo2; PatternParser::parseToPatternInfo(u"a0b;c-0d", patternInfo2, status); assertSuccess("Spot 4", status); - mod.setPatternInfo(&patternInfo2); + mod.setPatternInfo(&patternInfo2, UNUM_FIELD_COUNT); mod.setPatternAttributes(UNUM_SIGN_AUTO, false); mod.setNumberProperties(1, StandardPlural::Form::COUNT); assertEquals("Pattern a0b;c-0d", u"a", getPrefix(mod, status)); @@ -88,7 +88,7 @@ void PatternModifierTest::testPatternWithNoPlaceholder() { ParsedPatternInfo patternInfo; PatternParser::parseToPatternInfo(u"abc", patternInfo, status); assertSuccess("Spot 1", status); - mod.setPatternInfo(&patternInfo); + mod.setPatternInfo(&patternInfo, UNUM_FIELD_COUNT); mod.setPatternAttributes(UNUM_SIGN_AUTO, false); DecimalFormatSymbols symbols(Locale::getEnglish(), status); CurrencySymbols currencySymbols({u"USD", status}, "en", status); @@ -131,7 +131,7 @@ void PatternModifierTest::testMutableEqualsImmutable() { ParsedPatternInfo patternInfo; PatternParser::parseToPatternInfo("a0b;c-0d", patternInfo, status); assertSuccess("Spot 1", status); - mod.setPatternInfo(&patternInfo); + mod.setPatternInfo(&patternInfo, UNUM_FIELD_COUNT); mod.setPatternAttributes(UNUM_SIGN_AUTO, false); DecimalFormatSymbols symbols(Locale::getEnglish(), status); CurrencySymbols currencySymbols({u"USD", status}, "en", status); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java index 746cbd0a735..efb41bce61a 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java @@ -309,7 +309,8 @@ public class AffixUtils { CharSequence affixPattern, NumberStringBuilder output, int position, - SymbolProvider provider) { + SymbolProvider provider, + NumberFormat.Field field) { assert affixPattern != null; int length = 0; long tag = 0L; @@ -324,7 +325,7 @@ public class AffixUtils { provider.getSymbol(typeOrCp), getFieldForType(typeOrCp)); } else { - length += output.insertCodePoint(position + length, typeOrCp, null); + length += output.insertCodePoint(position + length, typeOrCp, field); } } return length; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java index a651b3907d9..d8875610bf8 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java @@ -38,6 +38,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr // Pattern details AffixPatternProvider patternInfo; + Field field; SignDisplay signDisplay; boolean perMilleReplacesPercent; @@ -71,9 +72,13 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr * Sets a reference to the parsed decimal format pattern, usually obtained from * {@link PatternStringParser#parseToPatternInfo(String)}, but any implementation of * {@link AffixPatternProvider} is accepted. + * + * @param field + * Which field to use for literal characters in the pattern. */ - public void setPatternInfo(AffixPatternProvider patternInfo) { + public void setPatternInfo(AffixPatternProvider patternInfo, Field field) { this.patternInfo = patternInfo; + this.field = field; } /** @@ -343,13 +348,13 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr private int insertPrefix(NumberStringBuilder sb, int position) { prepareAffix(true); - int length = AffixUtils.unescape(currentAffix, sb, position, this); + int length = AffixUtils.unescape(currentAffix, sb, position, this, field); return length; } private int insertSuffix(NumberStringBuilder sb, int position) { prepareAffix(false); - int length = AffixUtils.unescape(currentAffix, sb, position, this); + int length = AffixUtils.unescape(currentAffix, sb, position, this, field); return length; } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java b/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java index 31d1a8404fc..04e0eb9ea90 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java @@ -19,6 +19,7 @@ import com.ibm.icu.impl.number.MutablePatternModifier.ImmutablePatternModifier; 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.NumberFormat; import com.ibm.icu.text.PluralRules; import com.ibm.icu.util.ULocale; @@ -96,7 +97,7 @@ public class CompactNotation extends Notation { } if (buildReference != null) { // Safe code path - precomputedMods = new HashMap(); + precomputedMods = new HashMap<>(); precomputeAllModifiers(buildReference); } else { // Unsafe code path @@ -106,12 +107,12 @@ public class CompactNotation extends Notation { /** Used by the safe code path */ private void precomputeAllModifiers(MutablePatternModifier buildReference) { - Set allPatterns = new HashSet(); + Set allPatterns = new HashSet<>(); data.getUniquePatterns(allPatterns); for (String patternString : allPatterns) { ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(patternString); - buildReference.setPatternInfo(patternInfo); + buildReference.setPatternInfo(patternInfo, NumberFormat.Field.COMPACT); precomputedMods.put(patternString, buildReference.createImmutable()); } } @@ -148,7 +149,7 @@ public class CompactNotation extends Notation { // Overwrite the PatternInfo in the existing modMiddle. assert micros.modMiddle instanceof MutablePatternModifier; ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(patternString); - ((MutablePatternModifier) micros.modMiddle).setPatternInfo(patternInfo); + ((MutablePatternModifier) micros.modMiddle).setPatternInfo(patternInfo, NumberFormat.Field.COMPACT); } // We already performed rounding. Do not perform it again. diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java index 17fbe912d8b..19b9b142a6e 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java @@ -322,7 +322,7 @@ class NumberFormatterImpl { // Middle modifier (patterns, positive/negative, currency symbols, percent) // The default middle modifier is weak (thus the false argument). MutablePatternModifier patternMod = new MutablePatternModifier(false); - patternMod.setPatternInfo((macros.affixProvider != null) ? macros.affixProvider : patternInfo); + patternMod.setPatternInfo((macros.affixProvider != null) ? macros.affixProvider : patternInfo, null); patternMod.setPatternAttributes(micros.sign, isPermille); if (patternMod.needsPlurals()) { if (rules == null) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java index e953a683a15..4304bfa13ed 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java @@ -1966,6 +1966,11 @@ public abstract class NumberFormat extends UFormat { */ public static final Field MEASURE_UNIT = new Field("measure unit"); + /** + * @draft ICU 64 + */ + public static final Field COMPACT = new Field("compact"); + /** * Constructs a new instance of NumberFormat.Field with the given field * name. diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java index 6696daa2bea..5c528c620b4 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java @@ -204,14 +204,14 @@ public class AffixUtilsTest { String input = cas[0]; String expected = cas[1]; sb.clear(); - AffixUtils.unescape(input, sb, 0, provider); + AffixUtils.unescape(input, sb, 0, provider, null); assertEquals("With symbol provider on <" + input + ">", expected, sb.toString()); } // Test insertion position sb.clear(); sb.append("abcdefg", null); - AffixUtils.unescape("-+%", sb, 4, provider); + AffixUtils.unescape("-+%", sb, 4, provider, null); assertEquals("Symbol provider into middle", "abcd123efg", sb.toString()); } @@ -237,7 +237,7 @@ public class AffixUtilsTest { private static String unescapeWithDefaults(String input) { NumberStringBuilder nsb = new NumberStringBuilder(); - int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER); + int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER, null); assertEquals("Return value of unescape", nsb.length(), length); return nsb.toString(); } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java index 7f70a554155..5f2c13c6e07 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java @@ -25,7 +25,7 @@ public class MutablePatternModifierTest { @Test public void basic() { MutablePatternModifier mod = new MutablePatternModifier(false); - mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b")); + mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b"), null); mod.setPatternAttributes(SignDisplay.AUTO, false); mod.setSymbols(DecimalFormatSymbols.getInstance(ULocale.ENGLISH), Currency.getInstance("USD"), @@ -51,7 +51,7 @@ public class MutablePatternModifierTest { assertEquals("a", getPrefix(mod)); assertEquals("b", getSuffix(mod)); - mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d")); + mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"), null); mod.setPatternAttributes(SignDisplay.AUTO, false); mod.setNumberProperties(1, null); assertEquals("a", getPrefix(mod)); @@ -76,7 +76,7 @@ public class MutablePatternModifierTest { @Test public void mutableEqualsImmutable() { MutablePatternModifier mod = new MutablePatternModifier(false); - mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d")); + mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"), null); mod.setPatternAttributes(SignDisplay.AUTO, false); mod.setSymbols(DecimalFormatSymbols.getInstance(ULocale.ENGLISH), null, UnitWidth.SHORT, null); DecimalQuantity fq = new DecimalQuantity_DualStorageBCD(1); @@ -106,7 +106,7 @@ public class MutablePatternModifierTest { @Test public void patternWithNoPlaceholder() { MutablePatternModifier mod = new MutablePatternModifier(false); - mod.setPatternInfo(PatternStringParser.parseToPatternInfo("abc")); + mod.setPatternInfo(PatternStringParser.parseToPatternInfo("abc"), null); mod.setPatternAttributes(SignDisplay.AUTO, false); mod.setSymbols(DecimalFormatSymbols.getInstance(ULocale.ENGLISH), Currency.getInstance("USD"), 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 c857343048e..f59e981533c 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 @@ -2317,6 +2317,124 @@ public class NumberFormatterApiTest { result, expectedFieldPositions); } + + { + String message = "Compact field basic"; + FormattedNumber result = assertFormatSingle( + message, + "compact-short", + NumberFormatter.with().notation(Notation.compactShort()), + ULocale.US, + 65000, + "65K"); + Object[][] expectedFieldPositions = new Object[][] { + // field, begin index, end index + {NumberFormat.Field.INTEGER, 0, 2}, + {NumberFormat.Field.COMPACT, 2, 3}}; + assertFieldPositions( + message, + result, + expectedFieldPositions); + } + + { + String message = "Compact field with spaces"; + FormattedNumber result = assertFormatSingle( + message, + "compact-long", + NumberFormatter.with().notation(Notation.compactLong()), + ULocale.US, + 65000, + "65 thousand"); + Object[][] expectedFieldPositions = new Object[][] { + // field, begin index, end index + {NumberFormat.Field.INTEGER, 0, 2}, + {NumberFormat.Field.COMPACT, 3, 11}}; + assertFieldPositions( + message, + result, + expectedFieldPositions); + } + + { + String message = "Compact field with inner space"; + FormattedNumber result = assertFormatSingle( + message, + "compact-long", + NumberFormatter.with().notation(Notation.compactLong()), + new ULocale("fil"), // locale with interesting data + 6000, + "6 na libo"); + Object[][] expectedFieldPositions = new Object[][] { + // field, begin index, end index + {NumberFormat.Field.INTEGER, 0, 1}, + {NumberFormat.Field.COMPACT, 2, 9}}; + assertFieldPositions( + message, + result, + expectedFieldPositions); + } + + { + String message = "Compact field with bidi mark"; + FormattedNumber result = assertFormatSingle( + message, + "compact-long", + NumberFormatter.with().notation(Notation.compactLong()), + new ULocale("he"), // locale with interesting data + 6000, + "\u200F6 אלף"); + Object[][] expectedFieldPositions = new Object[][] { + // field, begin index, end index + {NumberFormat.Field.INTEGER, 1, 2}, + {NumberFormat.Field.COMPACT, 3, 6}}; + assertFieldPositions( + message, + result, + expectedFieldPositions); + } + + { + String message = "Compact with currency fields"; + FormattedNumber result = assertFormatSingle( + message, + "compact-short currency/USD", + NumberFormatter.with().notation(Notation.compactShort()).unit(USD), + new ULocale("sr_Latn"), // locale with interesting data + 65000, + "65 hilj. US$"); + Object[][] expectedFieldPositions = new Object[][] { + // field, begin index, end index + {NumberFormat.Field.INTEGER, 0, 2}, + {NumberFormat.Field.COMPACT, 3, 8}, + {NumberFormat.Field.CURRENCY, 9, 12}}; + assertFieldPositions( + message, + result, + expectedFieldPositions); + } + + { + String message = "Compact with measure unit fields"; + FormattedNumber result = assertFormatSingle( + message, + "compact-long measure-unit/length-meter unit-width-full-name", + NumberFormatter.with().notation(Notation.compactLong()) + .unit(MeasureUnit.METER) + .unitWidth(UnitWidth.FULL_NAME), + ULocale.US, + 65000, + "65 thousand meters"); + Object[][] expectedFieldPositions = new Object[][] { + // field, begin index, end index + {NumberFormat.Field.INTEGER, 0, 2}, + {NumberFormat.Field.COMPACT, 3, 11}, + {NumberFormat.Field.MEASURE_UNIT, 12, 18}}; + assertFieldPositions( + message, + result, + expectedFieldPositions); + } } /** Handler for serialization compatibility test suite. */