ICU-20961 Return correct currency plural pattern from DecimalFormat
diff --git a/icu4c/source/i18n/number_mapper.cpp b/icu4c/source/i18n/number_mapper.cpp
index f0d313f..b730887 100644
--- a/icu4c/source/i18n/number_mapper.cpp
+++ b/icu4c/source/i18n/number_mapper.cpp
@@ -63,17 +63,8 @@
// AFFIXES //
/////////////
- AffixPatternProvider* affixProvider;
- if (properties.currencyPluralInfo.fPtr.isNull()) {
- warehouse.currencyPluralInfoAPP.setToBogus();
- warehouse.propertiesAPP.setTo(properties, status);
- affixProvider = &warehouse.propertiesAPP;
- } else {
- warehouse.currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, properties, status);
- warehouse.propertiesAPP.setToBogus();
- affixProvider = &warehouse.currencyPluralInfoAPP;
- }
- macros.affixProvider = affixProvider;
+ warehouse.affixProvider.setTo(properties, status);
+ macros.affixProvider = &warehouse.affixProvider.get();
///////////
// UNITS //
@@ -83,7 +74,7 @@
!properties.currency.isNull() ||
!properties.currencyPluralInfo.fPtr.isNull() ||
!properties.currencyUsage.isNull() ||
- affixProvider->hasCurrencySign());
+ warehouse.affixProvider.get().hasCurrencySign());
CurrencyUnit currency = resolveCurrency(properties, locale, status);
UCurrencyUsage currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD);
if (useCurrency) {
diff --git a/icu4c/source/i18n/number_mapper.h b/icu4c/source/i18n/number_mapper.h
index de7d9c3..88e0cb7 100644
--- a/icu4c/source/i18n/number_mapper.h
+++ b/icu4c/source/i18n/number_mapper.h
@@ -20,6 +20,10 @@
namespace impl {
+class AutoAffixPatternProvider;
+class CurrencyPluralInfoAffixProvider;
+
+
class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory {
public:
bool isBogus() const {
@@ -32,12 +36,6 @@
void setTo(const DecimalFormatProperties& properties, UErrorCode& status);
- PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state
-
- PropertiesAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) {
- setTo(properties, status);
- }
-
// AffixPatternProvider Methods:
char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE;
@@ -65,9 +63,14 @@
UnicodeString negSuffix;
bool isCurrencyPattern;
+ PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state
+
const UnicodeString& getStringInternal(int32_t flags) const;
bool fBogus{true};
+
+ friend class AutoAffixPatternProvider;
+ friend class CurrencyPluralInfoAffixProvider;
};
@@ -107,7 +110,43 @@
private:
PropertiesAffixPatternProvider affixesByPlural[StandardPlural::COUNT];
+ CurrencyPluralInfoAffixProvider() = default;
+
bool fBogus{true};
+
+ friend class AutoAffixPatternProvider;
+};
+
+
+class AutoAffixPatternProvider {
+ public:
+ inline AutoAffixPatternProvider() = default;
+
+ inline AutoAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) {
+ setTo(properties, status);
+ }
+
+ inline void setTo(const DecimalFormatProperties& properties, UErrorCode& status) {
+ if (properties.currencyPluralInfo.fPtr.isNull()) {
+ propertiesAPP.setTo(properties, status);
+ currencyPluralInfoAPP.setToBogus();
+ } else {
+ propertiesAPP.setToBogus();
+ currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, properties, status);
+ }
+ }
+
+ inline const AffixPatternProvider& get() const {
+ if (!currencyPluralInfoAPP.isBogus()) {
+ return currencyPluralInfoAPP;
+ } else {
+ return propertiesAPP;
+ }
+ }
+
+ private:
+ PropertiesAffixPatternProvider propertiesAPP;
+ CurrencyPluralInfoAffixProvider currencyPluralInfoAPP;
};
@@ -115,8 +154,7 @@
* A struct for ownership of a few objects needed for formatting.
*/
struct DecimalFormatWarehouse {
- PropertiesAffixPatternProvider propertiesAPP;
- CurrencyPluralInfoAffixProvider currencyPluralInfoAPP;
+ AutoAffixPatternProvider affixProvider;
CurrencySymbols currencySymbols;
};
diff --git a/icu4c/source/i18n/number_patternstring.cpp b/icu4c/source/i18n/number_patternstring.cpp
index 55b83f1..9d845056 100644
--- a/icu4c/source/i18n/number_patternstring.cpp
+++ b/icu4c/source/i18n/number_patternstring.cpp
@@ -686,10 +686,10 @@
int32_t exponentDigits = uprv_min(properties.minimumExponentDigits, dosMax);
bool exponentShowPlusSign = properties.exponentSignAlwaysShown;
- PropertiesAffixPatternProvider affixes(properties, status);
+ AutoAffixPatternProvider affixProvider(properties, status);
// Prefixes
- sb.append(affixes.getString(AffixPatternProvider::AFFIX_POS_PREFIX));
+ sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_POS_PREFIX));
int32_t afterPrefixPos = sb.length();
// Figure out the grouping sizes.
@@ -778,7 +778,7 @@
// Suffixes
int32_t beforeSuffixPos = sb.length();
- sb.append(affixes.getString(AffixPatternProvider::AFFIX_POS_SUFFIX));
+ sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_POS_SUFFIX));
// Resolve Padding
if (paddingWidth > 0 && !paddingLocation.isNull()) {
@@ -814,16 +814,16 @@
// Negative affixes
// Ignore if the negative prefix pattern is "-" and the negative suffix is empty
- if (affixes.hasNegativeSubpattern()) {
+ if (affixProvider.get().hasNegativeSubpattern()) {
sb.append(u';');
- sb.append(affixes.getString(AffixPatternProvider::AFFIX_NEG_PREFIX));
+ sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_NEG_PREFIX));
// Copy the positive digit format into the negative.
// This is optional; the pattern is the same as if '#' were appended here instead.
// NOTE: It is not safe to append the UnicodeString to itself, so we need to copy.
// See http://bugs.icu-project.org/trac/ticket/13707
UnicodeString copy(sb);
sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos);
- sb.append(affixes.getString(AffixPatternProvider::AFFIX_NEG_SUFFIX));
+ sb.append(affixProvider.get().getString(AffixPatternProvider::AFFIX_NEG_SUFFIX));
}
return sb;
diff --git a/icu4c/source/i18n/numparse_impl.cpp b/icu4c/source/i18n/numparse_impl.cpp
index bf58290..4b76da1 100644
--- a/icu4c/source/i18n/numparse_impl.cpp
+++ b/icu4c/source/i18n/numparse_impl.cpp
@@ -83,23 +83,14 @@
const DecimalFormatSymbols& symbols, bool parseCurrency,
UErrorCode& status) {
Locale locale = symbols.getLocale();
- PropertiesAffixPatternProvider localPAPP;
- CurrencyPluralInfoAffixProvider localCPIAP;
- AffixPatternProvider* affixProvider;
- if (properties.currencyPluralInfo.fPtr.isNull()) {
- localPAPP.setTo(properties, status);
- affixProvider = &localPAPP;
- } else {
- localCPIAP.setTo(*properties.currencyPluralInfo.fPtr, properties, status);
- affixProvider = &localCPIAP;
- }
- if (affixProvider == nullptr || U_FAILURE(status)) { return nullptr; }
+ AutoAffixPatternProvider affixProvider(properties, status);
+ if (U_FAILURE(status)) { return nullptr; }
CurrencyUnit currency = resolveCurrency(properties, locale, status);
CurrencySymbols currencySymbols(currency, locale, symbols, status);
bool isStrict = properties.parseMode.getOrDefault(PARSE_MODE_STRICT) == PARSE_MODE_STRICT;
Grouper grouper = Grouper::forProperties(properties);
int parseFlags = 0;
- if (affixProvider == nullptr || U_FAILURE(status)) { return nullptr; }
+ if (U_FAILURE(status)) { return nullptr; }
if (!properties.parseCaseSensitive) {
parseFlags |= PARSE_FLAG_IGNORE_CASE;
}
@@ -121,7 +112,7 @@
if (grouper.getPrimary() <= 0) {
parseFlags |= PARSE_FLAG_GROUPING_DISABLED;
}
- if (parseCurrency || affixProvider->hasCurrencySign()) {
+ if (parseCurrency || affixProvider.get().hasCurrencySign()) {
parseFlags |= PARSE_FLAG_MONETARY_SEPARATORS;
}
if (!parseCurrency) {
@@ -143,13 +134,13 @@
parser->fLocalMatchers.affixTokenMatcherWarehouse = {&affixSetupData};
parser->fLocalMatchers.affixMatcherWarehouse = {&parser->fLocalMatchers.affixTokenMatcherWarehouse};
parser->fLocalMatchers.affixMatcherWarehouse.createAffixMatchers(
- *affixProvider, *parser, ignorables, parseFlags, status);
+ affixProvider.get(), *parser, ignorables, parseFlags, status);
////////////////////////
/// CURRENCY MATCHER ///
////////////////////////
- if (parseCurrency || affixProvider->hasCurrencySign()) {
+ if (parseCurrency || affixProvider.get().hasCurrencySign()) {
parser->addMatcher(parser->fLocalMatchers.currency = {currencySymbols, symbols, parseFlags, status});
}
@@ -159,10 +150,10 @@
// ICU-TC meeting, April 11, 2018: accept percent/permille only if it is in the pattern,
// and to maintain regressive behavior, divide by 100 even if no percent sign is present.
- if (!isStrict && affixProvider->containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) {
+ if (!isStrict && affixProvider.get().containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) {
parser->addMatcher(parser->fLocalMatchers.percent = {symbols});
}
- if (!isStrict && affixProvider->containsSymbolType(AffixPatternType::TYPE_PERMILLE, status)) {
+ if (!isStrict && affixProvider.get().containsSymbolType(AffixPatternType::TYPE_PERMILLE, status)) {
parser->addMatcher(parser->fLocalMatchers.permille = {symbols});
}
diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp
index 8b0f7e0..b4fc3a0 100644
--- a/icu4c/source/test/intltest/numfmtst.cpp
+++ b/icu4c/source/test/intltest/numfmtst.cpp
@@ -243,6 +243,7 @@
TESTCASE_AUTO(Test20499_CurrencyVisibleDigitsPlural);
TESTCASE_AUTO(Test13735_GroupingSizeGetter);
TESTCASE_AUTO(Test13734_StrictFlexibleWhitespace);
+ TESTCASE_AUTO(Test20961_CurrencyPluralPattern);
TESTCASE_AUTO_END;
}
@@ -9749,4 +9750,15 @@
}
}
+void NumberFormatTest::Test20961_CurrencyPluralPattern() {
+ IcuTestErrorCode status(*this, "Test20961_CurrencyPluralPattern");
+ {
+ LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
+ NumberFormat::createInstance("en-US", UNUM_CURRENCY_PLURAL, status)));
+ UnicodeString result;
+ decimalFormat->toPattern(result);
+ assertEquals("Currency pattern", u"#,##0.00 ¤¤¤", result);
+ }
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/numfmtst.h b/icu4c/source/test/intltest/numfmtst.h
index 80abc71..71b1a77 100644
--- a/icu4c/source/test/intltest/numfmtst.h
+++ b/icu4c/source/test/intltest/numfmtst.h
@@ -299,6 +299,7 @@
void Test20499_CurrencyVisibleDigitsPlural();
void Test13735_GroupingSizeGetter();
void Test13734_StrictFlexibleWhitespace();
+ void Test20961_CurrencyPluralPattern();
private:
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java
index 601f1c8..1ef3781 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java
@@ -93,7 +93,7 @@
boolean alwaysShowDecimal = properties.getDecimalSeparatorAlwaysShown();
int exponentDigits = Math.min(properties.getMinimumExponentDigits(), dosMax);
boolean exponentShowPlusSign = properties.getExponentSignAlwaysShown();
- PropertiesAffixPatternProvider affixes = new PropertiesAffixPatternProvider(properties);
+ AffixPatternProvider affixes = PropertiesAffixPatternProvider.forProperties(properties);
// Prefixes
sb.append(affixes.getString(AffixPatternProvider.FLAG_POS_PREFIX));
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PropertiesAffixPatternProvider.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PropertiesAffixPatternProvider.java
index e55e970..115d1b9 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PropertiesAffixPatternProvider.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PropertiesAffixPatternProvider.java
@@ -9,7 +9,15 @@
private final String negSuffix;
private final boolean isCurrencyPattern;
- public PropertiesAffixPatternProvider(DecimalFormatProperties properties) {
+ public static AffixPatternProvider forProperties(DecimalFormatProperties properties) {
+ if (properties.getCurrencyPluralInfo() == null) {
+ return new PropertiesAffixPatternProvider(properties);
+ } else {
+ return new CurrencyPluralInfoAffixProvider(properties.getCurrencyPluralInfo(), properties);
+ }
+ }
+
+ PropertiesAffixPatternProvider(DecimalFormatProperties properties) {
// There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the
// explicit setters (setPositivePrefix and friends). The way to resolve the settings is as follows:
//
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
index c347f90..86755c2 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
@@ -10,7 +10,6 @@
import com.ibm.icu.impl.StringSegment;
import com.ibm.icu.impl.number.AffixPatternProvider;
import com.ibm.icu.impl.number.AffixUtils;
-import com.ibm.icu.impl.number.CurrencyPluralInfoAffixProvider;
import com.ibm.icu.impl.number.CustomSymbolCurrency;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
@@ -138,12 +137,7 @@
boolean parseCurrency) {
ULocale locale = symbols.getULocale();
- AffixPatternProvider affixProvider;
- if (properties.getCurrencyPluralInfo() == null) {
- affixProvider = new PropertiesAffixPatternProvider(properties);
- } else {
- affixProvider = new CurrencyPluralInfoAffixProvider(properties.getCurrencyPluralInfo(), properties);
- }
+ AffixPatternProvider affixProvider = PropertiesAffixPatternProvider.forProperties(properties);
Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
ParseMode parseMode = properties.getParseMode();
if (parseMode == null) {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
index 82f75d5..3a5fcdc 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
@@ -6,7 +6,6 @@
import java.math.MathContext;
import com.ibm.icu.impl.number.AffixPatternProvider;
-import com.ibm.icu.impl.number.CurrencyPluralInfoAffixProvider;
import com.ibm.icu.impl.number.CustomSymbolCurrency;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.Grouper;
@@ -104,12 +103,7 @@
// AFFIXES //
/////////////
- AffixPatternProvider affixProvider;
- if (properties.getCurrencyPluralInfo() == null) {
- affixProvider = new PropertiesAffixPatternProvider(properties);
- } else {
- affixProvider = new CurrencyPluralInfoAffixProvider(properties.getCurrencyPluralInfo(), properties);
- }
+ AffixPatternProvider affixProvider = PropertiesAffixPatternProvider.forProperties(properties);
macros.affixProvider = affixProvider;
///////////
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
index 1729f2a..e555bb6 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
@@ -6711,4 +6711,10 @@
assertEquals("result: ", null, result);
}
}
+
+ @Test
+ public void test20961_CurrencyPluralPattern() {
+ DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(ULocale.US, NumberFormat.PLURALCURRENCYSTYLE);
+ assertEquals("Currency pattern", "#,##0.00 ¤¤¤", decimalFormat.toPattern());
+ }
}