ICU-20961 Return correct currency plural pattern from DecimalFormat

This commit is contained in:
Shane Carr 2020-02-14 19:18:43 -08:00 committed by Shane F. Carr
parent eb92d41a1c
commit e572de5516
11 changed files with 94 additions and 59 deletions

View File

@ -63,17 +63,8 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
// 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 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
!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) {

View File

@ -20,6 +20,10 @@ namespace number {
namespace impl {
class AutoAffixPatternProvider;
class CurrencyPluralInfoAffixProvider;
class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory {
public:
bool isBogus() const {
@ -32,12 +36,6 @@ class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemo
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 @@ class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemo
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 @@ class CurrencyPluralInfoAffixProvider : public AffixPatternProvider, public UMem
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 @@ class CurrencyPluralInfoAffixProvider : public AffixPatternProvider, public UMem
* A struct for ownership of a few objects needed for formatting.
*/
struct DecimalFormatWarehouse {
PropertiesAffixPatternProvider propertiesAPP;
CurrencyPluralInfoAffixProvider currencyPluralInfoAPP;
AutoAffixPatternProvider affixProvider;
CurrencySymbols currencySymbols;
};

View File

@ -686,10 +686,10 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
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 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
// 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 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
// 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;

View File

@ -83,23 +83,14 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
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 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
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 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
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 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
// 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});
}

View File

@ -243,6 +243,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
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::Test13734_StrictFlexibleWhitespace() {
}
}
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 */

View File

@ -299,6 +299,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void Test20499_CurrencyVisibleDigitsPlural();
void Test13735_GroupingSizeGetter();
void Test13734_StrictFlexibleWhitespace();
void Test20961_CurrencyPluralPattern();
private:
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);

View File

@ -93,7 +93,7 @@ public class PatternStringUtils {
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));

View File

@ -9,7 +9,15 @@ public class PropertiesAffixPatternProvider implements AffixPatternProvider {
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:
//

View File

@ -10,7 +10,6 @@ import java.util.List;
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 @@ public class NumberParserImpl {
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) {

View File

@ -6,7 +6,6 @@ import java.math.BigDecimal;
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 @@ final class NumberPropertyMapper {
// 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;
///////////

View File

@ -6711,4 +6711,10 @@ public class NumberFormatTest extends TestFmwk {
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());
}
}