Skip to content

Commit bffc848

Browse files
author
Justin Lu
committed
8333755: NumberFormat integer only parsing breaks when format has suffix
Reviewed-by: naoto
1 parent b5d5896 commit bffc848

File tree

5 files changed

+170
-102
lines changed

5 files changed

+170
-102
lines changed

src/java.base/share/classes/java/text/CompactNumberFormat.java

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,34 +1724,14 @@ public Number parse(String text, ParsePosition pos) {
17241724
// Call DecimalFormat.subparseNumber() method to parse the
17251725
// number part of the input text
17261726
position = decimalFormat.subparseNumber(text, position,
1727-
digitList, false, false, status);
1727+
digitList, false, false, status).fullPos();
17281728

17291729
if (position == -1) {
17301730
// Unable to parse the number successfully
17311731
pos.index = oldStart;
17321732
pos.errorIndex = oldStart;
17331733
return null;
17341734
}
1735-
1736-
// If parse integer only is true and the parsing is broken at
1737-
// decimal point, then pass/ignore all digits and move pointer
1738-
// at the start of suffix, to process the suffix part
1739-
if (isParseIntegerOnly() && position < text.length()
1740-
&& text.charAt(position) == symbols.getDecimalSeparator()) {
1741-
position++; // Pass decimal character
1742-
for (; position < text.length(); ++position) {
1743-
char ch = text.charAt(position);
1744-
int digit = ch - symbols.getZeroDigit();
1745-
if (digit < 0 || digit > 9) {
1746-
digit = Character.digit(ch, 10);
1747-
// Parse all digit characters
1748-
if (!(digit >= 0 && digit <= 9)) {
1749-
break;
1750-
}
1751-
}
1752-
}
1753-
}
1754-
17551735
// Number parsed successfully; match prefix and
17561736
// suffix to obtain multiplier
17571737
pos.index = position;
@@ -2372,6 +2352,8 @@ public void setGroupingUsed(boolean newValue) {
23722352
* parsed as the value {@code 1234000} (1234 (integer part) * 1000
23732353
* (thousand)) and the fractional part would be skipped.
23742354
* The exact format accepted by the parse operation is locale dependent.
2355+
* @implSpec This implementation does not set the {@code ParsePosition} index
2356+
* to the position of the decimal symbol, but rather the end of the string.
23752357
*
23762358
* @return {@code true} if compact numbers should be parsed as integers
23772359
* only; {@code false} otherwise

src/java.base/share/classes/java/text/DecimalFormat.java

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2150,10 +2150,7 @@ private void append(StringBuffer result, String string,
21502150
* #getGroupingSize()} is not adhered to
21512151
* <li> {@link #isGroupingUsed()} returns {@code false}, and the grouping
21522152
* symbol is found
2153-
* <li> {@link #isParseIntegerOnly()} returns {@code true}, and the decimal
2154-
* separator is found
2155-
* <li> {@link #isGroupingUsed()} returns {@code true} and {@link
2156-
* #isParseIntegerOnly()} returns {@code false}, and the grouping
2153+
* <li> {@link #isGroupingUsed()} returns {@code true} and the grouping
21572154
* symbol occurs after the decimal separator
21582155
* <li> Any other characters are found, that are not the expected symbols,
21592156
* and are not digits that occur within the numerical portion
@@ -2379,7 +2376,8 @@ private final boolean subparse(String text, ParsePosition parsePosition,
23792376

23802377
// position will serve as new index when success, otherwise it will
23812378
// serve as errorIndex when failure
2382-
position = subparseNumber(text, position, digits, true, isExponent, status);
2379+
NumericPosition pos = subparseNumber(text, position, digits, true, isExponent, status);
2380+
position = pos.fullPos;
23832381

23842382
// First character after the prefix was un-parseable, should
23852383
// fail regardless if lenient or strict.
@@ -2422,9 +2420,15 @@ private final boolean subparse(String text, ParsePosition parsePosition,
24222420
return false;
24232421
}
24242422

2425-
// No failures, thus increment the index by the suffix
2426-
parsePosition.index = position +
2427-
(gotPositive ? positiveSuffix.length() : negativeSuffix.length());
2423+
// When parsing integer only, index should be int pos
2424+
// If intPos is 0, the entire value was integer
2425+
if (isParseIntegerOnly() && pos.intPos > 0) {
2426+
parsePosition.index = pos.intPos;
2427+
} else {
2428+
// increment the index by the suffix
2429+
parsePosition.index = position +
2430+
(gotPositive ? positiveSuffix.length() : negativeSuffix.length());
2431+
}
24282432
} else {
24292433
parsePosition.index = position;
24302434
}
@@ -2437,6 +2441,19 @@ private final boolean subparse(String text, ParsePosition parsePosition,
24372441
return true;
24382442
}
24392443

2444+
/**
2445+
* NumericPosition is a helper record class that stores two indices of interest.
2446+
* {@code fullPos} is either the first unparseable character or -1 in case
2447+
* of no valid number parsed. {@code intPos} reflects the position of
2448+
* a parsed decimal symbol, if one exists. When parsing with {@code isParseIntegerOnly()},
2449+
* {@code fullPos} is used to match the suffix, and reset the {@code ParsePosition}
2450+
* index to {@code intPos}.
2451+
*
2452+
* @param fullPos an index that reflects the full traversal of the numerical String
2453+
* @param intPos an index that reflects the position of a parsed decimal symbol.
2454+
*/
2455+
record NumericPosition(int fullPos, int intPos) {}
2456+
24402457
/**
24412458
* Parses a number from the given {@code text}. The text is parsed
24422459
* beginning at {@code position}, until an unparseable character is seen.
@@ -2449,14 +2466,15 @@ private final boolean subparse(String text, ParsePosition parsePosition,
24492466
* @param status upon return contains boolean status flags indicating
24502467
* whether the value is infinite and whether it is
24512468
* positive
2452-
* @return returns the position of the first unparseable character or
2453-
* -1 in case of no valid number parsed
2469+
* @return returns a {@code NumericPosition} that stores both a full
2470+
* traversal index, and an int only index.
24542471
*/
2455-
int subparseNumber(String text, int position,
2456-
DigitList digits, boolean checkExponent,
2457-
boolean isExponent, boolean[] status) {
2472+
NumericPosition subparseNumber(String text, int position,
2473+
DigitList digits, boolean checkExponent,
2474+
boolean isExponent, boolean[] status) {
24582475
// process digits or Inf, find decimal position
24592476
status[STATUS_INFINITE] = false;
2477+
int intIndex = 0;
24602478
if (!isExponent && text.regionMatches(position, symbols.getInfinity(), 0,
24612479
symbols.getInfinity().length())) {
24622480
position += symbols.getInfinity().length();
@@ -2516,7 +2534,7 @@ int subparseNumber(String text, int position,
25162534
if (parseStrict && isGroupingUsed() && position == startPos + groupingSize
25172535
&& prevSeparatorIndex == -groupingSize && !sawDecimal
25182536
&& digit >= 0 && digit <= 9) {
2519-
return position;
2537+
return new NumericPosition(position, intIndex);
25202538
}
25212539

25222540
if (digit == 0) {
@@ -2538,37 +2556,44 @@ int subparseNumber(String text, int position,
25382556
--digits.decimalAt;
25392557
} else {
25402558
++digitCount;
2541-
digits.append((char)(digit + '0'));
2559+
if (!sawDecimal || !isParseIntegerOnly()) {
2560+
digits.append((char)(digit + '0'));
2561+
}
25422562
}
25432563
} else if (digit > 0 && digit <= 9) { // [sic] digit==0 handled above
25442564
sawDigit = true;
25452565
++digitCount;
2546-
digits.append((char)(digit + '0'));
2566+
if (!sawDecimal || !isParseIntegerOnly()) {
2567+
digits.append((char) (digit + '0'));
2568+
}
25472569

25482570
// Cancel out backup setting (see grouping handler below)
25492571
backup = -1;
25502572
} else if (!isExponent && ch == decimal) {
25512573
// Check grouping size on decimal separator
25522574
if (parseStrict && isGroupingViolation(position, prevSeparatorIndex)) {
2553-
return groupingViolationIndex(position, prevSeparatorIndex);
2575+
return new NumericPosition(
2576+
groupingViolationIndex(position, prevSeparatorIndex), intIndex);
25542577
}
25552578
// If we're only parsing integers, or if we ALREADY saw the
25562579
// decimal, then don't parse this one.
2557-
if (isParseIntegerOnly() || sawDecimal) {
2580+
if (sawDecimal) {
25582581
break;
25592582
}
2583+
intIndex = position;
25602584
digits.decimalAt = digitCount; // Not digits.count!
25612585
sawDecimal = true;
25622586
} else if (!isExponent && ch == grouping && isGroupingUsed()) {
25632587
if (parseStrict) {
25642588
// text should not start with grouping when strict
25652589
if (position == startPos) {
2566-
return startPos;
2590+
return new NumericPosition(startPos, intIndex);
25672591
}
25682592
// when strict, fail if grouping occurs after decimal OR
25692593
// current group violates grouping size
25702594
if (sawDecimal || (isGroupingViolation(position, prevSeparatorIndex))) {
2571-
return groupingViolationIndex(position, prevSeparatorIndex);
2595+
return new NumericPosition(
2596+
groupingViolationIndex(position, prevSeparatorIndex), intIndex);
25722597
}
25732598
prevSeparatorIndex = position; // track previous
25742599
} else {
@@ -2621,7 +2646,8 @@ int subparseNumber(String text, int position,
26212646
// "1,234%" and "1,234" both end with pos = 5, since '%' breaks
26222647
// the loop before incrementing position. In both cases, check
26232648
// should be done at pos = 4
2624-
return groupingViolationIndex(position - 1, prevSeparatorIndex);
2649+
return new NumericPosition(
2650+
groupingViolationIndex(position - 1, prevSeparatorIndex), intIndex);
26252651
}
26262652
}
26272653

@@ -2636,8 +2662,9 @@ int subparseNumber(String text, int position,
26362662
digits.decimalAt = digitCount; // Not digits.count!
26372663
}
26382664

2639-
// Adjust for exponent, if any
2640-
if (exponent != 0) {
2665+
// If parsing integer only, adjust exponent if it occurs
2666+
// in integer portion, otherwise ignore it
2667+
if (!sawDecimal || !isParseIntegerOnly()) {
26412668
digits.decimalAt = shiftDecimalAt(digits.decimalAt, exponent);
26422669
}
26432670

@@ -2646,10 +2673,10 @@ int subparseNumber(String text, int position,
26462673
// parse "$" with pattern "$#0.00". (return index 0 and error
26472674
// index 1).
26482675
if (!sawDigit && digitCount == 0) {
2649-
return -1;
2676+
return new NumericPosition(-1, intIndex);
26502677
}
26512678
}
2652-
return position;
2679+
return new NumericPosition(position, intIndex);
26532680
}
26542681

26552682
// Calculate the final decimal position based off the exponent value
@@ -2917,7 +2944,8 @@ public int getMultiplier () {
29172944
* have '{@code U+2030}'.
29182945
*
29192946
* <P>Example: with multiplier 100, 1.23 is formatted as "123", and
2920-
* "123" is parsed into 1.23.
2947+
* "123" is parsed into 1.23. If {@code isParseIntegerOnly()} returns {@code true},
2948+
* "123" is parsed into 1.
29212949
*
29222950
* @param newValue the new multiplier
29232951
* @see #getMultiplier

src/java.base/share/classes/java/text/NumberFormat.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,11 @@ public Number parse(String source) throws ParseException {
468468
}
469469

470470
/**
471-
* Returns true if this format will parse numbers as integers only.
471+
* Returns {@code true} if this format will parse numbers as integers only.
472+
* The {@code ParsePosition} index will be set to the position of the decimal
473+
* symbol. The exact format accepted by the parse operation is locale dependent.
472474
* For example in the English locale, with ParseIntegerOnly true, the
473-
* string "1234." would be parsed as the integer value 1234 and parsing
474-
* would stop at the "." character. Of course, the exact format accepted
475-
* by the parse operation is locale dependent and determined by sub-classes
476-
* of NumberFormat.
475+
* string "123.45" would be parsed as the integer value 123.
477476
*
478477
* @return {@code true} if numbers should be parsed as integers only;
479478
* {@code false} otherwise

test/jdk/java/text/Format/NumberFormat/BigDecimalParse.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@
2323

2424
/*
2525
* @test
26-
* @bug 4018937 8008577 8174269
26+
* @bug 4018937 8008577 8174269 8333755
2727
* @summary Confirm that methods which are newly added to support BigDecimal and BigInteger work as expected.
2828
* @run junit/othervm BigDecimalParse
2929
*/
3030

3131
import java.math.BigDecimal;
32-
import java.text.*;
33-
import java.util.*;
32+
import java.text.DecimalFormat;
33+
import java.text.Format;
34+
import java.text.MessageFormat;
35+
import java.text.NumberFormat;
36+
import java.text.ParsePosition;
37+
import java.util.Locale;
3438

3539
import org.junit.jupiter.api.Test;
3640
import org.junit.jupiter.api.BeforeAll;
@@ -543,23 +547,23 @@ public void test_Parse_in_MessageFormat_NotParseIntegerOnly() {
543547
static final int[][] parsePosition2 = { // {errorIndex, index}
544548
/*
545549
* Should keep in mind that the expected result is different from
546-
* DecimalFormat.parse() for some cases.
550+
* DecimalFormat.parse() for some cases. This is because parsing integer
551+
* only will return a successful parse for the subformat, but since the index
552+
* returned is not equal to the length, at the MessageFormat level, this
553+
* will be interpreted as a failed parse, and so the DecimalFormat index
554+
* should be reflected as the MessageFormat errorIndex.
547555
*/
548556
{28, 0}, // parsing stopped at '.'
549557
{29, 0}, // parsing stopped at '.'
550558
{29, 0}, // parsing stopped at '.'
551-
{2, 0}, // parsing stopped at '(' because cannot find ')'
552-
{2, 0}, // parsing stopped at the first numeric
553-
// because cannot find '%'
554-
{2, 0}, // parsing stopped at the first numeric
555-
// because cannot find '%'
559+
{30, 0}, // parsing stopped at '.'
560+
{31, 0}, // parsing stopped at '.'
561+
{32, 0}, // parsing stopped at '.'
556562
{28, 0}, // parsing stopped at '.'
557563
{29, 0}, // parsing stopped at '.'
558-
559564
{-1, 57}, {-1, 58}, {-1, 59}, {-1, 61},
560565
{56, 0}, // parsing stopped at '.'
561-
// because cannot find '%'
562-
{2, 0}, // parsing stopped at '(' because cannot find ')'
566+
{57, 0}, // parsing stopped at '.'
563567
{-1, 60}, {-1, 61},
564568
{28, 0}, // parsing stopped at '.'
565569
{-1, 88},

0 commit comments

Comments
 (0)