ICU-22303 Support parsing infinity/NaN when decimal pattern match is required

If a DecimalFormat pattern contains a decimal point and
setDecimalPatternMatchRequired is true, then DecimalFormat parse() fails
to parse infinity/NaN representations. This is because infinity/NaN
parsing does not set the HAS_DECIMAL_SEPARATOR_FLAG and so the
RequireDecimalSeparatorValidator fails.

This modifies the RequireDecimalSeparatorValidator so that it does not
fail if the INFINITY or NAN flags are set, making it so decimal
separators are not required if the infinity/NaN representations are
parsed.
This commit is contained in:
Steve Lawrence 2023-10-27 17:19:28 -04:00 committed by Rich Gillam
parent 255eb4ef3e
commit 3edd9c828a
5 changed files with 41 additions and 4 deletions

View file

@ -46,8 +46,9 @@ RequireDecimalSeparatorValidator::RequireDecimalSeparatorValidator(bool patternH
}
void RequireDecimalSeparatorValidator::postProcess(ParsedNumber& result) const {
bool parseIsInfNaN = 0 != (result.flags & FLAG_INFINITY) || 0 != (result.flags & FLAG_NAN);
bool parseHasDecimalSeparator = 0 != (result.flags & FLAG_HAS_DECIMAL_SEPARATOR);
if (parseHasDecimalSeparator != fPatternHasDecimalSeparator) {
if (!parseIsInfNaN && parseHasDecimalSeparator != fPatternHasDecimalSeparator) {
result.flags |= FLAG_FAIL;
}
}

View file

@ -227,6 +227,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(Test11649_DecFmtCurrencies);
TESTCASE_AUTO(Test13148_ParseGroupingSeparators);
TESTCASE_AUTO(Test12753_PatternDecimalPoint);
TESTCASE_AUTO(Test22303_PatternDecimalPoint_InfNaN);
TESTCASE_AUTO(Test11647_PatternCurrencySymbols);
TESTCASE_AUTO(Test11913_BigDecimal);
TESTCASE_AUTO(Test11020_RoundingInScientificNotation);
@ -9423,9 +9424,26 @@ void NumberFormatTest::Test12753_PatternDecimalPoint() {
df.parse(u"123",result, status);
assertEquals("Parsing integer succeeded even though setDecimalPatternMatchRequired was set",
U_INVALID_FORMAT_ERROR, status);
}
}
void NumberFormatTest::Test11647_PatternCurrencySymbols() {
void NumberFormatTest::Test22303_PatternDecimalPoint_InfNaN() {
UErrorCode status = U_ZERO_ERROR;
DecimalFormatSymbols symbols(Locale::getUS(), status);
symbols.setSymbol(DecimalFormatSymbols::kInfinitySymbol, u"infinity", false);
symbols.setSymbol(DecimalFormatSymbols::kNaNSymbol, u"notanumber", false);
DecimalFormat df(u"0.00", symbols, status);
if (!assertSuccess("", status)) return;
df.setDecimalPatternMatchRequired(true);
Formattable result;
df.parse(u"infinity", result, status);
assertEquals("Should parse to +INF even though decimal is required", INFINITY, result.getDouble());
df.parse(u"notanumber", result, status);
assertEquals("Should parse to NaN even though decimal is required", NAN, result.getDouble());
df.parse("-infinity", result, status);
assertEquals("Should parse to -INF even though decimal is required", -INFINITY, result.getDouble());
}
void NumberFormatTest::Test11647_PatternCurrencySymbols() {
UErrorCode status = U_ZERO_ERROR;
DecimalFormat df(status);
df.applyPattern(u"¤¤¤¤#", status);

View file

@ -283,6 +283,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void Test11649_DecFmtCurrencies();
void Test13148_ParseGroupingSeparators();
void Test12753_PatternDecimalPoint();
void Test22303_PatternDecimalPoint_InfNaN();
void Test11647_PatternCurrencySymbols();
void Test11913_BigDecimal();
void Test11020_RoundingInScientificNotation();

View file

@ -5511,6 +5511,22 @@ public class NumberFormatTest extends CoreTestFmwk {
}
}
@Test
public void Test22303() throws ParseException {
ULocale locale = new ULocale("en-US");
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setInfinity("infinity");
symbols.setNaN("notanumber");
DecimalFormat df = new DecimalFormat("0.00", symbols);
df.setDecimalPatternMatchRequired(true);
Number result = df.parse("infinity");
assertEquals("Should parse to +INF even though decimal is required", Double.POSITIVE_INFINITY, result);
result = df.parse("notanumber");
assertEquals("Should parse to NaN even though decimal is required", Double.NaN, result);
result = df.parse("-infinity");
assertEquals("Should parse to -INF even though decimal is required", Double.NEGATIVE_INFINITY, result);
}
@Test
public void Test12962() {
String pat = "**0.00";

View file

@ -23,8 +23,9 @@ public class RequireDecimalSeparatorValidator extends ValidationMatcher {
@Override
public void postProcess(ParsedNumber result) {
boolean parseIsInfNaN = 0 != (result.flags & ParsedNumber.FLAG_INFINITY) || 0 != (result.flags & ParsedNumber.FLAG_NAN);
boolean parseHasDecimalSeparator = 0 != (result.flags & ParsedNumber.FLAG_HAS_DECIMAL_SEPARATOR);
if (parseHasDecimalSeparator != patternHasDecimalSeparator) {
if (!parseIsInfNaN && parseHasDecimalSeparator != patternHasDecimalSeparator) {
result.flags |= ParsedNumber.FLAG_FAIL;
}
}