diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index 1f249888a28..04acca1ceb5 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -2379,8 +2379,8 @@ public class DecimalFormat extends NumberFormat { NumericPosition pos = subparseNumber(text, position, digits, true, isExponent, status); position = pos.fullPos; - // First character after the prefix was un-parseable, should - // fail regardless if lenient or strict. + // First character after the prefix was un-parseable or parsing integer + // only with no integer portion. Should fail regardless if lenient or strict. if (position == -1) { parsePosition.index = oldStart; parsePosition.errorIndex = oldStart; @@ -2421,8 +2421,8 @@ public class DecimalFormat extends NumberFormat { } // When parsing integer only, index should be int pos - // If intPos is 0, the entire value was integer - if (isParseIntegerOnly() && pos.intPos > 0) { + // If intPos is -1, the entire value was integer and index should be full pos + if (isParseIntegerOnly() && pos.intPos != -1) { parsePosition.index = pos.intPos; } else { // increment the index by the suffix @@ -2474,7 +2474,7 @@ public class DecimalFormat extends NumberFormat { boolean isExponent, boolean[] status) { // process digits or Inf, find decimal position status[STATUS_INFINITE] = false; - int intIndex = 0; + int intIndex = -1; if (!isExponent && text.regionMatches(position, symbols.getInfinity(), 0, symbols.getInfinity().length())) { position += symbols.getInfinity().length(); @@ -2570,6 +2570,10 @@ public class DecimalFormat extends NumberFormat { // Cancel out backup setting (see grouping handler below) backup = -1; } else if (!isExponent && ch == decimal) { + if (isParseIntegerOnly() && startPos == position) { + // Parsing int only with no integer portion, fail + return new NumericPosition(-1, intIndex); + } // Check grouping size on decimal separator if (parseStrict && isGroupingViolation(position, prevSeparatorIndex)) { return new NumericPosition( diff --git a/test/jdk/java/text/Format/NumberFormat/LenientParseTest.java b/test/jdk/java/text/Format/NumberFormat/LenientParseTest.java index 41f8961ff32..c85fe0f6cbb 100644 --- a/test/jdk/java/text/Format/NumberFormat/LenientParseTest.java +++ b/test/jdk/java/text/Format/NumberFormat/LenientParseTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8327640 8331485 8333456 + * @bug 8327640 8331485 8333456 8335668 * @summary Test suite for NumberFormat parsing when lenient. * @run junit/othervm -Duser.language=en -Duser.country=US LenientParseTest * @run junit/othervm -Duser.language=ja -Duser.country=JP LenientParseTest @@ -128,6 +128,28 @@ public class LenientParseTest { dFmt.setParseIntegerOnly(false); } + // 8335668: Parsing with integer only against String with no integer portion + // should fail, not return 0. Expected error index should be 0 + @Test + public void integerParseOnlyFractionOnlyTest() { + var fmt = NumberFormat.getIntegerInstance(); + failParse(fmt, localizeText("."), 0); + failParse(fmt, localizeText(".0"), 0); + failParse(fmt, localizeText(".55"), 0); + } + + // 8335668: Parsing with integer only against String with no integer portion + // should fail, not return 0. Expected error index should be 0 + @Test // Non-localized, run once + @EnabledIfSystemProperty(named = "user.language", matches = "en") + public void compactIntegerParseOnlyFractionOnlyTest() { + var fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); + fmt.setParseIntegerOnly(true); + failParse(fmt, ".K", 0); + failParse(fmt, ".0K", 0); + failParse(fmt, ".55K", 0); + } + @Test // Non-localized, only run once @EnabledIfSystemProperty(named = "user.language", matches = "en") public void badExponentParseNumberFormatTest() { @@ -313,7 +335,11 @@ public class LenientParseTest { Arguments.of("10000", 10000d), Arguments.of("100,000", 100000d), Arguments.of("1,000,000", 1000000d), - Arguments.of("10,000,000", 10000000d)) + Arguments.of("10,000,000", 10000000d), + // Smaller value cases (w/ decimal) + Arguments.of(".1", .1d), + Arguments.of("1.1", 1.1d), + Arguments.of("11.1", 11.1d)) .map(args -> Arguments.of( localizeText(String.valueOf(args.get()[0])), args.get()[1])); } diff --git a/test/jdk/java/text/Format/NumberFormat/StrictParseTest.java b/test/jdk/java/text/Format/NumberFormat/StrictParseTest.java index 333bc1b0506..3e90ccb39ce 100644 --- a/test/jdk/java/text/Format/NumberFormat/StrictParseTest.java +++ b/test/jdk/java/text/Format/NumberFormat/StrictParseTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8327640 8331485 8333755 + * @bug 8327640 8331485 8333755 8335668 * @summary Test suite for NumberFormat parsing with strict leniency * @run junit/othervm -Duser.language=en -Duser.country=US StrictParseTest * @run junit/othervm -Duser.language=ja -Duser.country=JP StrictParseTest @@ -203,6 +203,28 @@ public class StrictParseTest { } } + // 8335668: Parsing with integer only against String with no integer portion + // should fail, not return 0. Expected error index should be 0 + @Test + public void integerParseOnlyFractionOnlyTest() { + var fmt = NumberFormat.getIntegerInstance(); + failParse(fmt, localizeText("."), 0); + failParse(fmt, localizeText(".0"), 0); + failParse(fmt, localizeText(".55"), 0); + } + + // 8335668: Parsing with integer only against String with no integer portion + // should fail, not return 0. Expected error index should be 0 + @Test // Non-localized, run once + @EnabledIfSystemProperty(named = "user.language", matches = "en") + public void compactIntegerParseOnlyFractionOnlyTest() { + var fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); + fmt.setParseIntegerOnly(true); + failParse(fmt, ".K", 0); + failParse(fmt, ".0K", 0); + failParse(fmt, ".55K", 0); + } + // 8333755: Parsing behavior should follow normal strict behavior // when it comes to failures. @ParameterizedTest @@ -426,8 +448,8 @@ public class StrictParseTest { Arguments.of("1,234a", 5), Arguments.of("1,.a", 2), Arguments.of("1.a", 2), - Arguments.of(".22a", 3), - Arguments.of(".1a1", 2), + Arguments.of("1.22a", 4), + Arguments.of("1.1a1", 3), Arguments.of("1,234,a", 5), // Double decimal Arguments.of("1,234..5", 5)) @@ -453,7 +475,11 @@ public class StrictParseTest { Arguments.of("10000", 10000d), Arguments.of("100,000", 100000d), Arguments.of("1,000,000", 1000000d), - Arguments.of("10,000,000", 10000000d)) + Arguments.of("10,000,000", 10000000d), + // Smaller value cases (w/ decimal) + Arguments.of(".1", .1d), + Arguments.of("1.1", 1.1d), + Arguments.of("11.1", 11.1d)) .map(args -> Arguments.of( localizeText(String.valueOf(args.get()[0])), args.get()[1])); }