/* * 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 bigIntegers = new ArrayList(); private static final ArrayList bigDecimals = new ArrayList(); // 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 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 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; } }