undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
480
test/jdk/java/text/Format/NumberFormat/LenientParseTest.java
Normal file
480
test/jdk/java/text/Format/NumberFormat/LenientParseTest.java
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @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
|
||||
* @run junit/othervm -Duser.language=zh -Duser.country=CN LenientParseTest
|
||||
* @run junit/othervm -Duser.language=tr -Duser.country=TR LenientParseTest
|
||||
* @run junit/othervm -Duser.language=de -Duser.country=DE LenientParseTest
|
||||
* @run junit/othervm -Duser.language=fr -Duser.country=FR LenientParseTest
|
||||
* @run junit/othervm -Duser.language=ar -Duser.country=AR LenientParseTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.text.CompactNumberFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
// Tests lenient parsing, this is done by testing the NumberFormat factory instances
|
||||
// against a number of locales with different formatting conventions. The locales
|
||||
// used all use a grouping size of 3. When lenient, parsing only fails
|
||||
// if the prefix and/or suffix are not found, or the first character after the
|
||||
// prefix is un-parseable. The tested locales all use groupingSize of 3.
|
||||
public class LenientParseTest {
|
||||
|
||||
// Used to retrieve the locale's expected symbols
|
||||
private static final DecimalFormatSymbols dfs =
|
||||
new DecimalFormatSymbols(Locale.getDefault());
|
||||
private static final DecimalFormat dFmt = (DecimalFormat)
|
||||
NumberFormat.getNumberInstance(Locale.getDefault());
|
||||
private static final DecimalFormat cFmt =
|
||||
(DecimalFormat) NumberFormat.getCurrencyInstance(Locale.getDefault());
|
||||
private static final DecimalFormat pFmt =
|
||||
(DecimalFormat) NumberFormat.getPercentInstance(Locale.getDefault());
|
||||
private static final CompactNumberFormat cmpctFmt =
|
||||
(CompactNumberFormat) NumberFormat.getCompactNumberInstance(Locale.getDefault(),
|
||||
NumberFormat.Style.SHORT);
|
||||
|
||||
// All NumberFormats should parse leniently (which is the default)
|
||||
static {
|
||||
// To effectively test compactNumberFormat, these should be set accordingly
|
||||
cmpctFmt.setParseIntegerOnly(false);
|
||||
cmpctFmt.setGroupingUsed(true);
|
||||
}
|
||||
|
||||
// ---- NumberFormat tests ----
|
||||
// Test prefix/suffix behavior with a predefined DecimalFormat
|
||||
// Non-localized, only run once
|
||||
@ParameterizedTest
|
||||
@MethodSource("badParseStrings")
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void numFmtFailParseTest(String toParse, int expectedErrorIndex) {
|
||||
// Format with grouping size = 3, prefix = a, suffix = b
|
||||
DecimalFormat nonLocalizedDFmt = new DecimalFormat("a#,#00.00b");
|
||||
failParse(nonLocalizedDFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
// All input Strings should parse fully and return the expected value.
|
||||
// Expected index should be the length of the parse string, since it parses fully
|
||||
@ParameterizedTest
|
||||
@MethodSource("validFullParseStrings")
|
||||
public void numFmtSuccessFullParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(dFmt, toParse, toParse.length()));
|
||||
}
|
||||
|
||||
// All input Strings should parse partially and return expected value
|
||||
// with the expected final index
|
||||
@ParameterizedTest
|
||||
@MethodSource("validPartialParseStrings")
|
||||
public void numFmtSuccessPartialParseTest(String toParse, double expectedValue,
|
||||
int expectedIndex) {
|
||||
assertEquals(expectedValue, successParse(dFmt, toParse, expectedIndex));
|
||||
}
|
||||
|
||||
// Parse partially due to no grouping
|
||||
@ParameterizedTest
|
||||
@MethodSource("noGroupingParseStrings")
|
||||
public void numFmtStrictGroupingNotUsed(String toParse, double expectedValue, int expectedIndex) {
|
||||
dFmt.setGroupingUsed(false);
|
||||
assertEquals(expectedValue, successParse(dFmt, toParse, expectedIndex));
|
||||
dFmt.setGroupingUsed(true);
|
||||
}
|
||||
|
||||
// Parse partially due to integer only
|
||||
@ParameterizedTest
|
||||
@MethodSource("integerOnlyParseStrings")
|
||||
public void numFmtStrictIntegerOnlyUsed(String toParse, int expectedValue, int expectedIndex) {
|
||||
dFmt.setParseIntegerOnly(true);
|
||||
assertEquals(expectedValue, successParse(dFmt, toParse, expectedIndex));
|
||||
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() {
|
||||
// Some fmt, with an "E" exponent string
|
||||
DecimalFormat fmt = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
|
||||
// Upon non-numeric in exponent, parse will still successfully complete
|
||||
// but index should end on the last valid char in exponent
|
||||
assertEquals(1.23E45, successParse(fmt, "1.23E45.123", 7));
|
||||
assertEquals(1.23E45, successParse(fmt, "1.23E45.", 7));
|
||||
assertEquals(1.23E45, successParse(fmt, "1.23E45FOO3222", 7));
|
||||
}
|
||||
|
||||
// ---- CurrencyFormat tests ----
|
||||
// All input Strings should pass and return expected value.
|
||||
@ParameterizedTest
|
||||
@MethodSource("currencyValidFullParseStrings")
|
||||
public void currFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(cFmt, toParse, toParse.length()));
|
||||
}
|
||||
|
||||
// Strings may parse partially or fail. This is because the mapped
|
||||
// data may cause the error to occur before the suffix can be found, (if the locale
|
||||
// uses a suffix).
|
||||
@ParameterizedTest
|
||||
@MethodSource("currencyValidPartialParseStrings")
|
||||
public void currFmtParseTest(String toParse, double expectedValue,
|
||||
int expectedIndex) {
|
||||
if (cFmt.getPositiveSuffix().length() > 0) {
|
||||
// Since the error will occur before suffix is found, exception is thrown.
|
||||
failParse(cFmt, toParse, expectedIndex);
|
||||
} else {
|
||||
// Empty suffix, thus even if the error occurs, we have already found the
|
||||
// prefix, and simply parse partially
|
||||
assertEquals(expectedValue, successParse(cFmt, toParse, expectedIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- PercentFormat tests ----
|
||||
// All input Strings should pass and return expected value.
|
||||
@ParameterizedTest
|
||||
@MethodSource("percentValidFullParseStrings")
|
||||
public void percentFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(pFmt, toParse, toParse.length()));
|
||||
}
|
||||
|
||||
// Strings may parse partially or fail. This is because the mapped
|
||||
// data may cause the error to occur before the suffix can be found, (if the locale
|
||||
// uses a suffix).
|
||||
@ParameterizedTest
|
||||
@MethodSource("percentValidPartialParseStrings")
|
||||
public void percentFmtParseTest(String toParse, double expectedValue,
|
||||
int expectedIndex) {
|
||||
if (pFmt.getPositiveSuffix().length() > 0) {
|
||||
// Since the error will occur before suffix is found, exception is thrown.
|
||||
failParse(pFmt, toParse, expectedIndex);
|
||||
} else {
|
||||
// Empty suffix, thus even if the error occurs, we have already found the
|
||||
// prefix, and simply parse partially
|
||||
assertEquals(expectedValue, successParse(pFmt, toParse, expectedIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- CompactNumberFormat tests ----
|
||||
// Can match to both the decimalFormat patterns and the compact patterns
|
||||
// Unlike the other tests, this test is only ran against the US Locale and
|
||||
// tests against data built with the thousands format (K).
|
||||
@ParameterizedTest
|
||||
@MethodSource("compactValidPartialParseStrings")
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtFailParseTest(String toParse, double expectedValue, int expectedErrorIndex) {
|
||||
assertEquals(expectedValue, successParse(cmpctFmt, toParse, expectedErrorIndex));
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("compactValidFullParseStrings")
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(cmpctFmt, toParse, toParse.length()));
|
||||
}
|
||||
|
||||
// 8333456: Parse values with no compact suffix -> which allows parsing to iterate
|
||||
// position to the same value as string length which throws
|
||||
// StringIndexOutOfBoundsException upon charAt invocation
|
||||
@ParameterizedTest
|
||||
@MethodSource("compactValidNoSuffixParseStrings")
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtSuccessParseIntOnlyTest(String toParse, double expectedValue) {
|
||||
cmpctFmt.setParseIntegerOnly(true);
|
||||
assertEquals(expectedValue, successParse(cmpctFmt, toParse, toParse.length()));
|
||||
cmpctFmt.setParseIntegerOnly(false);
|
||||
}
|
||||
|
||||
// ---- Helper test methods ----
|
||||
|
||||
// Method is used when a String should parse successfully. This does not indicate
|
||||
// that the entire String was used, however. The index and errorIndex values
|
||||
// should be as expected.
|
||||
private double successParse(NumberFormat fmt, String toParse, int expectedIndex) {
|
||||
Number parsedValue = assertDoesNotThrow(() -> fmt.parse(toParse));
|
||||
ParsePosition pp = new ParsePosition(0);
|
||||
assertDoesNotThrow(() -> fmt.parse(toParse, pp));
|
||||
assertEquals(-1, pp.getErrorIndex(),
|
||||
"ParsePosition ErrorIndex is not in correct location");
|
||||
assertEquals(expectedIndex, pp.getIndex(),
|
||||
"ParsePosition Index is not in correct location");
|
||||
return parsedValue.doubleValue();
|
||||
}
|
||||
|
||||
// Method is used when a String should fail parsing. Indicated by either a thrown
|
||||
// ParseException, or null is returned depending on which parse method is invoked.
|
||||
// errorIndex should be as expected.
|
||||
private void failParse(NumberFormat fmt, String toParse, int expectedErrorIndex) {
|
||||
ParsePosition pp = new ParsePosition(0);
|
||||
assertThrows(ParseException.class, () -> fmt.parse(toParse));
|
||||
assertNull(fmt.parse(toParse, pp));
|
||||
assertEquals(expectedErrorIndex, pp.getErrorIndex());
|
||||
}
|
||||
|
||||
// ---- Data Providers ----
|
||||
|
||||
// Strings that should fail when parsed leniently.
|
||||
// Given as Arguments<String, expectedErrorIndex>
|
||||
// Non-localized data. For reference, the pattern of nonLocalizedDFmt is
|
||||
// "a#,#00.00b"
|
||||
private static Stream<Arguments> badParseStrings() {
|
||||
return Stream.of(
|
||||
// No prefix
|
||||
Arguments.of("1,1b", 0),
|
||||
// No suffix
|
||||
Arguments.of("a1,11", 5),
|
||||
// Digit does not follow the last grouping separator
|
||||
// Current behavior fails on the grouping separator
|
||||
Arguments.of("a1,11,z", 5),
|
||||
// No suffix after grouping
|
||||
Arguments.of("a1,11,", 5),
|
||||
// No prefix and suffix
|
||||
Arguments.of("1,11", 0),
|
||||
// First character after prefix is un-parseable
|
||||
// Behavior is to expect error index at 0, not 1
|
||||
Arguments.of("ac1,11", 0));
|
||||
}
|
||||
|
||||
// These data providers use US locale grouping and decimal separators
|
||||
// for readability, however, the data is tested against multiple locales
|
||||
// and is converted appropriately at runtime.
|
||||
|
||||
// Strings that should parse successfully, and consume the entire String
|
||||
// Form of Arguments(parseString, expectedParsedNumber)
|
||||
private static Stream<Arguments> validFullParseStrings() {
|
||||
return Stream.of(
|
||||
// Many subsequent grouping symbols
|
||||
Arguments.of("1,,,1", 11d),
|
||||
Arguments.of("11,,,11,,,11", 111111d),
|
||||
// Bad grouping size (with decimal)
|
||||
Arguments.of("1,1.", 11d),
|
||||
Arguments.of("11,111,11.", 1111111d),
|
||||
// Improper grouping size (with decimal and digits after)
|
||||
Arguments.of("1,1.1", 11.1d),
|
||||
Arguments.of("1,11.1", 111.1d),
|
||||
Arguments.of("1,1111.1", 11111.1d),
|
||||
Arguments.of("11,111,11.1", 1111111.1d),
|
||||
// Starts with grouping symbol
|
||||
Arguments.of(",111,,1,1", 11111d),
|
||||
Arguments.of(",1", 1d),
|
||||
Arguments.of(",,1", 1d),
|
||||
// Leading Zeros (not digits)
|
||||
Arguments.of("000,1,1", 11d),
|
||||
Arguments.of("000,111,11,,1", 111111d),
|
||||
Arguments.of("0,000,1,,1,1", 111d),
|
||||
Arguments.of("1,234.00", 1234d),
|
||||
Arguments.of("1,234.0", 1234d),
|
||||
Arguments.of("1,234.", 1234d),
|
||||
Arguments.of("1,234.00123", 1234.00123d),
|
||||
Arguments.of("1,234.012", 1234.012d),
|
||||
Arguments.of("1,234.224", 1234.224d),
|
||||
Arguments.of("1", 1d),
|
||||
Arguments.of("10", 10d),
|
||||
Arguments.of("100", 100d),
|
||||
Arguments.of("1000", 1000d),
|
||||
Arguments.of("1,000", 1000d),
|
||||
Arguments.of("10,000", 10000d),
|
||||
Arguments.of("10000", 10000d),
|
||||
Arguments.of("100,000", 100000d),
|
||||
Arguments.of("1,000,000", 1000000d),
|
||||
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]));
|
||||
}
|
||||
|
||||
// Strings that should parse successfully, but do not use the entire String
|
||||
// Form of Arguments(parseString, expectedParsedNumber, expectedIndex)
|
||||
private static Stream<Arguments> validPartialParseStrings() {
|
||||
return Stream.of(
|
||||
// End with grouping symbol
|
||||
Arguments.of("11,", 11d, 2),
|
||||
Arguments.of("11,,", 11d, 3),
|
||||
Arguments.of("11,,,", 11d, 4),
|
||||
// Random chars that aren't the expected symbols
|
||||
Arguments.of("1,1P111", 11d, 3),
|
||||
Arguments.of("1.1P111", 1.1d, 3),
|
||||
Arguments.of("1P,1111", 1d, 1),
|
||||
Arguments.of("1P.1111", 1d, 1),
|
||||
Arguments.of("1,1111P", 11111d, 6),
|
||||
// Grouping occurs after decimal separator)
|
||||
Arguments.of("1.11,11", 1.11d, 4),
|
||||
Arguments.of("1.,11,11", 1d, 2))
|
||||
.map(args -> Arguments.of(
|
||||
localizeText(String.valueOf(args.get()[0])), args.get()[1], args.get()[2]));
|
||||
}
|
||||
|
||||
// Test data input for when parse integer only is true
|
||||
// Form of Arguments(parseString, expectedParsedNumber, expectedIndex)
|
||||
private static Stream<Arguments> integerOnlyParseStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of("1234.1234", 1234, 4),
|
||||
Arguments.of("1234.12", 1234, 4),
|
||||
Arguments.of("1234.1a", 1234, 4),
|
||||
Arguments.of("1234.", 1234, 4))
|
||||
.map(args -> Arguments.of(
|
||||
localizeText(String.valueOf(args.get()[0])), args.get()[1], args.get()[2]));
|
||||
}
|
||||
|
||||
// Test data input for when no grouping is true
|
||||
// Form of Arguments(parseString, expectedParsedNumber, expectedIndex)
|
||||
private static Stream<Arguments> noGroupingParseStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of("12,34", 12d, 2),
|
||||
Arguments.of("1234,", 1234d, 4),
|
||||
Arguments.of("123,456.789", 123d, 3))
|
||||
.map(args -> Arguments.of(
|
||||
localizeText(String.valueOf(args.get()[0])), args.get()[1], args.get()[2]));
|
||||
}
|
||||
|
||||
// Mappers for respective data providers to adjust values accordingly
|
||||
// Localized percent prefix/suffix is added, with appropriate expected values
|
||||
// adjusted. Expected parsed number should be divided by 100.
|
||||
private static Stream<Arguments> percentValidPartialParseStrings() {
|
||||
return validPartialParseStrings().map(args ->
|
||||
Arguments.of(pFmt.getPositivePrefix() + args.get()[0] + pFmt.getPositiveSuffix(),
|
||||
(double) args.get()[1] / 100, (int) args.get()[2] + pFmt.getPositivePrefix().length())
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> percentValidFullParseStrings() {
|
||||
return validFullParseStrings().map(args -> Arguments.of(
|
||||
pFmt.getPositivePrefix() + args.get()[0] + pFmt.getPositiveSuffix(),
|
||||
(double) args.get()[1] / 100)
|
||||
);
|
||||
}
|
||||
|
||||
// Mappers for respective data providers to adjust values accordingly
|
||||
// Localized percent prefix/suffix is added, with appropriate expected values
|
||||
// adjusted. Separators replaced for monetary versions.
|
||||
private static Stream<Arguments> currencyValidPartialParseStrings() {
|
||||
return validPartialParseStrings().map(args -> Arguments.of(
|
||||
cFmt.getPositivePrefix() + String.valueOf(args.get()[0])
|
||||
.replace(dfs.getGroupingSeparator(), dfs.getMonetaryGroupingSeparator())
|
||||
.replace(dfs.getDecimalSeparator(), dfs.getMonetaryDecimalSeparator())
|
||||
+ cFmt.getPositiveSuffix(),
|
||||
args.get()[1], (int) args.get()[2] + cFmt.getPositivePrefix().length())
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> currencyValidFullParseStrings() {
|
||||
return validFullParseStrings().map(args -> Arguments.of(
|
||||
cFmt.getPositivePrefix() + String.valueOf(args.get()[0])
|
||||
.replace(dfs.getGroupingSeparator(), dfs.getMonetaryGroupingSeparator())
|
||||
.replace(dfs.getDecimalSeparator(), dfs.getMonetaryDecimalSeparator())
|
||||
+ cFmt.getPositiveSuffix(),
|
||||
args.get()[1])
|
||||
);
|
||||
}
|
||||
|
||||
// Compact Pattern Data Provider provides test input for both DecimalFormat patterns
|
||||
// and the compact patterns. As there is no method to retrieve compact patterns,
|
||||
// thus test only against US English locale, and use a hard coded K - 1000
|
||||
private static Stream<Arguments> compactValidPartialParseStrings() {
|
||||
return Stream.concat(validPartialParseStrings().map(args -> Arguments.of(args.get()[0],
|
||||
args.get()[1], args.get()[2])), validPartialParseStrings().map(args -> Arguments.of(args.get()[0] + "K",
|
||||
args.get()[1], args.get()[2]))
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> compactValidFullParseStrings() {
|
||||
return Stream.concat(validFullParseStrings().map(args -> Arguments.of(args.get()[0],
|
||||
args.get()[1])), validFullParseStrings().map(args -> Arguments.of(args.get()[0] + "K",
|
||||
(double)args.get()[1] * 1000.0))
|
||||
);
|
||||
}
|
||||
|
||||
// No compact suffixes
|
||||
private static Stream<Arguments> compactValidNoSuffixParseStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of("5", 5),
|
||||
Arguments.of("50", 50),
|
||||
Arguments.of("50.", 50),
|
||||
Arguments.of("5,000", 5000),
|
||||
Arguments.of("5,000.", 5000),
|
||||
Arguments.of("5,000.00", 5000)
|
||||
);
|
||||
}
|
||||
|
||||
// Replace the grouping and decimal separators with localized variants
|
||||
// Used during localization of data
|
||||
private static String localizeText(String text) {
|
||||
// As this is a single pass conversion, this is safe for multiple replacement,
|
||||
// even if a ',' could be a decimal separator for a locale.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == ',') {
|
||||
sb.append(dfs.getGroupingSeparator());
|
||||
} else if (c == '.') {
|
||||
sb.append(dfs.getDecimalSeparator());
|
||||
} else if (c == '0') {
|
||||
sb.append(dfs.getZeroDigit());
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue