parser->addMatcher(parser->fLocalMatchers.currency = {currencySymbols, symbols, status});
}
+ ///////////////
+ /// PERCENT ///
+ ///////////////
+
+ // 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 (affixProvider->containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) {
+ parser->addMatcher(parser->fLocalMatchers.percent = {symbols});
+ // causes number to be always scaled by 100:
+ parser->addMatcher(parser->fLocalValidators.percentFlags = {ResultFlags::FLAG_PERCENT});
+ }
+ if (affixProvider->containsSymbolType(AffixPatternType::TYPE_PERMILLE, status)) {
+ parser->addMatcher(parser->fLocalMatchers.permille = {symbols});
+ // causes number to be always scaled by 1000:
+ parser->addMatcher(parser->fLocalValidators.permilleFlags = {ResultFlags::FLAG_PERMILLE});
+ }
+
///////////////////////////////
/// OTHER STANDARD MATCHERS ///
///////////////////////////////
if (!isStrict) {
parser->addMatcher(parser->fLocalMatchers.plusSign = {symbols, false});
parser->addMatcher(parser->fLocalMatchers.minusSign = {symbols, false});
- parser->addMatcher(parser->fLocalMatchers.percent = {symbols});
- parser->addMatcher(parser->fLocalMatchers.permille = {symbols});
}
parser->addMatcher(parser->fLocalMatchers.nan = {symbols});
parser->addMatcher(parser->fLocalMatchers.infinity = {symbols});
RequireExponentValidator exponent;
RequireNumberValidator number;
MultiplierParseHandler multiplier;
+ FlagHandler percentFlags;
+ FlagHandler permilleFlags;
} fLocalValidators;
explicit NumberParserImpl(parse_flags_t parseFlags);
}
+FlagHandler::FlagHandler(result_flags_t flags)
+ : fFlags(flags) {}
+
+void FlagHandler::postProcess(ParsedNumber& result) const {
+ result.flags |= fFlags;
+}
+
+UnicodeString FlagHandler::toString() const {
+ return u"<Flags>";
+}
+
+
#endif /* #if !UCONFIG_NO_FORMATTING */
};
+/**
+ * Unconditionally applies a given set of flags to the ParsedNumber in the post-processing step.
+ */
+class FlagHandler : public ValidationMatcher, public UMemory {
+ public:
+ FlagHandler() = default;
+
+ FlagHandler(result_flags_t flags);
+
+ void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+ UnicodeString toString() const U_OVERRIDE;
+
+ private:
+ result_flags_t fFlags;
+};
+
+
} // namespace impl
} // namespace numparse
U_NAMESPACE_END
TESTCASE_AUTO(Test11735_ExceptionIssue);
TESTCASE_AUTO(Test11035_FormatCurrencyAmount);
TESTCASE_AUTO(Test11318_DoubleConversion);
+ TESTCASE_AUTO(TestParsePercentRegression);
TESTCASE_AUTO_END;
}
assertEquals("Should render all digits", u"999,999,999,999,999.9", appendTo);
}
+void NumberFormatTest::TestParsePercentRegression() {
+ IcuTestErrorCode status(*this, "TestParsePercentRegression");
+ LocalPointer<DecimalFormat> df1((DecimalFormat*) NumberFormat::createInstance("en", status));
+ LocalPointer<DecimalFormat> df2((DecimalFormat*) NumberFormat::createPercentInstance("en", status));
+ df1->setLenient(TRUE);
+ df2->setLenient(TRUE);
+
+ {
+ ParsePosition ppos;
+ Formattable result;
+ df1->parse("50%", result, ppos);
+ assertEquals("df1 should accept a number but not the percent sign", 2, ppos.getIndex());
+ assertEquals("df1 should return the number as 50", 50.0, result.getDouble(status));
+ }
+ {
+ ParsePosition ppos;
+ Formattable result;
+ df2->parse("50%", result, ppos);
+ assertEquals("df2 should accept the percent sign", 3, ppos.getIndex());
+ assertEquals("df2 should return the number as 0.5", 0.5, result.getDouble(status));
+ }
+ {
+ ParsePosition ppos;
+ Formattable result;
+ df2->parse("50", result, ppos);
+ assertEquals("df2 should return the number as 0.5 even though the percent sign is missing",
+ 0.5,
+ result.getDouble(status));
+ }
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
void Test11735_ExceptionIssue();
void Test11035_FormatCurrencyAmount();
void Test11318_DoubleConversion();
+ void TestParsePercentRegression();
private:
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
parse output breaks
55% 0.55
// J and K get null
-// P requires the symbol to be present and gets 55
-55 0.55 CJKP
+// C and P scale by 100 even if the percent sign is not present
+55 0.55 JK
test trailing grouping separators in pattern
// This test is for #13115
--- /dev/null
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+/**
+ * Unconditionally applies a given set of flags to the ParsedNumber in the post-processing step.
+ */
+public class FlagHandler extends ValidationMatcher {
+
+ public static final FlagHandler PERCENT = new FlagHandler(ParsedNumber.FLAG_PERCENT);
+ public static final FlagHandler PERMILLE = new FlagHandler(ParsedNumber.FLAG_PERMILLE);
+
+ private final int flags;
+
+ private FlagHandler(int flags) {
+ this.flags = flags;
+ }
+
+ @Override
+ public void postProcess(ParsedNumber result) {
+ result.flags |= flags;
+ }
+
+ @Override
+ public String toString() {
+ return "<FlagsHandler " + Integer.toHexString(flags) + ">";
+ }
+}
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;
parser.addMatcher(CombinedCurrencyMatcher.getInstance(currency, symbols));
}
+ ///////////////
+ /// PERCENT ///
+ ///////////////
+
+ // 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 (affixProvider.containsSymbolType(AffixUtils.TYPE_PERCENT)) {
+ parser.addMatcher(PercentMatcher.getInstance(symbols));
+ // causes number to be always scaled by 100:
+ parser.addMatcher(FlagHandler.PERCENT);
+ }
+ if (affixProvider.containsSymbolType(AffixUtils.TYPE_PERMILLE)) {
+ parser.addMatcher(PermilleMatcher.getInstance(symbols));
+ // causes number to be always scaled by 1000:
+ parser.addMatcher(FlagHandler.PERMILLE);
+ }
+
///////////////////////////////
/// OTHER STANDARD MATCHERS ///
///////////////////////////////
if (!isStrict) {
parser.addMatcher(PlusSignMatcher.getInstance(symbols, false));
parser.addMatcher(MinusSignMatcher.getInstance(symbols, false));
- parser.addMatcher(PercentMatcher.getInstance(symbols));
- parser.addMatcher(PermilleMatcher.getInstance(symbols));
}
parser.addMatcher(NanMatcher.getInstance(symbols, parseFlags));
parser.addMatcher(InfinityMatcher.getInstance(symbols));
parse output breaks
55% 0.55
// J and K get null
-// P requires the symbol to be present and gets 55
-55 0.55 JKP
+// C and P scale by 100 even if the percent sign is not present
+55 0.55 JK
test trailing grouping separators in pattern
// This test is for #13115
assertEquals("Should consume the trailing bidi since it is in the symbol", 5, ppos.getIndex());
ppos.setIndex(0);
result = df.parse("-42a\u200E ", ppos);
- assertEquals("Should not parse as percent", new Long(-42), result);
+ assertEquals("Should parse as percent", -0.42, result.doubleValue());
assertEquals("Should not consume the trailing bidi or whitespace", 4, ppos.getIndex());
// A few more cases based on the docstring:
DecimalFormat df = new DecimalFormat("-0", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
expect2(df, -5, "--5");
}
+
+ @Test
+ public void testParsePercentRegression() {
+ DecimalFormat df1 = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH);
+ DecimalFormat df2 = (DecimalFormat) NumberFormat.getPercentInstance(ULocale.ENGLISH);
+ df1.setParseStrict(false);
+ df2.setParseStrict(false);
+
+ {
+ ParsePosition ppos = new ParsePosition(0);
+ Number result = df1.parse("50%", ppos);
+ assertEquals("df1 should accept a number but not the percent sign", 2, ppos.getIndex());
+ assertEquals("df1 should return the number as 50", 50.0, result.doubleValue());
+ }
+ {
+ ParsePosition ppos = new ParsePosition(0);
+ Number result = df2.parse("50%", ppos);
+ assertEquals("df2 should accept the percent sign", 3, ppos.getIndex());
+ assertEquals("df2 should return the number as 0.5", 0.5, result.doubleValue());
+ }
+ {
+ ParsePosition ppos = new ParsePosition(0);
+ Number result = df2.parse("50", ppos);
+ assertEquals("df2 should return the number as 0.5 even though the percent sign is missing",
+ 0.5,
+ result.doubleValue());
+ }
+ }
}