UnicodeString sb;
bool includeUnpaired = 0 != (parseFlags & PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES);
UNumberSignDisplay signDisplay = (0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) ? UNUM_SIGN_ALWAYS
- : UNUM_SIGN_NEVER;
+ : UNUM_SIGN_AUTO;
int32_t numAffixMatchers = 0;
int32_t numAffixPatternMatchers = 0;
} else if (success) {
// Match succeeded, and this is NOT a flexible matcher. Proceed to the next matcher.
it++;
+ // Small hack: if there is another matcher coming, do not accept trailing weak chars.
+ // Needed for proper handling of currency spacing.
+ if (it < end() && segment.getOffset() != result.charEnd && result.charEnd > matcherOffset) {
+ segment.setOffset(result.charEnd);
+ }
} else if (isFlexible) {
// Match failed, and this is a flexible matcher. Try again with the next matcher.
it++;
const DecimalFormatSymbols& symbols, bool parseCurrency,
UErrorCode& status) {
Locale locale = symbols.getLocale();
- PropertiesAffixPatternProvider patternInfo(properties, status);
+ PropertiesAffixPatternProvider localPAPP;
+ CurrencyPluralInfoAffixProvider localCPIAP;
+ AffixPatternProvider* affixProvider;
+ if (properties.currencyPluralInfo.fPtr.isNull()) {
+ localPAPP.setTo(properties, status);
+ affixProvider = &localPAPP;
+ } else {
+ localCPIAP.setTo(*properties.currencyPluralInfo.fPtr, status);
+ affixProvider = &localCPIAP;
+ }
+ if (affixProvider == nullptr || 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; }
// Fraction grouping is disabled by default because it has never been supported in DecimalFormat
parseFlags |= PARSE_FLAG_FRACTION_GROUPING_DISABLED;
if (!properties.parseCaseSensitive) {
if (grouper.getPrimary() <= 0) {
parseFlags |= PARSE_FLAG_GROUPING_DISABLED;
}
- if (parseCurrency || patternInfo.hasCurrencySign()) {
+ if (parseCurrency || affixProvider->hasCurrencySign()) {
parseFlags |= PARSE_FLAG_MONETARY_SEPARATORS;
}
parser->fLocalMatchers.affixTokenMatcherWarehouse = {&affixSetupData};
parser->fLocalMatchers.affixMatcherWarehouse = {&parser->fLocalMatchers.affixTokenMatcherWarehouse};
parser->fLocalMatchers.affixMatcherWarehouse.createAffixMatchers(
- patternInfo, *parser, ignorables, parseFlags, status);
+ *affixProvider, *parser, ignorables, parseFlags, status);
////////////////////////
/// CURRENCY MATCHER ///
////////////////////////
- if (parseCurrency || patternInfo.hasCurrencySign()) {
+ if (parseCurrency || affixProvider->hasCurrencySign()) {
parser->addMatcher(parser->fLocalMatchers.currency = {currencySymbols, symbols, status});
}
{"en_US", "1", "USD", "$1.00", "USD\\u00A01.00", "1.00 US dollars"},
{"en_US", "1234.56", "USD", "$1,234.56", "USD\\u00A01,234.56", "1,234.56 US dollars"},
{"en_US", "-1234.56", "USD", "-$1,234.56", "-USD\\u00A01,234.56", "-1,234.56 US dollars"},
- {"zh_CN", "1", "USD", "US$1.00", "USD\\u00A01.00", "1.00\\u7F8E\\u5143"},
- {"zh_CN", "1234.56", "USD", "US$1,234.56", "USD\\u00A01,234.56", "1,234.56\\u7F8E\\u5143"},
- {"zh_CN", "1", "CNY", "\\uFFE51.00", "CNY\\u00A01.00", "1.00\\u4EBA\\u6C11\\u5E01"},
- {"zh_CN", "1234.56", "CNY", "\\uFFE51,234.56", "CNY\\u00A01,234.56", "1,234.56\\u4EBA\\u6C11\\u5E01"},
+ {"zh_CN", "1", "USD", "US$1.00", "USD\\u00A01.00", "1.00\\u00A0\\u7F8E\\u5143"},
+ {"zh_CN", "1234.56", "USD", "US$1,234.56", "USD\\u00A01,234.56", "1,234.56\\u00A0\\u7F8E\\u5143"},
+ {"zh_CN", "1", "CNY", "\\uFFE51.00", "CNY\\u00A01.00", "1.00\\u00A0\\u4EBA\\u6C11\\u5E01"},
+ {"zh_CN", "1234.56", "CNY", "\\uFFE51,234.56", "CNY\\u00A01,234.56", "1,234.56\\u00A0\\u4EBA\\u6C11\\u5E01"},
{"ru_RU", "1", "RUB", "1,00\\u00A0\\u20BD", "1,00\\u00A0RUB", "1,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"},
{"ru_RU", "2", "RUB", "2,00\\u00A0\\u20BD", "2,00\\u00A0RUB", "2,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"},
{"ru_RU", "5", "RUB", "5,00\\u00A0\\u20BD", "5,00\\u00A0RUB", "5,00 \\u0440\\u043E\\u0441\\u0441\\u0438\\u0439\\u0441\\u043A\\u043E\\u0433\\u043E \\u0440\\u0443\\u0431\\u043B\\u044F"},
errln("FAIL: Expected " + formatResult + " actual: " + strBuf);
}
// test parsing, and test parsing for all currency formats.
- for (int j = 3; j < 6; ++j) {
+ // NOTE: ICU 62 requires that the currency format match the pattern in strict mode.
+ //for (int j = 3; j < 6; ++j) {
+ for (int j = 3 + kIndex; j <= 3 + kIndex; j++) {
// DATA[i][3] is the currency format result using
// CURRENCYSTYLE formatter.
// DATA[i][4] is the currency format result using
boolean includeUnpaired = 0 != (parseFlags & ParsingUtils.PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES);
SignDisplay signDisplay = (0 != (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED))
? SignDisplay.ALWAYS
- : SignDisplay.NEVER;
+ : SignDisplay.AUTO;
AffixPatternMatcher posPrefix = null;
AffixPatternMatcher posSuffix = null;
import com.ibm.icu.impl.StringSegment;
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.DecimalFormatProperties.ParseMode;
boolean parseCurrency) {
ULocale locale = symbols.getULocale();
- AffixPatternProvider patternInfo = new PropertiesAffixPatternProvider(properties);
+ AffixPatternProvider affixProvider;
+ if (properties.getCurrencyPluralInfo() == null) {
+ affixProvider = new PropertiesAffixPatternProvider(properties);
+ } else {
+ affixProvider = new CurrencyPluralInfoAffixProvider(properties.getCurrencyPluralInfo());
+ }
Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
boolean isStrict = properties.getParseMode() == ParseMode.STRICT;
Grouper grouper = Grouper.forProperties(properties);
if (grouper.getPrimary() <= 0) {
parseFlags |= ParsingUtils.PARSE_FLAG_GROUPING_DISABLED;
}
- if (parseCurrency || patternInfo.hasCurrencySign()) {
+ if (parseCurrency || affixProvider.hasCurrencySign()) {
parseFlags |= ParsingUtils.PARSE_FLAG_MONETARY_SEPARATORS;
}
IgnorablesMatcher ignorables = isStrict ? IgnorablesMatcher.STRICT : IgnorablesMatcher.DEFAULT;
//////////////////////
// Set up a pattern modifier with mostly defaults to generate AffixMatchers.
- AffixMatcher.createMatchers(patternInfo, parser, factory, ignorables, parseFlags);
+ AffixMatcher.createMatchers(affixProvider, parser, factory, ignorables, parseFlags);
////////////////////////
/// CURRENCY MATCHER ///
////////////////////////
- if (parseCurrency || patternInfo.hasCurrencySign()) {
+ if (parseCurrency || affixProvider.hasCurrencySign()) {
parser.addMatcher(CombinedCurrencyMatcher.getInstance(currency, symbols));
}
} else if (success) {
// Match succeeded, and this is NOT a flexible matcher. Proceed to the next matcher.
i++;
+ // Small hack: if there is another matcher coming, do not accept trailing weak chars.
+ // Needed for proper handling of currency spacing.
+ if (i < matchers.size() && segment.getOffset() != result.charEnd && result.charEnd > matcherOffset) {
+ segment.setOffset(result.charEnd);
+ }
} else if (isFlexible) {
// Match failed, and this is a flexible matcher. Try again with the next matcher.
i++;
assertEquals("Should parse successfully", 0.08, percentage.doubleValue());
assertEquals("Should consume whole string", 3, ppos.getIndex());
}
+
+ @Test
+ public void testStrictParseCurrencyLongNames() {
+ DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(ULocale.ENGLISH, DecimalFormat.PLURALCURRENCYSTYLE);
+ df.setParseStrict(true);
+ df.setCurrency(Currency.getInstance("USD"));
+ double input = 514.23;
+ String formatted = df.format(input);
+ assertEquals("Should format as expected", "514.23 US dollars", formatted);
+ ParsePosition ppos = new ParsePosition(0);
+ CurrencyAmount ca = df.parseCurrency(formatted, ppos);
+ assertEquals("Should consume whole number", ppos.getIndex(), 17);
+ assertEquals("Number should round-trip", ca.getNumber().doubleValue(), input);
+ assertEquals("Should get correct currency", ca.getCurrency().getCurrencyCode(), "USD");
+ }
+
+ @Test
+ public void testStrictParseCurrencySpacing() {
+ DecimalFormat df = new DecimalFormat("¤ 0", DecimalFormatSymbols.getInstance(ULocale.ROOT));
+ df.setCurrency(Currency.getInstance("USD"));
+ df.setParseStrict(true);
+ expect2(df, -51.42, "-US$ 51.42");
+ }
}