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
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2023, 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 4018937
|
||||
* @summary Confirm that DecimalFormat.parse() parses BigDecimal and BigInteger
|
||||
* string values as expected. Specifically, ensure a ParseException is
|
||||
* not thrown as well as the parsed value being numerically correct.
|
||||
* Tests large String values with combinations of multipliers and exponents.
|
||||
* @run junit BigDecimalCompatibilityTest
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
public class BigDecimalCompatibilityTest {
|
||||
|
||||
private static DecimalFormat df = new DecimalFormat();
|
||||
// Save JVM default Locale
|
||||
private static final Locale savedLocale = Locale.getDefault();
|
||||
|
||||
// ---- Used for the test data (start) ----
|
||||
|
||||
// Both ArrayList composed of Arguments(String longString, int multiplier)
|
||||
private static final ArrayList<Arguments> bigIntegers = new ArrayList<Arguments>();
|
||||
private static final ArrayList<Arguments> bigDecimals = new ArrayList<Arguments>();
|
||||
|
||||
// Long string data to generate combinations of test values
|
||||
private static final String[] inputData = {
|
||||
"0".repeat(400),
|
||||
"1234567890".repeat(40)};
|
||||
|
||||
// Variety of exponents to test parse() against
|
||||
private static final String[] exponents = {
|
||||
"E-100", "E100", "E-900", "E900", ""
|
||||
};
|
||||
|
||||
// Variety of multipliers that DecimalFormat can apply
|
||||
private static final int[] multipliers = {
|
||||
-1, 1, -100, 100, -9999, 9999
|
||||
};
|
||||
// ---- Used for the test data (end) ----
|
||||
|
||||
// Set JVM default Locale to US and populate the test arrayLists
|
||||
@BeforeAll
|
||||
static void initAll() {
|
||||
Locale.setDefault(Locale.US);
|
||||
buildTestData();
|
||||
}
|
||||
|
||||
/*
|
||||
* Uses inputData and exponents to build long string
|
||||
* decimal and integer values and populate bigDecimals and bigIntegers
|
||||
* accordingly. Attaches a multiplier value as well to the test data.
|
||||
*/
|
||||
private static void buildTestData() {
|
||||
for (String longString1 : inputData) {
|
||||
for (String longString2 : inputData) {
|
||||
String bigInteger = longString1 + longString2;
|
||||
for (int multiplier : multipliers) {
|
||||
bigIntegers.add(Arguments.of(bigInteger, multiplier));
|
||||
bigIntegers.add(Arguments.of('-' + bigInteger, multiplier));
|
||||
}
|
||||
for (String longString3 : inputData) {
|
||||
for (String longString4 : inputData) {
|
||||
for (String exponent : exponents) {
|
||||
String bigDecimal = longString1 + longString2 + '.'
|
||||
+ longString3 + longString4 + exponent;
|
||||
for (int multiplier : multipliers) {
|
||||
bigDecimals.add(Arguments.of(bigDecimal, multiplier));
|
||||
bigDecimals.add(Arguments.of('-' + bigDecimal, multiplier));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore JVM default Locale
|
||||
@AfterAll
|
||||
static void tearDownAll() {
|
||||
Locale.setDefault(savedLocale);
|
||||
}
|
||||
|
||||
// Tests strings with length 1600+. See test() for specific details.
|
||||
@ParameterizedTest
|
||||
@MethodSource("bigDecimalProvider")
|
||||
public void bigDecimalParseTest(String longString, int multiplier) {
|
||||
test(longString, multiplier);
|
||||
}
|
||||
|
||||
// Returns 960 arrangements of bigDecimal string values and multipliers
|
||||
// In the form of (String, int).
|
||||
private static Stream<Arguments> bigDecimalProvider() {
|
||||
return bigDecimals.stream();
|
||||
}
|
||||
|
||||
// Tests strings with length 800+. See test() for specific details.
|
||||
@ParameterizedTest
|
||||
@MethodSource("bigIntegerProvider")
|
||||
public void bigIntegerParseTest(String longString, int multiplier) {
|
||||
test(longString, multiplier);
|
||||
}
|
||||
|
||||
// Returns 48 arrangements of bigInteger string values and multipliers
|
||||
// In the form of (String, int).
|
||||
private static Stream<Arguments> bigIntegerProvider() {
|
||||
return bigIntegers.stream();
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests that parsing a large BigDecimal/BigInteger string value
|
||||
* will not throw a ParseException with setParseBigDecimal as true.
|
||||
* Parses with a variety of multiplier values. Then ensures that the parsed
|
||||
* value is the expected number.
|
||||
*/
|
||||
private static void test(String longString, int multiplier) {
|
||||
// Reset DecimalFormat for a clean test
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
// wide enough to support the long string test data
|
||||
df.setMaximumFractionDigits(Integer.MAX_VALUE);
|
||||
df.setMultiplier(multiplier);
|
||||
|
||||
// Check parse and returned value. This was originally intended to ensure
|
||||
// a ParseException is not thrown
|
||||
Number parsedValue = assertDoesNotThrow(()-> df.parse(longString),
|
||||
"Should not throw an Exception");
|
||||
BigDecimal expectedValue = getExpected(longString, multiplier);
|
||||
assertEquals(expectedValue, parsedValue, "With multiplier: " + multiplier);
|
||||
}
|
||||
|
||||
// Utility to get a numerically correct value of a long string.
|
||||
// Dependent on BigDecimal implementation
|
||||
private static BigDecimal getExpected(String longString, int multiplier) {
|
||||
BigDecimal expected = new BigDecimal(longString);
|
||||
try {
|
||||
expected = expected.divide(new BigDecimal(multiplier));
|
||||
}
|
||||
catch (ArithmeticException e) {
|
||||
expected = expected.divide(new BigDecimal(multiplier), RoundingMode.HALF_EVEN);
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
}
|
||||
1054
test/jdk/java/text/Format/NumberFormat/BigDecimalFormat.java
Normal file
1054
test/jdk/java/text/Format/NumberFormat/BigDecimalFormat.java
Normal file
File diff suppressed because it is too large
Load diff
725
test/jdk/java/text/Format/NumberFormat/BigDecimalParse.java
Normal file
725
test/jdk/java/text/Format/NumberFormat/BigDecimalParse.java
Normal file
|
|
@ -0,0 +1,725 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 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 4018937 8008577 8174269 8333755
|
||||
* @summary Confirm that methods which are newly added to support BigDecimal and BigInteger work as expected.
|
||||
* @run junit/othervm BigDecimalParse
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.Format;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class BigDecimalParse {
|
||||
|
||||
// Change JVM default Locale
|
||||
@BeforeAll
|
||||
static void initAll() {
|
||||
Locale.setDefault(Locale.forLanguageTag("en-US-u-cf-account"));
|
||||
}
|
||||
|
||||
|
||||
static final String nonsep_int =
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890";
|
||||
|
||||
static final String sep_int =
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890," +
|
||||
"123,456,789,012,345,678,901,234,567,890";
|
||||
|
||||
static final String nonsep_zero =
|
||||
"000000000000000000000000000000000000000000000000000000000000" +
|
||||
"000000000000000000000000000000000000000000000000000000000000" +
|
||||
"000000000000000000000000000000000000000000000000000000000000" +
|
||||
"000000000000000000000000000000000000000000000000000000000000" +
|
||||
"000000000000000000000000000000000000000000000000000000000000" +
|
||||
"000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
static final String sep_zero =
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000," +
|
||||
"000,000,000,000,000,000,000,000,000,000";
|
||||
|
||||
static final String fra =
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789";
|
||||
|
||||
|
||||
Number parsed = null;
|
||||
ParsePosition pp;
|
||||
boolean exceptionOccurred;
|
||||
String msg;
|
||||
DecimalFormat df;
|
||||
|
||||
/**
|
||||
* Test for normal big numbers which have the fraction part
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_BigDecimal() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
// From: 1234...7890.012...789
|
||||
// To: BigDecimal 1234...7890.012...789
|
||||
check(nonsep_int + "." + fra, new BigDecimal(nonsep_int + "." + fra));
|
||||
|
||||
// From: -1,234...7,890.012...789
|
||||
// To: BigDecimal -1234...7890.012...789
|
||||
check("-" + sep_int + "." + fra,
|
||||
new BigDecimal("-" + nonsep_int + "." + fra));
|
||||
|
||||
// From: 000...0000.0...0
|
||||
// To: BigDecimal 0E-360
|
||||
check(nonsep_zero + "." + nonsep_zero,
|
||||
new BigDecimal(nonsep_zero + "." + nonsep_zero));
|
||||
|
||||
// From: 0.000...0000123...789E370
|
||||
// To: BigDecimal 0.0123...789
|
||||
check("0.0000000000" + nonsep_zero + fra + "E370",
|
||||
new BigDecimal("0.0000000000" + nonsep_zero + fra + "E370"));
|
||||
|
||||
// From: 0.1123...890E-360
|
||||
// To: BigDecimal 1.123...890E-361
|
||||
check("0.1" + nonsep_int + "E-360",
|
||||
new BigDecimal("0.1" + nonsep_int + "E-360"));
|
||||
|
||||
// From: 000...0000.0...0123...7890
|
||||
// To: BigDecimal 1.234...890E-361
|
||||
check(nonsep_zero + "." + nonsep_zero + nonsep_int,
|
||||
new BigDecimal(nonsep_zero + "." + nonsep_zero + nonsep_int));
|
||||
|
||||
// From: 0.123...890E360
|
||||
// To: BigDecimal 123...890
|
||||
check("0." + nonsep_int + "E360",
|
||||
new BigDecimal("0." + nonsep_int + "E360"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for normal big numbers which have the fraction part with multiplier
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_BigDecimal_usingMultiplier() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
// From: 250,0...0,000.000...000
|
||||
// To: 1000...0000.000...000
|
||||
df.setMultiplier(250000000);
|
||||
check("250,000,000," + sep_zero + "." + nonsep_zero,
|
||||
new BigDecimal("1" + nonsep_zero + "." + nonsep_zero));
|
||||
|
||||
// From: -250,0...0,000.000...000
|
||||
// To: -1000...0000.000...000
|
||||
check("-250,000,000," + sep_zero + "." + nonsep_zero,
|
||||
new BigDecimal("-1" + nonsep_zero + "." + nonsep_zero));
|
||||
|
||||
// From: 250,0...0,000.000...000
|
||||
// To: -1000...0000.000...000
|
||||
df.setMultiplier(-250000000);
|
||||
check("250,000,000," + sep_zero + "." + nonsep_zero,
|
||||
new BigDecimal("-1" + nonsep_zero + "." + nonsep_zero));
|
||||
|
||||
// From: -250,0...0,000.000...000
|
||||
// To: 1000...0000.000...000
|
||||
check("-250,000,000," + sep_zero + "." + nonsep_zero,
|
||||
new BigDecimal("1" + nonsep_zero + "." + nonsep_zero));
|
||||
|
||||
// Confirm that ArithmeticException is handled properly
|
||||
// From: 1000.000
|
||||
// To: 333.333
|
||||
df.setMultiplier(3);
|
||||
check("1000.000", new BigDecimal("333.333"));
|
||||
|
||||
// Confirm that ArithmeticException is handled properly
|
||||
// From: 10000.0000
|
||||
// To: 303.0303
|
||||
df.setMultiplier(33);
|
||||
check("10000.0000", new BigDecimal("303.0303"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for division by zero (BigDecimal)
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_BigDecimal_DivisionByZero() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
df.setMultiplier(0);
|
||||
|
||||
// From: 1000.000
|
||||
// To: Double.POSITIVE_INFINITY
|
||||
check("1000.000", Double.POSITIVE_INFINITY);
|
||||
|
||||
// From: -1000
|
||||
// To: Double.NEGATIVE_INFINITY
|
||||
check("-1000", Double.NEGATIVE_INFINITY);
|
||||
|
||||
// From: -0.00
|
||||
// To: Double.NaN
|
||||
check("-0.00", Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for division by zero (Double)
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_Double_DivisionByZero() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(false);
|
||||
df.setMultiplier(0);
|
||||
|
||||
// From: 1000.000
|
||||
// To: Double.POSITIVE_INFINITY
|
||||
check("1000.000", Double.POSITIVE_INFINITY);
|
||||
|
||||
// From: -1000.000
|
||||
// To: Double.NEGATIVE_INFINITY
|
||||
check("-1000.000", Double.NEGATIVE_INFINITY);
|
||||
|
||||
// From: 0.0
|
||||
// To: Double.NaN
|
||||
check("0.0", Double.NaN);
|
||||
|
||||
// From: -0.0 (Double)
|
||||
// To: Double.NaN
|
||||
check("-0.0", Double.NaN);
|
||||
|
||||
// From: Double.NaN
|
||||
// To: Double.NaN
|
||||
check("NaN", Double.NaN);
|
||||
|
||||
// From: Double.POSITIVE_INFINITY
|
||||
// To: Double.NaN
|
||||
check("\u221e", Double.POSITIVE_INFINITY);
|
||||
|
||||
// From: Double.NEGATIVE_INFINITY
|
||||
// To: Double.NaN
|
||||
check("-\u221e", Double.NEGATIVE_INFINITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for division by zero (Long)
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_Long_DivisionByZero() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(false);
|
||||
df.setMultiplier(0);
|
||||
|
||||
// From: 1000
|
||||
// To: Double.POSITIVE_INFINITY
|
||||
check("1000", Double.POSITIVE_INFINITY);
|
||||
|
||||
// From: -1000
|
||||
// To: Double.NEGATIVE_INFINITY
|
||||
check("-1000", Double.NEGATIVE_INFINITY);
|
||||
|
||||
// From: -000 (Long)
|
||||
// To: Double.NaN
|
||||
check("-000", Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for normal big numbers which don't have the fraction part
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_BigInteger() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
// From: 123...890
|
||||
// To: BigDecimal 123...890
|
||||
check(nonsep_int + nonsep_int, new BigDecimal(nonsep_int + nonsep_int));
|
||||
|
||||
// From: 123,4...7,890
|
||||
// To: BigDecimal 1234...7890
|
||||
check(sep_int + "," + sep_int, new BigDecimal(nonsep_int + nonsep_int));
|
||||
|
||||
// From: -000...000123...890
|
||||
// To: BigDecimal -123...890
|
||||
check("-" + nonsep_zero + nonsep_int, new BigDecimal("-" + nonsep_int));
|
||||
|
||||
// From: -000,0...0,000,123,4...7,890
|
||||
// To: BigDecimal -123...890
|
||||
check("-" + sep_zero + "," + sep_int, new BigDecimal("-" + nonsep_int));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for normal big numbers which don't have the fraction part with
|
||||
* multiplier
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_BigInteger_usingMultiplier() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
// From: 250,0...0,000
|
||||
// To: 1000...0000
|
||||
df.setMultiplier(250000000);
|
||||
check("250,000,000," + sep_zero, new BigDecimal("1" + nonsep_zero));
|
||||
|
||||
// From: -250,0...0,000
|
||||
// To: -1000...0000
|
||||
check("-250,000,000," + sep_zero, new BigDecimal("-1" + nonsep_zero));
|
||||
|
||||
// From: 250,0...0,000
|
||||
// To: -1000...0000
|
||||
df.setMultiplier(-250000000);
|
||||
check("250,000,000," + sep_zero, new BigDecimal("-1" + nonsep_zero));
|
||||
|
||||
// From: -250,0...0,000
|
||||
// To: 1000...0000
|
||||
check("-250,000,000," + sep_zero, new BigDecimal("1" + nonsep_zero));
|
||||
|
||||
// From: 250,0...0,000E-360
|
||||
// To: -1000...0000.000...000
|
||||
check("250,000,000," + sep_zero + "," + sep_zero + "E-360",
|
||||
new BigDecimal("-1" + nonsep_zero + "." + nonsep_zero));
|
||||
|
||||
// Confirm that a division which results in a irrational number is done
|
||||
// properly
|
||||
// From: 1000
|
||||
// To: 333
|
||||
df.setMultiplier(3);
|
||||
check("1000", new BigDecimal("333"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for special numbers
|
||||
* Double.NaN
|
||||
* Double.POSITIVE_INFINITY
|
||||
* Double.NEGATIVE_INFINITY
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_SpecialNumber() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
String[] numbers = {
|
||||
"0", "0.0", "25", "25.0", "25.5", "\u221e", "NaN",
|
||||
"-0", "-0.0", "-25", "-25.0", "-25.5", "-\u221e",
|
||||
};
|
||||
int multipliers[] = {5, -5};
|
||||
Number[][] expected = {
|
||||
{
|
||||
new BigDecimal("0"), new BigDecimal("0.0"), new BigDecimal("5"),
|
||||
new BigDecimal("5.0"), new BigDecimal("5.1"),
|
||||
Double.POSITIVE_INFINITY, Double.NaN,
|
||||
new BigDecimal("0"), new BigDecimal("0.0"),
|
||||
new BigDecimal("-5"), new BigDecimal("-5.0"),
|
||||
new BigDecimal("-5.1"),
|
||||
Double.NEGATIVE_INFINITY, Double.NaN,
|
||||
},
|
||||
{
|
||||
new BigDecimal("0"), new BigDecimal("0.0"),
|
||||
new BigDecimal("-5"), new BigDecimal("-5.0"),
|
||||
new BigDecimal("-5.1"),
|
||||
Double.NEGATIVE_INFINITY, Double.NaN,
|
||||
new BigDecimal("0"), new BigDecimal("0.0"), new BigDecimal("5"),
|
||||
new BigDecimal("5.0"), new BigDecimal("5.1"),
|
||||
Double.POSITIVE_INFINITY,
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < multipliers.length; i++) {
|
||||
df.setMultiplier(multipliers[i]);
|
||||
for (int j = 0; j < numbers.length; j++) {
|
||||
check(String.valueOf(numbers[j]), expected[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for special numbers
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_Other() {
|
||||
df = new DecimalFormat();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
String[] numbers = {
|
||||
"-9223372036854775808", // Long.MIN_VALUE
|
||||
};
|
||||
int multipliers[] = {1, -1};
|
||||
String[][] expected = {
|
||||
{"-9223372036854775808"}, // Long.MIN_VALUE
|
||||
{"9223372036854775808"}, // Long.MAX_VALUE+1 = abs(MIN_VALUE)
|
||||
};
|
||||
|
||||
for (int i = 0; i < multipliers.length; i++) {
|
||||
df.setMultiplier(multipliers[i]);
|
||||
for (int j = 0; j < numbers.length; j++) {
|
||||
check(String.valueOf(numbers[j]),
|
||||
new BigDecimal(expected[i][j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final String[] patterns = {
|
||||
" {0, number} ",
|
||||
" {0, number} ",
|
||||
" {0, number, currency} ",
|
||||
" {0, number, currency} ",
|
||||
" {0, number, percent} ",
|
||||
" {0, number, percent} ",
|
||||
" {0, number,#,##0.###E0} ",
|
||||
" {0, number,#,##0.###E0} ",
|
||||
|
||||
" {0, number} ",
|
||||
" {0, number} ",
|
||||
" {0, number, integer} ",
|
||||
" {0, number, integer} ",
|
||||
" {0, number, currency} ",
|
||||
" {0, number, currency} ",
|
||||
" {0, number, percent} ",
|
||||
" {0, number, percent} ",
|
||||
" {0, number,#,##0.###E0} ",
|
||||
" {0, number,#,##0.###E0} ",
|
||||
};
|
||||
static final String[] from = {
|
||||
" 12,345,678,901,234,567,890.98765432109876543210987654321 ",
|
||||
" -12,345,678,901,234,567,890.98765432109876543210987654321 ",
|
||||
" $12,345,678,901,234,567,890.98765432109876543210987654321 ",
|
||||
" ($12,345,678,901,234,567,890.98765432109876543210987654321) ",
|
||||
" 1,234,567,890,123,456,789,098.76543210987654321098765432100% ",
|
||||
" -1,234,567,890,123,456,789,098.76543210987654321098765432100% ",
|
||||
" 12,345,678,901,234,567,890.98765432109876543210987654321E-20 ",
|
||||
" -12,345,678,901,234,567,890.98765432109876543210987654321E-20 ",
|
||||
|
||||
" 9,876,543,210,987,654,321,098,765,432,109,876,543,210 ",
|
||||
" -9,876,543,210,987,654,321,098,765,432,109,876,543,210 ",
|
||||
" 9,876,543,210,987,654,321,098,765,432,109,876,543,210E5 ",
|
||||
" -9,876,543,210,987,654,321,098,765,432,109,876,543,210E-5 ",
|
||||
" $9,876,543,210,987,654,321,098,765,432,109,876,543,210.00 ",
|
||||
" ($9,876,543,210,987,654,321,098,765,432,109,876,543,210.00) ",
|
||||
" 987,654,321,098,765,432,109,876,543,210,987,654,321,012% ",
|
||||
" -987,654,321,098,765,432,109,876,543,210,987,654,321,012% ",
|
||||
" 98,765,432,109,876,543,210.98765432109876543210E20 ",
|
||||
" -987,654,321,098,765,432,109,876,543,210,987,654,321,000,000,000,000,000,000,000E-20 ",
|
||||
};
|
||||
|
||||
static final String[] expected1 = { // isParseIntegerOnly() == false
|
||||
"12345678901234567890.98765432109876543210987654321",
|
||||
"-12345678901234567890.98765432109876543210987654321",
|
||||
"12345678901234567890.98765432109876543210987654321",
|
||||
"-12345678901234567890.98765432109876543210987654321",
|
||||
"12345678901234567890.98765432109876543210987654321",
|
||||
"-12345678901234567890.98765432109876543210987654321",
|
||||
"0.1234567890123456789098765432109876543210987654321",
|
||||
"-0.1234567890123456789098765432109876543210987654321",
|
||||
|
||||
"9876543210987654321098765432109876543210",
|
||||
"-9876543210987654321098765432109876543210",
|
||||
"9.876543210987654321098765432109876543210E44",
|
||||
"-98765432109876543210987654321098765.43210",
|
||||
"9876543210987654321098765432109876543210.00",
|
||||
"-9876543210987654321098765432109876543210.00",
|
||||
"9876543210987654321098765432109876543210.12",
|
||||
"-9876543210987654321098765432109876543210.12",
|
||||
"9876543210987654321098765432109876543210",
|
||||
"-9876543210987654321098765432109876543210.00000000000000000000",
|
||||
};
|
||||
static final int[] parsePosition1 = {
|
||||
60, 61, 61, 63, 64, 65, 64, 65,
|
||||
57, 58, 59, 61, 61, 63, 60, 61, 54, 88,
|
||||
};
|
||||
|
||||
/**
|
||||
* Test for MessageFormat: setParseIntegerOnly(false)
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_MessageFormat_NotParseIntegerOnly() {
|
||||
for (int i=0; i < patterns.length; i++) {
|
||||
pp = new ParsePosition(0);
|
||||
Object[] parsed = null;
|
||||
|
||||
try {
|
||||
MessageFormat mf = new MessageFormat(patterns[i]);
|
||||
Format[] formats = mf.getFormats();
|
||||
for (int j=0; j < formats.length; j++) {
|
||||
((DecimalFormat)formats[j]).setParseBigDecimal(true);
|
||||
}
|
||||
|
||||
parsed = mf.parse(from[i], pp);
|
||||
|
||||
if (pp.getErrorIndex() != -1) {
|
||||
fail("Case" + (i+1) +
|
||||
": getErrorIndex() returns wrong value. expected:-1, got:"+
|
||||
pp.getErrorIndex() + " for " + from[i]);
|
||||
}
|
||||
if (pp.getIndex() != parsePosition1[i]) {
|
||||
fail("Case" + (i+1) +
|
||||
": getIndex() returns wrong value. expected:" +
|
||||
parsePosition1[i] + ", got:"+ pp.getIndex() +
|
||||
" for " + from[i]);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
fail("Unexpected exception: " + e.getMessage());
|
||||
}
|
||||
|
||||
checkType(from[i], getType(new BigDecimal(expected1[i])),
|
||||
getType((Number)parsed[0]));
|
||||
checkParse(from[i], new BigDecimal(expected1[i]),
|
||||
(Number)parsed[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static final String[] expected2 = { // isParseIntegerOnly() == true
|
||||
"12345678901234567890",
|
||||
"-12345678901234567890",
|
||||
"12345678901234567890",
|
||||
"-12345678901234567890",
|
||||
"12345678901234567890",
|
||||
"-12345678901234567890",
|
||||
"0",
|
||||
"0",
|
||||
|
||||
"9876543210987654321098765432109876543210",
|
||||
"-9876543210987654321098765432109876543210",
|
||||
"9.876543210987654321098765432109876543210E44",
|
||||
"-98765432109876543210987654321098765.43210",
|
||||
"9876543210987654321098765432109876543210",
|
||||
"-9876543210987654321098765432109876543210",
|
||||
"9876543210987654321098765432109876543210.12",
|
||||
"-9876543210987654321098765432109876543210.12",
|
||||
"9876543210987654321098765432109876543210",
|
||||
"-9876543210987654321098765432109876543210.00000000000000000000",
|
||||
};
|
||||
static final int[][] parsePosition2 = { // {errorIndex, index}
|
||||
/*
|
||||
* Should keep in mind that the expected result is different from
|
||||
* DecimalFormat.parse() for some cases. This is because parsing integer
|
||||
* only will return a successful parse for the subformat, but since the index
|
||||
* returned is not equal to the length, at the MessageFormat level, this
|
||||
* will be interpreted as a failed parse, and so the DecimalFormat index
|
||||
* should be reflected as the MessageFormat errorIndex.
|
||||
*/
|
||||
{28, 0}, // parsing stopped at '.'
|
||||
{29, 0}, // parsing stopped at '.'
|
||||
{29, 0}, // parsing stopped at '.'
|
||||
{30, 0}, // parsing stopped at '.'
|
||||
{31, 0}, // parsing stopped at '.'
|
||||
{32, 0}, // parsing stopped at '.'
|
||||
{28, 0}, // parsing stopped at '.'
|
||||
{29, 0}, // parsing stopped at '.'
|
||||
{-1, 57}, {-1, 58}, {-1, 59}, {-1, 61},
|
||||
{56, 0}, // parsing stopped at '.'
|
||||
{57, 0}, // parsing stopped at '.'
|
||||
{-1, 60}, {-1, 61},
|
||||
{28, 0}, // parsing stopped at '.'
|
||||
{-1, 88},
|
||||
};
|
||||
|
||||
/**
|
||||
* Test for MessageFormat: setParseIntegerOnly(true)
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_MessageFormat_ParseIntegerOnly() {
|
||||
for (int i=0; i < patterns.length; i++) {
|
||||
pp = new ParsePosition(0);
|
||||
Object[] parsed = null;
|
||||
|
||||
try {
|
||||
MessageFormat mf = new MessageFormat(patterns[i]);
|
||||
Format[] formats = mf.getFormats();
|
||||
for (int j=0; j < formats.length; j++) {
|
||||
((DecimalFormat)formats[j]).setParseBigDecimal(true);
|
||||
((DecimalFormat)formats[j]).setParseIntegerOnly(true);
|
||||
}
|
||||
|
||||
parsed = mf.parse(from[i], pp);
|
||||
|
||||
if (pp.getErrorIndex() != parsePosition2[i][0]) {
|
||||
fail("Case" + (i+1) +
|
||||
": getErrorIndex() returns wrong value. expected:" +
|
||||
parsePosition2[i][0] + ", got:"+ pp.getErrorIndex() +
|
||||
" for " + from[i]);
|
||||
}
|
||||
if (pp.getIndex() != parsePosition2[i][1]) {
|
||||
fail("Case" + (i+1) +
|
||||
": getIndex() returns wrong value. expected:" +
|
||||
parsePosition2[i][1] + ", got:"+ pp.getIndex() +
|
||||
" for " + from[i]);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
fail("Unexpected exception: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (parsePosition2[i][0] == -1) {
|
||||
checkType(from[i], getType(new BigDecimal(expected2[i])),
|
||||
getType((Number)parsed[0]));
|
||||
checkParse(from[i], new BigDecimal(expected2[i]),
|
||||
(Number)parsed[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final String[] from3 = {
|
||||
"12,345,678,901,234,567,890.98765432109876543210987654321",
|
||||
"-12,345,678,901,234,567,890.98765432109876543210987654321",
|
||||
"9,876,543,210,987,654,321,098,765,432,109,876,543,210",
|
||||
"-9,876,543,210,987,654,321,098,765,432,109,876,543,210",
|
||||
"1234556790000E-8",
|
||||
};
|
||||
static final String[] expected3 = {
|
||||
"12345678901234567890",
|
||||
"-12345678901234567890",
|
||||
"9876543210987654321098765432109876543210",
|
||||
"-9876543210987654321098765432109876543210",
|
||||
"12345.56790000",
|
||||
};
|
||||
static final int[][] parsePosition3 = { // {errorIndex, index}
|
||||
{-1, 26},
|
||||
{-1, 27},
|
||||
{-1, 53},
|
||||
{-1, 54},
|
||||
{-1, 16},
|
||||
};
|
||||
|
||||
/**
|
||||
* Test for DecimalFormat: setParseIntegerOnly(true)
|
||||
*/
|
||||
@Test
|
||||
public void test_Parse_in_DecimalFormat_ParseIntegerOnly() {
|
||||
DecimalFormat df = (DecimalFormat)NumberFormat.getIntegerInstance();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
for (int i=0; i < from3.length; i++) {
|
||||
pp = new ParsePosition(0);
|
||||
Number parsed = null;
|
||||
|
||||
try {
|
||||
parsed = df.parse(from3[i], pp);
|
||||
|
||||
if (pp.getErrorIndex() != parsePosition3[i][0]) {
|
||||
fail("Case" + (i+1) +
|
||||
": getErrorIndex() returns wrong value. expected:" +
|
||||
parsePosition3[i][0] + ", got:"+ pp.getErrorIndex() +
|
||||
" for " + from3[i]);
|
||||
}
|
||||
if (pp.getIndex() != parsePosition3[i][1]) {
|
||||
fail("Case" + (i+1) +
|
||||
": getIndex() returns wrong value. expected:" +
|
||||
parsePosition3[i][1] + ", got:"+ pp.getIndex() +
|
||||
" for " + from3[i]);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
fail("Unexpected exception: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (parsePosition3[i][0] == -1) {
|
||||
checkType(from3[i], getType(new BigDecimal(expected3[i])),
|
||||
getType(parsed));
|
||||
checkParse(from3[i], new BigDecimal(expected3[i]), parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void check(String from, Number to) {
|
||||
pp = new ParsePosition(0);
|
||||
try {
|
||||
parsed = df.parse(from, pp);
|
||||
}
|
||||
catch(Exception e) {
|
||||
exceptionOccurred = true;
|
||||
fail(e.getMessage());
|
||||
}
|
||||
if (!exceptionOccurred) {
|
||||
checkParse(from, to, parsed);
|
||||
checkType(from, getType(to), getType(parsed));
|
||||
checkParsePosition(from, from.length(), pp.getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParse(String orig, Number expected, Number got) {
|
||||
if (!expected.equals(got)) {
|
||||
fail("Parsing... failed." +
|
||||
"\n original: " + orig +
|
||||
"\n parsed: " + got +
|
||||
"\n expected: " + expected + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkType(String orig, String expected, String got) {
|
||||
if (!expected.equals(got)) {
|
||||
fail("Parsing... unexpected Class returned." +
|
||||
"\n original: " + orig +
|
||||
"\n got: " + got +
|
||||
"\n expected: " + expected + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParsePosition(String orig, int expected, int got) {
|
||||
if (expected != got) {
|
||||
fail("Parsing... wrong ParsePosition returned." +
|
||||
"\n original: " + orig +
|
||||
"\n got: " + got +
|
||||
"\n expected: " + expected + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private String getType(Number number) {
|
||||
return number.getClass().getName();
|
||||
}
|
||||
}
|
||||
186
test/jdk/java/text/Format/NumberFormat/Bug4208135.java
Normal file
186
test/jdk/java/text/Format/NumberFormat/Bug4208135.java
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2023, 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
|
||||
* @summary Confirm that the decimal separator is shown when explicitly requested
|
||||
* (or not shown if not requested). Tests against double, long, BigDecimal,
|
||||
* and BigInteger with a combination of different patterns.
|
||||
* @bug 4208135
|
||||
* @run junit Bug4208135
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class Bug4208135 {
|
||||
|
||||
private static DecimalFormat df;
|
||||
// Save JVM default Locale
|
||||
private static final Locale savedLocale = Locale.getDefault();
|
||||
|
||||
// Set JVM default locale to US
|
||||
@BeforeAll
|
||||
static void init() {
|
||||
Locale.setDefault(Locale.US);
|
||||
}
|
||||
|
||||
// Restore JVM default locale
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
Locale.setDefault(savedLocale);
|
||||
}
|
||||
|
||||
// Confirm that decimal separator shown when formatting a number
|
||||
@ParameterizedTest
|
||||
@MethodSource("fractionalDigitsWithSeparatorProvider")
|
||||
public void fractionalDigitsWithSeparatorTest(Number num, String expected) {
|
||||
df = getDF("0.#E0", true);
|
||||
String actualFormatted = df.format(num);
|
||||
assertEquals(expected, actualFormatted, getErrMsg("0.#E0", true));
|
||||
}
|
||||
|
||||
// Combination of numbers and a fractional exponent pattern with a separator
|
||||
private static Stream<Arguments> fractionalDigitsWithSeparatorProvider() {
|
||||
return Stream.of(
|
||||
Arguments.of(0.0, "0.E0"),
|
||||
Arguments.of(10.0, "1.E1"),
|
||||
Arguments.of(1000.0, "1.E3"),
|
||||
Arguments.of(0L, "0.E0"),
|
||||
Arguments.of(10L, "1.E1"),
|
||||
Arguments.of(1000L, "1.E3"),
|
||||
Arguments.of(new BigDecimal("0.0"), "0.E0"),
|
||||
Arguments.of(new BigDecimal("10.0"), "1.E1"),
|
||||
Arguments.of(new BigDecimal("1000.0"), "1.E3"),
|
||||
Arguments.of(new BigInteger("00"), "0.E0"),
|
||||
Arguments.of(new BigInteger("10"), "1.E1"),
|
||||
Arguments.of(new BigInteger("1000"), "1.E3")
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm that decimal separator not shown when formatting a number
|
||||
@ParameterizedTest
|
||||
@MethodSource("fractionalDigitsNoSeparatorProvider")
|
||||
public void fractionalDigitsNoSeparatorTest(Number num, String expected) {
|
||||
df = getDF("0.#E0", false);
|
||||
String actualFormatted = df.format(num);
|
||||
assertEquals(expected, actualFormatted, getErrMsg("0.#E0", false));
|
||||
}
|
||||
|
||||
// Combination of numbers and a fractional exponent pattern with no separator
|
||||
private static Stream<Arguments> fractionalDigitsNoSeparatorProvider() {
|
||||
return Stream.of(
|
||||
Arguments.of(0.0, "0E0"),
|
||||
Arguments.of(10.0, "1E1"),
|
||||
Arguments.of(1000.0, "1E3"),
|
||||
Arguments.of(0L, "0E0"),
|
||||
Arguments.of(10L, "1E1"),
|
||||
Arguments.of(1000L, "1E3"),
|
||||
Arguments.of(new BigDecimal("0.0"), "0E0"),
|
||||
Arguments.of(new BigDecimal("10.0"), "1E1"),
|
||||
Arguments.of(new BigDecimal("1000.0"), "1E3"),
|
||||
Arguments.of(new BigInteger("00"), "0E0"),
|
||||
Arguments.of(new BigInteger("10"), "1E1"),
|
||||
Arguments.of(new BigInteger("1000"), "1E3")
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm that decimal separator shown when formatting a number
|
||||
@ParameterizedTest
|
||||
@MethodSource("noFractionalDigitsWithSeparatorProvider")
|
||||
public void noFractionalDigitsWithSeparatorTest(Number num, String expected) {
|
||||
df = getDF("0.###", true);
|
||||
String actualFormatted = df.format(num);
|
||||
assertEquals(expected, actualFormatted, getErrMsg("0.###", true));
|
||||
}
|
||||
|
||||
// Combination of numbers and a non-fractional exponent pattern with a separator
|
||||
private static Stream<Arguments> noFractionalDigitsWithSeparatorProvider() {
|
||||
return Stream.of(
|
||||
Arguments.of(0.0, "0."),
|
||||
Arguments.of(10.0, "10."),
|
||||
Arguments.of(1000.0, "1000."),
|
||||
Arguments.of(0L, "0."),
|
||||
Arguments.of(10L, "10."),
|
||||
Arguments.of(1000L, "1000."),
|
||||
Arguments.of(new BigDecimal("0.0"), "0."),
|
||||
Arguments.of(new BigDecimal("10.0"), "10."),
|
||||
Arguments.of(new BigDecimal("1000.0"), "1000."),
|
||||
Arguments.of(new BigInteger("00"), "0."),
|
||||
Arguments.of(new BigInteger("10"), "10."),
|
||||
Arguments.of(new BigInteger("1000"), "1000.")
|
||||
);
|
||||
}
|
||||
|
||||
// Confirm that decimal separator not shown when formatting a number
|
||||
@ParameterizedTest
|
||||
@MethodSource("noFractionalDigitsNoSeparatorProvider")
|
||||
public void noFractionalDigitsNoSeparatorTest(Number num, String expected) {
|
||||
df = getDF("0.###", false);
|
||||
String actualFormatted = df.format(num);
|
||||
assertEquals(expected, actualFormatted, getErrMsg("0.###", false));
|
||||
}
|
||||
|
||||
// Combination of numbers and a non-fractional exponent pattern with no separator
|
||||
private static Stream<Arguments> noFractionalDigitsNoSeparatorProvider() {
|
||||
return Stream.of(
|
||||
Arguments.of(0.0, "0"),
|
||||
Arguments.of(10.0, "10"),
|
||||
Arguments.of(1000.0, "1000"),
|
||||
Arguments.of(0L, "0"),
|
||||
Arguments.of(10L, "10"),
|
||||
Arguments.of(1000L, "1000"),
|
||||
Arguments.of(new BigDecimal("0.0"), "0"),
|
||||
Arguments.of(new BigDecimal("10.0"), "10"),
|
||||
Arguments.of(new BigDecimal("1000.0"), "1000"),
|
||||
Arguments.of(new BigInteger("00"), "0"),
|
||||
Arguments.of(new BigInteger("10"), "10"),
|
||||
Arguments.of(new BigInteger("1000"), "1000")
|
||||
);
|
||||
}
|
||||
|
||||
// Creates clean DF and sets the pattern and separatorShown value
|
||||
private static DecimalFormat getDF(String pattern, boolean separatorShown) {
|
||||
df = new DecimalFormat();
|
||||
df.applyPattern(pattern);
|
||||
df.setDecimalSeparatorAlwaysShown(separatorShown);
|
||||
return df;
|
||||
}
|
||||
|
||||
// Utility to get a helpful error message when values are not as expected
|
||||
private static String getErrMsg(String pattern, boolean separatorShown) {
|
||||
return String.format("Fails with pattern= %s, with separatorShown = %s",
|
||||
pattern, separatorShown);
|
||||
}
|
||||
}
|
||||
464
test/jdk/java/text/Format/NumberFormat/Bug4833877.java
Normal file
464
test/jdk/java/text/Format/NumberFormat/Bug4833877.java
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2016, 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
|
||||
* @summary Confirm that the negative multiplier works as expected.
|
||||
* @bug 4833877
|
||||
*/
|
||||
|
||||
import java.math.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
public class Bug4833877 {
|
||||
|
||||
static DecimalFormat df;
|
||||
|
||||
static boolean err = false;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
Locale defaultLoc = Locale.getDefault();
|
||||
Locale.setDefault(Locale.US);
|
||||
|
||||
/* ================================================================ */
|
||||
|
||||
df = new DecimalFormat();
|
||||
df.setMaximumFractionDigits(50);
|
||||
df.setMultiplier(4);
|
||||
|
||||
/*
|
||||
* Test for double/Double
|
||||
*/
|
||||
checkFormat(252.5252525252525, "1,010.10101010101");
|
||||
checkParse("-1,010.10101010101", -252.5252525252525);
|
||||
|
||||
checkFormat(-2222.2222, "-8,888.8888");
|
||||
checkParse("8888.8888", 2222.2222);
|
||||
|
||||
/*
|
||||
* Test for long/Long
|
||||
*/
|
||||
checkFormat(1000L, "4,000");
|
||||
checkParse("-4,000", -1000L);
|
||||
|
||||
checkFormat(-250L, "-1,000");
|
||||
checkParse("1000", 250L);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
/*
|
||||
* Test for BigDecimal
|
||||
*/
|
||||
checkFormat(new BigDecimal("22222.222222222222222222222"),
|
||||
"88,888.888888888888888888888");
|
||||
checkParse("-88,888.888888888888888888888",
|
||||
new BigDecimal("-22222.222222222222222222222"));
|
||||
|
||||
checkFormat(new BigDecimal("-1111111111111111111.111111111111111111"),
|
||||
"-4,444,444,444,444,444,444.444444444444444444");
|
||||
checkParse("4444444444444444444.444444444444444444",
|
||||
new BigDecimal("1111111111111111111.111111111111111111"));
|
||||
|
||||
/*
|
||||
* Test for BigInteger
|
||||
*/
|
||||
checkFormat(new BigInteger("22222222222222222222222222"),
|
||||
"88,888,888,888,888,888,888,888,888");
|
||||
checkParse("-88,888,888,888,888,888,888,888,888",
|
||||
new BigDecimal("-22222222222222222222222222"));
|
||||
|
||||
checkFormat(new BigInteger("-1111111111111111111111111"),
|
||||
"-4,444,444,444,444,444,444,444,444");
|
||||
checkParse("4444444444444444444444444",
|
||||
new BigDecimal("1111111111111111111111111"));
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(false);
|
||||
df.setMultiplier(-4);
|
||||
|
||||
/*
|
||||
* Test for double/Double
|
||||
*/
|
||||
checkFormat(252.5252525252525, "-1,010.10101010101");
|
||||
checkParse("-1,010.10101010101", 252.5252525252525);
|
||||
|
||||
checkFormat(-2222.2222, "8,888.8888");
|
||||
checkParse("8888.8888", -2222.2222);
|
||||
|
||||
/*
|
||||
* Test for long/Long
|
||||
*/
|
||||
checkFormat(1000L, "-4,000");
|
||||
checkParse("-4,000", 1000L);
|
||||
|
||||
checkFormat(-250L, "1,000");
|
||||
checkParse("1000", -250L);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
/*
|
||||
* Test for BigDecimal
|
||||
*/
|
||||
checkFormat(new BigDecimal("22222.222222222222222222222"),
|
||||
"-88,888.888888888888888888888");
|
||||
checkParse("-88,888.888888888888888888888",
|
||||
new BigDecimal("22222.222222222222222222222"));
|
||||
|
||||
checkFormat(new BigDecimal("-1111111111111111111.111111111111111111"),
|
||||
"4,444,444,444,444,444,444.444444444444444444");
|
||||
checkParse("4444444444444444444.444444444444444444",
|
||||
new BigDecimal("-1111111111111111111.111111111111111111"));
|
||||
|
||||
/*
|
||||
* Test for BigInteger
|
||||
*/
|
||||
checkFormat(new BigInteger("22222222222222222222222222"),
|
||||
"-88,888,888,888,888,888,888,888,888");
|
||||
checkParse("-88,888,888,888,888,888,888,888,888",
|
||||
new BigDecimal("22222222222222222222222222"));
|
||||
|
||||
checkFormat(new BigInteger("-1111111111111111111111111"),
|
||||
"4,444,444,444,444,444,444,444,444");
|
||||
checkParse("4444444444444444444444444",
|
||||
new BigDecimal("-1111111111111111111111111"));
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(false);
|
||||
df.setMultiplier(-3);
|
||||
|
||||
/*
|
||||
* Test for double/Double
|
||||
*/
|
||||
checkFormat(3333.3333333, "-9,999.9999999");
|
||||
checkParse("-10,000.00000000000", 3333.3333333333335);// rounding error
|
||||
|
||||
df.setParseIntegerOnly(true);
|
||||
checkFormat(-3333.3333333, "9,999.9999999");
|
||||
checkParse("10,000.00000000000", -3333L);
|
||||
df.setParseIntegerOnly(false);
|
||||
checkFormat(-3333.3333333, "9,999.9999999");
|
||||
checkParse("10,000.00000000000", -3333.3333333333335);// rounding error
|
||||
|
||||
/*
|
||||
* Test for long/Long
|
||||
*/
|
||||
checkFormat(3333L, "-9,999");
|
||||
df.setParseIntegerOnly(true);
|
||||
checkParse("-10,000", 3333L);
|
||||
df.setParseIntegerOnly(false);
|
||||
checkParse("-10000", 3333.3333333333335);// rounding error
|
||||
|
||||
checkFormat(-3333L, "9,999");
|
||||
df.setParseIntegerOnly(true);
|
||||
checkParse("10,000", -3333L);
|
||||
df.setParseIntegerOnly(false);
|
||||
checkParse("10000", -3333.3333333333335);// rounding error
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
/*
|
||||
* Test for BigDecimal
|
||||
*/
|
||||
checkFormat(new BigDecimal("33333.333333333333333333333"),
|
||||
"-99,999.999999999999999999999");
|
||||
checkParse("-100,000.000000000000000000000",
|
||||
new BigDecimal("33333.333333333333333333333"));
|
||||
|
||||
checkFormat(new BigDecimal("-33333.333333333333333333333"),
|
||||
"99,999.999999999999999999999");
|
||||
checkParse("100,000.000000000000000000000",
|
||||
new BigDecimal("-33333.333333333333333333333"));
|
||||
|
||||
/*
|
||||
* Test for BigInteger
|
||||
*/
|
||||
checkFormat(new BigInteger("33333333333333333333333333"),
|
||||
"-99,999,999,999,999,999,999,999,999");
|
||||
checkParse("-100,000,000,000,000,000,000,000,000",
|
||||
new BigDecimal("33333333333333333333333333"));
|
||||
|
||||
checkFormat(new BigInteger("-33333333333333333333333333"),
|
||||
"99,999,999,999,999,999,999,999,999");
|
||||
df.setParseIntegerOnly(true);
|
||||
checkParse("100,000,000,000,000,000,000,000,000.000",
|
||||
new BigDecimal("-33333333333333333333333333"));
|
||||
df.setParseIntegerOnly(false);
|
||||
checkParse("100,000,000,000,000,000,000,000,000.000",
|
||||
new BigDecimal("-33333333333333333333333333.333"));
|
||||
|
||||
/* ================================================================ */
|
||||
|
||||
df = new DecimalFormat("0.#E0;-0.#E0");
|
||||
df.setMaximumFractionDigits(50);
|
||||
df.setMultiplier(4);
|
||||
|
||||
/*
|
||||
* Test for double/Double
|
||||
*/
|
||||
checkFormat(252.5252525252525, "1.01010101010101E3");
|
||||
checkParse("-1.01010101010101E3", -2.525252525252525E2);
|
||||
|
||||
checkFormat(-2222.2222, "-8.8888888E3");
|
||||
checkParse("8888.8888", 2.2222222E3);
|
||||
|
||||
/*
|
||||
* Test for long/Long
|
||||
*/
|
||||
checkFormat(1000L, "4E3");
|
||||
checkParse("-4E3", -1000L);
|
||||
|
||||
checkFormat(-250L, "-1E3");
|
||||
checkParse("1000", 250L);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
/*
|
||||
* Test for BigDecimal
|
||||
*/
|
||||
|
||||
checkFormat(new BigDecimal("22222.222222222222222222222"),
|
||||
"8.8888888888888888888888888E4");
|
||||
checkParse("-8.8888888888888888888888888E4",
|
||||
new BigDecimal("-2.2222222222222222222222222E4"));
|
||||
|
||||
checkFormat(new BigDecimal("-1111111111111111111.111111111111111111"),
|
||||
"-4.444444444444444444444444444444444444E18");
|
||||
checkParse("4444444444444444444.444444444444444444",
|
||||
new BigDecimal("1111111111111111111.111111111111111111"));
|
||||
|
||||
/*
|
||||
* Test for BigInteger
|
||||
*/
|
||||
checkFormat(new BigInteger("22222222222222222222222222"),
|
||||
"8.8888888888888888888888888E25");
|
||||
checkParse("-8.8888888888888888888888888E25",
|
||||
new BigDecimal("-22222222222222222222222222"));
|
||||
|
||||
checkFormat(new BigInteger("-1111111111111111111111111"),
|
||||
"-4.444444444444444444444444E24");
|
||||
checkParse("4444444444444444444444444",
|
||||
new BigDecimal("1111111111111111111111111"));
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(false);
|
||||
df.setMultiplier(-4);
|
||||
|
||||
/*
|
||||
* Test for double/Double
|
||||
*/
|
||||
checkFormat(252.5252525252525, "-1.01010101010101E3");
|
||||
checkParse("-1.01010101010101E3", 2.525252525252525E2);
|
||||
|
||||
checkFormat(-2222.2222, "8.8888888E3");
|
||||
checkParse("8888.8888", -2.2222222E3);
|
||||
|
||||
/*
|
||||
* Test for long/Long
|
||||
*/
|
||||
checkFormat(1000L, "-4E3");
|
||||
checkParse("-4E3", 1000L);
|
||||
|
||||
checkFormat(-250L, "1E3");
|
||||
checkParse("1000", -250L);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
/*
|
||||
* Test for BigDecimal
|
||||
*/
|
||||
|
||||
checkFormat(new BigDecimal("22222.222222222222222222222"),
|
||||
"-8.8888888888888888888888888E4");
|
||||
checkParse("-8.8888888888888888888888888E4",
|
||||
new BigDecimal("2.2222222222222222222222222E4"));
|
||||
|
||||
checkFormat(new BigDecimal("-1111111111111111111.111111111111111111"),
|
||||
"4.444444444444444444444444444444444444E18");
|
||||
checkParse("4444444444444444444.444444444444444444",
|
||||
new BigDecimal("-1111111111111111111.111111111111111111"));
|
||||
|
||||
/*
|
||||
* Test for BigInteger
|
||||
*/
|
||||
checkFormat(new BigInteger("22222222222222222222222222"),
|
||||
"-8.8888888888888888888888888E25");
|
||||
checkParse("-8.8888888888888888888888888E25",
|
||||
new BigDecimal("22222222222222222222222222"));
|
||||
|
||||
checkFormat(new BigInteger("-1111111111111111111111111"),
|
||||
"4.444444444444444444444444E24");
|
||||
checkParse("4444444444444444444444444",
|
||||
new BigDecimal("-1111111111111111111111111"));
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(false);
|
||||
df.setMultiplier(-3);
|
||||
|
||||
/*
|
||||
* Test for double/Double
|
||||
*/
|
||||
checkFormat(3333.3333333, "-9.9999999999E3");
|
||||
checkParse("-1.00000000000000E3", 3.33333333333333333E2);
|
||||
|
||||
df.setParseIntegerOnly(true);
|
||||
checkFormat(-3333.3333333, "9.9999999999E3");
|
||||
checkParse("10.00000000000000E3",-3L);
|
||||
df.setParseIntegerOnly(false);
|
||||
checkFormat(-3333.3333333, "9.9999999999E3");
|
||||
checkParse("10.00000000000000E3", -3.33333333333333333E3);
|
||||
|
||||
/*
|
||||
* Test for long/Long
|
||||
*/
|
||||
checkFormat(3333L, "-9.999E3");
|
||||
df.setParseIntegerOnly(true);
|
||||
checkParse("-1.0E4", 0L);
|
||||
df.setParseIntegerOnly(false);
|
||||
checkParse("-1.0E4", 3333.3333333333335);
|
||||
|
||||
checkFormat(-3333L, "9.999E3");
|
||||
df.setParseIntegerOnly(true);
|
||||
checkParse("10.0E4", -3L);
|
||||
df.setParseIntegerOnly(false);
|
||||
checkParse("10.0E4", -33333.3333333333336);
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
/*
|
||||
* Test for BigDecimal
|
||||
*/
|
||||
|
||||
checkFormat(new BigDecimal("333.333333333333333333333333"),
|
||||
"-9.99999999999999999999999999E2");
|
||||
checkParse("-1.0000000000000000000000000E3",
|
||||
new BigDecimal("3.333333333333333333333333E2"));
|
||||
|
||||
df.setParseIntegerOnly(true);
|
||||
checkFormat(new BigDecimal("-333.333333333333333333333333"),
|
||||
"9.99999999999999999999999999E2");
|
||||
checkParse("10.0000000000000000000000000E3",
|
||||
new BigDecimal("-3"));
|
||||
df.setParseIntegerOnly(false);
|
||||
checkFormat(new BigDecimal("-333.333333333333333333333333"),
|
||||
"9.99999999999999999999999999E2");
|
||||
checkParse("1.0000000000000000000000000E3",
|
||||
new BigDecimal("-3.333333333333333333333333E2"));
|
||||
|
||||
/*
|
||||
* Test for BigInteger
|
||||
*/
|
||||
checkFormat(new BigInteger("33333333333333333333333333"),
|
||||
"-9.9999999999999999999999999E25");
|
||||
checkParse("-100000000000000000000000000",
|
||||
new BigDecimal("33333333333333333333333333"));
|
||||
|
||||
checkFormat(new BigInteger("-33333333333333333333333333"),
|
||||
"9.9999999999999999999999999E25");
|
||||
df.setParseIntegerOnly(true);
|
||||
checkParse("100000000000000000000000000000",
|
||||
new BigDecimal("-33333333333333333333333333333"));
|
||||
df.setParseIntegerOnly(false);
|
||||
checkParse("100000000000000000000000000.000",
|
||||
new BigDecimal("-33333333333333333333333333.333"));
|
||||
|
||||
/* ================================================================ */
|
||||
|
||||
Locale.setDefault(defaultLoc);
|
||||
|
||||
if (err) {
|
||||
throw new RuntimeException("Wrong format/parse with DecimalFormat");
|
||||
}
|
||||
}
|
||||
|
||||
static void checkFormat(Number num, String expected) {
|
||||
String got = df.format(num);
|
||||
if (!got.equals(expected)) {
|
||||
err = true;
|
||||
System.err.println(" DecimalFormat format(" +
|
||||
num.getClass().getName() +
|
||||
") error:" +
|
||||
"\n\tnumber: " + num +
|
||||
"\n\tpattern: " + df.toPattern() +
|
||||
"\n\tmultiplier: " + df.getMultiplier() +
|
||||
"\n\tgot: " + got +
|
||||
"\n\texpected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkParse(String text, Double expected) {
|
||||
Double got = (Double)df.parse(text, new ParsePosition(0));
|
||||
if (!got.equals(expected)) {
|
||||
err = true;
|
||||
System.err.println(" DecimalFormat parse(double) error:" +
|
||||
"\n\ttext: " + text +
|
||||
"\n\tpattern: " + df.toPattern() +
|
||||
"\n\tmultiplier: " + df.getMultiplier() +
|
||||
"\n\tgot: " + got +
|
||||
"\n\texpected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkParse(String text, Long expected) {
|
||||
Long got = (Long)df.parse(text, new ParsePosition(0));
|
||||
if (!got.equals(expected)) {
|
||||
err = true;
|
||||
System.err.println(" DecimalFormat parse(long) error:" +
|
||||
"\n\ttext: " + text +
|
||||
"\n\tpattern: " + df.toPattern() +
|
||||
"\n\tmultiplier: " + df.getMultiplier() +
|
||||
"\n\tgot: " + got +
|
||||
"\n\texpected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkParse(String text, BigDecimal expected) {
|
||||
BigDecimal got = (BigDecimal)df.parse(text, new ParsePosition(0));
|
||||
if (!got.equals(expected)) {
|
||||
err = true;
|
||||
System.err.println(" DecimalFormat parse(BigDecimal) error:" +
|
||||
"\n\ttext: " + text +
|
||||
"\n\tpattern: " + df.toPattern() +
|
||||
"\n\tmultiplier: " + df.getMultiplier() +
|
||||
"\n\tgot: " + got +
|
||||
"\n\texpected: " + expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
282
test/jdk/java/text/Format/NumberFormat/Bug4838107.java
Normal file
282
test/jdk/java/text/Format/NumberFormat/Bug4838107.java
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 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 4838107 8008577 8174269
|
||||
* @summary Confirm that DecimalFormat can format a number with a negative
|
||||
* exponent number correctly. Tests also involve using a DecimalFormat
|
||||
* with a custom pattern or a custom minus sign.
|
||||
* @run junit Bug4838107
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/*
|
||||
* This bug is about exponential formatting. But I added test cases for:
|
||||
* - Double and BigDecimal numbers which don't have exponent parts.
|
||||
* - Long and BigInteger numbers which don't support exponential
|
||||
* notation.
|
||||
* because there are few test cases for suffix and prefix.
|
||||
* And also, I added test cases to guarantee further formatting and
|
||||
* parsing using the same DecimalFormat instance will not change the
|
||||
* Number's value anymore.
|
||||
*/
|
||||
public class Bug4838107 {
|
||||
|
||||
// Save JVM default Locale
|
||||
private static final Locale savedLocale = Locale.getDefault();
|
||||
|
||||
// Set JVM default Locale to US
|
||||
@BeforeAll
|
||||
static void init() {
|
||||
Locale.setDefault(Locale.US);
|
||||
}
|
||||
|
||||
// Restore the original JVM default locale
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
Locale.setDefault(savedLocale);
|
||||
}
|
||||
|
||||
// Check that negative exponent number recognized for doubles
|
||||
@ParameterizedTest
|
||||
@MethodSource("doubles")
|
||||
public void doubleTest(Number num, String str, DecimalFormat df) {
|
||||
test(num, str, df);
|
||||
}
|
||||
|
||||
// Provides a double to be formatted, which is compared to the expected String.
|
||||
// Additionally, provides a DecimalFormat to do the formatting (can have a custom
|
||||
// pattern and minus sign). Given in the form (double, String, DecimalFormat).
|
||||
private static Stream<Arguments> doubles() {
|
||||
DecimalFormat defaultDf = new DecimalFormat();
|
||||
DecimalFormat customDf1 = getDecimalFormat("<P>#.###E00<S>", 'm');
|
||||
DecimalFormat customDf2 = getDecimalFormat("<P>#.###E00<S>;#.###E00", 'm');
|
||||
DecimalFormat customDf3 = getDecimalFormat("#.###E00;<P>#.###E00<S>", 'm');
|
||||
DecimalFormat customDf4 = getDecimalFormat("<P>#.###E00<S>;<p>-#.###E00<s>", 'm');
|
||||
return Stream.of(
|
||||
// Test with default pattern
|
||||
Arguments.of(1234D, "1,234", defaultDf),
|
||||
Arguments.of(0.1234, "0.123", defaultDf), // rounded
|
||||
Arguments.of(-1234D, "-1,234", defaultDf),
|
||||
Arguments.of(-0.1234, "-0.123", defaultDf), // rounded
|
||||
Arguments.of(Double.POSITIVE_INFINITY, "\u221e", defaultDf),
|
||||
Arguments.of(Double.NEGATIVE_INFINITY, "-\u221e", defaultDf),
|
||||
Arguments.of(Double.NaN, "NaN", defaultDf), // without prefix and suffix
|
||||
Arguments.of(0.0, "0", defaultDf),
|
||||
Arguments.of(-0.0, "-0", defaultDf), // with the minus sign
|
||||
// Test with a pattern and the minus sign
|
||||
Arguments.of(1234D, "<P>1.234E03<S>", customDf1),
|
||||
Arguments.of(0.1234, "<P>1.234Em01<S>", customDf1),
|
||||
Arguments.of(-1234D, "m<P>1.234E03<S>", customDf1),
|
||||
Arguments.of(-0.1234, "m<P>1.234Em01<S>", customDf1),
|
||||
Arguments.of(1234D, "<P>1.234E03<S>", customDf2),
|
||||
Arguments.of(0.1234, "<P>1.234Em01<S>", customDf2),
|
||||
Arguments.of(-1234D, "1.234E03", customDf2),
|
||||
Arguments.of(-0.1234, "1.234Em01", customDf2),
|
||||
Arguments.of(1234D, "1.234E03", customDf3),
|
||||
Arguments.of(0.1234, "1.234Em01", customDf3),
|
||||
Arguments.of(-1234D, "<P>1.234E03<S>", customDf3),
|
||||
Arguments.of(-0.1234, "<P>1.234Em01<S>", customDf3),
|
||||
Arguments.of(1234D, "<P>1.234E03<S>", customDf4),
|
||||
Arguments.of(0.1234, "<P>1.234Em01<S>", customDf4),
|
||||
Arguments.of(-1234D, "<p>m1.234E03<s>", customDf4),
|
||||
Arguments.of(-0.1234, "<p>m1.234Em01<s>", customDf4),
|
||||
Arguments.of(Double.POSITIVE_INFINITY, "<P>\u221e<S>", customDf4),
|
||||
Arguments.of(Double.NEGATIVE_INFINITY, "<p>m\u221e<s>", customDf4),
|
||||
Arguments.of(Double.NaN, "NaN", customDf4), // without prefix and suffix
|
||||
Arguments.of(0.0, "<P>0E00<S>", customDf4),
|
||||
Arguments.of(-0.0, "<p>m0E00<s>", customDf4) // with the minus sign
|
||||
);
|
||||
}
|
||||
|
||||
// Check that negative exponent number recognized for longs
|
||||
@ParameterizedTest
|
||||
@MethodSource("longs")
|
||||
public void longTest(Number num, String str, DecimalFormat df) {
|
||||
test(num, str, df);
|
||||
}
|
||||
|
||||
// Same as doubles() data provider, but with long values
|
||||
// Given in the form (long, String, DecimalFormat).
|
||||
private static Stream<Arguments> longs() {
|
||||
DecimalFormat defaultDf = new DecimalFormat();
|
||||
DecimalFormat customDf = getDecimalFormat(
|
||||
"<P>#,###<S>;<p>-#,###<s>", 'm');
|
||||
return Stream.of(
|
||||
// Test with default pattern
|
||||
Arguments.of(123456789L, "123,456,789", defaultDf),
|
||||
Arguments.of(-123456789L, "-123,456,789", defaultDf),
|
||||
Arguments.of(0L, "0", defaultDf),
|
||||
Arguments.of(-0L, "0", defaultDf),
|
||||
// Test with a pattern and the minus sign
|
||||
Arguments.of(123456789L, "<P>123,456,789<S>", customDf),
|
||||
Arguments.of(-123456789L, "<p>m123,456,789<s>", customDf),
|
||||
Arguments.of(0L, "<P>0<S>", customDf),
|
||||
Arguments.of(-0L, "<P>0<S>", customDf)
|
||||
);
|
||||
}
|
||||
|
||||
// Check that negative exponent number recognized for bigDecimals
|
||||
@ParameterizedTest
|
||||
@MethodSource("bigDecimals")
|
||||
public void bigDecimalTest(Number num, String str, DecimalFormat df) {
|
||||
test(num, str, df);
|
||||
}
|
||||
|
||||
// Same as doubles() data provider, but with BigDecimal values
|
||||
// Given in the form (BigDecimal, String, DecimalFormat).
|
||||
private static Stream<Arguments> bigDecimals() {
|
||||
DecimalFormat defaultDf = new DecimalFormat();
|
||||
DecimalFormat customDf = getDecimalFormat(
|
||||
"<P>#.####################E00<S>;<p>-#.####################E00<s>", 'm');
|
||||
return Stream.of(
|
||||
// Test with default pattern
|
||||
Arguments.of(new BigDecimal("123456789012345678901234567890"),
|
||||
"123,456,789,012,345,678,901,234,567,890", defaultDf),
|
||||
Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"),
|
||||
"0", defaultDf),
|
||||
Arguments.of(new BigDecimal("-123456789012345678901234567890"),
|
||||
"-123,456,789,012,345,678,901,234,567,890", defaultDf),
|
||||
Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"),
|
||||
"-0", defaultDf),
|
||||
Arguments.of(new BigDecimal("0"), "0", defaultDf),
|
||||
Arguments.of(new BigDecimal("-0"), "0", defaultDf),
|
||||
// Test with a pattern and the minus sign
|
||||
Arguments.of(new BigDecimal("123456789012345678901234567890"),
|
||||
"<P>1.23456789012345678901E29<S>", customDf),
|
||||
Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"),
|
||||
"<P>1.23456789012345678901Em10<S>", customDf),
|
||||
Arguments.of(new BigDecimal("-123456789012345678901234567890"),
|
||||
"<p>m1.23456789012345678901E29<s>", customDf),
|
||||
Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"),
|
||||
"<p>m1.23456789012345678901Em10<s>", customDf),
|
||||
Arguments.of(new BigDecimal("0"), "<P>0E00<S>", customDf),
|
||||
Arguments.of(new BigDecimal("-0"), "<P>0E00<S>", customDf)
|
||||
);
|
||||
}
|
||||
|
||||
// Check that negative exponent number recognized for bigIntegers
|
||||
@ParameterizedTest
|
||||
@MethodSource("bigIntegers")
|
||||
public void bigIntegerTest(Number num, String str, DecimalFormat df) {
|
||||
test(num, str, df);
|
||||
}
|
||||
|
||||
// Same as doubles() data provider, but with BigInteger values
|
||||
// Given in the form (BigInteger, String, DecimalFormat).
|
||||
private static Stream<Arguments> bigIntegers() {
|
||||
DecimalFormat defaultDf = new DecimalFormat();
|
||||
DecimalFormat customDf = getDecimalFormat(
|
||||
"<P>#,###<S>;<p>-#,###<s>", 'm');
|
||||
return Stream.of(
|
||||
// Test with default pattern
|
||||
Arguments.of(new BigInteger("123456789012345678901234567890"),
|
||||
"123,456,789,012,345,678,901,234,567,890", defaultDf),
|
||||
Arguments.of(new BigInteger("-123456789012345678901234567890"),
|
||||
"-123,456,789,012,345,678,901,234,567,890", defaultDf),
|
||||
Arguments.of(new BigInteger("0"), "0", defaultDf),
|
||||
Arguments.of(new BigInteger("-0"), "0", defaultDf),
|
||||
// Test with a pattern and the minus sign
|
||||
Arguments.of(new BigInteger("123456789012345678901234567890"),
|
||||
"<P>123,456,789,012,345,678,901,234,567,890<S>", customDf),
|
||||
Arguments.of(new BigInteger("-123456789012345678901234567890"),
|
||||
"<p>m123,456,789,012,345,678,901,234,567,890<s>", customDf),
|
||||
Arguments.of(new BigInteger("0"), "<P>0<S>", customDf),
|
||||
Arguments.of(new BigInteger("-0"), "<P>0<S>", customDf)
|
||||
);
|
||||
}
|
||||
|
||||
// Check that the formatted value is correct and also check that
|
||||
// it can be round-tripped via parse() and format()
|
||||
private static void test(Number num, String str, DecimalFormat df) {
|
||||
String formatted = df.format(num);
|
||||
assertEquals(str, formatted, String.format("DecimalFormat format(%s) " +
|
||||
"Error: number: %s, minus sign: %s", num.getClass().getName(), num, df.getDecimalFormatSymbols().getMinusSign()));
|
||||
|
||||
if (num instanceof BigDecimal || num instanceof BigInteger) {
|
||||
df.setParseBigDecimal(true);
|
||||
}
|
||||
testRoundTrip(formatted, str, num, df);
|
||||
}
|
||||
|
||||
// Test that a parsed value can be round-tripped via format() and parse()
|
||||
private static void testRoundTrip(String formatted, String str,
|
||||
Number num, DecimalFormat df) {
|
||||
Number parsed1 = null, parsed2 = null;
|
||||
try {
|
||||
parsed1 = df.parse(formatted);
|
||||
formatted = df.format(parsed1);
|
||||
parsed2 = df.parse(formatted);
|
||||
assertEquals(parsed2, parsed1, """
|
||||
DecimalFormat round trip parse(%s) error:
|
||||
original number: %s
|
||||
parsed number: %s
|
||||
(%s)
|
||||
formatted number: %s
|
||||
re-parsed number: %s
|
||||
(%s)
|
||||
minus sign: %s
|
||||
""".formatted(num.getClass().getName(), str, parsed1, parsed1.getClass().getName(),
|
||||
formatted, parsed2, parsed2.getClass().getName(), df.getDecimalFormatSymbols().getMinusSign()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail("""
|
||||
DecimalFormat parse(%s) threw an Exception: %s
|
||||
original number: %s
|
||||
parsed number: %s
|
||||
(%s)
|
||||
formatted number: %s
|
||||
re-parsed number: %s
|
||||
(%s)
|
||||
minus sign: %s
|
||||
""".formatted(num.getClass().getName(), e.getMessage(), str, parsed1, parsed1.getClass().getName(),
|
||||
formatted, parsed2, parsed2.getClass().getName(), df.getDecimalFormatSymbols().getMinusSign()));
|
||||
}
|
||||
}
|
||||
|
||||
// Set up custom DecimalFormat with DecimalFormatSymbols
|
||||
private static DecimalFormat getDecimalFormat(String pattern, char minusSign) {
|
||||
DecimalFormat df = new DecimalFormat();
|
||||
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
|
||||
df.applyPattern(pattern);
|
||||
dfs.setMinusSign(minusSign);
|
||||
df.setDecimalFormatSymbols(dfs);
|
||||
return df;
|
||||
}
|
||||
}
|
||||
104
test/jdk/java/text/Format/NumberFormat/Bug4944439.java
Normal file
104
test/jdk/java/text/Format/NumberFormat/Bug4944439.java
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2025, 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 4944439 8372609
|
||||
* @summary Confirm that numbers where all digits after the decimal separator are 0
|
||||
* and which are between Long.MIN_VALUE and Long.MAX_VALUE are returned
|
||||
* as Long(not double).
|
||||
* @run junit Bug4944439
|
||||
*/
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
|
||||
public class Bug4944439 {
|
||||
|
||||
private static final NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
// Check return type and value returned by DecimalFormat.parse() for longs
|
||||
@ParameterizedTest
|
||||
@MethodSource("longs")
|
||||
public void parseLongTest(String s) {
|
||||
// This was originally intended to ensure a ParseException is not thrown
|
||||
Number parsedNumber = assertDoesNotThrow(() -> df.parse(s),
|
||||
"DecimalFormat.parse(\"%s\") should not throw an Exception");
|
||||
assertInstanceOf(Long.class, parsedNumber,
|
||||
"DecimalFormat.parse(\"%s\") did not return Long");
|
||||
// Grab integer portion of value
|
||||
Long expectedVal = Long.valueOf(s.substring(0, s.indexOf('.')));
|
||||
assertEquals(parsedNumber, expectedVal,
|
||||
"DecimalFormat.parse(\"%s\") returned numerically incorrect value");
|
||||
}
|
||||
|
||||
// Test some values between Long.MIN_VALUE and Long.MAX_VALUE
|
||||
private static Stream<String> longs() {
|
||||
ArrayList<String> longs = new ArrayList<>();
|
||||
addLongData(Long.MIN_VALUE, Long.MIN_VALUE+10, longs);
|
||||
addLongData(-10, 10, longs);
|
||||
addLongData(Long.MAX_VALUE-10, Long.MAX_VALUE-1, longs);
|
||||
longs.add("9223372036854775807.00");
|
||||
longs.add("0.0");
|
||||
return longs.stream();
|
||||
}
|
||||
|
||||
// Utility to add values between parameters(long, to) to testLongs ArrayList
|
||||
private static void addLongData(long from, long to, ArrayList<String> testLongs){
|
||||
for (long l = from; l <= to; l++) {
|
||||
testLongs.add(l + ".00");
|
||||
}
|
||||
}
|
||||
|
||||
// Check return type and value returned by DecimalFormat.parse() for doubles
|
||||
@ParameterizedTest
|
||||
@MethodSource("doubles")
|
||||
public void parseDoubleTest(String s) {
|
||||
// This was originally intended to ensure a ParseException is not thrown
|
||||
Number parsedNumber = assertDoesNotThrow(() -> df.parse(s),
|
||||
"DecimalFormat.parse(\"%s\") should not throw an Exception");
|
||||
assertInstanceOf(Double.class, parsedNumber,
|
||||
"DecimalFormat.parse(\"%s\") did not return Double");
|
||||
Double expectedVal = Double.valueOf(s);
|
||||
assertEquals(parsedNumber, expectedVal,
|
||||
"DecimalFormat.parse(\"%s\") returned numerically incorrect value");
|
||||
}
|
||||
|
||||
// Check values not between Long.MIN_VALUE and Long.MAX_VALUE
|
||||
private static Stream<String> doubles() {
|
||||
return Stream.of(
|
||||
"-9223372036854775809", // Long.MIN_VALUE-1
|
||||
"9223372036854775808", // Long.MAX_VALUE+1
|
||||
"-0.0"
|
||||
);
|
||||
}
|
||||
}
|
||||
72
test/jdk/java/text/Format/NumberFormat/Bug4990596.java
Normal file
72
test/jdk/java/text/Format/NumberFormat/Bug4990596.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2004, 2023, 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 4990596
|
||||
* @summary Make sure that any subclass of Number can be formatted using
|
||||
* DecimalFormat.format() without throwing an exception.
|
||||
* @run junit Bug4990596
|
||||
*/
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
public class Bug4990596 {
|
||||
|
||||
// Test that a custom subclass of Number can be formatted by
|
||||
// DecimalFormat without throwing an IllegalArgumentException
|
||||
@Test
|
||||
public void formatSubclassedNumberTest() {
|
||||
assertDoesNotThrow(() -> new DecimalFormat().format(new MutableInteger(0)),
|
||||
"DecimalFormat.format() should support subclasses of Number");
|
||||
}
|
||||
|
||||
// A custom subclass of Number. Prior to this fix, if an instance of this
|
||||
// class was formatted by DecimalFormat, an exception would be thrown.
|
||||
@SuppressWarnings("serial")
|
||||
public static class MutableInteger extends Number {
|
||||
public int value;
|
||||
|
||||
public MutableInteger() {
|
||||
}
|
||||
public MutableInteger(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
public double doubleValue() {
|
||||
return this.value;
|
||||
}
|
||||
public float floatValue() {
|
||||
return this.value;
|
||||
}
|
||||
public int intValue() {
|
||||
return this.value;
|
||||
}
|
||||
public long longValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
test/jdk/java/text/Format/NumberFormat/Bug6278616.java
Normal file
78
test/jdk/java/text/Format/NumberFormat/Bug6278616.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2023, 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
|
||||
* @summary Confirm that AtomicInteger and AtomicLong are formatted correctly.
|
||||
* That is, make sure they are not treated as a double when formatted
|
||||
* anymore (which can result in the loss of precision).
|
||||
* @bug 6278616
|
||||
* @run junit Bug6278616
|
||||
*/
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class Bug6278616 {
|
||||
|
||||
private static final NumberFormat nf = NumberFormat.getInstance();
|
||||
|
||||
// Test that NumberFormat formats numerically equivalent int
|
||||
// and AtomicInteger values the same
|
||||
@ParameterizedTest
|
||||
@MethodSource("ints")
|
||||
public void formattedAtomicIntTest(int testInt) {
|
||||
String formattedInt = nf.format(testInt);
|
||||
String formattedAtomicInt = nf.format(new AtomicInteger(testInt));
|
||||
assertEquals(formattedAtomicInt, formattedInt, "Formatting numerically" +
|
||||
" equivalent AtomicInteger and int should produce the same String value");
|
||||
}
|
||||
|
||||
// Various int values
|
||||
private static int[] ints() {
|
||||
return new int[] { Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE};
|
||||
}
|
||||
|
||||
// Test that NumberFormat formats numerically equivalent long
|
||||
// and AtomicLong values the same
|
||||
@ParameterizedTest
|
||||
@MethodSource("longs")
|
||||
public void formattedAtomicLongTest(long testLong) {
|
||||
String formattedLong = nf.format(testLong);
|
||||
String formattedAtomicLong = nf.format(new AtomicLong(testLong));
|
||||
assertEquals(formattedAtomicLong, formattedLong, "Formatting numerically" +
|
||||
" equivalent AtomicLong and long should produce the same String value");
|
||||
}
|
||||
|
||||
// Various long values
|
||||
private static long[] longs() {
|
||||
return new long[] { Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE};
|
||||
}
|
||||
}
|
||||
52
test/jdk/java/text/Format/NumberFormat/Bug8132125.java
Normal file
52
test/jdk/java/text/Format/NumberFormat/Bug8132125.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2025, 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 8132125 8202537 8354548
|
||||
* @summary Checks Swiss' number elements
|
||||
* @modules jdk.localedata
|
||||
* @run junit Bug8132125
|
||||
*/
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class Bug8132125 {
|
||||
|
||||
// Ensure the CLDRConverter does not omit the Swiss number elements
|
||||
@Test
|
||||
public void swissNumElementsTest() {
|
||||
Locale deCH = Locale.of("de", "CH");
|
||||
NumberFormat nf = NumberFormat.getInstance(deCH);
|
||||
|
||||
// "\u002E" as decimal separator, "\u0027" as grouping separator
|
||||
String expected = "54'839'483.142";
|
||||
String actual = nf.format(54839483.1415);
|
||||
assertEquals(expected, actual, "incorrect number elements for de_CH");
|
||||
}
|
||||
}
|
||||
116
test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java
Normal file
116
test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2025, 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 4290801 4942982 5102005 8008577 8021121 8210153 8227313 8301991
|
||||
* 8174269
|
||||
* @summary Basic tests for currency formatting.
|
||||
* Tests both COMPAT and CLDR data.
|
||||
* @modules jdk.localedata
|
||||
* @run junit CurrencyFormat
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TimeZone;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class CurrencyFormat {
|
||||
|
||||
// Tests the formatting of data for COMPAT + CLDR under various currencies
|
||||
// Using a NumberFormat generated by getCurrencyInstance()
|
||||
@ParameterizedTest
|
||||
@MethodSource("currencyFormatDataProvider")
|
||||
public void currencyFormatTest(String expected, Currency currency,
|
||||
NumberFormat format, Locale locale) {
|
||||
if (currency != null) {
|
||||
format.setCurrency(currency);
|
||||
int digits = currency.getDefaultFractionDigits();
|
||||
format.setMinimumFractionDigits(digits);
|
||||
format.setMaximumFractionDigits(digits);
|
||||
}
|
||||
String result = format.format(1234.56);
|
||||
assertEquals(expected, result, String.format("Failed with locale: %s%s",
|
||||
locale, (currency == null ? ", default currency" : (", currency: " + currency))));
|
||||
}
|
||||
|
||||
// Generate a combination of expected data for 1234.56 formatted
|
||||
// under various currencies/locale provider/locale
|
||||
private static Stream<Arguments> currencyFormatDataProvider() {
|
||||
ArrayList<Arguments> data = new ArrayList<Arguments>();
|
||||
Locale[] locales = {
|
||||
Locale.US,
|
||||
Locale.JAPAN,
|
||||
Locale.GERMANY,
|
||||
Locale.ITALY,
|
||||
Locale.of("it", "IT", "EURO"),
|
||||
Locale.forLanguageTag("de-AT"),
|
||||
Locale.forLanguageTag("fr-CH"),
|
||||
};
|
||||
Currency[] currencies = {
|
||||
null,
|
||||
Currency.getInstance("USD"),
|
||||
Currency.getInstance("JPY"),
|
||||
Currency.getInstance("DEM"),
|
||||
Currency.getInstance("EUR"),
|
||||
};
|
||||
String[][] expectedCLDRData = {
|
||||
{"$1,234.56", "$1,234.56", "\u00a51,235", "DEM1,234.56", "\u20ac1,234.56"},
|
||||
{"\uFFE51,235", "$1,234.56", "\uFFE51,235", "DEM1,234.56", "\u20ac1,234.56"},
|
||||
{"1.234,56\u00a0\u20ac", "1.234,56\u00a0$", "1.235\u00a0\u00a5", "1.234,56\u00a0DM", "1.234,56\u00a0\u20ac"},
|
||||
{"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"},
|
||||
{"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"},
|
||||
{"\u20ac\u00a01.234,56", "$\u00a01.234,56", "\u00a5\u00a01.235", "DM\u00a01.234,56", "\u20ac\u00a01.234,56"},
|
||||
{"1\u202f234.56\u00a0CHF", "1\u202f234.56\u00a0$US", "1\u202f235\u00a0JPY", "1\u202f234.56\u00a0DEM", "1\u202f234.56\u00a0\u20ac"},
|
||||
};
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
Locale locale = locales[i];
|
||||
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
|
||||
for (int j = 0; j < currencies.length; j++) {
|
||||
Currency currency = currencies[j];
|
||||
String expected = expectedCLDRData[i][j];
|
||||
data.add(Arguments.of(expected, currency, format, locale));
|
||||
}
|
||||
}
|
||||
return data.stream();
|
||||
}
|
||||
}
|
||||
78
test/jdk/java/text/Format/NumberFormat/DFSExponential.java
Normal file
78
test/jdk/java/text/Format/NumberFormat/DFSExponential.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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 4068067
|
||||
* @summary test NumberFormat with exponential separator symbols. It also tests the new
|
||||
* public methods in DecimalFormatSymbols, setExponentSeparator() and
|
||||
* getExponentSeparator()
|
||||
* @run junit DFSExponential
|
||||
*/
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class DFSExponential {
|
||||
|
||||
@Test
|
||||
public void TestDFSExponential() {
|
||||
DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
|
||||
String[] pat = { "0.####E0", "00.000E00", "##0.####E000", "0.###E0;[0.###E0]"};
|
||||
double[] val = { 0.01234, 123456789, 1.23e300, -3.141592653e-271};
|
||||
String[][] valFormat = {
|
||||
{"1.234x10^-2", "1.2346x10^8", "1.23x10^300", "-3.1416x10^-271"},
|
||||
{"12.340x10^-03", "12.346x10^07", "12.300x10^299", "-31.416x10^-272"},
|
||||
{"12.34x10^-003", "123.4568x10^006", "1.23x10^300", "-314.1593x10^-273"},
|
||||
{"1.234x10^-2", "1.235x10^8", "1.23x10^300", "[3.142x10^-271]"},
|
||||
};
|
||||
System.out.println("Default exponent separator: "+sym.getExponentSeparator());
|
||||
try {
|
||||
sym.setExponentSeparator("x10^");
|
||||
} catch (NullPointerException e){
|
||||
fail("null String was passed to set an exponent separator symbol");
|
||||
}
|
||||
System.out.println("Current exponent separator: "+sym.getExponentSeparator());
|
||||
|
||||
for (int p=0; p<pat.length; ++p){
|
||||
DecimalFormat fmt = new DecimalFormat(pat[p], sym);
|
||||
System.out.println(" Pattern: " + fmt.toPattern());
|
||||
String locPattern = fmt.toLocalizedPattern();
|
||||
System.out.println(" Localized pattern: "+locPattern);
|
||||
for (int v=0; v<val.length; ++v) {
|
||||
String s = fmt.format(val[v]);
|
||||
System.out.println(" " + val[v]+" --> "+s);
|
||||
if (valFormat[p][v].equals(s)){
|
||||
System.out.println(": Passed");
|
||||
} else{
|
||||
fail(" Failed: Should be formatted as "+valFormat[p][v]+ "but got "+s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
148
test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java
Normal file
148
test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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 8220309 8230284
|
||||
* @library /java/text/testlib
|
||||
* @summary Test String representation of MinusSign/Percent/PerMill symbols.
|
||||
* This test assumes CLDR has numbering systems for "arab" and
|
||||
* "arabext", and their minus/percent representations include
|
||||
* BiDi formatting control characters.
|
||||
* @run junit/othervm DFSMinusPerCentMill
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class DFSMinusPerCentMill {
|
||||
private enum Type {
|
||||
NUMBER, PERCENT, CURRENCY, INTEGER, COMPACT, PERMILL
|
||||
}
|
||||
|
||||
private static final Locale US_ARAB = Locale.forLanguageTag("en-US-u-nu-arab");
|
||||
private static final Locale US_ARABEXT = Locale.forLanguageTag("en-US-u-nu-arabext");
|
||||
private static final double SRC_NUM = -1234.56;
|
||||
|
||||
Object[][] formatData() {
|
||||
return new Object[][] {
|
||||
// Locale, FormatStyle, expected format, expected single char symbol
|
||||
{US_ARAB, Type.NUMBER, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
|
||||
{US_ARAB, Type.PERCENT, "\u061c-\u0661\u0662\u0663\u066c\u0664\u0665\u0666\u066a\u061c"},
|
||||
{US_ARAB, Type.CURRENCY, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666\u00a0$"},
|
||||
{US_ARAB, Type.INTEGER, "\u061c-\u0661\u066c\u0662\u0663\u0665"},
|
||||
{US_ARAB, Type.COMPACT, "\u061c-\u0661K"},
|
||||
{US_ARAB, Type.PERMILL, "\u061c-\u0661\u0662\u0663\u0664\u0665\u0666\u0660\u0609"},
|
||||
|
||||
{US_ARABEXT, Type.NUMBER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
|
||||
{US_ARABEXT, Type.PERCENT, "\u200e-\u200e\u06f1\u06f2\u06f3\u066c\u06f4\u06f5\u06f6\u066a"},
|
||||
{US_ARABEXT, Type.CURRENCY, "\u200e-\u200e$\u00a0\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
|
||||
{US_ARABEXT, Type.INTEGER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f5"},
|
||||
{US_ARABEXT, Type.COMPACT, "\u200e-\u200e\u06f1K"},
|
||||
{US_ARABEXT, Type.PERMILL, "\u200e-\u200e\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f0\u0609"},
|
||||
};
|
||||
}
|
||||
|
||||
Object[][] charSymbols() {
|
||||
return new Object[][]{
|
||||
// Locale, percent, per mille, minus sign
|
||||
{US_ARAB, '\u066a', '\u0609', '-'},
|
||||
{US_ARABEXT, '\u066a', '\u0609', '-'},
|
||||
};
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("formatData")
|
||||
void testFormatData(Locale l, Type style, String expected) {
|
||||
NumberFormat nf = null;
|
||||
switch (style) {
|
||||
case NUMBER:
|
||||
nf = NumberFormat.getNumberInstance(l);
|
||||
break;
|
||||
case PERCENT:
|
||||
nf = NumberFormat.getPercentInstance(l);
|
||||
break;
|
||||
case CURRENCY:
|
||||
nf = NumberFormat.getCurrencyInstance(l);
|
||||
break;
|
||||
case INTEGER:
|
||||
nf = NumberFormat.getIntegerInstance(l);
|
||||
break;
|
||||
case COMPACT:
|
||||
nf = NumberFormat.getCompactNumberInstance(l, NumberFormat.Style.SHORT);
|
||||
break;
|
||||
case PERMILL:
|
||||
nf = new DecimalFormat("#.#\u2030", DecimalFormatSymbols.getInstance(l));
|
||||
break;
|
||||
}
|
||||
|
||||
assertEquals(expected, nf.format(SRC_NUM));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("charSymbols")
|
||||
void testCharSymbols(Locale l, char percent, char permill, char minus) {
|
||||
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
|
||||
assertEquals(percent, dfs.getPercent());
|
||||
assertEquals(permill, dfs.getPerMill());
|
||||
assertEquals(minus, dfs.getMinusSign());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSerialization() throws Exception {
|
||||
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
new ObjectOutputStream(bos).writeObject(dfs);
|
||||
DecimalFormatSymbols dfsSerialized = (DecimalFormatSymbols)new ObjectInputStream(
|
||||
new ByteArrayInputStream(bos.toByteArray())
|
||||
).readObject();
|
||||
|
||||
assertEquals(dfsSerialized, dfs);
|
||||
|
||||
// set minus/percent/permille
|
||||
dfs.setMinusSign('a');
|
||||
dfs.setPercent('b');
|
||||
dfs.setPerMill('c');
|
||||
bos = new ByteArrayOutputStream();
|
||||
new ObjectOutputStream(bos).writeObject(dfs);
|
||||
dfsSerialized = (DecimalFormatSymbols)new ObjectInputStream(
|
||||
new ByteArrayInputStream(bos.toByteArray())
|
||||
).readObject();
|
||||
|
||||
assertEquals(dfsSerialized, dfs);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright (c) 1998, 2023, 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
|
||||
* @summary test International Decimal Format API
|
||||
* @run junit IntlTestDecimalFormatAPI
|
||||
*/
|
||||
/*
|
||||
(C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
||||
(C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
|
||||
|
||||
The original version of this source code and documentation is copyrighted and
|
||||
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
|
||||
provided under terms of a License Agreement between Taligent and Sun. This
|
||||
technology is protected by multiple US and International patents. This notice and
|
||||
attribution to Taligent may not be removed.
|
||||
Taligent is a registered trademark of Taligent, Inc.
|
||||
*/
|
||||
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class IntlTestDecimalFormatAPI
|
||||
{
|
||||
// This test checks various generic API methods in DecimalFormat to achieve 100% API coverage.
|
||||
@Test
|
||||
public void TestAPI()
|
||||
{
|
||||
Locale reservedLocale = Locale.getDefault();
|
||||
try {
|
||||
System.out.println("DecimalFormat API test---"); System.out.println("");
|
||||
Locale.setDefault(Locale.ENGLISH);
|
||||
|
||||
// ======= Test constructors
|
||||
|
||||
System.out.println("Testing DecimalFormat constructors");
|
||||
|
||||
DecimalFormat def = new DecimalFormat();
|
||||
|
||||
final String pattern = new String("#,##0.# FF");
|
||||
DecimalFormat pat = null;
|
||||
try {
|
||||
pat = new DecimalFormat(pattern);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
fail("ERROR: Could not create DecimalFormat (pattern)");
|
||||
}
|
||||
|
||||
DecimalFormatSymbols symbols =
|
||||
new DecimalFormatSymbols(Locale.FRENCH);
|
||||
|
||||
DecimalFormat cust1 = new DecimalFormat(pattern, symbols);
|
||||
|
||||
// ======= Test clone(), assignment, and equality
|
||||
|
||||
System.out.println("Testing clone() and equality operators");
|
||||
|
||||
Format clone = (Format) def.clone();
|
||||
if( ! def.equals(clone)) {
|
||||
fail("ERROR: Clone() failed");
|
||||
}
|
||||
|
||||
// ======= Test various format() methods
|
||||
|
||||
System.out.println("Testing various format() methods");
|
||||
|
||||
// final double d = -10456.0037; // this appears as
|
||||
// -10456.003700000001 on NT
|
||||
// final double d = -1.04560037e-4; // this appears as
|
||||
// -1.0456003700000002E-4 on NT
|
||||
final double d = -10456.00370000000000; // this works!
|
||||
final long l = 100000000;
|
||||
System.out.println("" + d + " is the double value");
|
||||
|
||||
StringBuffer res1 = new StringBuffer();
|
||||
StringBuffer res2 = new StringBuffer();
|
||||
StringBuffer res3 = new StringBuffer();
|
||||
StringBuffer res4 = new StringBuffer();
|
||||
FieldPosition pos1 = new FieldPosition(0);
|
||||
FieldPosition pos2 = new FieldPosition(0);
|
||||
FieldPosition pos3 = new FieldPosition(0);
|
||||
FieldPosition pos4 = new FieldPosition(0);
|
||||
|
||||
res1 = def.format(d, res1, pos1);
|
||||
System.out.println("" + d + " formatted to " + res1);
|
||||
|
||||
res2 = pat.format(l, res2, pos2);
|
||||
System.out.println("" + l + " formatted to " + res2);
|
||||
|
||||
res3 = cust1.format(d, res3, pos3);
|
||||
System.out.println("" + d + " formatted to " + res3);
|
||||
|
||||
res4 = cust1.format(l, res4, pos4);
|
||||
System.out.println("" + l + " formatted to " + res4);
|
||||
|
||||
// ======= Test parse()
|
||||
|
||||
System.out.println("Testing parse()");
|
||||
|
||||
String text = new String("-10,456.0037");
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
String patt = new String("#,##0.#");
|
||||
pat.applyPattern(patt);
|
||||
double d2 = pat.parse(text, pos).doubleValue();
|
||||
if(d2 != d) {
|
||||
fail("ERROR: Roundtrip failed (via parse(" +
|
||||
d2 + " != " + d + ")) for " + text);
|
||||
}
|
||||
System.out.println(text + " parsed into " + (long) d2);
|
||||
|
||||
// ======= Test getters and setters
|
||||
|
||||
System.out.println("Testing getters and setters");
|
||||
|
||||
final DecimalFormatSymbols syms = pat.getDecimalFormatSymbols();
|
||||
def.setDecimalFormatSymbols(syms);
|
||||
if(!pat.getDecimalFormatSymbols().equals(
|
||||
def.getDecimalFormatSymbols())) {
|
||||
fail("ERROR: set DecimalFormatSymbols() failed");
|
||||
}
|
||||
|
||||
String posPrefix;
|
||||
pat.setPositivePrefix("+");
|
||||
posPrefix = pat.getPositivePrefix();
|
||||
System.out.println("Positive prefix (should be +): " + posPrefix);
|
||||
if(posPrefix != "+") {
|
||||
fail("ERROR: setPositivePrefix() failed");
|
||||
}
|
||||
|
||||
String negPrefix;
|
||||
pat.setNegativePrefix("-");
|
||||
negPrefix = pat.getNegativePrefix();
|
||||
System.out.println("Negative prefix (should be -): " + negPrefix);
|
||||
if(negPrefix != "-") {
|
||||
fail("ERROR: setNegativePrefix() failed");
|
||||
}
|
||||
|
||||
String posSuffix;
|
||||
pat.setPositiveSuffix("_");
|
||||
posSuffix = pat.getPositiveSuffix();
|
||||
System.out.println("Positive suffix (should be _): " + posSuffix);
|
||||
if(posSuffix != "_") {
|
||||
fail("ERROR: setPositiveSuffix() failed");
|
||||
}
|
||||
|
||||
String negSuffix;
|
||||
pat.setNegativeSuffix("~");
|
||||
negSuffix = pat.getNegativeSuffix();
|
||||
System.out.println("Negative suffix (should be ~): " + negSuffix);
|
||||
if(negSuffix != "~") {
|
||||
fail("ERROR: setNegativeSuffix() failed");
|
||||
}
|
||||
|
||||
long multiplier = 0;
|
||||
pat.setMultiplier(8);
|
||||
multiplier = pat.getMultiplier();
|
||||
System.out.println("Multiplier (should be 8): " + multiplier);
|
||||
if(multiplier != 8) {
|
||||
fail("ERROR: setMultiplier() failed");
|
||||
}
|
||||
|
||||
int groupingSize = 0;
|
||||
pat.setGroupingSize(2);
|
||||
groupingSize = pat.getGroupingSize();
|
||||
System.out.println("Grouping size (should be 2): " + (long) groupingSize);
|
||||
if(groupingSize != 2) {
|
||||
fail("ERROR: setGroupingSize() failed");
|
||||
}
|
||||
|
||||
pat.setDecimalSeparatorAlwaysShown(true);
|
||||
boolean tf = pat.isDecimalSeparatorAlwaysShown();
|
||||
System.out.println("DecimalSeparatorIsAlwaysShown (should be true) is " +
|
||||
(tf ? "true" : "false"));
|
||||
if(tf != true) {
|
||||
fail("ERROR: setDecimalSeparatorAlwaysShown() failed");
|
||||
}
|
||||
|
||||
String funkyPat;
|
||||
funkyPat = pat.toPattern();
|
||||
System.out.println("Pattern is " + funkyPat);
|
||||
|
||||
String locPat;
|
||||
locPat = pat.toLocalizedPattern();
|
||||
System.out.println("Localized pattern is " + locPat);
|
||||
|
||||
// ======= Test applyPattern()
|
||||
|
||||
System.out.println("Testing applyPattern()");
|
||||
|
||||
String p1 = new String("#,##0.0#;(#,##0.0#)");
|
||||
System.out.println("Applying pattern " + p1);
|
||||
pat.applyPattern(p1);
|
||||
String s2;
|
||||
s2 = pat.toPattern();
|
||||
System.out.println("Extracted pattern is " + s2);
|
||||
if( ! s2.equals(p1) ) {
|
||||
fail("ERROR: toPattern() result did not match " +
|
||||
"pattern applied");
|
||||
}
|
||||
|
||||
String p2 = new String("#,##0.0# FF;(#,##0.0# FF)");
|
||||
System.out.println("Applying pattern " + p2);
|
||||
pat.applyLocalizedPattern(p2);
|
||||
String s3;
|
||||
s3 = pat.toLocalizedPattern();
|
||||
System.out.println("Extracted pattern is " + s3);
|
||||
if( ! s3.equals(p2) ) {
|
||||
fail("ERROR: toLocalizedPattern() result did not match " +
|
||||
"pattern applied");
|
||||
}
|
||||
|
||||
// ======= Test getStaticClassID()
|
||||
|
||||
// logln("Testing instanceof()");
|
||||
|
||||
// try {
|
||||
// NumberFormat test = new DecimalFormat();
|
||||
|
||||
// if (! (test instanceof DecimalFormat)) {
|
||||
// errln("ERROR: instanceof failed");
|
||||
// }
|
||||
// }
|
||||
// catch (Exception e) {
|
||||
// errln("ERROR: Couldn't create a DecimalFormat");
|
||||
// }
|
||||
} finally {
|
||||
// restore the reserved locale
|
||||
Locale.setDefault(reservedLocale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 1998, 2025, 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 8282625 8366517
|
||||
* @summary test International Decimal Format Symbols
|
||||
* @run junit IntlTestDecimalFormatSymbols
|
||||
*/
|
||||
/*
|
||||
(C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
||||
(C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
|
||||
|
||||
The original version of this source code and documentation is copyrighted and
|
||||
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
|
||||
provided under terms of a License Agreement between Taligent and Sun. This
|
||||
technology is protected by multiple US and International patents. This notice and
|
||||
attribution to Taligent may not be removed.
|
||||
Taligent is a registered trademark of Taligent, Inc.
|
||||
*/
|
||||
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class IntlTestDecimalFormatSymbols
|
||||
{
|
||||
// Test the API of DecimalFormatSymbols; primarily a simple get/set set.
|
||||
@Test
|
||||
public void TestSymbols()
|
||||
{
|
||||
DecimalFormatSymbols fr = new DecimalFormatSymbols(Locale.FRENCH);
|
||||
|
||||
DecimalFormatSymbols en = new DecimalFormatSymbols(Locale.ENGLISH);
|
||||
|
||||
if(en.equals(fr)) {
|
||||
fail("ERROR: English DecimalFormatSymbols equal to French");
|
||||
}
|
||||
|
||||
// just do some VERY basic tests to make sure that get/set work
|
||||
|
||||
if (!fr.getLocale().equals(Locale.FRENCH)) {
|
||||
fail("ERROR: French DecimalFormatSymbols not Locale.FRENCH");
|
||||
}
|
||||
|
||||
if (!en.getLocale().equals(Locale.ENGLISH)) {
|
||||
fail("ERROR: English DecimalFormatSymbols not Locale.ENGLISH");
|
||||
}
|
||||
|
||||
char zero = en.getZeroDigit();
|
||||
fr.setZeroDigit(zero);
|
||||
if(fr.getZeroDigit() != en.getZeroDigit()) {
|
||||
fail("ERROR: get/set ZeroDigit failed");
|
||||
}
|
||||
|
||||
char group = en.getGroupingSeparator();
|
||||
fr.setGroupingSeparator(group);
|
||||
if(fr.getGroupingSeparator() != en.getGroupingSeparator()) {
|
||||
fail("ERROR: get/set GroupingSeparator failed");
|
||||
}
|
||||
|
||||
char decimal = en.getDecimalSeparator();
|
||||
fr.setDecimalSeparator(decimal);
|
||||
if(fr.getDecimalSeparator() != en.getDecimalSeparator()) {
|
||||
fail("ERROR: get/set DecimalSeparator failed");
|
||||
}
|
||||
|
||||
char perMill = en.getPerMill();
|
||||
fr.setPerMill(perMill);
|
||||
if(fr.getPerMill() != en.getPerMill()) {
|
||||
fail("ERROR: get/set PerMill failed");
|
||||
}
|
||||
|
||||
char percent = en.getPercent();
|
||||
fr.setPercent(percent);
|
||||
if(fr.getPercent() != en.getPercent()) {
|
||||
fail("ERROR: get/set Percent failed");
|
||||
}
|
||||
|
||||
char digit = en.getDigit();
|
||||
fr.setDigit(digit);
|
||||
if(fr.getPercent() != en.getPercent()) {
|
||||
fail("ERROR: get/set Percent failed");
|
||||
}
|
||||
|
||||
char patternSeparator = en.getPatternSeparator();
|
||||
fr.setPatternSeparator(patternSeparator);
|
||||
if(fr.getPatternSeparator() != en.getPatternSeparator()) {
|
||||
fail("ERROR: get/set PatternSeparator failed");
|
||||
}
|
||||
|
||||
String infinity = en.getInfinity();
|
||||
fr.setInfinity(infinity);
|
||||
String infinity2 = fr.getInfinity();
|
||||
if(! infinity.equals(infinity2)) {
|
||||
fail("ERROR: get/set Infinity failed");
|
||||
}
|
||||
|
||||
String nan = en.getNaN();
|
||||
fr.setNaN(nan);
|
||||
String nan2 = fr.getNaN();
|
||||
if(! nan.equals(nan2)) {
|
||||
fail("ERROR: get/set NaN failed");
|
||||
}
|
||||
|
||||
char minusSign = en.getMinusSign();
|
||||
fr.setMinusSign(minusSign);
|
||||
if(fr.getMinusSign() != en.getMinusSign()) {
|
||||
fail("ERROR: get/set MinusSign failed");
|
||||
}
|
||||
|
||||
// char exponential = en.getExponentialSymbol();
|
||||
// fr.setExponentialSymbol(exponential);
|
||||
// if(fr.getExponentialSymbol() != en.getExponentialSymbol()) {
|
||||
// errln("ERROR: get/set Exponential failed");
|
||||
// }
|
||||
|
||||
DecimalFormatSymbols foo = new DecimalFormatSymbols();
|
||||
|
||||
en = (DecimalFormatSymbols) fr.clone();
|
||||
|
||||
if(! en.equals(fr)) {
|
||||
fail("ERROR: Clone failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullLocaleTest() {
|
||||
assertThrows(NullPointerException.class, () -> new DecimalFormatSymbols(null));
|
||||
assertThrows(NullPointerException.class, () -> DecimalFormatSymbols.getInstance(null));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright (c) 1998, 2023, 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
|
||||
* @summary test International Number Format API
|
||||
* @modules jdk.localedata
|
||||
* @run junit IntlTestNumberFormatAPI
|
||||
*/
|
||||
/*
|
||||
(C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
||||
(C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
|
||||
|
||||
The original version of this source code and documentation is copyrighted and
|
||||
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
|
||||
provided under terms of a License Agreement between Taligent and Sun. This
|
||||
technology is protected by multiple US and International patents. This notice and
|
||||
attribution to Taligent may not be removed.
|
||||
Taligent is a registered trademark of Taligent, Inc.
|
||||
*/
|
||||
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class IntlTestNumberFormatAPI
|
||||
{
|
||||
// This test checks various generic API methods in DecimalFormat to achieve 100% API coverage.
|
||||
@Test
|
||||
public void TestAPI()
|
||||
{
|
||||
Locale reservedLocale = Locale.getDefault();
|
||||
try {
|
||||
System.out.println("NumberFormat API test---"); System.out.println("");
|
||||
Locale.setDefault(Locale.ENGLISH);
|
||||
|
||||
// ======= Test constructors
|
||||
|
||||
System.out.println("Testing NumberFormat constructors");
|
||||
|
||||
NumberFormat def = NumberFormat.getInstance();
|
||||
|
||||
NumberFormat fr = NumberFormat.getInstance(Locale.FRENCH);
|
||||
|
||||
NumberFormat cur = NumberFormat.getCurrencyInstance();
|
||||
|
||||
NumberFormat cur_fr =
|
||||
NumberFormat.getCurrencyInstance(Locale.FRENCH);
|
||||
|
||||
NumberFormat per = NumberFormat.getPercentInstance();
|
||||
|
||||
NumberFormat per_fr =
|
||||
NumberFormat.getPercentInstance(Locale.FRENCH);
|
||||
|
||||
// ======= Test equality
|
||||
|
||||
System.out.println("Testing equality operator");
|
||||
|
||||
if( per_fr.equals(cur_fr) ) {
|
||||
fail("ERROR: == failed");
|
||||
}
|
||||
|
||||
// ======= Test various format() methods
|
||||
|
||||
System.out.println("Testing various format() methods");
|
||||
|
||||
// final double d = -10456.0037; // this appears as
|
||||
// -10456.003700000001 on NT
|
||||
// final double d = -1.04560037e-4; // this appears as
|
||||
// -1.0456003700000002E-4 on NT
|
||||
final double d = -10456.00370000000000; // this works!
|
||||
final long l = 100000000;
|
||||
|
||||
String res1 = new String();
|
||||
String res2 = new String();
|
||||
StringBuffer res3 = new StringBuffer();
|
||||
StringBuffer res4 = new StringBuffer();
|
||||
StringBuffer res5 = new StringBuffer();
|
||||
StringBuffer res6 = new StringBuffer();
|
||||
FieldPosition pos1 = new FieldPosition(0);
|
||||
FieldPosition pos2 = new FieldPosition(0);
|
||||
FieldPosition pos3 = new FieldPosition(0);
|
||||
FieldPosition pos4 = new FieldPosition(0);
|
||||
|
||||
res1 = cur_fr.format(d);
|
||||
System.out.println( "" + d + " formatted to " + res1);
|
||||
|
||||
res2 = cur_fr.format(l);
|
||||
System.out.println("" + l + " formatted to " + res2);
|
||||
|
||||
res3 = cur_fr.format(d, res3, pos1);
|
||||
System.out.println( "" + d + " formatted to " + res3);
|
||||
|
||||
res4 = cur_fr.format(l, res4, pos2);
|
||||
System.out.println("" + l + " formatted to " + res4);
|
||||
|
||||
res5 = cur_fr.format(d, res5, pos3);
|
||||
System.out.println("" + d + " formatted to " + res5);
|
||||
|
||||
res6 = cur_fr.format(l, res6, pos4);
|
||||
System.out.println("" + l + " formatted to " + res6);
|
||||
|
||||
|
||||
// ======= Test parse()
|
||||
|
||||
System.out.println("Testing parse()");
|
||||
|
||||
// String text = new String("-10,456.0037");
|
||||
String text = new String("-10456,0037");
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
ParsePosition pos01 = new ParsePosition(0);
|
||||
double d1 = ((Number)fr.parseObject(text, pos)).doubleValue();
|
||||
if(d1 != d) {
|
||||
fail("ERROR: Roundtrip failed (via parse()) for " + text);
|
||||
}
|
||||
System.out.println(text + " parsed into " + d1);
|
||||
|
||||
double d2 = fr.parse(text, pos01).doubleValue();
|
||||
if(d2 != d) {
|
||||
fail("ERROR: Roundtrip failed (via parse()) for " + text);
|
||||
}
|
||||
System.out.println(text + " parsed into " + d2);
|
||||
|
||||
double d3 = 0;
|
||||
try {
|
||||
d3 = fr.parse(text).doubleValue();
|
||||
}
|
||||
catch (ParseException e) {
|
||||
fail("ERROR: parse() failed");
|
||||
}
|
||||
if(d3 != d) {
|
||||
fail("ERROR: Roundtrip failed (via parse()) for " + text);
|
||||
}
|
||||
System.out.println(text + " parsed into " + d3);
|
||||
|
||||
|
||||
// ======= Test getters and setters
|
||||
|
||||
System.out.println("Testing getters and setters");
|
||||
|
||||
final Locale[] locales = NumberFormat.getAvailableLocales();
|
||||
long count = locales.length;
|
||||
System.out.println("Got " + count + " locales" );
|
||||
for(int i = 0; i < count; i++) {
|
||||
String name;
|
||||
name = locales[i].getDisplayName();
|
||||
System.out.println(name);
|
||||
}
|
||||
|
||||
fr.setParseIntegerOnly( def.isParseIntegerOnly() );
|
||||
if(fr.isParseIntegerOnly() != def.isParseIntegerOnly() ) {
|
||||
fail("ERROR: setParseIntegerOnly() failed");
|
||||
}
|
||||
|
||||
fr.setGroupingUsed( def.isGroupingUsed() );
|
||||
if(fr.isGroupingUsed() != def.isGroupingUsed() ) {
|
||||
fail("ERROR: setGroupingUsed() failed");
|
||||
}
|
||||
|
||||
fr.setMaximumIntegerDigits( def.getMaximumIntegerDigits() );
|
||||
if(fr.getMaximumIntegerDigits() != def.getMaximumIntegerDigits() ) {
|
||||
fail("ERROR: setMaximumIntegerDigits() failed");
|
||||
}
|
||||
|
||||
fr.setMinimumIntegerDigits( def.getMinimumIntegerDigits() );
|
||||
if(fr.getMinimumIntegerDigits() != def.getMinimumIntegerDigits() ) {
|
||||
fail("ERROR: setMinimumIntegerDigits() failed");
|
||||
}
|
||||
|
||||
fr.setMaximumFractionDigits( def.getMaximumFractionDigits() );
|
||||
if(fr.getMaximumFractionDigits() != def.getMaximumFractionDigits() ) {
|
||||
fail("ERROR: setMaximumFractionDigits() failed");
|
||||
}
|
||||
|
||||
fr.setMinimumFractionDigits( def.getMinimumFractionDigits() );
|
||||
if(fr.getMinimumFractionDigits() != def.getMinimumFractionDigits() ) {
|
||||
fail("ERROR: setMinimumFractionDigits() failed");
|
||||
}
|
||||
|
||||
// ======= Test getStaticClassID()
|
||||
|
||||
// logln("Testing instanceof()");
|
||||
|
||||
// try {
|
||||
// NumberFormat test = new DecimalFormat();
|
||||
|
||||
// if (! (test instanceof DecimalFormat)) {
|
||||
// errln("ERROR: instanceof failed");
|
||||
// }
|
||||
// }
|
||||
// catch (Exception e) {
|
||||
// errln("ERROR: Couldn't create a DecimalFormat");
|
||||
// }
|
||||
} finally {
|
||||
// restore the reserved locale
|
||||
Locale.setDefault(reservedLocale);
|
||||
}
|
||||
}
|
||||
}
|
||||
251
test/jdk/java/text/Format/NumberFormat/LenientMinusSignTest.java
Normal file
251
test/jdk/java/text/Format/NumberFormat/LenientMinusSignTest.java
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 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 8363972
|
||||
* @summary Unit tests for lenient minus parsing
|
||||
* @modules jdk.localedata
|
||||
* java.base/java.text:+open
|
||||
* @run junit LenientMinusSignTest
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.text.CompactNumberFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class LenientMinusSignTest {
|
||||
private static final Locale FINNISH = Locale.of("fi");
|
||||
private static final DecimalFormatSymbols DFS =
|
||||
new DecimalFormatSymbols(Locale.ROOT);
|
||||
private static final String MINUS_PATTERN = "\u002D";
|
||||
|
||||
// "parseLenient" data from CLDR v47. These data are subject to change
|
||||
private static Stream<String> minus() {
|
||||
return Stream.of(
|
||||
MINUS_PATTERN, // "-" Hyphen-Minus
|
||||
"\uFF0D", // "-" Fullwidth Hyphen-Minus
|
||||
"\uFE63", // "﹣" Small Hyphen-Minus
|
||||
"\u2010", // "‐" Hyphen
|
||||
"\u2011", // "‑" Non-Breaking Hyphen
|
||||
"\u2012", // "‒" Figure Dash
|
||||
"\u2013", // "–" En Dash
|
||||
"\u2212", // "−" Minus Sign
|
||||
"\u207B", // "⁻" Superscript Minus
|
||||
"\u208B", // "₋" Subscript Minus
|
||||
"\u2796" // "➖" Heavy Minus Sign
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFinnishMinus() throws ParseException {
|
||||
// originally reported in JDK-8189097
|
||||
// Should not throw a ParseException
|
||||
assertEquals(NumberFormat.getInstance(FINNISH).parse(MINUS_PATTERN + "1,5"), -1.5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFinnishMinusStrict() {
|
||||
// Should throw a ParseException
|
||||
var nf = NumberFormat.getInstance(FINNISH);
|
||||
nf.setStrict(true);
|
||||
assertThrows(ParseException.class, () -> nf.parse(MINUS_PATTERN + "1,5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReadObject() throws IOException, ClassNotFoundException, ParseException {
|
||||
// check if deserialized NF works with lenient minus. Using the Finnish example
|
||||
var nf = NumberFormat.getInstance(FINNISH);
|
||||
NumberFormat nfDeser;
|
||||
byte[] serialized;
|
||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream out = new ObjectOutputStream(bos)) {
|
||||
out.writeObject(nf);
|
||||
out.flush();
|
||||
serialized = bos.toByteArray();
|
||||
}
|
||||
try (ByteArrayInputStream bis = new ByteArrayInputStream(serialized);
|
||||
ObjectInputStream in = new ObjectInputStream(bis)) {
|
||||
nfDeser = (NumberFormat) in.readObject();
|
||||
}
|
||||
assertEquals(nfDeser.parse(MINUS_PATTERN + "1,5"), -1.5);
|
||||
}
|
||||
|
||||
// White box test. modifies the private `lenientMinusSigns` field in the DFS
|
||||
@Test
|
||||
void testSupplementary() throws IllegalAccessException, NoSuchFieldException, ParseException {
|
||||
var dfs = new DecimalFormatSymbols(Locale.ROOT);
|
||||
MethodHandles.privateLookupIn(DecimalFormatSymbols.class, MethodHandles.lookup())
|
||||
.findVarHandle(DecimalFormatSymbols.class, "lenientMinusSigns", String.class)
|
||||
.set(dfs, "-🙂");
|
||||
// Direct match. Should succeed
|
||||
var df = new DecimalFormat("#.#;🙂#.#", dfs);
|
||||
assertEquals(df.parse("🙂1.5"), -1.5);
|
||||
|
||||
// Fail if the lengths of negative prefixes differ
|
||||
assertThrows(ParseException.class, () -> df.parse("-1.5"));
|
||||
var df2= new DecimalFormat("#.#;-#.#", dfs);
|
||||
assertThrows(ParseException.class, () -> df2.parse("🙂1.5"));
|
||||
}
|
||||
|
||||
@Nested
|
||||
class DecimalFormatTest {
|
||||
private static final String PREFIX = "+#;-#";
|
||||
private static final String SUFFIX = "#+;#-";
|
||||
private static final String LONG_PREFIX = "pos#;-neg#";
|
||||
private static final String LONG_SUFFIX = "#pos;#neg-";
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLenientPrefix(String sign) throws ParseException {
|
||||
var df = new DecimalFormat(PREFIX, DFS);
|
||||
df.setStrict(false);
|
||||
assertEquals(MINUS_PATTERN + "1", df.format(df.parse(sign + "1")));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLenientSuffix(String sign) throws ParseException {
|
||||
var df = new DecimalFormat(SUFFIX, DFS);
|
||||
df.setStrict(false);
|
||||
assertEquals("1" + MINUS_PATTERN, df.format(df.parse("1" + sign)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testStrictPrefix(String sign) throws ParseException {
|
||||
var df = new DecimalFormat(PREFIX, DFS);
|
||||
df.setStrict(true);
|
||||
if (sign.equals(MINUS_PATTERN)) {
|
||||
assertEquals(MINUS_PATTERN + "1", df.format(df.parse(sign + "1")));
|
||||
} else {
|
||||
assertThrows(ParseException.class, () -> df.parse(sign + "1"));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testStrictSuffix(String sign) throws ParseException {
|
||||
var df = new DecimalFormat(SUFFIX, DFS);
|
||||
df.setStrict(true);
|
||||
if (sign.equals(MINUS_PATTERN)) {
|
||||
assertEquals("1" + MINUS_PATTERN, df.format(df.parse("1" + sign)));
|
||||
} else {
|
||||
assertThrows(ParseException.class, () -> df.parse("1" + sign));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLongPrefix(String sign) throws ParseException {
|
||||
var df = new DecimalFormat(LONG_PREFIX, DFS);
|
||||
assertEquals(MINUS_PATTERN + "neg1", df.format(df.parse(sign + "neg1")));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLongSuffix(String sign) throws ParseException {
|
||||
var df = new DecimalFormat(LONG_SUFFIX, DFS);
|
||||
assertEquals("1neg" + MINUS_PATTERN, df.format(df.parse("1neg" + sign)));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CompactNumberFormatTest {
|
||||
private static final String[] PREFIX = {"+0;-0"};
|
||||
private static final String[] SUFFIX = {"0+;0-"};
|
||||
private static final String[] LONG_PREFIX = {"pos0;-neg0"};
|
||||
private static final String[] LONG_SUFFIX = {"0pos;0neg-"};
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLenientPrefix(String sign) throws ParseException {
|
||||
var cnf = new CompactNumberFormat("0", DFS, PREFIX);
|
||||
cnf.setStrict(false);
|
||||
assertEquals(MINUS_PATTERN + "1", cnf.format(cnf.parse(sign + "1")));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLenientSuffix(String sign) throws ParseException {
|
||||
var cnf = new CompactNumberFormat("0", DFS, SUFFIX);
|
||||
cnf.setStrict(false);
|
||||
assertEquals("1" + MINUS_PATTERN, cnf.format(cnf.parse("1" + sign)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testStrictPrefix(String sign) throws ParseException {
|
||||
var cnf = new CompactNumberFormat("0", DFS, PREFIX);
|
||||
cnf.setStrict(true);
|
||||
if (sign.equals(MINUS_PATTERN)) {
|
||||
assertEquals(MINUS_PATTERN + "1", cnf.format(cnf.parse(sign + "1")));
|
||||
} else {
|
||||
assertThrows(ParseException.class, () -> cnf.parse(sign + "1"));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testStrictSuffix(String sign) throws ParseException {
|
||||
var cnf = new CompactNumberFormat("0", DFS, SUFFIX);
|
||||
cnf.setStrict(true);
|
||||
if (sign.equals(MINUS_PATTERN)) {
|
||||
assertEquals("1" + MINUS_PATTERN, cnf.format(cnf.parse("1" + sign)));
|
||||
} else {
|
||||
assertThrows(ParseException.class, () -> cnf.parse("1" + sign));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLongPrefix(String sign) throws ParseException {
|
||||
var cnf = new CompactNumberFormat("0", DFS, LONG_PREFIX);
|
||||
assertEquals(MINUS_PATTERN + "neg1", cnf.format(cnf.parse(sign + "neg1")));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("LenientMinusSignTest#minus")
|
||||
public void testLongSuffix(String sign) throws ParseException {
|
||||
var cnf = new CompactNumberFormat("0", DFS, LONG_SUFFIX);
|
||||
assertEquals("1neg" + MINUS_PATTERN, cnf.format(cnf.parse( "1neg" + sign)));
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 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 7073852 8008577 8174269 8333582
|
||||
* @summary Support multiple scripts for digits and decimal symbols per locale
|
||||
* @run main MultipleNumberScriptTest
|
||||
*/
|
||||
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
public class MultipleNumberScriptTest {
|
||||
|
||||
static Locale[] locales = {
|
||||
Locale.of("ar"),
|
||||
Locale.of("ar", "EG"),
|
||||
Locale.of("ar", "DZ"),
|
||||
Locale.forLanguageTag("ar-EG-u-nu-arab"),
|
||||
Locale.forLanguageTag("ar-EG-u-nu-latn"),
|
||||
Locale.forLanguageTag("ar-DZ-u-nu-arab"),
|
||||
Locale.forLanguageTag("ar-DZ-u-nu-latn"),
|
||||
Locale.forLanguageTag("ee"),
|
||||
Locale.forLanguageTag("ee-GH"),
|
||||
Locale.forLanguageTag("ee-GH-u-nu-latn"),
|
||||
Locale.of("th", "TH", "TH"),
|
||||
Locale.forLanguageTag("th-TH"),
|
||||
Locale.forLanguageTag("th-TH-u-nu-thai"),
|
||||
Locale.forLanguageTag("th-TH-u-nu-hoge"),
|
||||
};
|
||||
|
||||
// expected numbering system for each locale
|
||||
static String[] expectedNumSystem = {
|
||||
"latn", // ar - CLDR-17553
|
||||
"arab", // ar-EG
|
||||
"latn", // ar-DZ
|
||||
"arab", // ar-EG-u-nu-arab
|
||||
"latn", // ar-EG-u-nu-latn
|
||||
"arab", // ar-DZ-u-nu-arab
|
||||
"latn", // ar-DZ-u-nu-latn
|
||||
"latn", // ee
|
||||
"latn", // ee-GH
|
||||
"latn", // ee-GH-u-nu-latn
|
||||
"thai", // th_TH_TH
|
||||
"latn", // th-TH
|
||||
"thai", // th-TH-u-nu-thai
|
||||
"latn", // th-TH-u-nu-hoge (invalid)
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
int num = 123456;
|
||||
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
NumberFormat nf = NumberFormat.getIntegerInstance(locales[i]);
|
||||
String formatted = nf.format(num);
|
||||
System.out.printf("%s is %s in %s locale (expected in %s script).\n",
|
||||
num, formatted, locales[i], expectedNumSystem[i]);
|
||||
if (!checkResult(formatted, expectedNumSystem[i])) {
|
||||
throw new RuntimeException("test failed. expected number system was not returned.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean checkResult(String formatted, String numSystem) {
|
||||
switch (numSystem) {
|
||||
case "arab":
|
||||
return formatted.charAt(0) == '\u0661';
|
||||
case "latn":
|
||||
return formatted.charAt(0) == '1';
|
||||
case "thai":
|
||||
return formatted.charAt(0) == '\u0e51';
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
test/jdk/java/text/Format/NumberFormat/NumberFormatRounding.java
Normal file
130
test/jdk/java/text/Format/NumberFormat/NumberFormatRounding.java
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2016, 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 4092330 6246348
|
||||
* @summary Tests for rounding mode in NumberFormat
|
||||
*/
|
||||
|
||||
import java.math.*;
|
||||
import java.text.*;
|
||||
|
||||
public class NumberFormatRounding {
|
||||
|
||||
static final String AE = "ArithmeticException";
|
||||
|
||||
static final double src[] = {5.5, 2.5, 1.6, 1.1, 1.0, -1.0, -1.1, -1.6, -2.5, -5.5,
|
||||
5.501, -5.501, 5.500, -5.500, 1.001, -1.001, 4.501, -4.501, 4.500, -4.500};
|
||||
static final String up[] = {"6", "3", "2", "2", "1", "-1", "-2", "-2", "-3", "-6",
|
||||
"6", "-6", "6", "-6", "2", "-2", "5", "-5", "5", "-5"};
|
||||
static final String down[] = {"5", "2", "1", "1", "1", "-1", "-1", "-1", "-2", "-5",
|
||||
"5", "-5", "5", "-5", "1", "-1", "4", "-4", "4", "-4"};
|
||||
static final String ceiling[] = {"6", "3", "2", "2", "1", "-1", "-1", "-1", "-2", "-5",
|
||||
"6", "-5", "6", "-5", "2", "-1", "5", "-4", "5", "-4"};
|
||||
static final String floor[] = {"5", "2", "1", "1", "1", "-1", "-2", "-2", "-3", "-6",
|
||||
"5", "-6", "5", "-6", "1", "-2", "4", "-5", "4", "-5"};
|
||||
static final String half_up[] = {"6", "3", "2", "1", "1", "-1", "-1", "-2", "-3", "-6",
|
||||
"6", "-6", "6", "-6", "1", "-1", "5", "-5", "5", "-5"};
|
||||
static final String half_down[] = {"5", "2", "2", "1", "1", "-1", "-1", "-2", "-2", "-5",
|
||||
"6", "-6", "5", "-5", "1", "-1", "5", "-5", "4", "-4"};
|
||||
static final String half_even[] = {"6", "2", "2", "1", "1", "-1", "-1", "-2", "-2", "-6",
|
||||
"6", "-6", "6", "-6", "1", "-1", "5", "-5", "4", "-4"};
|
||||
static final String unnecessary[] = {AE, AE, AE, AE, "1", "-1", AE, AE, AE, AE,
|
||||
AE, AE, AE, AE, AE, AE, AE, AE, AE, AE};
|
||||
|
||||
public static void main(String[] args) {
|
||||
basicTest();
|
||||
|
||||
roundTest(RoundingMode.UP, up);
|
||||
roundTest(RoundingMode.DOWN, down);
|
||||
roundTest(RoundingMode.CEILING, ceiling);
|
||||
roundTest(RoundingMode.FLOOR, floor);
|
||||
roundTest(RoundingMode.HALF_UP, half_up);
|
||||
roundTest(RoundingMode.HALF_DOWN, half_down);
|
||||
roundTest(RoundingMode.HALF_EVEN, half_even);
|
||||
roundTest(RoundingMode.UNNECESSARY, unnecessary);
|
||||
}
|
||||
|
||||
static void basicTest() {
|
||||
NumberFormat nf = NumberFormat.getIntegerInstance();
|
||||
|
||||
if (nf.getRoundingMode() != RoundingMode.HALF_EVEN) {
|
||||
throw new RuntimeException("default rounding is not HALF_EVEN");
|
||||
}
|
||||
|
||||
try {
|
||||
nf.setRoundingMode(null);
|
||||
throw new RuntimeException(
|
||||
"NullPointerException is not thrown by calling setRoundingMode(null)");
|
||||
} catch (NullPointerException npe) {
|
||||
// continue testing
|
||||
}
|
||||
|
||||
ChoiceFormat cf = new ChoiceFormat("");
|
||||
|
||||
try {
|
||||
cf.setRoundingMode(RoundingMode.HALF_EVEN);
|
||||
throw new RuntimeException(
|
||||
"UnsupportedOperationException is not thrown by calling setRoundingMode()");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// continue testing
|
||||
}
|
||||
|
||||
try {
|
||||
cf.getRoundingMode();
|
||||
throw new RuntimeException(
|
||||
"UnsupportedOperationException is not thrown by calling getRoundingMode()");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// continue testing
|
||||
}
|
||||
}
|
||||
|
||||
static void roundTest(RoundingMode rm, String[] expected) {
|
||||
NumberFormat nf = NumberFormat.getIntegerInstance();
|
||||
nf.setRoundingMode(rm);
|
||||
|
||||
if (nf.getRoundingMode() != rm) {
|
||||
throw new RuntimeException("set rounding mode is not returned by get method");
|
||||
}
|
||||
|
||||
for (int i = 0; i < src.length; i ++) {
|
||||
String result = null;
|
||||
try {
|
||||
result = nf.parse(nf.format(src[i])).toString();
|
||||
if (!result.equals(expected[i])) {
|
||||
throw new RuntimeException("rounding test #"+i+" failed. mode: "+rm+" src: "+src[i]+" expected: "+expected[i]+" result: "+result);
|
||||
}
|
||||
} catch (ArithmeticException ae) {
|
||||
if (expected[i].equals(AE)) {
|
||||
continue;
|
||||
} else {
|
||||
result = AE;
|
||||
throw new RuntimeException("rounding test #"+i+" failed. mode: "+rm+" src: "+src[i]+" expected: "+expected[i]+" result: "+result);
|
||||
}
|
||||
} catch (ParseException pe) {
|
||||
throw new RuntimeException("ParseException ocurred.", pe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1901
test/jdk/java/text/Format/NumberFormat/NumberRegression.java
Normal file
1901
test/jdk/java/text/Format/NumberFormat/NumberRegression.java
Normal file
File diff suppressed because it is too large
Load diff
158
test/jdk/java/text/Format/NumberFormat/NumberRoundTrip.java
Normal file
158
test/jdk/java/text/Format/NumberFormat/NumberRoundTrip.java
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2025, 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 4266589 8031145 8164791 8316696 8368001
|
||||
* @summary NumberFormat round trip testing of parsing and formatting.
|
||||
* This test checks 4 factory instances per locale against ~20 numeric inputs.
|
||||
* Samples ~1/4 of the available locales provided by NumberFormat.
|
||||
* @key randomness
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.RandomFactory
|
||||
* @run junit NumberRoundTrip
|
||||
*/
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.test.lib.RandomFactory;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/**
|
||||
* This class tests the round-trip behavior of NumberFormat, DecimalFormat, and DigitList.
|
||||
* Round-trip behavior is tested by taking a numeric value and formatting it, then
|
||||
* parsing the resulting string, and comparing this result with the original value.
|
||||
* Two tests are applied: String preservation, and numeric preservation. String
|
||||
* preservation is exact; numeric preservation is not. However, numeric preservation
|
||||
* should extend to the few least-significant bits.
|
||||
*/
|
||||
public class NumberRoundTrip {
|
||||
|
||||
private static final Random RND = RandomFactory.getRandom();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void testNumberFormatRoundTrip(NumberFormat fmt) {
|
||||
fmt.setMaximumFractionDigits(Integer.MAX_VALUE);
|
||||
Stream.concat(numbers.stream(), randomNumbers())
|
||||
.forEach(num -> test(fmt, num));
|
||||
}
|
||||
|
||||
private void test(NumberFormat fmt, Number num) {
|
||||
String originalFormatted = fmt.format(num);
|
||||
Number parsedNum = Assertions.assertDoesNotThrow(() -> fmt.parse(originalFormatted),
|
||||
"Failed parse(format(%s))".formatted(num));
|
||||
String parsedFormatted = fmt.format(parsedNum);
|
||||
var equal = originalFormatted.equals(parsedFormatted);
|
||||
// Try BigDecimal parsing, if not equal
|
||||
if (!equal) {
|
||||
var df = Assertions.assertInstanceOf(DecimalFormat.class, fmt);
|
||||
df.setParseBigDecimal(true);
|
||||
parsedNum = Assertions.assertDoesNotThrow(() -> fmt.parse(originalFormatted),
|
||||
"Failed BigDecimal parse(format(%s))".formatted(num));
|
||||
parsedFormatted = fmt.format(parsedNum);
|
||||
df.setParseBigDecimal(false);
|
||||
Assertions.assertEquals(originalFormatted, parsedFormatted,
|
||||
"Failed to round-trip format(parse(format(%s)))".formatted(num));
|
||||
}
|
||||
// Numeric mismatch to the amount of 1e-14 is tolerable
|
||||
var error = proportionalError(num, parsedNum);
|
||||
Assertions.assertFalse(error > 1e-14,
|
||||
"Round tripping %s caused numeric error: %s".formatted(num, error));
|
||||
}
|
||||
|
||||
// Regular, number, currency, and percent instance per locale
|
||||
private static Stream<Arguments> testNumberFormatRoundTrip() {
|
||||
return Stream.concat(
|
||||
// Default Locale
|
||||
Stream.of(
|
||||
Arguments.of(NumberFormat.getInstance()),
|
||||
Arguments.of(NumberFormat.getNumberInstance()),
|
||||
Arguments.of(NumberFormat.getCurrencyInstance()),
|
||||
Arguments.of(NumberFormat.getPercentInstance())),
|
||||
// ~1000 locales returned from provider.
|
||||
// Too expensive to test all locales, so sample a reasonable amount
|
||||
Arrays.stream(NumberFormat.getAvailableLocales())
|
||||
.filter(_ -> RND.nextDouble() < .25)
|
||||
.flatMap(loc -> Stream.of(
|
||||
Arguments.of(NumberFormat.getInstance(loc)),
|
||||
Arguments.of(NumberFormat.getNumberInstance(loc)),
|
||||
Arguments.of(NumberFormat.getCurrencyInstance(loc)),
|
||||
Arguments.of(NumberFormat.getPercentInstance(loc)))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Fixed set of numbers to test each locale against
|
||||
private static final List<Number> numbers = List.of(
|
||||
Double.NaN,
|
||||
Double.POSITIVE_INFINITY,
|
||||
Double.NEGATIVE_INFINITY,
|
||||
500,
|
||||
0,
|
||||
5555555555555555L,
|
||||
55555555555555555L,
|
||||
9223372036854775807L,
|
||||
9223372036854775808.0,
|
||||
-9223372036854775808L,
|
||||
-9223372036854775809.0
|
||||
);
|
||||
|
||||
// Compute fresh batch of random numbers per locale
|
||||
private Stream<Number> randomNumbers() {
|
||||
return Stream.of(
|
||||
randomDouble(1),
|
||||
randomDouble(10000),
|
||||
Math.floor(randomDouble(10000)),
|
||||
randomDouble(1e50),
|
||||
randomDouble(1e-50),
|
||||
randomDouble(1e100),
|
||||
// The use of double d such that isInfinite(100d) causes the
|
||||
// numeric test to fail with percent formats (bug 4266589).
|
||||
// Largest double s.t. 100d < Inf: d=1.7976931348623156E306
|
||||
randomDouble(1e306),
|
||||
randomDouble(1e-323),
|
||||
randomDouble(1e-100)
|
||||
);
|
||||
}
|
||||
|
||||
// Return a random value from -range..+range.
|
||||
private static double randomDouble(double range) {
|
||||
return RND.nextDouble(-range, range);
|
||||
}
|
||||
|
||||
private static double proportionalError(Number a, Number b) {
|
||||
double aa = a.doubleValue(), bb = b.doubleValue();
|
||||
double error = aa - bb;
|
||||
if (aa != 0 && bb != 0) error /= aa;
|
||||
return Math.abs(error);
|
||||
}
|
||||
}
|
||||
421
test/jdk/java/text/Format/NumberFormat/NumberTest.java
Normal file
421
test/jdk/java/text/Format/NumberFormat/NumberTest.java
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 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 4122840 4135202 4408066 4838107 8008577 8174269
|
||||
* @summary test NumberFormat
|
||||
* @modules java.base/sun.util.resources
|
||||
* jdk.localedata
|
||||
* @compile -XDignore.symbol.file NumberTest.java
|
||||
* @run junit NumberTest
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import sun.util.resources.LocaleData;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class NumberTest
|
||||
{
|
||||
// Test pattern handling
|
||||
@Test
|
||||
public void TestPatterns()
|
||||
{
|
||||
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US);
|
||||
String pat[] = { "#.#", "#.", ".#", "#" };
|
||||
String newpat[] = { "#0.#", "#0.", "#.0", "#" };
|
||||
String num[] = { "0", "0.", ".0", "0" };
|
||||
for (int i=0; i<pat.length; ++i)
|
||||
{
|
||||
DecimalFormat fmt = new DecimalFormat(pat[i], sym);
|
||||
String newp = fmt.toPattern();
|
||||
if (!newp.equals(newpat[i]))
|
||||
fail("FAIL: Pattern " + pat[i] + " should transmute to " + newpat[i] +
|
||||
"; " + newp + " seen instead");
|
||||
|
||||
String s = fmt.format(0);
|
||||
if (!s.equals(num[i]))
|
||||
{
|
||||
fail("FAIL: Pattern " + pat[i] + " should format zero as " + num[i] +
|
||||
"; " + s + " seen instead");
|
||||
System.out.println("Min integer digits = " + fmt.getMinimumIntegerDigits());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test exponential pattern
|
||||
@Test
|
||||
public void TestExponential() {
|
||||
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US);
|
||||
String pat[] = { "0.####E0", "00.000E00", "##0.####E000", "0.###E0;[0.###E0]" };
|
||||
double val[] = { 0.01234, 123456789, 1.23e300, -3.141592653e-271 };
|
||||
long lval[] = { 0, -1, 1, 123456789 };
|
||||
String valFormat[] = {
|
||||
"1.234E-2", "1.2346E8", "1.23E300", "-3.1416E-271",
|
||||
"12.340E-03", "12.346E07", "12.300E299", "-31.416E-272",
|
||||
"12.34E-003", "123.4568E006", "1.23E300", "-314.1593E-273",
|
||||
"1.234E-2", "1.235E8", "1.23E300", "[3.142E-271]"
|
||||
};
|
||||
String lvalFormat[] = {
|
||||
"0E0", "-1E0", "1E0", "1.2346E8",
|
||||
"00.000E00", "-10.000E-01", "10.000E-01", "12.346E07",
|
||||
"0E000", "-1E000", "1E000", "123.4568E006",
|
||||
"0E0", "[1E0]", "1E0", "1.235E8"
|
||||
};
|
||||
double valParse[] = {
|
||||
0.01234, 123460000, 1.23E300, -3.1416E-271,
|
||||
0.01234, 123460000, 1.23E300, -3.1416E-271,
|
||||
0.01234, 123456800, 1.23E300, -3.141593E-271,
|
||||
0.01234, 123500000, 1.23E300, -3.142E-271,
|
||||
};
|
||||
long lvalParse[] = {
|
||||
0, -1, 1, 123460000,
|
||||
0, -1, 1, 123460000,
|
||||
0, -1, 1, 123456800,
|
||||
0, -1, 1, 123500000,
|
||||
};
|
||||
int ival = 0, ilval = 0;
|
||||
for (int p=0; p<pat.length; ++p) {
|
||||
DecimalFormat fmt = new DecimalFormat(pat[p], sym);
|
||||
System.out.println("Pattern \"" + pat[p] + "\" -toPattern-> \"" +
|
||||
fmt.toPattern() + '"');
|
||||
|
||||
for (int v=0; v<val.length; ++v) {
|
||||
String s = fmt.format(val[v]);
|
||||
System.out.println(" Format " + val[v] + " -> " + escape(s));
|
||||
if (!s.equals(valFormat[v+ival])) {
|
||||
fail("FAIL: Expected " + valFormat[v+ival] +
|
||||
", got " + s +
|
||||
", pattern=" + fmt.toPattern());
|
||||
}
|
||||
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Number a = fmt.parse(s, pos);
|
||||
if (pos.getIndex() == s.length()) {
|
||||
System.out.println(" Parse -> " + a);
|
||||
if (a.doubleValue() != valParse[v+ival]) {
|
||||
fail("FAIL: Expected " + valParse[v+ival] +
|
||||
", got " + a.doubleValue() +
|
||||
", pattern=" + fmt.toPattern());
|
||||
}
|
||||
} else {
|
||||
fail(" FAIL: Partial parse (" + pos.getIndex() +
|
||||
" chars) -> " + a);
|
||||
}
|
||||
}
|
||||
for (int v=0; v<lval.length; ++v) {
|
||||
String s = fmt.format(lval[v]);
|
||||
System.out.println(" Format " + lval[v] + "L -> " + escape(s));
|
||||
if (!s.equals(lvalFormat[v+ilval])) {
|
||||
fail("ERROR: Expected " + lvalFormat[v+ilval] +
|
||||
", got " + s +
|
||||
", pattern=" + fmt.toPattern());
|
||||
}
|
||||
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
Number a = fmt.parse(s, pos);
|
||||
if (pos.getIndex() == s.length()) {
|
||||
System.out.println(" Parse -> " + a);
|
||||
if (a.longValue() != lvalParse[v+ilval]) {
|
||||
fail("FAIL: Expected " + lvalParse[v+ilval] +
|
||||
", got " + a +
|
||||
", pattern=" + fmt.toPattern());
|
||||
}
|
||||
} else {
|
||||
fail(" FAIL: Partial parse (" + pos.getIndex() +
|
||||
" chars) -> " + a);
|
||||
}
|
||||
}
|
||||
ival += val.length;
|
||||
ilval += lval.length;
|
||||
}
|
||||
}
|
||||
|
||||
// Test the handling of quotes
|
||||
@Test
|
||||
public void TestQuotes()
|
||||
{
|
||||
String pat;
|
||||
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US);
|
||||
DecimalFormat fmt = new DecimalFormat(pat = "a'fo''o'b#", sym);
|
||||
String s = fmt.format(123);
|
||||
System.out.println("Pattern \"" + pat + "\"");
|
||||
System.out.println(" Format 123 -> " + escape(s));
|
||||
if (!s.equals("afo'ob123")) fail("FAIL: Expected afo'ob123");
|
||||
|
||||
fmt = new DecimalFormat(pat = "a''b#", sym);
|
||||
s = fmt.format(123);
|
||||
System.out.println("Pattern \"" + pat + "\"");
|
||||
System.out.println(" Format 123 -> " + escape(s));
|
||||
if (!s.equals("a'b123")) fail("FAIL: Expected a'b123");
|
||||
}
|
||||
|
||||
// Test the use of the currency sign
|
||||
@Test
|
||||
public void TestCurrencySign()
|
||||
{
|
||||
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US);
|
||||
DecimalFormat fmt = new DecimalFormat("\u00A4#,##0.00;-\u00A4#,##0.00", sym);
|
||||
// Can't test this properly until currency API goes public
|
||||
// DecimalFormatSymbols sym = fmt.getDecimalFormatSymbols();
|
||||
|
||||
String s = fmt.format(1234.56);
|
||||
System.out.println("Pattern \"" + fmt.toPattern() + "\"");
|
||||
System.out.println(" Format " + 1234.56 + " -> " + escape(s));
|
||||
if (!s.equals("$1,234.56")) fail("FAIL: Expected $1,234.56");
|
||||
s = fmt.format(-1234.56);
|
||||
System.out.println(" Format " + -1234.56 + " -> " + escape(s));
|
||||
if (!s.equals("-$1,234.56")) fail("FAIL: Expected -$1,234.56");
|
||||
|
||||
fmt = new DecimalFormat("\u00A4\u00A4 #,##0.00;\u00A4\u00A4 -#,##0.00", sym);
|
||||
s = fmt.format(1234.56);
|
||||
System.out.println("Pattern \"" + fmt.toPattern() + "\"");
|
||||
System.out.println(" Format " + 1234.56 + " -> " + escape(s));
|
||||
if (!s.equals("USD 1,234.56")) fail("FAIL: Expected USD 1,234.56");
|
||||
s = fmt.format(-1234.56);
|
||||
System.out.println(" Format " + -1234.56 + " -> " + escape(s));
|
||||
if (!s.equals("USD -1,234.56")) fail("FAIL: Expected USD -1,234.56");
|
||||
}
|
||||
static String escape(String s)
|
||||
{
|
||||
StringBuffer buf = new StringBuffer();
|
||||
char HEX[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
|
||||
for (int i=0; i<s.length(); ++i)
|
||||
{
|
||||
char c = s.charAt(i);
|
||||
if (c <= (char)0x7F) buf.append(c);
|
||||
else
|
||||
{
|
||||
buf.append("\\U");
|
||||
buf.append(HEX[(c & 0xF000) >> 12]);
|
||||
buf.append(HEX[(c & 0x0F00) >> 8]);
|
||||
buf.append(HEX[(c & 0x00F0) >> 4]);
|
||||
buf.append(HEX[c & 0x000F]);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
// Test simple currency format
|
||||
// Bug 4024941; this code used to throw a NumberFormat exception
|
||||
@Test
|
||||
public void TestCurrency() {
|
||||
NumberFormat currencyFmt =
|
||||
NumberFormat.getCurrencyInstance(Locale.CANADA_FRENCH);
|
||||
String s = currencyFmt.format(1.50);
|
||||
System.out.println("Un pauvre ici a..........." + s);
|
||||
if (!s.equals("1,50\u00a0$")) {
|
||||
fail("FAIL: Expected 1,50\u00a0$; got " + s + "; "+ dumpFmt(currencyFmt));
|
||||
}
|
||||
currencyFmt = NumberFormat.getCurrencyInstance(Locale.GERMANY);
|
||||
s = currencyFmt.format(1.50);
|
||||
System.out.println("Un pauvre en Allemagne a.." + s);
|
||||
if (!s.equals("1,50\u00a0\u20AC")) {
|
||||
fail("FAIL: Expected 1,50\u00a0\u20AC; got " + s + "; " + dumpFmt(currencyFmt));
|
||||
}
|
||||
currencyFmt = NumberFormat.getCurrencyInstance(Locale.FRANCE);
|
||||
s = currencyFmt.format(1.50);
|
||||
System.out.println("Un pauvre en France a....." + s);
|
||||
if (!s.equals("1,50\u00a0\u20AC")) {
|
||||
fail("FAIL: Expected 1,50\u00a0\u20AC; got " + s + "; " + dumpFmt(currencyFmt));
|
||||
}
|
||||
}
|
||||
|
||||
String dumpFmt(NumberFormat numfmt) {
|
||||
DecimalFormat fmt = (DecimalFormat)numfmt;
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("pattern \"");
|
||||
buf.append(fmt.toPattern());
|
||||
buf.append("\", currency \"");
|
||||
buf.append(fmt.getDecimalFormatSymbols().getCurrencySymbol());
|
||||
buf.append("\"");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
// Test numeric parsing
|
||||
// Bug 4059870
|
||||
@Test
|
||||
public void TestParse()
|
||||
{
|
||||
String arg = "0";
|
||||
java.text.DecimalFormat format = new java.text.DecimalFormat("00");
|
||||
try {
|
||||
Number n = format.parse(arg);
|
||||
System.out.println("parse(" + arg + ") = " + n);
|
||||
if (n.doubleValue() != 0.0) fail("FAIL: Expected 0");
|
||||
} catch (Exception e) { fail("Exception caught: " + e); }
|
||||
}
|
||||
|
||||
// Test rounding
|
||||
@Test
|
||||
public void TestRounding487() {
|
||||
NumberFormat nf = NumberFormat.getInstance(Locale.US);
|
||||
roundingTest(nf, 0.00159999, 4, "0.0016");
|
||||
roundingTest(nf, 0.00995, 4, "0.01");
|
||||
roundingTest(nf, 12.7995, 3, "12.8");
|
||||
roundingTest(nf, 12.4999, 0, "12");
|
||||
roundingTest(nf, -19.5, 0, "-20");
|
||||
}
|
||||
|
||||
void roundingTest(NumberFormat nf, double x, int maxFractionDigits, String expected) {
|
||||
nf.setMaximumFractionDigits(maxFractionDigits);
|
||||
String out = nf.format(x);
|
||||
System.out.println("" + x + " formats with " + maxFractionDigits + " fractional digits to " + out);
|
||||
if (!out.equals(expected)) {
|
||||
fail("FAIL: Expected " + expected + ", got " + out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 4135202
|
||||
* DecimalFormat should recognize not only Latin digits 0-9 (\u0030-\u0039)
|
||||
* but also various other ranges of Unicode digits, such as Arabic
|
||||
* digits \u0660-\u0669 and Devanagari digits \u0966-\u096F, to name
|
||||
* a couple.
|
||||
* @see java.lang.Character#isDigit(char)
|
||||
*/
|
||||
@Test
|
||||
public void TestUnicodeDigits() {
|
||||
char[] zeros = {
|
||||
0x0030, // ISO-LATIN-1 digits ('0' through '9')
|
||||
0x0660, // Arabic-Indic digits
|
||||
0x06F0, // Extended Arabic-Indic digits
|
||||
0x0966, // Devanagari digits
|
||||
0x09E6, // Bengali digits
|
||||
0x0A66, // Gurmukhi digits
|
||||
0x0AE6, // Gujarati digits
|
||||
0x0B66, // Oriya digits
|
||||
0x0BE6, // Tamil digits
|
||||
0x0C66, // Telugu digits
|
||||
0x0CE6, // Kannada digits
|
||||
0x0D66, // Malayalam digits
|
||||
0x0E50, // Thai digits
|
||||
0x0ED0, // Lao digits
|
||||
0x0F20, // Tibetan digits
|
||||
0xFF10, // Fullwidth digits
|
||||
};
|
||||
NumberFormat format = NumberFormat.getInstance();
|
||||
for (int i=0; i<zeros.length; ++i) {
|
||||
char zero = zeros[i];
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append((char)(zero+3));
|
||||
buf.append((char)(zero+1));
|
||||
buf.append((char)(zero+4));
|
||||
int n = -1;
|
||||
try {
|
||||
n = format.parse(buf.toString()).intValue();
|
||||
}
|
||||
catch (ParseException e) { n = -2; }
|
||||
if (n != 314)
|
||||
fail("Can't parse Unicode " + Integer.toHexString(zero) + " as digit (" + n + ")");
|
||||
else
|
||||
System.out.println("Parse digit " + Integer.toHexString(zero) + " ok");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 4122840
|
||||
* Make sure that the currency symbol is not hard-coded in any locale.
|
||||
*/
|
||||
@Test
|
||||
public void TestCurrencySubstitution() {
|
||||
final String SYM = "<currency>";
|
||||
final String INTL_SYM = "<intl.currency>";
|
||||
Locale[] locales = NumberFormat.getAvailableLocales();
|
||||
for (int i=0; i<locales.length; ++i) {
|
||||
NumberFormat nf = NumberFormat.getCurrencyInstance(locales[i]);
|
||||
if (nf instanceof DecimalFormat) {
|
||||
DecimalFormat df = (DecimalFormat)nf;
|
||||
String genericPos = df.format(1234.5678);
|
||||
String genericNeg = df.format(-1234.5678);
|
||||
DecimalFormatSymbols sym = df.getDecimalFormatSymbols();
|
||||
sym.setCurrencySymbol(SYM);
|
||||
sym.setInternationalCurrencySymbol(INTL_SYM);
|
||||
// We have to make a new DecimalFormat from scratch in order
|
||||
// to make the new symbols 'take'. This may be a bug or
|
||||
// design flaw in DecimalFormat.
|
||||
String[] patterns = LocaleData.getBundle("sun.text.resources.FormatData", locales[i])
|
||||
.getStringArray("NumberPatterns");
|
||||
df = new DecimalFormat(patterns[1 /*CURRENCYSTYLE*/], sym);
|
||||
String customPos = df.format(1234.5678);
|
||||
String customNeg = df.format(-1234.5678);
|
||||
if (genericPos.equals(customPos) || genericNeg.equals(customNeg)) {
|
||||
fail("FAIL: " + locales[i] +
|
||||
" not using currency symbol substitution: " + genericPos);
|
||||
}
|
||||
else {
|
||||
if (customPos.indexOf(SYM) >= 0) {
|
||||
if (customNeg.indexOf(INTL_SYM) >= 0)
|
||||
fail("Fail: Positive and negative patterns use different symbols");
|
||||
else
|
||||
System.out.println("Ok: " + locales[i] +
|
||||
" uses currency symbol: " + genericPos +
|
||||
", " + customPos);
|
||||
}
|
||||
else if (customPos.indexOf(INTL_SYM) >= 0) {
|
||||
if (customNeg.indexOf(SYM) >= 0)
|
||||
fail("Fail: Positive and negative patterns use different symbols");
|
||||
else
|
||||
System.out.println("Ok: " + locales[i] +
|
||||
" uses intl. currency symbol: " + genericPos +
|
||||
", " + customPos);
|
||||
}
|
||||
else {
|
||||
fail("FAIL: " + locales[i] +
|
||||
" contains no currency symbol (impossible!)");
|
||||
}
|
||||
}
|
||||
}
|
||||
else System.out.println("Skipping " + locales[i] + "; not a DecimalFormat");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestIntegerFormat() throws ParseException {
|
||||
NumberFormat format = NumberFormat.getIntegerInstance(Locale.GERMANY);
|
||||
|
||||
float[] formatInput = { 12345.67f, -12345.67f, -0, 0 };
|
||||
String[] formatExpected = { "12.346", "-12.346", "0", "0" };
|
||||
|
||||
for (int i = 0; i < formatInput.length; i++) {
|
||||
String result = format.format(formatInput[i]);
|
||||
if (!result.equals(formatExpected[i])) {
|
||||
fail("FAIL: Expected " + formatExpected[i] + ", got " + result);
|
||||
}
|
||||
}
|
||||
|
||||
String[] parseInput = { "0", "-0", "12.345,67", "-12.345,67" };
|
||||
float[] parseExpected = { 0, 0, 12345, -12345 };
|
||||
|
||||
for (int i = 0; i < parseInput.length; i++) {
|
||||
float result = format.parse(parseInput[i]).floatValue();
|
||||
if (result != parseExpected[i]) {
|
||||
fail("FAIL: Expected " + parseExpected[i] + ", got " + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
277
test/jdk/java/text/Format/NumberFormat/PositionTest.java
Normal file
277
test/jdk/java/text/Format/NumberFormat/PositionTest.java
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2025, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
(C) Copyright Taligent, Inc. 1996 - All Rights Reserved
|
||||
(C) Copyright IBM Corp. 1996 - All Rights Reserved
|
||||
|
||||
The original version of this source code and documentation is copyrighted and
|
||||
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
|
||||
provided under terms of a License Agreement between Taligent and Sun. This
|
||||
technology is protected by multiple US and International patents. This notice and
|
||||
attribution to Taligent may not be removed.
|
||||
Taligent is a registered trademark of Taligent, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4109023 4153060 4153061 8366400
|
||||
* @summary test ParsePosition and FieldPosition
|
||||
* @run junit PositionTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
|
||||
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.fail;
|
||||
|
||||
public class PositionTest {
|
||||
|
||||
// Parsing text which contains un-parseable data, but the index
|
||||
// begins at the valid portion. Ensure PP is properly updated.
|
||||
@Test
|
||||
public void modifiedPositionTest() {
|
||||
var df = new DecimalFormat("YY#");
|
||||
df.setStrict(false); // Lenient by default, set for test explicitness
|
||||
var pp = new ParsePosition(9);
|
||||
assertEquals(123L, assertDoesNotThrow(() -> df.parse("FOOBARBAZYY123", pp)));
|
||||
assertEquals(-1, pp.getErrorIndex());
|
||||
assertEquals(14, pp.getIndex());
|
||||
}
|
||||
|
||||
// Clearly invalid index value that could not work under any scenarios
|
||||
// Specifically, ensuring no SIOOBE during affix matching
|
||||
@Test
|
||||
public void invalidPositionParseTest() {
|
||||
var df = new DecimalFormat();
|
||||
df.setStrict(false); // Lenient by default, set for test explicitness
|
||||
assertNull(assertDoesNotThrow(() -> df.parse("1", new ParsePosition(-1))));
|
||||
assertNull(assertDoesNotThrow(() -> df.parse("1", new ParsePosition(Integer.MAX_VALUE))));
|
||||
}
|
||||
|
||||
// When prefix matching, position + affix length is greater than parsed String length
|
||||
// Ensure we do not index out of bounds of the length of the parsed String
|
||||
@Test
|
||||
public void prefixMatchingTest() {
|
||||
var df = new DecimalFormat("ZZZ#;YYY#");
|
||||
df.setStrict(false); // Lenient by default, set for test explicitness
|
||||
// 0 + 3 > 2 = (pos + prefix > text)
|
||||
assertNull(assertDoesNotThrow(() -> df.parse("Z1", new ParsePosition(0))));
|
||||
assertNull(assertDoesNotThrow(() -> df.parse("Y1", new ParsePosition(0))));
|
||||
}
|
||||
|
||||
// When suffix matching, position + affix length is greater than parsed String length
|
||||
// Ensure we do not index out of bounds of the length of the parsed String
|
||||
@Test
|
||||
public void suffixMatchingTest() {
|
||||
var df = new DecimalFormat("#ZZ;#YY");
|
||||
df.setStrict(false); // Lenient by default, set for test explicitness
|
||||
// Matches prefix properly first. Then 3 + 2 > 4 = (pos + suffix > text)
|
||||
assertNull(assertDoesNotThrow(() -> df.parse("123Z", new ParsePosition(0))));
|
||||
assertNull(assertDoesNotThrow(() -> df.parse("123Y", new ParsePosition(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestParsePosition() {
|
||||
ParsePosition pp1 = new ParsePosition(0);
|
||||
if (pp1.getIndex() == 0) {
|
||||
System.out.println("PP constructor() tested.");
|
||||
}else{
|
||||
fail("*** PP getIndex or constructor() result");
|
||||
}
|
||||
|
||||
{
|
||||
int to = 5;
|
||||
ParsePosition pp2 = new ParsePosition ( to );
|
||||
if (pp2.getIndex() == 5) {
|
||||
System.out.println("PP getIndex and constructor(TextOffset) tested.");
|
||||
}else{
|
||||
fail("*** PP getIndex or constructor(TextOffset) result");
|
||||
}
|
||||
pp2.setIndex( 3 );
|
||||
if (pp2.getIndex() == 3) {
|
||||
System.out.println("PP setIndex tested.");
|
||||
}else{
|
||||
fail("*** PP getIndex or setIndex result");
|
||||
}
|
||||
}
|
||||
|
||||
ParsePosition pp2, pp3;
|
||||
pp2 = new ParsePosition( 3 );
|
||||
pp3 = new ParsePosition( 5 );
|
||||
ParsePosition pp4 = new ParsePosition(5);
|
||||
if (! pp2.equals(pp3)) {
|
||||
System.out.println("PP not equals tested.");
|
||||
}else{
|
||||
fail("*** PP not equals fails");
|
||||
}
|
||||
if (pp3.equals(pp4)) {
|
||||
System.out.println("PP equals tested.");
|
||||
}else{
|
||||
fail("*** PP equals fails (" + pp3.getIndex() + " != " + pp4.getIndex() + ")");
|
||||
}
|
||||
|
||||
ParsePosition pp5;
|
||||
pp5 = pp4;
|
||||
if (pp4.equals(pp5)) {
|
||||
System.out.println("PP operator= tested.");
|
||||
}else{
|
||||
fail("*** PP operator= operator== or operator != result");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestFieldPosition() {
|
||||
FieldPosition fp = new FieldPosition( 7 );
|
||||
|
||||
if (fp.getField() == 7) {
|
||||
System.out.println("FP constructor(int) and getField tested.");
|
||||
}else{
|
||||
fail("*** FP constructor(int) or getField");
|
||||
}
|
||||
|
||||
FieldPosition fph = new FieldPosition( 3 );
|
||||
if ( fph.getField() != 3) fail("*** FP getField or heap constr.");
|
||||
|
||||
boolean err1 = false;
|
||||
boolean err2 = false;
|
||||
boolean err3 = false;
|
||||
// for (long i = -50; i < 50; i++ ) {
|
||||
// fp.setField( i+8 );
|
||||
// fp.setBeginIndex( i+6 );
|
||||
// fp.setEndIndex( i+7 );
|
||||
// if (fp.getField() != i+8) err1 = true;
|
||||
// if (fp.getBeginIndex() != i+6) err2 = true;
|
||||
// if (fp.getEndIndex() != i+7) err3 = true;
|
||||
// }
|
||||
if (!err1) {
|
||||
System.out.println("FP setField and getField tested.");
|
||||
}else{
|
||||
fail("*** FP setField or getField");
|
||||
}
|
||||
if (!err2) {
|
||||
System.out.println("FP setBeginIndex and getBeginIndex tested.");
|
||||
}else{
|
||||
fail("*** FP setBeginIndex or getBeginIndex");
|
||||
}
|
||||
if (!err3) {
|
||||
System.out.println("FP setEndIndex and getEndIndex tested.");
|
||||
}else{
|
||||
fail("*** FP setEndIndex or getEndIndex");
|
||||
}
|
||||
|
||||
System.out.println("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestFieldPosition_example() {
|
||||
//***** no error detection yet !!!!!!!
|
||||
//***** this test is for compiler checks and visual verification only.
|
||||
double doubleNum[] = { 123456789.0, -12345678.9, 1234567.89, -123456.789,
|
||||
12345.6789, -1234.56789, 123.456789, -12.3456789, 1.23456789};
|
||||
int dNumSize = doubleNum.length;
|
||||
|
||||
DecimalFormat fmt = (DecimalFormat) NumberFormat.getInstance();
|
||||
fmt.setDecimalSeparatorAlwaysShown(true);
|
||||
|
||||
final int tempLen = 20;
|
||||
StringBuffer temp;
|
||||
|
||||
for (int i=0; i<dNumSize; i++) {
|
||||
temp = new StringBuffer(); // Get new buffer
|
||||
|
||||
FieldPosition pos = new FieldPosition(NumberFormat.INTEGER_FIELD);
|
||||
StringBuffer buf = new StringBuffer();
|
||||
//char fmtText[tempLen];
|
||||
//ToCharString(fmt->format(doubleNum[i], buf, pos), fmtText);
|
||||
StringBuffer res = fmt.format(doubleNum[i], buf, pos);
|
||||
int tempOffset = (tempLen <= (tempLen - pos.getEndIndex())) ?
|
||||
tempLen : (tempLen - pos.getEndIndex());
|
||||
for (int j=0; j<tempOffset; j++) temp.append('='); // initialize
|
||||
//cout << temp << fmtText << endl;
|
||||
System.out.println("FP " + temp + res);
|
||||
}
|
||||
|
||||
System.out.println("");
|
||||
}
|
||||
/* @bug 4109023
|
||||
* Need to override ParsePosition.equals and FieldPosition.equals.
|
||||
*/
|
||||
@Test
|
||||
public void Test4109023()
|
||||
{
|
||||
|
||||
ParsePosition p = new ParsePosition(3);
|
||||
ParsePosition p2 = new ParsePosition(3);
|
||||
if (!p.equals(p2))
|
||||
fail("Error : ParsePosition.equals() failed");
|
||||
FieldPosition fp = new FieldPosition(2);
|
||||
FieldPosition fp2 = new FieldPosition(2);
|
||||
if (!fp.equals(fp2))
|
||||
fail("Error : FieldPosition.equals() failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @bug 4153060
|
||||
* ParsePosition.hashCode() returns different values on equal objects.
|
||||
*/
|
||||
@Test
|
||||
public void Test4153060() {
|
||||
ParsePosition p = new ParsePosition(53);
|
||||
ParsePosition q = new ParsePosition(53);
|
||||
if (!p.equals(q)) {
|
||||
fail("" + p + " and " + q + " are not equal and should be");
|
||||
}
|
||||
if (p.hashCode() != q.hashCode()) {
|
||||
fail("ParsePosition.hashCode() different for equal objects");
|
||||
} else {
|
||||
System.out.println("hashCode(" + p + ") = " + p.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @bug 4153061
|
||||
* FieldPosition.hashCode() returns different values on equal objects.
|
||||
*/
|
||||
@Test
|
||||
public void Test4153061() {
|
||||
FieldPosition p = new FieldPosition(53);
|
||||
FieldPosition q = new FieldPosition(53);
|
||||
if (!p.equals(q)) {
|
||||
fail("" + p + " and " + q + " are not equal and should be");
|
||||
}
|
||||
if (p.hashCode() != q.hashCode()) {
|
||||
fail("FieldPosition.hashCode() different for equal objects");
|
||||
} else {
|
||||
System.out.println("hashCode(" + p + ") = " + p.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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
|
||||
* @summary Unit test for the isStrict() and setStrict() parsing related methods
|
||||
* @run junit StrictMethodsTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.text.CompactNumberFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class StrictMethodsTest {
|
||||
|
||||
// Check that DecimalFormat implements isStrict()/setStrict()
|
||||
// Ensure that the default value is false, and can be set to true via API
|
||||
@Test
|
||||
public void decimalFormatTest() {
|
||||
DecimalFormat dFmt = (DecimalFormat) NumberFormat.getInstance();
|
||||
assertFalse(dFmt.isStrict());
|
||||
dFmt.setStrict(true);
|
||||
assertTrue(dFmt.isStrict());
|
||||
}
|
||||
|
||||
// Check that CompactNumberFormat implements isStrict()/setStrict()
|
||||
// Ensure that the default value is false, and can be set to true via API
|
||||
@Test
|
||||
public void compactFormatTest() {
|
||||
CompactNumberFormat cFmt = (CompactNumberFormat) NumberFormat.getCompactNumberInstance();
|
||||
assertFalse(cFmt.isStrict());
|
||||
cFmt.setStrict(true);
|
||||
assertTrue(cFmt.isStrict());
|
||||
}
|
||||
|
||||
// Check that NumberFormat throws exception for isStrict()/setStrict()
|
||||
// when subclass does not implement said methods
|
||||
@Test
|
||||
public void numberFormatTest() {
|
||||
FooFormat fmt = new FooFormat();
|
||||
assertThrows(UnsupportedOperationException.class, fmt::isStrict);
|
||||
assertThrows(UnsupportedOperationException.class, () -> fmt.setStrict(false));
|
||||
}
|
||||
|
||||
// Dummy NumberFormat class to check that isStrict() and setStrict()
|
||||
// are not implemented by default
|
||||
private static class FooFormat extends NumberFormat {
|
||||
|
||||
// Provide overrides for abstract methods
|
||||
@Override
|
||||
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number parse(String source, ParsePosition parsePosition) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
618
test/jdk/java/text/Format/NumberFormat/StrictParseTest.java
Normal file
618
test/jdk/java/text/Format/NumberFormat/StrictParseTest.java
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* 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 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
|
||||
* @run junit/othervm -Duser.language=zh -Duser.country=CN StrictParseTest
|
||||
* @run junit/othervm -Duser.language=tr -Duser.country=TR StrictParseTest
|
||||
* @run junit/othervm -Duser.language=de -Duser.country=DE StrictParseTest
|
||||
* @run junit/othervm -Duser.language=fr -Duser.country=FR StrictParseTest
|
||||
* @run junit/othervm -Duser.language=ar -Duser.country=AR StrictParseTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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 strict 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.
|
||||
public class StrictParseTest {
|
||||
|
||||
// Used to retrieve the locale's expected symbols
|
||||
private static final DecimalFormatSymbols dfs =
|
||||
new DecimalFormatSymbols(Locale.getDefault());
|
||||
// We re-use these formats for the respective factory tests
|
||||
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);
|
||||
private static final NumberFormat[] FORMATS = new NumberFormat[]{dFmt, cFmt, pFmt, cmpctFmt};
|
||||
|
||||
// Restore defaults before runs
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
for (NumberFormat fmt : FORMATS) {
|
||||
fmt.setStrict(true);
|
||||
fmt.setParseIntegerOnly(false);
|
||||
fmt.setGroupingUsed(true);
|
||||
}
|
||||
// Grouping Size is not defined at NumberFormat level
|
||||
// Compact needs to manually init grouping size
|
||||
cmpctFmt.setGroupingSize(3);
|
||||
}
|
||||
|
||||
// ---- NumberFormat tests ----
|
||||
|
||||
// Guarantee some edge case test input
|
||||
@Test // Non-localized, run once
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void uniqueCaseNumberFormatTest() {
|
||||
// Format with grouping size = 3, prefix = a, suffix = b
|
||||
DecimalFormat nonLocalizedDFmt = new DecimalFormat("a#,#00.00b");
|
||||
nonLocalizedDFmt.setStrict(true);
|
||||
// Text after suffix
|
||||
failParse(nonLocalizedDFmt, "a12bfoo", 3);
|
||||
failParse(nonLocalizedDFmt, "a123,456.00bc", 11);
|
||||
// Text after prefix
|
||||
failParse(nonLocalizedDFmt, "ac123", 0);
|
||||
// Missing suffix
|
||||
failParse(nonLocalizedDFmt, "a123", 4);
|
||||
// Prefix contains a decimal separator
|
||||
failParse(nonLocalizedDFmt, ".a123", 0);
|
||||
// Test non grouping size of 3
|
||||
nonLocalizedDFmt.setGroupingSize(1);
|
||||
successParse(nonLocalizedDFmt, "a1,2,3,4b");
|
||||
failParse(nonLocalizedDFmt, "a1,2,3,45,6b", 8);
|
||||
nonLocalizedDFmt.setGroupingSize(5);
|
||||
successParse(nonLocalizedDFmt, "a12345,67890b");
|
||||
successParse(nonLocalizedDFmt, "a1234,67890b");
|
||||
failParse(nonLocalizedDFmt, "a123456,7890b", 6);
|
||||
}
|
||||
|
||||
|
||||
// 8333755: Check that parsing with integer only against a suffix value works
|
||||
@Test // Non-localized, run once
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void integerOnlyParseWithSuffixTest() {
|
||||
// Some pattern with a suffix
|
||||
DecimalFormat fmt = new DecimalFormat("0.00b");
|
||||
fmt.setParseIntegerOnly(true);
|
||||
assertEquals(5d, successParse(fmt, "5.55b", 1));
|
||||
assertEquals(5d, successParse(fmt, "5b", 2));
|
||||
assertEquals(5555d, successParse(fmt, "5,555.55b", 5));
|
||||
assertEquals(5d, successParse(fmt, "5.55E55b", 1));
|
||||
}
|
||||
|
||||
@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);
|
||||
fmt.setStrict(true);
|
||||
// Upon non-numeric in exponent, parse will exit early and suffix will not
|
||||
// exactly match, causing failure
|
||||
failParse(fmt, "1.23E45.1", 7);
|
||||
failParse(fmt, "1.23E45.", 7);
|
||||
failParse(fmt, "1.23E45FOO3222", 7);
|
||||
}
|
||||
|
||||
// All input Strings should fail
|
||||
@ParameterizedTest
|
||||
@MethodSource("badParseStrings")
|
||||
public void numFmtFailParseTest(String toParse, int expectedErrorIndex) {
|
||||
failParse(dFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
// All input Strings should pass and return expected value.
|
||||
@ParameterizedTest
|
||||
@MethodSource("validParseStrings")
|
||||
public void numFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(dFmt, toParse));
|
||||
}
|
||||
|
||||
// All input Strings should fail
|
||||
@ParameterizedTest
|
||||
@MethodSource("negativeBadParseStrings")
|
||||
public void negNumFmtFailParseTest(String toParse, int expectedErrorIndex) {
|
||||
failParse(dFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
// All input Strings should pass and return expected value.
|
||||
@ParameterizedTest
|
||||
@MethodSource("negativeValidParseStrings")
|
||||
public void negNumFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(dFmt, toParse));
|
||||
}
|
||||
|
||||
// Exception should be thrown if grouping separator occurs anywhere
|
||||
// Don't pass badParseStrings as a data source, since they may fail for other reasons
|
||||
@ParameterizedTest
|
||||
@MethodSource({"validParseStrings", "noGroupingParseStrings"})
|
||||
public void numFmtStrictGroupingNotUsed(String toParse) {
|
||||
// When grouping is not used, if a grouping separator is found,
|
||||
// a failure should occur
|
||||
dFmt.setGroupingUsed(false);
|
||||
int failIndex = toParse.indexOf(
|
||||
dFmt.getDecimalFormatSymbols().getGroupingSeparator());
|
||||
if (failIndex > -1) {
|
||||
failParse(dFmt, toParse, failIndex);
|
||||
} else {
|
||||
successParse(dFmt, toParse);
|
||||
}
|
||||
}
|
||||
|
||||
// 8333755: Parsing behavior should follow normal strict behavior
|
||||
// However the index returned, should be before decimal point
|
||||
// and the value parsed equal to the integer portion
|
||||
@ParameterizedTest
|
||||
@MethodSource("validIntegerOnlyParseStrings")
|
||||
public void numFmtStrictIntegerOnlyUsedTest(String toParse, Number expVal) {
|
||||
dFmt.setParseIntegerOnly(true);
|
||||
int expectedIndex = toParse.indexOf(dfs.getDecimalSeparator());
|
||||
if (expectedIndex > -1) {
|
||||
assertEquals(successParse(dFmt, toParse, expectedIndex), expVal);
|
||||
} else {
|
||||
assertEquals(successParse(dFmt, toParse), expVal);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@MethodSource("badParseStrings")
|
||||
public void numFmtStrictIntegerOnlyUsedFailTest(String toParse, int expectedErrorIndex) {
|
||||
dFmt.setParseIntegerOnly(true);
|
||||
failParse(dFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
// ---- CurrencyFormat tests ----
|
||||
@ParameterizedTest
|
||||
@MethodSource("currencyBadParseStrings")
|
||||
public void currFmtFailParseTest(String toParse, int expectedErrorIndex) {
|
||||
failParse(cFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("currencyValidParseStrings")
|
||||
public void currFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(cFmt, toParse));
|
||||
}
|
||||
|
||||
// ---- PercentFormat tests ----
|
||||
@ParameterizedTest
|
||||
@MethodSource("percentBadParseStrings")
|
||||
public void percentFmtFailParseTest(String toParse, int expectedErrorIndex) {
|
||||
failParse(pFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("percentValidParseStrings")
|
||||
public void percentFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(pFmt, toParse));
|
||||
}
|
||||
|
||||
// ---- CompactNumberFormat tests ----
|
||||
// Can match to both the decimalFormat patterns and the compact patterns
|
||||
// Thus we test leniency for both. 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("compactBadParseStrings")
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtFailParseTest(String toParse, int expectedErrorIndex) {
|
||||
failParse(cmpctFmt, toParse, expectedErrorIndex);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("compactValidParseStrings")
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtSuccessParseTest(String toParse, double expectedValue) {
|
||||
assertEquals(expectedValue, successParse(cmpctFmt, toParse));
|
||||
}
|
||||
|
||||
// Checks some odd leniency edge cases between matching of default pattern
|
||||
// and compact pattern.
|
||||
@Test // Non-localized, run once
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtEdgeParseTest() {
|
||||
// Uses a compact format with unique and non-empty prefix/suffix for both
|
||||
// default and compact patterns
|
||||
CompactNumberFormat cnf = new CompactNumberFormat("a##0.0#b", DecimalFormatSymbols
|
||||
.getInstance(Locale.US), new String[]{"", "c0d"});
|
||||
cnf.setStrict(true);
|
||||
|
||||
// Existing behavior of failed prefix parsing has errorIndex return
|
||||
// the beginning of prefix, even if the error occurred later in the prefix.
|
||||
// Prefix empty
|
||||
failParse(cnf, "12345d", 0);
|
||||
failParse(cnf, "1b", 0);
|
||||
// Prefix bad
|
||||
failParse(cnf, "aa1d", 0);
|
||||
failParse(cnf, "cc1d", 0);
|
||||
failParse(cnf, "aa1b", 0);
|
||||
failParse(cnf, "cc1b", 0);
|
||||
|
||||
// Suffix error index is always the start of the failed suffix
|
||||
// not necessarily where the error occurred in the suffix. This is
|
||||
// consistent with the prefix error index behavior.
|
||||
// Suffix empty
|
||||
failParse(cnf, "a1", 2);
|
||||
failParse(cnf, "c1", 2);
|
||||
// Suffix bad
|
||||
failParse(cnf, "a1dd", 2);
|
||||
failParse(cnf, "c1dd", 2);
|
||||
failParse(cnf, "a1bb", 2);
|
||||
failParse(cnf, "c1bb", 2);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource({"validIntegerOnlyParseStrings", "compactValidIntegerOnlyParseStrings"})
|
||||
@EnabledIfSystemProperty(named = "user.language", matches = "en")
|
||||
public void compactFmtSuccessParseIntOnlyTest(String toParse, double expectedValue) {
|
||||
// compact does not accept exponents
|
||||
if (toParse.indexOf('E') > -1) {
|
||||
return;
|
||||
}
|
||||
cmpctFmt.setParseIntegerOnly(true);
|
||||
assertEquals(expectedValue, successParse(cmpctFmt, toParse, toParse.length()));
|
||||
}
|
||||
|
||||
// Ensure that on failure, the original index of the PP remains the same
|
||||
@Test
|
||||
public void parsePositionIndexTest() {
|
||||
failParse(dFmt, localizeText("123,456,,789.00"), 8, 4);
|
||||
}
|
||||
|
||||
// ---- Helper test methods ----
|
||||
|
||||
// Should parse entire String successfully, and return correctly parsed value.
|
||||
private Number successParse(NumberFormat fmt, String toParse) {
|
||||
return successParse(fmt, toParse, toParse.length());
|
||||
}
|
||||
|
||||
// Overloaded method that allows for an expected ParsePosition index value
|
||||
// that is not the string length.
|
||||
private Number successParse(NumberFormat fmt, String toParse, int expectedIndex) {
|
||||
// For Strings that don't have grouping separators, we test them with
|
||||
// grouping off so that they do not fail under the expectation that
|
||||
// grouping symbols should occur
|
||||
if (!toParse.contains(String.valueOf(dfs.getGroupingSeparator())) &&
|
||||
!toParse.contains(String.valueOf(dfs.getMonetaryGroupingSeparator()))) {
|
||||
fmt.setGroupingUsed(false);
|
||||
}
|
||||
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");
|
||||
fmt.setGroupingUsed(true);
|
||||
return parsedValue.doubleValue();
|
||||
}
|
||||
|
||||
// Method which tests a parsing failure. Either a ParseException is thrown,
|
||||
// or null is returned depending on which parse method is invoked. When failing,
|
||||
// index should remain the initial index set to the ParsePosition while
|
||||
// errorIndex is the index of failure.
|
||||
private void failParse(NumberFormat fmt, String toParse, int expectedErrorIndex) {
|
||||
failParse(fmt, toParse, expectedErrorIndex, 0);
|
||||
}
|
||||
|
||||
// Variant to check non 0 initial parse index
|
||||
private void failParse(NumberFormat fmt, String toParse,
|
||||
int expectedErrorIndex, int initialParseIndex) {
|
||||
ParsePosition pp = new ParsePosition(initialParseIndex);
|
||||
assertThrows(ParseException.class, () -> fmt.parse(toParse));
|
||||
assertNull(fmt.parse(toParse, pp));
|
||||
assertEquals(expectedErrorIndex, pp.getErrorIndex());
|
||||
assertEquals(initialParseIndex, pp.getIndex());
|
||||
}
|
||||
|
||||
// ---- Data Providers ----
|
||||
// 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 fail when parsed with strict leniency.
|
||||
// Given as Arguments<String, expectedErrorIndex>
|
||||
private static Stream<Arguments> badParseStrings() {
|
||||
return Stream.of(
|
||||
// Grouping symbol focus
|
||||
// Grouping symbol right before decimal
|
||||
Arguments.of("1,.", 2),
|
||||
Arguments.of("1,.1", 2),
|
||||
// Does not end with proper grouping size
|
||||
Arguments.of("1,1", 2),
|
||||
Arguments.of("1,11", 3),
|
||||
Arguments.of("1,1111", 5),
|
||||
Arguments.of("11,111,11", 8),
|
||||
// Does not end with proper grouping size (with decimal)
|
||||
Arguments.of("1,1.", 3),
|
||||
Arguments.of("1,11.", 4),
|
||||
Arguments.of("1,1111.", 5),
|
||||
Arguments.of("11,111,11.", 9),
|
||||
// Ends on a grouping symbol
|
||||
// Suffix matches correctly, so failure is on the ","
|
||||
Arguments.of("11,111,", 6),
|
||||
Arguments.of("11,", 2),
|
||||
Arguments.of("11,,", 3),
|
||||
// Ends with grouping symbol. Failure should occur on grouping,
|
||||
// even if non recognized char after
|
||||
Arguments.of("11,a", 2),
|
||||
// Improper grouping size (with decimal and digits after)
|
||||
Arguments.of("1,1.1", 3),
|
||||
Arguments.of("1,11.1", 4),
|
||||
Arguments.of("1,1111.1", 5),
|
||||
Arguments.of("11,111,11.1", 9),
|
||||
// Subsequent grouping symbols
|
||||
Arguments.of("1,,1", 2),
|
||||
Arguments.of("1,1,,1", 3),
|
||||
Arguments.of("1,,1,1", 2),
|
||||
// Invalid grouping sizes
|
||||
Arguments.of("1,11,111", 4),
|
||||
Arguments.of("11,11,111", 5),
|
||||
Arguments.of("111,11,11", 6),
|
||||
// First group is too large
|
||||
Arguments.of("1111,11,111", 3),
|
||||
Arguments.of("00000,11,111", 3),
|
||||
Arguments.of("111,1111111111", 7),
|
||||
Arguments.of("111,11", 5),
|
||||
Arguments.of("111,1111111111.", 7),
|
||||
Arguments.of("111,11.", 6),
|
||||
Arguments.of("111,1111111111.", 7),
|
||||
// Starts with grouping symbol
|
||||
Arguments.of(",111,,1,1", 0),
|
||||
Arguments.of(",1", 0),
|
||||
Arguments.of(",,1", 0),
|
||||
// Leading Zeros (not digits)
|
||||
Arguments.of("000,1,1", 5),
|
||||
Arguments.of("000,111,11,,1", 10),
|
||||
Arguments.of("0,000,1,,1,1", 7),
|
||||
// Bad suffix
|
||||
Arguments.of("1a", 1),
|
||||
// Bad chars in numerical portion
|
||||
Arguments.of("123a4", 3),
|
||||
Arguments.of("123.4a5", 5),
|
||||
// Variety of edge cases
|
||||
Arguments.of("123,456.77a", 10),
|
||||
Arguments.of("1,234a", 5),
|
||||
Arguments.of("1,.a", 2),
|
||||
Arguments.of("1.a", 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))
|
||||
.map(args -> Arguments.of(
|
||||
localizeText(String.valueOf(args.get()[0])), args.get()[1]));
|
||||
}
|
||||
|
||||
// Strings that should parse fully. (Both in lenient and strict)
|
||||
// Given as Arguments<String, expectedParsedNumber>
|
||||
private static Stream<Arguments> validParseStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of("1,234.55", 1234.55d),
|
||||
Arguments.of("1,234.5", 1234.5d),
|
||||
Arguments.of("1,234.00", 1234d),
|
||||
Arguments.of("1,234.0", 1234d),
|
||||
Arguments.of("1,234.", 1234d),
|
||||
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]));
|
||||
}
|
||||
|
||||
// Separate test data set for integer only.
|
||||
// Valid parse strings, that would parse successfully for integer/non-integer parse
|
||||
private static Stream<Arguments> validIntegerOnlyParseStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of("234", 234d),
|
||||
Arguments.of("234.", 234d),
|
||||
Arguments.of("234.1", 234d),
|
||||
Arguments.of("1,234.1", 1234d),
|
||||
Arguments.of("234.12345", 234d),
|
||||
Arguments.of("234.543E23", 234d),
|
||||
Arguments.of("234,000.55E22", 234000d),
|
||||
Arguments.of("234E22", 234E22))
|
||||
.map(args -> Arguments.of(localizeText(String.valueOf(args.get()[0])), args.get()[1]));
|
||||
}
|
||||
|
||||
// Separate test data set for no grouping. Can not use "badParseStrings", as
|
||||
// there is test data where the failure may occur from some other issue,
|
||||
// not related to grouping
|
||||
private static Stream<Arguments> noGroupingParseStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of("12,34.a"),
|
||||
Arguments.of("123,.a1"),
|
||||
Arguments.of(",1234"),
|
||||
Arguments.of("123,"))
|
||||
.map(args -> Arguments.of(localizeText(String.valueOf(args.get()[0]))));
|
||||
}
|
||||
|
||||
// Negative variant of a numerical format
|
||||
private static Stream<Arguments> negativeBadParseStrings() {
|
||||
return badParseStrings().map(args -> Arguments.of(
|
||||
dFmt.getNegativePrefix() + args.get()[0] + dFmt.getNegativeSuffix(),
|
||||
(int)args.get()[1] + dFmt.getNegativePrefix().length())
|
||||
);
|
||||
}
|
||||
|
||||
// Negative variant of a numerical format
|
||||
private static Stream<Arguments> negativeValidParseStrings() {
|
||||
return validParseStrings().map(args -> Arguments.of(
|
||||
dFmt.getNegativePrefix() + args.get()[0] + dFmt.getNegativeSuffix(),
|
||||
(double) args.get()[1] * -1)
|
||||
);
|
||||
}
|
||||
|
||||
// Same as original with a percent prefix/suffix.
|
||||
// Additionally, increment expected error index if a prefix is added
|
||||
private static Stream<Arguments> percentBadParseStrings() {
|
||||
return badParseStrings().map(args -> Arguments.of(
|
||||
pFmt.getPositivePrefix() + args.get()[0] + pFmt.getPositiveSuffix(),
|
||||
(int)args.get()[1] + pFmt.getPositivePrefix().length())
|
||||
);
|
||||
}
|
||||
|
||||
// Expected parsed value should be / 100 as it is a percent format.
|
||||
private static Stream<Arguments> percentValidParseStrings() {
|
||||
return validParseStrings().map(args -> Arguments.of(
|
||||
pFmt.getPositivePrefix() + args.get()[0] + pFmt.getPositiveSuffix(),
|
||||
(double)args.get()[1] / 100.0)
|
||||
);
|
||||
}
|
||||
|
||||
// Same as original with a currency prefix/suffix, but replace separators
|
||||
// with monetary variants. Additionally, increment expected error index
|
||||
// if a prefix is added
|
||||
private static Stream<Arguments> currencyBadParseStrings() {
|
||||
return badParseStrings().map(args -> Arguments.of(
|
||||
cFmt.getPositivePrefix() + String.valueOf(args.get()[0])
|
||||
.replace(dfs.getGroupingSeparator(), dfs.getMonetaryGroupingSeparator())
|
||||
.replace(dfs.getDecimalSeparator(), dfs.getMonetaryDecimalSeparator())
|
||||
+ cFmt.getPositiveSuffix(),
|
||||
(int)args.get()[1] + cFmt.getPositivePrefix().length())
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> currencyValidParseStrings() {
|
||||
return validParseStrings().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> compactBadParseStrings() {
|
||||
return Stream.concat(
|
||||
badParseStrings().map(args -> Arguments.of(args.get()[0], args.get()[1])),
|
||||
badParseStrings().map(args -> Arguments.of(args.get()[0] + "K", args.get()[1]))
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> compactValidParseStrings() {
|
||||
return Stream.concat(
|
||||
validParseStrings().map(args -> Arguments.of(
|
||||
args.get()[0], args.get()[1])),
|
||||
validParseStrings().map(args -> Arguments.of(
|
||||
args.get()[0] + "K", (double) args.get()[1] * 1000))
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> compactValidIntegerOnlyParseStrings() {
|
||||
return validIntegerOnlyParseStrings().map(args -> Arguments.of(
|
||||
args.get()[0] + "K", (double) args.get()[1] * 1000)
|
||||
);
|
||||
}
|
||||
|
||||
// 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 == 'E') {
|
||||
sb.append(dfs.getExponentSeparator());
|
||||
}
|
||||
else if (c == '0') {
|
||||
sb.append(dfs.getZeroDigit());
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 1999, 2016, 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 4201262
|
||||
* @summary Make sure that DecimalFormatSymbols.getPatternSeparator returns ';' in ja locale.
|
||||
*/
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
public class TestgetPatternSeparator_ja {
|
||||
|
||||
public static void main(String[] argv) throws Exception {
|
||||
DecimalFormat df = (DecimalFormat)NumberFormat.getInstance(Locale.JAPAN);
|
||||
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
|
||||
if (dfs.getPatternSeparator() != ';') {
|
||||
throw new Exception("DecimalFormatSymbols.getPatternSeparator doesn't return ';' in ja locale");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue