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:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2018, 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.
*/
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CompactFormatAndParseHelper {
static void testFormat(NumberFormat cnf, Object number,
String expected) {
String result = cnf.format(number);
assertEquals(expected, result, "Incorrect formatting of the number '"
+ number + "'");
}
static void testParse(NumberFormat cnf, String parseString,
Number expected, ParsePosition position, Class<? extends Number> returnType) throws ParseException {
Number number;
if (position == null) {
number = cnf.parse(parseString);
} else {
number = cnf.parse(parseString, position);
}
if (returnType != null) {
assertEquals(returnType, number.getClass(),
"Incorrect return type for string '" + parseString + "'");
}
assertEquals(expected, number, "Incorrect parsing of the string '"
+ parseString + "'");
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2019, 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 8222756
* @summary Checks the plurals work with SPI provider
* @modules jdk.localedata
* @library provider
* @build provider/module-info provider/test.NumberFormatProviderImpl
* @run main/othervm -Djava.locale.providers=SPI,CLDR SPIProviderTest
*/
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Locale;
public class SPIProviderTest {
private static final Locale QAA = Locale.forLanguageTag("qaa");
private static final Locale QAB = Locale.forLanguageTag("qab");
public static void main(String... args) {
new SPIProviderTest();
}
SPIProviderTest() {
Arrays.stream(testData())
.forEach(SPIProviderTest::testSPIProvider);
}
Object[][] testData() {
return new Object[][]{
// Locale, Number, expected
{QAA, 1_000, "1K"},
{QAA, -1_000, "-1K"},
{QAA, 2_000, "2K"},
{QAA, -2_000, "-2K"},
{QAA, 1_000_000, "1M"},
{QAA, -1_000_000, "-1M"},
{QAA, 2_000_000, "2M"},
{QAA, -2_000_000, "-2M"},
{QAB, 1_000, "1K"},
{QAB, -1_000, "(1K)"},
{QAB, 2_000, "2KK"},
{QAB, -2_000, "-2KK"},
{QAB, 3_000, "3KKK"},
{QAB, -3_000, "-3KKK"},
{QAB, 5_000, "5KKKK"},
{QAB, -5_000, "-5KKKK"},
{QAB, 10_000, "10000"},
{QAB, -10_000, "-10000"},
{QAB, 1_000_000, "1 M"},
{QAB, -1_000_000, "(1 M)"},
{QAB, 2_000_000, "2 MM"},
{QAB, -2_000_000, "(2 MM)"},
{QAB, 3_000_000, "3 MMM"},
{QAB, -3_000_000, "-3 MMM"},
{QAB, 5_000_000, "5 MMMM"},
{QAB, -5_000_000, "-5 MMMM"},
};
}
public static void testSPIProvider(Object... args) {
Locale loc = (Locale)args[0];
Number number = (Number)args[1];
String expected = (String)args[2];
System.out.printf("Testing locale: %s, number: %d, expected: %s\n", loc, number, expected);
NumberFormat nf =
NumberFormat.getCompactNumberInstance(loc, NumberFormat.Style.SHORT);
String formatted = nf.format(number);
System.out.printf(" formatted: %s\n", formatted);
if (!formatted.equals(expected)) {
throw new RuntimeException("formatted and expected strings do not match.");
}
try {
Number parsed = nf.parse(formatted);
System.out.printf(" parsed: %s\n", parsed);
if (parsed.intValue() != number.intValue()) {
throw new RuntimeException("parsed and input numbers do not match.");
}
} catch (ParseException pe) {
throw new RuntimeException(pe);
}
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2018, 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 8177552
* @summary Checks the rounding of formatted number in compact number formatting
* @run junit/othervm TestCNFRounding
*/
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.math.RoundingMode;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestCNFRounding {
private static final List<RoundingMode> MODES = List.of(
RoundingMode.HALF_EVEN,
RoundingMode.HALF_UP,
RoundingMode.HALF_DOWN,
RoundingMode.UP,
RoundingMode.DOWN,
RoundingMode.CEILING,
RoundingMode.FLOOR);
Object[][] roundingData() {
return new Object[][]{
// Number, half_even, half_up, half_down, up, down, ceiling, floor
{5500, new String[]{"6K", "6K", "5K", "6K", "5K", "6K", "5K"}},
{2500, new String[]{"2K", "3K", "2K", "3K", "2K", "3K", "2K"}},
{1600, new String[]{"2K", "2K", "2K", "2K", "1K", "2K", "1K"}},
{1100, new String[]{"1K", "1K", "1K", "2K", "1K", "2K", "1K"}},
{1000, new String[]{"1K", "1K", "1K", "1K", "1K", "1K", "1K"}},
{-1000, new String[]{"-1K", "-1K", "-1K", "-1K", "-1K", "-1K", "-1K"}},
{-1100, new String[]{"-1K", "-1K", "-1K", "-2K", "-1K", "-1K", "-2K"}},
{-1600, new String[]{"-2K", "-2K", "-2K", "-2K", "-1K", "-1K", "-2K"}},
{-2500, new String[]{"-2K", "-3K", "-2K", "-3K", "-2K", "-2K", "-3K"}},
{-5500, new String[]{"-6K", "-6K", "-5K", "-6K", "-5K", "-5K", "-6K"}},
{5501, new String[]{"6K", "6K", "6K", "6K", "5K", "6K", "5K"}},
{-5501, new String[]{"-6K", "-6K", "-6K", "-6K", "-5K", "-5K", "-6K"}},
{1001, new String[]{"1K", "1K", "1K", "2K", "1K", "2K", "1K"}},
{-1001, new String[]{"-1K", "-1K", "-1K", "-2K", "-1K", "-1K", "-2K"}},
{4501, new String[]{"5K", "5K", "5K", "5K", "4K", "5K", "4K"}},
{-4501, new String[]{"-5K", "-5K", "-5K", "-5K", "-4K", "-4K", "-5K"}},
{4500, new String[]{"4K", "5K", "4K", "5K", "4K", "5K", "4K"}},
{-4500, new String[]{"-4K", "-5K", "-4K", "-5K", "-4K", "-4K", "-5K"}},};
}
Object[][] roundingFract() {
return new Object[][]{
// Number, half_even, half_up, half_down, up, down, ceiling, floor
{5550, new String[]{"5.5K", "5.5K", "5.5K", "5.6K", "5.5K", "5.6K", "5.5K"}},
{2550, new String[]{"2.5K", "2.5K", "2.5K", "2.6K", "2.5K", "2.6K", "2.5K"}},
{1660, new String[]{"1.7K", "1.7K", "1.7K", "1.7K", "1.6K", "1.7K", "1.6K"}},
{1110, new String[]{"1.1K", "1.1K", "1.1K", "1.2K", "1.1K", "1.2K", "1.1K"}},
{1000, new String[]{"1.0K", "1.0K", "1.0K", "1.0K", "1.0K", "1.0K", "1.0K"}},
{-1000, new String[]{"-1.0K", "-1.0K", "-1.0K", "-1.0K", "-1.0K", "-1.0K", "-1.0K"}},
{-1110, new String[]{"-1.1K", "-1.1K", "-1.1K", "-1.2K", "-1.1K", "-1.1K", "-1.2K"}},
{-1660, new String[]{"-1.7K", "-1.7K", "-1.7K", "-1.7K", "-1.6K", "-1.6K", "-1.7K"}},
{-2550, new String[]{"-2.5K", "-2.5K", "-2.5K", "-2.6K", "-2.5K", "-2.5K", "-2.6K"}},
{-5550, new String[]{"-5.5K", "-5.5K", "-5.5K", "-5.6K", "-5.5K", "-5.5K", "-5.6K"}},
{5551, new String[]{"5.6K", "5.6K", "5.6K", "5.6K", "5.5K", "5.6K", "5.5K"}},
{-5551, new String[]{"-5.6K", "-5.6K", "-5.6K", "-5.6K", "-5.5K", "-5.5K", "-5.6K"}},
{1001, new String[]{"1.0K", "1.0K", "1.0K", "1.1K", "1.0K", "1.1K", "1.0K"}},
{-1001, new String[]{"-1.0K", "-1.0K", "-1.0K", "-1.1K", "-1.0K", "-1.0K", "-1.1K"}},
{4551, new String[]{"4.6K", "4.6K", "4.6K", "4.6K", "4.5K", "4.6K", "4.5K"}},
{-4551, new String[]{"-4.6K", "-4.6K", "-4.6K", "-4.6K", "-4.5K", "-4.5K", "-4.6K"}},
{4500, new String[]{"4.5K", "4.5K", "4.5K", "4.5K", "4.5K", "4.5K", "4.5K"}},
{-4500, new String[]{"-4.5K", "-4.5K", "-4.5K", "-4.5K", "-4.5K", "-4.5K", "-4.5K"}},};
}
Object[][] rounding2Fract() {
return new Object[][]{
// Number, half_even, half_up, half_down
{1115, new String[]{"1.11K", "1.11K", "1.11K"}},
{1125, new String[]{"1.12K", "1.13K", "1.12K"}},
{1135, new String[]{"1.14K", "1.14K", "1.14K"}},
{3115, new String[]{"3.12K", "3.12K", "3.12K"}},
{3125, new String[]{"3.12K", "3.13K", "3.12K"}},
{3135, new String[]{"3.13K", "3.13K", "3.13K"}},
{6865, new String[]{"6.87K", "6.87K", "6.87K"}},
{6875, new String[]{"6.88K", "6.88K", "6.87K"}},
{6885, new String[]{"6.88K", "6.88K", "6.88K"}},
{3124, new String[]{"3.12K", "3.12K", "3.12K"}},
{3126, new String[]{"3.13K", "3.13K", "3.13K"}},
{3128, new String[]{"3.13K", "3.13K", "3.13K"}},
{6864, new String[]{"6.86K", "6.86K", "6.86K"}},
{6865, new String[]{"6.87K", "6.87K", "6.87K"}},
{6868, new String[]{"6.87K", "6.87K", "6.87K"}},
{4685, new String[]{"4.68K", "4.68K", "4.68K"}},
{4687, new String[]{"4.69K", "4.69K", "4.69K"}},
{4686, new String[]{"4.69K", "4.69K", "4.69K"}},};
}
@Test
void testNullMode() {
assertThrows(NullPointerException.class, () -> {
NumberFormat fmt = NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
fmt.setRoundingMode(null);
});
}
@Test
void testDefaultRoundingMode() {
NumberFormat fmt = NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
assertEquals(RoundingMode.HALF_EVEN, fmt.getRoundingMode(),
"Default RoundingMode should be " + RoundingMode.HALF_EVEN);
}
@ParameterizedTest
@MethodSource("roundingData")
void testRounding(Object number, String[] expected) {
for (int index = 0; index < MODES.size(); index++) {
testRoundingMode(number, expected[index], 0, MODES.get(index));
}
}
@ParameterizedTest
@MethodSource("roundingFract")
void testRoundingFract(Object number, String[] expected) {
for (int index = 0; index < MODES.size(); index++) {
testRoundingMode(number, expected[index], 1, MODES.get(index));
}
}
@ParameterizedTest
@MethodSource("rounding2Fract")
void testRounding2Fract(Object number, String[] expected) {
List<RoundingMode> rModes = List.of(RoundingMode.HALF_EVEN,
RoundingMode.HALF_UP, RoundingMode.HALF_DOWN);
for (int index = 0; index < rModes.size(); index++) {
testRoundingMode(number, expected[index], 2, rModes.get(index));
}
}
private void testRoundingMode(Object number, String expected,
int fraction, RoundingMode rounding) {
NumberFormat fmt = NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
fmt.setRoundingMode(rounding);
assertEquals(rounding, fmt.getRoundingMode(),
"RoundingMode set is not returned by getRoundingMode");
fmt.setMinimumFractionDigits(fraction);
String result = fmt.format(number);
assertEquals(expected, result, "Incorrect formatting of number "
+ number + " using rounding mode: " + rounding);
}
}

View file

@ -0,0 +1,95 @@
/*
* 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 8368328
* @summary Tests if CompactNumberFormat.clone() creates an independent object
* @run junit/othervm --add-opens java.base/java.text=ALL-UNNAMED TestClone
*/
import java.lang.invoke.MethodHandles;
import java.text.CompactNumberFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
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.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
public class TestClone {
// Concurrently parse numbers using cloned instances as originally
// reported in the bug. This test could produce false negative results,
// depending on the testing environment
@Test
void randomAccessTest() {
var original =
NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
var threads = IntStream.range(0, 10)
.mapToObj(num -> new Thread(() -> {
var clone = (NumberFormat) original.clone();
for (int i = 0; i < 1000; i++) {
assertDoesNotThrow(() ->
assertEquals(num, clone.parse(String.valueOf(num)).intValue()));
}
})).toList();
threads.forEach(Thread::start);
threads.forEach(t -> {
try {
t.join();
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
});
}
private static Stream<Arguments> referenceFields() throws ClassNotFoundException {
return Stream.of(
Arguments.of("compactPatterns", String[].class),
Arguments.of("symbols", DecimalFormatSymbols.class),
Arguments.of("decimalFormat", DecimalFormat.class),
Arguments.of("defaultDecimalFormat", DecimalFormat.class),
Arguments.of("digitList", Class.forName("java.text.DigitList"))
);
}
// Explicitly checks if the cloned object has its own references for
// "compactPatterns", "symbols", "decimalFormat", "defaultDecimalFormat",
// and "digitList"
@ParameterizedTest
@MethodSource("referenceFields")
void whiteBoxTest(String fieldName, Class<?> type) throws Throwable {
var original = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
var clone = original.clone();
var lookup = MethodHandles.privateLookupIn(CompactNumberFormat.class, MethodHandles.lookup());
assertNotSame(lookup.findGetter(CompactNumberFormat.class, fieldName, type).invoke(original),
lookup.findGetter(CompactNumberFormat.class, fieldName, type).invoke(clone));
}
}

View file

@ -0,0 +1,700 @@
/*
* Copyright (c) 2018, 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 8177552 8217721 8222756 8295372 8306116 8319990 8338690 8363972
* 8354548
* @summary Checks the functioning of compact number format
* @modules jdk.localedata
* @run junit/othervm TestCompactNumber
*/
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.math.BigDecimal;
import java.math.BigInteger;
import java.text.FieldPosition;
import java.text.Format;
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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestCompactNumber {
private static final NumberFormat FORMAT_DZ_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("dz"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_EN_US_SHORT = NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_EN_LONG = NumberFormat
.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_HI_IN_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("hi", "IN"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_JA_JP_SHORT = NumberFormat
.getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_IT_SHORT = NumberFormat
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_IT_LONG = NumberFormat
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_CA_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("ca"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_AS_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("as"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_BRX_SHORT = NumberFormat
.getCompactNumberInstance(Locale.of("brx"), NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_SW_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("sw"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_SE_SHORT = NumberFormat
.getCompactNumberInstance(Locale.of("se"), NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_DE_LONG = NumberFormat
.getCompactNumberInstance(Locale.GERMAN, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_SL_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("sl"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_ES_LONG_FD1 = NumberFormat
.getCompactNumberInstance(Locale.of("es"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_DE_LONG_FD2 = NumberFormat
.getCompactNumberInstance(Locale.GERMAN, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_IT_LONG_FD3 = NumberFormat
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_PT_LONG_FD4 = NumberFormat
.getCompactNumberInstance(Locale.of("pt"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_PL_LONG = NumberFormat
.getCompactNumberInstance(Locale.of("pl"), NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_FR_LONG = NumberFormat
.getCompactNumberInstance(Locale.FRENCH, NumberFormat.Style.LONG);
static {
FORMAT_ES_LONG_FD1.setMaximumFractionDigits(1);
FORMAT_DE_LONG_FD2.setMaximumFractionDigits(2);
FORMAT_IT_LONG_FD3.setMaximumFractionDigits(3);
FORMAT_PT_LONG_FD4.setMaximumFractionDigits(4);
}
Object[][] compactFormatData() {
return new Object[][]{
// compact number format instance, number to format, formatted output
{FORMAT_DZ_LONG, 1000.09, "སྟོང་ཕ"
+ "ྲག ༡"},
{FORMAT_DZ_LONG, -999.99, "-སྟོང་ཕ"
+ "ྲག ༡"},
{FORMAT_DZ_LONG, -0.0, "-༠"},
{FORMAT_DZ_LONG, 3000L, "སྟོང་ཕ"
+ "ྲག ༣"},
{FORMAT_DZ_LONG, new BigInteger("12345678901234567890"), "ད"
+ "ུང་ཕྱུར་ས"
+ "་ཡ་ ༡༢༣༤༥༧"},
// negative
{FORMAT_DZ_LONG, new BigInteger("-12345678901234567890"), "-ད"
+ "ུང་ཕྱུར་ས"
+ "་ཡ་ ༡༢༣༤༥༧"},
{FORMAT_DZ_LONG, new BigDecimal("12345678901234567890.89"), "ད"
+ "ུང་ཕྱུར་ས"
+ "་ཡ་ ༡༢༣༤༥༧"},
{FORMAT_DZ_LONG, new BigDecimal("-12345678901234567890.89"), "-ད"
+ "ུང་ཕྱུར་ས"
+ "་ཡ་ ༡༢༣༤༥༧"},
// Zeros
{FORMAT_EN_US_SHORT, 0, "0"},
{FORMAT_EN_US_SHORT, 0.0, "0"},
{FORMAT_EN_US_SHORT, -0.0, "-0"},
// Less than 1000 no suffix
{FORMAT_EN_US_SHORT, 499, "499"},
// Boundary number
{FORMAT_EN_US_SHORT, 1000.0, "1K"},
// Long
{FORMAT_EN_US_SHORT, 3000L, "3K"},
{FORMAT_EN_US_SHORT, 30000L, "30K"},
{FORMAT_EN_US_SHORT, 300000L, "300K"},
{FORMAT_EN_US_SHORT, 3000000L, "3M"},
{FORMAT_EN_US_SHORT, 30000000L, "30M"},
{FORMAT_EN_US_SHORT, 300000000L, "300M"},
{FORMAT_EN_US_SHORT, 3000000000L, "3B"},
{FORMAT_EN_US_SHORT, 30000000000L, "30B"},
{FORMAT_EN_US_SHORT, 300000000000L, "300B"},
{FORMAT_EN_US_SHORT, 3000000000000L, "3T"},
{FORMAT_EN_US_SHORT, 30000000000000L, "30T"},
{FORMAT_EN_US_SHORT, 300000000000000L, "300T"},
{FORMAT_EN_US_SHORT, 3000000000000000L, "3000T"},
// Negatives
{FORMAT_EN_US_SHORT, -3000L, "-3K"},
{FORMAT_EN_US_SHORT, -30000L, "-30K"},
{FORMAT_EN_US_SHORT, -300000L, "-300K"},
{FORMAT_EN_US_SHORT, -3000000L, "-3M"},
{FORMAT_EN_US_SHORT, -30000000L, "-30M"},
{FORMAT_EN_US_SHORT, -300000000L, "-300M"},
{FORMAT_EN_US_SHORT, -3000000000L, "-3B"},
{FORMAT_EN_US_SHORT, -30000000000L, "-30B"},
{FORMAT_EN_US_SHORT, -300000000000L, "-300B"},
{FORMAT_EN_US_SHORT, -3000000000000L, "-3T"},
{FORMAT_EN_US_SHORT, -30000000000000L, "-30T"},
{FORMAT_EN_US_SHORT, -300000000000000L, "-300T"},
{FORMAT_EN_US_SHORT, -3000000000000000L, "-3000T"},
// Double
{FORMAT_EN_US_SHORT, 3000.0, "3K"},
{FORMAT_EN_US_SHORT, 30000.0, "30K"},
{FORMAT_EN_US_SHORT, 300000.0, "300K"},
{FORMAT_EN_US_SHORT, 3000000.0, "3M"},
{FORMAT_EN_US_SHORT, 30000000.0, "30M"},
{FORMAT_EN_US_SHORT, 300000000.0, "300M"},
{FORMAT_EN_US_SHORT, 3000000000.0, "3B"},
{FORMAT_EN_US_SHORT, 30000000000.0, "30B"},
{FORMAT_EN_US_SHORT, 300000000000.0, "300B"},
{FORMAT_EN_US_SHORT, 3000000000000.0, "3T"},
{FORMAT_EN_US_SHORT, 30000000000000.0, "30T"},
{FORMAT_EN_US_SHORT, 300000000000000.0, "300T"},
{FORMAT_EN_US_SHORT, 3000000000000000.0, "3000T"},
// Negatives
{FORMAT_EN_US_SHORT, -3000.0, "-3K"},
{FORMAT_EN_US_SHORT, -30000.0, "-30K"},
{FORMAT_EN_US_SHORT, -300000.0, "-300K"},
{FORMAT_EN_US_SHORT, -3000000.0, "-3M"},
{FORMAT_EN_US_SHORT, -30000000.0, "-30M"},
{FORMAT_EN_US_SHORT, -300000000.0, "-300M"},
{FORMAT_EN_US_SHORT, -3000000000.0, "-3B"},
{FORMAT_EN_US_SHORT, -30000000000.0, "-30B"},
{FORMAT_EN_US_SHORT, -300000000000.0, "-300B"},
{FORMAT_EN_US_SHORT, -3000000000000.0, "-3T"},
{FORMAT_EN_US_SHORT, -30000000000000.0, "-30T"},
{FORMAT_EN_US_SHORT, -300000000000000.0, "-300T"},
{FORMAT_EN_US_SHORT, -3000000000000000.0, "-3000T"},
// BigInteger
{FORMAT_EN_US_SHORT, new BigInteger("12345678901234567890"),
"12345679T"},
{FORMAT_EN_US_SHORT, new BigInteger("-12345678901234567890"),
"-12345679T"},
//BigDecimal
{FORMAT_EN_US_SHORT, new BigDecimal("12345678901234567890.89"),
"12345679T"},
{FORMAT_EN_US_SHORT, new BigDecimal("-12345678901234567890.89"),
"-12345679T"},
{FORMAT_EN_US_SHORT, new BigDecimal("12345678901234567890123466767.89"),
"12345678901234568T"},
{FORMAT_EN_US_SHORT, new BigDecimal(
"12345678901234567890878732267863209.89"),
"12345678901234567890879T"},
// number as exponent
{FORMAT_EN_US_SHORT, 9.78313E+3, "10K"},
// Less than 1000 no suffix
{FORMAT_EN_LONG, 999, "999"},
// Round the value and then format
{FORMAT_EN_LONG, 999.99, "1 thousand"},
// 10 thousand
{FORMAT_EN_LONG, 99000, "99 thousand"},
// Long path
{FORMAT_EN_LONG, 330000, "330 thousand"},
// Double path
{FORMAT_EN_LONG, 3000.90, "3 thousand"},
// BigInteger path
{FORMAT_EN_LONG, new BigInteger("12345678901234567890"),
"12345679 trillion"},
//BigDecimal path
{FORMAT_EN_LONG, new BigDecimal("12345678901234567890.89"),
"12345679 trillion"},
// Less than 1000 no suffix
{FORMAT_HI_IN_LONG, -999, "-999"},
// Round the value with 0 fraction digits and format it
{FORMAT_HI_IN_LONG, -999.99, "-1 हज़ार"},
// 10 thousand
{FORMAT_HI_IN_LONG, 99000, "99 हज़ार"},
// Long path
{FORMAT_HI_IN_LONG, 330000, "3 लाख"},
// Double path
{FORMAT_HI_IN_LONG, 3000.90, "3 हज़ार"},
// BigInteger path
{FORMAT_HI_IN_LONG, new BigInteger("12345678901234567890"),
"123456789 खरब"},
// BigDecimal path
{FORMAT_HI_IN_LONG, new BigDecimal("12345678901234567890.89"),
"123456789 खरब"},
// 1000 does not have any suffix in "ja" locale
{FORMAT_JA_JP_SHORT, -999.99, "-1,000"},
// 0-9999 does not have any suffix
{FORMAT_JA_JP_SHORT, 9999, "9,999"},
// 99000/10000 => 9.9万 rounded to 10万
{FORMAT_JA_JP_SHORT, 99000, "10万"},
// Negative
{FORMAT_JA_JP_SHORT, -99000, "-10万"},
// Long path
{FORMAT_JA_JP_SHORT, 330000, "33万"},
// Double path
{FORMAT_JA_JP_SHORT, 3000.90, "3,001"},
// BigInteger path
{FORMAT_JA_JP_SHORT, new BigInteger("12345678901234567890"),
"1235京"},
// BigDecimal path
{FORMAT_JA_JP_SHORT, new BigDecimal("12345678901234567890.89"),
"1235京"},
// less than 1000 no suffix
{FORMAT_IT_SHORT, 499, "499"},
// Boundary number
{FORMAT_IT_SHORT, 1000, "1K"},
// Long path
{FORMAT_IT_SHORT, 3000000L, "3 Mln"},
// Double path
{FORMAT_IT_SHORT, 3000000.0, "3 Mln"},
// BigInteger path
{FORMAT_IT_SHORT, new BigInteger("12345678901234567890"),
"12345679 Bln"},
// BigDecimal path
{FORMAT_IT_SHORT, new BigDecimal("12345678901234567890.89"),
"12345679 Bln"},
{FORMAT_CA_LONG, 999, "999"},
{FORMAT_CA_LONG, 999.99, "1 miler"},
{FORMAT_CA_LONG, 99000, "99 milers"},
{FORMAT_CA_LONG, 330000, "330 milers"},
{FORMAT_CA_LONG, 3000.90, "3 milers"},
{FORMAT_CA_LONG, 1000000, "1 milió"},
{FORMAT_CA_LONG, new BigInteger("12345678901234567890"),
"12345679 bilions"},
{FORMAT_CA_LONG, new BigDecimal("12345678901234567890.89"),
"12345679 bilions"},
{FORMAT_AS_LONG, 5000.0, "৫ হাজাৰ"},
{FORMAT_AS_LONG, 50000.0, "৫০ হাজাৰ"},
{FORMAT_AS_LONG, 500000.0, "৫ লাখ"},
{FORMAT_AS_LONG, 5000000.0, "৫ নিযুত"},
{FORMAT_AS_LONG, 50000000.0, "৫০ নিযুত"},
{FORMAT_AS_LONG, 500000000.0, "৫০০ নিযুত"},
{FORMAT_AS_LONG, 5000000000.0, "৫ শত কোটি"},
{FORMAT_AS_LONG, 50000000000.0, "৫০ শত কোটি"},
{FORMAT_AS_LONG, 500000000000.0, "৫০০ শত কোটি"},
{FORMAT_AS_LONG, 5000000000000.0, "৫ শত পৰাৰ্দ্ধ"},
{FORMAT_AS_LONG, 50000000000000.0, "৫০ শত পৰাৰ্দ্ধ"},
{FORMAT_AS_LONG, 500000000000000.0, "৫০০ শত পৰাৰ্দ্ধ"},
{FORMAT_AS_LONG, 5000000000000000.0, "৫০০০ শত পৰাৰ্দ্ধ"},
{FORMAT_AS_LONG, new BigInteger("12345678901234567890"),
"১২৩৪৫৬৭৯ শত পৰাৰ্দ্ধ"},
{FORMAT_AS_LONG, new BigDecimal("12345678901234567890123466767.89"),
"১২৩৪৫৬৭৮৯০১২৩৪৫৬৮ শত পৰাৰ্দ্ধ"},
{FORMAT_BRX_SHORT, 999, "999"},
{FORMAT_BRX_SHORT, 999.99, "1के"},
{FORMAT_BRX_SHORT, 99000, "99के"},
{FORMAT_BRX_SHORT, 330000, "330के"},
{FORMAT_BRX_SHORT, 3000.90, "3के"},
{FORMAT_BRX_SHORT, 1000000, "1एम"},
{FORMAT_BRX_SHORT, new BigInteger("12345678901234567890"),
"12345679ति"},
{FORMAT_BRX_SHORT, new BigDecimal("12345678901234567890.89"),
"12345679ति"},
// Less than 1000 no suffix
{FORMAT_SW_LONG, 499, "499"},
// Boundary number
{FORMAT_SW_LONG, 1000, "elfu 1"},
// Long path
{FORMAT_SW_LONG, 3000000L, "milioni 3"},
// Long path, negative
{FORMAT_SW_LONG, -3000000L, "milioni -3"},
// Double path
{FORMAT_SW_LONG, 3000000.0, "milioni 3"},
// Double path, negative
{FORMAT_SW_LONG, -3000000.0, "milioni -3"},
// BigInteger path
{FORMAT_SW_LONG, new BigInteger("12345678901234567890"),
"trilioni 12345679"},
// BigDecimal path
{FORMAT_SW_LONG, new BigDecimal("12345678901234567890.89"),
"trilioni 12345679"},
// Positives
// No compact form
{FORMAT_SE_SHORT, 999, "999"},
// Long
{FORMAT_SE_SHORT, 8000000L, "8 mn"},
// Double
{FORMAT_SE_SHORT, 8000.98, "8 dt"},
// Big integer
{FORMAT_SE_SHORT, new BigInteger("12345678901234567890"), "12345679 bn"},
// Big decimal
{FORMAT_SE_SHORT, new BigDecimal("12345678901234567890.98"), "12345679 bn"},
// Negatives
// No compact form
{FORMAT_SE_SHORT, -999, "−999"},
// Long
{FORMAT_SE_SHORT, -8000000L, "−8 mn"},
// Double
{FORMAT_SE_SHORT, -8000.98, "−8 dt"},
// BigInteger
{FORMAT_SE_SHORT, new BigInteger("-12345678901234567890"), "−12345679 bn"},
// BigDecimal
{FORMAT_SE_SHORT, new BigDecimal("-12345678901234567890.98"), "−12345679 bn"},
// Plurals
// DE: one:i = 1 and v = 0
{FORMAT_DE_LONG, 1_000_000, "1 Million"},
{FORMAT_DE_LONG, 2_000_000, "2 Millionen"},
// SL: one:v = 0 and i % 100 = 1
// two:v = 0 and i % 100 = 2
// few:v = 0 and i % 100 = 3..4 or v != 0
{FORMAT_SL_LONG, 1_000_000, "1 milijon"},
{FORMAT_SL_LONG, 2_000_000, "2 milijona"},
{FORMAT_SL_LONG, 3_000_000, "3 milijoni"},
{FORMAT_SL_LONG, 5_000_000, "5 milijonov"},
// Fractional plurals
{FORMAT_ES_LONG_FD1, 1_234_500, "1,2 millones"},
{FORMAT_DE_LONG_FD2, 1_234_500, "1,23 Millionen"},
{FORMAT_IT_LONG_FD3, 1_234_500, "1,234 milioni"},
{FORMAT_PT_LONG_FD4, 1_234_500, "1,2345 milhões"},
// 8338690
{FORMAT_PL_LONG, 5_000, "5 tysięcy"},
{FORMAT_PL_LONG, 4_949, "5 tysięcy"},
{FORMAT_FR_LONG, 1_949, "2 mille"},
{FORMAT_IT_LONG, 1_949, "2 mila"},
};
}
Object[][] compactParseData() {
return new Object[][]{
// compact number format instance, string to parse, parsed number, return type
{FORMAT_DZ_LONG, "སྟོང་ཕྲ"
+ "ག ༡", 1000L, Long.class},
{FORMAT_DZ_LONG, "-སྟོང་ཕྲ"
+ "ག ༣", -3000L, Long.class},
{FORMAT_DZ_LONG, "དུང་ཕྱུར"
+ "་ས་ཡ་ ༡"
+ "༢༣༤༥༧", 1.23457E19, Double.class},
{FORMAT_DZ_LONG, "-དུང་ཕྱུར"
+ "་ས་ཡ་ ༡"
+ "༢༣༤༥༧", -1.23457E19, Double.class},
{FORMAT_EN_US_SHORT, "-0.0", -0.0, Double.class},
{FORMAT_EN_US_SHORT, "-0", -0.0, Double.class},
{FORMAT_EN_US_SHORT, "0", 0L, Long.class},
{FORMAT_EN_US_SHORT, "499", 499L, Long.class},
{FORMAT_EN_US_SHORT, "-499", -499L, Long.class},
{FORMAT_EN_US_SHORT, "499.89", 499.89, Double.class},
{FORMAT_EN_US_SHORT, "-499.89", -499.89, Double.class},
{FORMAT_EN_US_SHORT, "1K", 1000L, Long.class},
{FORMAT_EN_US_SHORT, "-1K", -1000L, Long.class},
{FORMAT_EN_US_SHORT, "3K", 3000L, Long.class},
{FORMAT_EN_US_SHORT, "17K", 17000L, Long.class},
{FORMAT_EN_US_SHORT, "-17K", -17000L, Long.class},
{FORMAT_EN_US_SHORT, "-3K", -3000L, Long.class},
{FORMAT_EN_US_SHORT, "12345678901234567890", 1.2345678901234567E19, Double.class},
{FORMAT_EN_US_SHORT, "12345679T", 1.2345679E19, Double.class},
{FORMAT_EN_US_SHORT, "-12345679T", -1.2345679E19, Double.class},
{FORMAT_EN_US_SHORT, "599.01K", 599010L, Long.class},
{FORMAT_EN_US_SHORT, "-599.01K", -599010L, Long.class},
{FORMAT_EN_US_SHORT, "599444444.90T", 5.994444449E20, Double.class},
{FORMAT_EN_US_SHORT, "-599444444.90T", -5.994444449E20, Double.class},
{FORMAT_EN_US_SHORT, "123456789012345.5678K", 123456789012345568L, Long.class},
{FORMAT_EN_US_SHORT, "17.000K", 17000L, Long.class},
{FORMAT_EN_US_SHORT, "123.56678K", 123566.78000, Double.class},
{FORMAT_EN_US_SHORT, "-123.56678K", -123566.78000, Double.class},
{FORMAT_EN_LONG, "999", 999L, Long.class},
{FORMAT_EN_LONG, "1 thousand", 1000L, Long.class},
{FORMAT_EN_LONG, "3 thousand", 3000L, Long.class},
{FORMAT_EN_LONG, "12345679 trillion", 1.2345679E19, Double.class},
{FORMAT_HI_IN_LONG, "999", 999L, Long.class},
{FORMAT_HI_IN_LONG, "-999", -999L, Long.class},
{FORMAT_HI_IN_LONG, "1 हज़ार", 1000L, Long.class},
{FORMAT_HI_IN_LONG, "-1 हज़ार", -1000L, Long.class},
{FORMAT_HI_IN_LONG, "3 हज़ार", 3000L, Long.class},
{FORMAT_HI_IN_LONG, "12345679 खरब", 1234567900000000000L, Long.class},
{FORMAT_HI_IN_LONG, "-12345679 खरब", -1234567900000000000L, Long.class},
{FORMAT_JA_JP_SHORT, "-99", -99L, Long.class},
{FORMAT_JA_JP_SHORT, "1万", 10000L, Long.class},
{FORMAT_JA_JP_SHORT, "30万", 300000L, Long.class},
{FORMAT_JA_JP_SHORT, "-30万", -300000L, Long.class},
{FORMAT_JA_JP_SHORT, "12345679兆", 1.2345679E19, Double.class},
{FORMAT_JA_JP_SHORT, "-12345679兆", -1.2345679E19, Double.class},
{FORMAT_IT_SHORT, "-99", -99L, Long.class},
{FORMAT_IT_SHORT, "1 Mln", 1000000L, Long.class},
{FORMAT_IT_SHORT, "30 Mln", 30000000L, Long.class},
{FORMAT_IT_SHORT, "-30 Mln", -30000000L, Long.class},
{FORMAT_IT_SHORT, "12345679 Bln", 1.2345679E19, Double.class},
{FORMAT_IT_SHORT, "-12345679 Bln", -1.2345679E19, Double.class},
{FORMAT_SW_LONG, "-0.0", -0.0, Double.class},
{FORMAT_SW_LONG, "499", 499L, Long.class},
{FORMAT_SW_LONG, "elfu 1", 1000L, Long.class},
{FORMAT_SW_LONG, "elfu 3", 3000L, Long.class},
{FORMAT_SW_LONG, "elfu 17", 17000L, Long.class},
{FORMAT_SW_LONG, "elfu -3", -3000L, Long.class},
{FORMAT_SW_LONG, "499", 499L, Long.class},
{FORMAT_SW_LONG, "-499", -499L, Long.class},
{FORMAT_SW_LONG, "elfu 1", 1000L, Long.class},
{FORMAT_SW_LONG, "elfu 3", 3000L, Long.class},
{FORMAT_SW_LONG, "elfu -3", -3000L, Long.class},
{FORMAT_SW_LONG, "elfu 17", 17000L, Long.class},
{FORMAT_SW_LONG, "trilioni 12345679", 1.2345679E19, Double.class},
{FORMAT_SW_LONG, "trilioni -12345679", -1.2345679E19, Double.class},
{FORMAT_SW_LONG, "elfu 599.01", 599010L, Long.class},
{FORMAT_SW_LONG, "elfu -599.01", -599010L, Long.class},
{FORMAT_SE_SHORT, "999", 999L, Long.class},
{FORMAT_SE_SHORT, "8 mn", 8000000L, Long.class},
{FORMAT_SE_SHORT, "8 dt", 8000L, Long.class},
{FORMAT_SE_SHORT, "12345679 bn", 1.2345679E19, Double.class},
{FORMAT_SE_SHORT, "12345679,89 bn", 1.2345679890000001E19, Double.class},
{FORMAT_SE_SHORT, "\u2212999", -999L, Long.class},
{FORMAT_SE_SHORT, "\u22128\u00a0mn", -8000000L, Long.class},
// lenient parsing. Hyphen-minus should match the localized minus sign
{FORMAT_SE_SHORT, "−8 mn", -8000000L, Long.class},
{FORMAT_SE_SHORT, "\u22128 dt", -8000L, Long.class},
{FORMAT_SE_SHORT, "\u221212345679 bn", -1.2345679E19, Double.class},
{FORMAT_SE_SHORT, "\u221212345679,89 bn", -1.2345679890000001E19, Double.class},
// Plurals
// DE: one:i = 1 and v = 0
{FORMAT_DE_LONG, "1 Million", 1_000_000L, Long.class},
{FORMAT_DE_LONG, "2 Millionen", 2_000_000L, Long.class},
// SL: one:v = 0 and i % 100 = 1
// two:v = 0 and i % 100 = 2
// few:v = 0 and i % 100 = 3..4 or v != 0
{FORMAT_SL_LONG, "1 milijon", 1_000_000L, Long.class},
{FORMAT_SL_LONG, "2 milijona", 2_000_000L, Long.class},
{FORMAT_SL_LONG, "3 milijoni", 3_000_000L, Long.class},
{FORMAT_SL_LONG, "5 milijonov", 5_000_000L, Long.class},
// Fractional plurals
{FORMAT_ES_LONG_FD1, "1,2 millones", 1_200_000L, Long.class},
{FORMAT_DE_LONG_FD2, "1,23 Millionen", 1_230_000L, Long.class},
{FORMAT_IT_LONG_FD3, "1,234 milioni", 1_234_000L, Long.class},
{FORMAT_PT_LONG_FD4, "1,2345 milhões", 1_234_500L, Long.class},
// 8338690
{FORMAT_PL_LONG, "5 tysięcy", 5_000L, Long.class},
{FORMAT_FR_LONG, "2 mille", 2_000L, Long.class},
{FORMAT_IT_LONG, "2 mila", 2_000L, Long.class},
};
}
Object[][] exceptionParseData() {
return new Object[][]{
// compact number instance, string to parse, null (no o/p; must throw exception)
// no number
{FORMAT_DZ_LONG, "སྟོང་ཕྲ"
+ "ག", null},
// Invalid prefix
{FORMAT_DZ_LONG, "-སྟོང,་ཕྲ"
+ "ག ༣", null},
// Invalid prefix for en_US
{FORMAT_EN_US_SHORT, "K12,347", null},
// Invalid prefix for ja_JP
{FORMAT_JA_JP_SHORT, "万1", null},
};
}
Object[][] invalidParseData() {
return new Object[][]{
// compact number instance, string to parse, parsed number
// Prefix and suffix do not match
{FORMAT_DZ_LONG, "སྟོང་ཕྲ"
+ "ག ༡ KM", 1000L},
// Exponents are unparseable
{FORMAT_EN_US_SHORT, "-1.05E4K", -1.05},
// Default instance does not allow grouping
{FORMAT_EN_US_SHORT, "12,347", 12L},
// Take partial suffix "K" as 1000 for en_US_SHORT patterns
{FORMAT_EN_US_SHORT, "12KM", 12000L},
// Invalid suffix
{FORMAT_HI_IN_LONG, "-1  क.", -1L},
// invalid plurals
{FORMAT_DE_LONG, "2 Million", 2L},
{FORMAT_SL_LONG, "2 milijon", 2L},
{FORMAT_SL_LONG, "2 milijone", 2L},
{FORMAT_SL_LONG, "2 milijonv", 2L},
{FORMAT_SL_LONG, "5 milijona", 5L},
{FORMAT_SL_LONG, "3 milijon", 3L},
{FORMAT_SL_LONG, "3 milijonv", 3L},
{FORMAT_SL_LONG, "5 milijon", 5L},
{FORMAT_SL_LONG, "5 milijona", 5L},
{FORMAT_SL_LONG, "5 milijone", 5L},
// 8338690
{FORMAT_PL_LONG, "5 tysiące", 5L},
{FORMAT_FR_LONG, "2 millier", 2L},
{FORMAT_IT_LONG, "2 mille", 2L},
};
}
Object[][] formatFieldPositionData() {
return new Object[][]{
//compact number instance, number to format, field, start position, end position, formatted string
{FORMAT_DZ_LONG, -3500, NumberFormat.Field.SIGN, 0, 1, "-སྟོང་ཕྲག ༤"},
{FORMAT_DZ_LONG, 3500, NumberFormat.Field.INTEGER, 9, 10, "སྟོང་ཕྲག ༤"},
{FORMAT_DZ_LONG, -3500, NumberFormat.Field.INTEGER, 10, 11, "-སྟོང་ཕྲག ༤"},
{FORMAT_DZ_LONG, 999, NumberFormat.Field.INTEGER, 0, 3, "༩༩༩"},
{FORMAT_DZ_LONG, -999, NumberFormat.Field.INTEGER, 1, 4, "-༩༩༩"},
{FORMAT_DZ_LONG, 3500, NumberFormat.Field.PREFIX, 0, 9, "སྟོང་ཕྲག ༤"},
{FORMAT_DZ_LONG, -3500, NumberFormat.Field.PREFIX, 0, 10, "-སྟོང་ཕྲག ༤"},
{FORMAT_DZ_LONG, 999, NumberFormat.Field.PREFIX, 0, 0, "༩༩༩"},
{FORMAT_EN_US_SHORT, -3500, NumberFormat.Field.SIGN, 0, 1, "-4K"},
{FORMAT_EN_US_SHORT, 3500, NumberFormat.Field.INTEGER, 0, 1, "4K"},
{FORMAT_EN_US_SHORT, 14900000067L, NumberFormat.Field.INTEGER, 0, 2, "15B"},
{FORMAT_EN_US_SHORT, -1000, NumberFormat.Field.PREFIX, 0, 1, "-1K"},
{FORMAT_EN_US_SHORT, 3500, NumberFormat.Field.SUFFIX, 1, 2, "4K"},
{FORMAT_EN_US_SHORT, 14900000067L, NumberFormat.Field.SUFFIX, 2, 3, "15B"},
{FORMAT_EN_LONG, 3500, NumberFormat.Field.INTEGER, 0, 1, "4 thousand"},
{FORMAT_EN_LONG, 14900000067L, NumberFormat.Field.INTEGER, 0, 2, "15 billion"},
{FORMAT_EN_LONG, 3500, NumberFormat.Field.SUFFIX, 1, 10, "4 thousand"},
{FORMAT_EN_LONG, 14900000067L, NumberFormat.Field.SUFFIX, 2, 10, "15 billion"},
{FORMAT_JA_JP_SHORT, 14900000067L, NumberFormat.Field.INTEGER, 0, 3, "149億"},
{FORMAT_JA_JP_SHORT, -999.99, NumberFormat.Field.INTEGER, 1, 6, "-1,000"},
{FORMAT_JA_JP_SHORT, 14900000067L, NumberFormat.Field.SUFFIX, 3, 4, "149億"},
{FORMAT_JA_JP_SHORT, -999.99, NumberFormat.Field.SUFFIX, 0, 0, "-1,000"},
{FORMAT_JA_JP_SHORT, -999.99, NumberFormat.Field.SIGN, 0, 1, "-1,000"},
{FORMAT_HI_IN_LONG, -14900000067L, NumberFormat.Field.SIGN, 0, 1,
"-15 अरब"},
{FORMAT_HI_IN_LONG, 3500, NumberFormat.Field.INTEGER, 0, 1,
"4 हज़ार"},
{FORMAT_HI_IN_LONG, 14900000067L, NumberFormat.Field.INTEGER, 0, 2,
"15 अरब"},
{FORMAT_HI_IN_LONG, 3500, NumberFormat.Field.SUFFIX, 1, 7,
"4 हज़ार"},
{FORMAT_HI_IN_LONG, 14900000067L, NumberFormat.Field.SUFFIX, 2, 6,
"15 अरब"},
{FORMAT_SE_SHORT, 8000000L, NumberFormat.Field.SUFFIX, 1, 4, "8 mn"},
{FORMAT_SE_SHORT, 8000.98, NumberFormat.Field.SUFFIX, 1, 4, "8 dt"},
{FORMAT_SE_SHORT, new BigInteger("12345678901234567890"), NumberFormat.Field.SUFFIX, 8, 11, "12345679 bn"},
{FORMAT_SE_SHORT, new BigDecimal("12345678901234567890.98"), NumberFormat.Field.SUFFIX, 8, 11, "12345679 bn"},
{FORMAT_SE_SHORT, -8000000L, NumberFormat.Field.INTEGER, 1, 2, "−8 mn"},
{FORMAT_SE_SHORT, -8000.98, NumberFormat.Field.SIGN, 0, 1, "−8 dt"},
{FORMAT_SE_SHORT, new BigDecimal("-48982865901234567890.98"), NumberFormat.Field.INTEGER, 1, 9, "−48982866 bn"},};
}
Object[][] varParsePosition() {
return new Object[][]{
// compact number instance, parse string, parsed number,
// start position, end position, error index
{FORMAT_DZ_LONG, "སྟོང་ཕྲ"
+ "ག ༡ KM", 1000L, 0, 10, -1},
// Invalid prefix returns null
{FORMAT_DZ_LONG, "Number is: -སྟོང,་ཕྲ"
+ "ག ༣", null, 11, 11, 11},
// Returns null
{FORMAT_DZ_LONG, "སྟོང་ཕྲ"
+ "ག", null, 0, 0, 0},
{FORMAT_EN_US_SHORT, "Exponent: -1.05E4K", -1.05, 10, 15, -1},
// Default instance does not allow grouping
{FORMAT_EN_US_SHORT, "12,347", 12L, 0, 2, -1},
// Invalid suffix "KM" for en_US_SHORT patterns
{FORMAT_EN_US_SHORT, "12KM", 12000L, 0, 3, -1},
// Invalid suffix
{FORMAT_HI_IN_LONG, "-1  क.", -1L, 0, 2, -1},
{FORMAT_EN_LONG, "Number is: 12345679 trillion",
1.2345679E19, 11, 28, -1},
{FORMAT_EN_LONG, "Number is: -12345679 trillion",
-1.2345679E19, 11, 29, -1},
{FORMAT_EN_LONG, "parse 12 thousand and four", 12000L, 6, 17, -1},};
}
@Test
void testInstanceCreation() {
Stream.of(NumberFormat.getAvailableLocales()).forEach(l -> NumberFormat
.getCompactNumberInstance(l, NumberFormat.Style.SHORT).format(10000));
Stream.of(NumberFormat.getAvailableLocales()).forEach(l -> NumberFormat
.getCompactNumberInstance(l, NumberFormat.Style.LONG).format(10000));
}
@Test
void testFormatWithNullParam() {
assertThrows(IllegalArgumentException.class, () -> {
FORMAT_EN_US_SHORT.format(null);
});
}
@ParameterizedTest
@MethodSource("compactFormatData")
void testFormat(NumberFormat cnf, Object number,
String expected) {
CompactFormatAndParseHelper.testFormat(cnf, number, expected);
}
@ParameterizedTest
@MethodSource("compactParseData")
void testParse(NumberFormat cnf, String parseString,
Number expected, Class<? extends Number> returnType) throws ParseException {
CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, returnType);
}
@ParameterizedTest
@MethodSource("compactParseData")
void testParsePosition(NumberFormat cnf, String parseString,
Number expected, Class<? extends Number> returnType) throws ParseException {
ParsePosition pos = new ParsePosition(0);
CompactFormatAndParseHelper.testParse(cnf, parseString, expected, pos, returnType);
assertEquals(parseString.length(), pos.getIndex());
assertEquals(-1, pos.getErrorIndex());
}
@ParameterizedTest
@MethodSource("varParsePosition")
void testVarParsePosition(NumberFormat cnf, String parseString,
Number expected, int startPosition, int indexPosition,
int errPosition) throws ParseException {
ParsePosition pos = new ParsePosition(startPosition);
CompactFormatAndParseHelper.testParse(cnf, parseString, expected, pos, null);
assertEquals(indexPosition, pos.getIndex());
assertEquals(errPosition, pos.getErrorIndex());
}
@ParameterizedTest
@MethodSource("exceptionParseData")
void throwsParseException(NumberFormat cnf, String parseString,
Number expected) {
assertThrows(ParseException.class, () -> CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null));
}
@ParameterizedTest
@MethodSource("invalidParseData")
void testInvalidParse(NumberFormat cnf, String parseString,
Number expected) throws ParseException {
CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null);
}
@ParameterizedTest
@MethodSource("formatFieldPositionData")
void testFormatWithFieldPosition(NumberFormat nf,
Object number, Format.Field field, int posStartExpected,
int posEndExpected, String expected) {
FieldPosition pos = new FieldPosition(field);
StringBuffer buf = new StringBuffer();
StringBuffer result = nf.format(number, buf, pos);
assertEquals(expected, result.toString(), "Incorrect formatting of the number '"
+ number + "'");
assertEquals(posStartExpected, pos.getBeginIndex(), "Incorrect start position"
+ " while formatting the number '" + number + "', for the field " + field);
assertEquals(posEndExpected, pos.getEndIndex(), "Incorrect end position"
+ " while formatting the number '" + number + "', for the field " + field);
}
}

View file

@ -0,0 +1,211 @@
/*
* Copyright (c) 2018, 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 8177552 8217254 8251499 8281317
* @summary Checks the validity of compact number patterns specified through
* CompactNumberFormat constructor
* @run junit/othervm TestCompactPatternsValidity
*/
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.List;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertThrows;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestCompactPatternsValidity {
// Max range 10^4
private static final String[] COMPACT_PATTERN1 = new String[]{"0", "0", "0", "0K", "00K"};
// Quoted special character '.' as prefix
private static final String[] COMPACT_PATTERN2 = new String[]{"0", "'.'K0"};
// Quoted special character '.' as suffix
private static final String[] COMPACT_PATTERN3 = new String[]{"0", "0", "0", "0K", "00K'.'"};
// Containing both prefix and suffix
private static final String[] COMPACT_PATTERN4 = new String[]{"", "", "H0H", "0K", "00K", "H0G"};
// Differing while specifying prefix and suffix
private static final String[] COMPACT_PATTERN5 = new String[]{"", "", "", "0K", "K0"};
// Containing both prefix ('.') and suffix (K)
private static final String[] COMPACT_PATTERN6 = new String[]{"0", "", "", "'.'0K"};
// Quoted special character ',' as suffix
private static final String[] COMPACT_PATTERN7 = new String[]{"", "0", "0", "0K','"};
// Most commonly used type of compact patterns with 15 elements
private static final String[] COMPACT_PATTERN8 = new String[]{"", "", "", "0K", "00K", "000K", "0M",
"00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"};
// All empty or special patterns; checking the default formatting behaviour
private static final String[] COMPACT_PATTERN9 = new String[]{"", "", "", "0", "0", "", "", "", "", "", "", "", "", "", ""};
// Patterns beyond 10^19; divisors beyond long range
private static final String[] COMPACT_PATTERN10 = new String[]{"", "", "", "0K", "00K", "000K", "0M", "00M",
"000M", "0B", "00B", "000B", "0T", "00T", "000T", "0L", "00L", "000L", "0XL", "00XL"};
// Containing positive;negative subpatterns
private static final String[] COMPACT_PATTERN11 = new String[]{"", "", "", "elfu 0;elfu -0", "elfu 00;elfu -00",
"elfu 000;elfu -000", "milioni 0;milioni -0", "milioni 00;milioni -00", "milioni 000;milioni -000"};
// Containing both prefix and suffix and positive;negative subpatern
private static final String[] COMPACT_PATTERN12 = new String[]{"", "", "H0H;H-0H", "0K;0K-", "00K;-00K", "H0G;-H0G"};
// A non empty pattern containing no 0s (min integer digits)
private static final String[] COMPACT_PATTERN13 =
new String[]{"", "", "", "Thousand", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "BeyondLong"};
// A non empty pattern containing no 0s (min integer digits), with plural rules
private static final String[] COMPACT_PATTERN14 =
new String[]{"", "", "", "{one:Kun other:0' 'Kun}"}; // from Somali in CLDR 38
Object[][] invalidCompactPatterns() {
return new Object[][] {
// compact patterns
// Pattern containing unquoted special character '.'
{new String[]{"", "", "", "0K", "00K."}},
// Pattern containing invalid single quote
{new String[]{"", "", "", "0 'do", "00K"}},
{new String[]{"", "", "", "0K", "00 don't"}},
// 0s (min integer digits) exceeding for the range at index 3
{new String[]{"", "", "0K", "00000K"}},
// null as a compact pattern
{new String[]{"", "", null, "00K"}},
};
}
Object[][] validPatternsFormat() {
return new Object[][] {
// compact patterns, numbers, expected output
{COMPACT_PATTERN1, List.of(200, 1000, 3000, 500000), List.of("200", "1K", "3K", "500K")},
{COMPACT_PATTERN2, List.of(1, 20, 3000), List.of("1", ".K2", ".K300")},
{COMPACT_PATTERN3, List.of(100.99, 1000, 30000), List.of("101", "1K", "30K.")},
{COMPACT_PATTERN4, List.of(0.0, 500, -500, 30000, 5000000), List.of("0", "H5H", "-H5H", "30K", "H50G")},
{COMPACT_PATTERN5, List.of(100, 1000, 30000), List.of("100", "1K", "K3")},
{COMPACT_PATTERN6, List.of(20.99, 1000, 30000), List.of("21", ".1K", ".30K")},
{COMPACT_PATTERN7, List.of(100, 1000, new BigInteger("12345678987654321")), List.of("100", "1K,", "12345678987654K,")},
{COMPACT_PATTERN8, List.of(new BigInteger("223565686837667632"), new BigDecimal("12322456774334.89766"), 30000, 3456.78,
new BigInteger("999999999999"), new BigDecimal("999999999999.0"), 999_999, 999_999_999),
List.of("223566T", "12T", "30K", "3K", "1T", "1T", "1M", "1B")},
{COMPACT_PATTERN9, List.of(new BigInteger("223566000000000000"), new BigDecimal("12345678987654567"), 30000, 3000),
List.of("223,566,000,000,000,000", "12,345,678,987,654,567", "30,000", "3,000")},
{COMPACT_PATTERN10, List.of(new BigInteger("100000000000000000"), new BigInteger("10000000000000000000"), new BigDecimal("555555555555555555555.89766"), 30000),
List.of("100L", "10XL", "556XL", "30K")},
{COMPACT_PATTERN11, List.of(20.99, -20.99, 1000, -1000, 30000, -30000, new BigInteger("12345678987654321"), new BigInteger("-12345678987654321")),
List.of("21", "-21", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988")},
{COMPACT_PATTERN12, List.of(0, 500, -500, 30000, -3000, 5000000), List.of("0", "H5H", "H-5H", "30K", "3K-", "H50G")},
{COMPACT_PATTERN13, List.of(1000, new BigInteger("10000000000000000000"), new BigDecimal("9999999999999999999.9")),
List.of("Thousand", "BeyondLong", "BeyondLong")},
};
}
Object[][] validPatternsParse() {
return new Object[][] {
// compact patterns, parse string, expected output
{COMPACT_PATTERN1, List.of(".56", "200", ".1K", "3K", "500K"), List.of(0.56, 200L, 100L, 3000L, 500000L)},
{COMPACT_PATTERN2, List.of("1", ".K2", ".K300"), List.of(1L, 20L, 3000L)},
{COMPACT_PATTERN3, List.of("101", "1K", "30K."), List.of(101L, 1000L, 30000L)},
{COMPACT_PATTERN4, List.of("0", "H5H", "-H5H", "30K", "H50G"), List.of(0L, 500L, -500L, 30000L, 5000000L)},
{COMPACT_PATTERN5, List.of("100", "1K", "K3"), List.of(100L, 1000L, 30000L)},
{COMPACT_PATTERN6, List.of("21", ".1K", ".30K"), List.of(21L, 1000L, 30000L)},
{COMPACT_PATTERN7, List.of("100", "1K,", "12345678987654K,"), List.of(100L, 1000L, 12345678987654000L)},
{COMPACT_PATTERN8, List.of("223566T", "12T", "30K", "3K"), List.of(223566000000000000L, 12000000000000L, 30000L, 3000L)},
{COMPACT_PATTERN10, List.of("1L", "100L", "10XL", "556XL", "30K"), List.of(1000000000000000L, 100000000000000000L, 1.0E19, 5.56E20, 30000L)},
{COMPACT_PATTERN11, List.of("21", "-21", "100.90", "-100.90", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988"),
List.of(21L, -21L, 100.90, -100.90, 1000L, -1000L, 30000L, -30000L, 12345678988000000L, -12345678988000000L)},
{COMPACT_PATTERN12, List.of("0", "H5H", "H-5H", "30K", "30K-", "H50G"), List.of(0L, 500L, -500L, 30000L, -30000L, 5000000L)},
{COMPACT_PATTERN13, List.of("Thousand", "BeyondLong"), List.of(1000L, new BigInteger("10000000000000000000"))},
};
}
Object[][] validPatternsFormatWithPluralRules() {
return new Object[][] {
// compact patterns, plural rules, numbers, expected output
{COMPACT_PATTERN14, "one:n = 1", List.of(1000, 2345), List.of("Kun", "2 Kun")},
};
}
Object[][] validPatternsParseWithPluralRules() {
return new Object[][] {
// compact patterns, plural rules, parse string, expected output
{COMPACT_PATTERN14, "one:n = 1", List.of("Kun", "2 Kun"), List.of(1000L, 2000L)},
};
}
@ParameterizedTest
@MethodSource("invalidCompactPatterns")
void testInvalidCompactPatterns(String[] compactPatterns) {
assertThrows(IllegalArgumentException.class, () -> {
new CompactNumberFormat("#,##0.0#", DecimalFormatSymbols
.getInstance(Locale.US), compactPatterns);
});
}
@ParameterizedTest
@MethodSource("validPatternsFormat")
void testValidPatternsFormat(String[] compactPatterns,
List<Object> numbers, List<String> expected) {
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns);
for (int index = 0; index < numbers.size(); index++) {
CompactFormatAndParseHelper.testFormat(fmt, numbers.get(index),
expected.get(index));
}
}
@ParameterizedTest
@MethodSource("validPatternsParse")
void testValidPatternsParse(String[] compactPatterns,
List<String> parseString, List<Number> numbers) throws ParseException {
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns);
for (int index = 0; index < parseString.size(); index++) {
CompactFormatAndParseHelper.testParse(fmt, parseString.get(index),
numbers.get(index), null, null);
}
}
@ParameterizedTest
@MethodSource("validPatternsFormatWithPluralRules")
void testValidPatternsFormatWithPluralRules(String[] compactPatterns, String pluralRules,
List<Object> numbers, List<String> expected) {
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns, pluralRules);
for (int index = 0; index < numbers.size(); index++) {
CompactFormatAndParseHelper.testFormat(fmt, numbers.get(index),
expected.get(index));
}
}
@ParameterizedTest
@MethodSource("validPatternsParseWithPluralRules")
void testValidPatternsParsewithPluralRules(String[] compactPatterns, String pluralRules,
List<String> parseString, List<Number> numbers) throws ParseException {
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns, pluralRules);
for (int index = 0; index < parseString.size(); index++) {
CompactFormatAndParseHelper.testParse(fmt, parseString.get(index),
numbers.get(index), null, null);
}
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2018, 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 8177552 8222756 8327640
* @summary Checks the equals and hashCode method of CompactNumberFormat
* @modules jdk.localedata
* @run junit/othervm TestEquality
*/
import org.junit.jupiter.api.Test;
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
public class TestEquality {
@Test
void testEquality() {
CompactNumberFormat cnf1 = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
CompactNumberFormat cnf2 = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
// A custom compact instance with the same state as
// compact number instance of "en_US" locale with SHORT style
String decimalPattern = "#,##0.###";
String[] compactPatterns = new String[]{
"",
"",
"",
"{one:0K other:0K}",
"{one:00K other:00K}",
"{one:000K other:000K}",
"{one:0M other:0M}",
"{one:00M other:00M}",
"{one:000M other:000M}",
"{one:0B other:0B}",
"{one:00B other:00B}",
"{one:000B other:000B}",
"{one:0T other:0T}",
"{one:00T other:00T}",
"{one:000T other:000T}"
};
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(Locale.US);
CompactNumberFormat cnf3 =
new CompactNumberFormat(decimalPattern, symbols, compactPatterns, "one:i = 1 and v = 0");
// A compact instance created with different decimalPattern than cnf3
CompactNumberFormat cnf4 = new CompactNumberFormat("#,#0.0#", symbols, compactPatterns);
// A compact instance created with different format symbols than cnf3
CompactNumberFormat cnf5 = new CompactNumberFormat(decimalPattern,
DecimalFormatSymbols.getInstance(Locale.JAPAN), compactPatterns);
// A compact instance created with different compact patterns than cnf3
CompactNumberFormat cnf6 = new CompactNumberFormat(decimalPattern,
symbols, new String[]{"", "", "", "0K", "00K", "000K"});
// Checking reflexivity
if (!cnf1.equals(cnf1)) {
throw new RuntimeException("[testEquality() reflexivity FAILED: The compared"
+ " objects must be equal]");
}
// Checking symmetry, checking equality of two same objects
if (!cnf1.equals(cnf2) || !cnf2.equals(cnf1)) {
throw new RuntimeException("[testEquality() symmetry FAILED: The compared"
+ " objects must be equal]");
}
// Checking transitivity, three objects must be equal
if (!cnf1.equals(cnf2) || !cnf2.equals(cnf3) || !cnf1.equals(cnf3)) {
throw new RuntimeException("[testEquality() transitivity FAILED: The compared"
+ " objects must be equal]");
}
// Objects must not be equal as the decimalPattern is different
checkEquals(cnf3, cnf4, false, "1st", "different decimal pattern");
// Objects must not be equal as the format symbols instance is different
checkEquals(cnf3, cnf5, false, "2nd", "different format symbols");
// Objects must not be equal as the compact patters are different
checkEquals(cnf3, cnf6, false, "3rd", "different compact patterns");
// Changing the min integer digits of first object; objects must not
// be equal
cnf1.setMinimumIntegerDigits(5);
checkEquals(cnf1, cnf2, false, "4th", "different min integer digits");
// Changing the min integer digits of second object; objects must
// be equal
cnf2.setMinimumIntegerDigits(5);
checkEquals(cnf1, cnf2, true, "5th", "");
// Changing the grouping size of first object; objects must not
// be equal
cnf1.setGroupingSize(4);
checkEquals(cnf1, cnf2, false, "6th", "different grouping size");
// Changing the grouping size if second object; objects must be equal
cnf2.setGroupingSize(4);
checkEquals(cnf1, cnf2, true, "7th", "");
// Changing the parseBigDecimal of first object; objects must not
// be equal
cnf1.setParseBigDecimal(true);
checkEquals(cnf1, cnf2, false, "8th", "different parse big decimal");
// Changing the parseBigDecimal of second object; objects must be equal
cnf2.setParseBigDecimal(true);
checkEquals(cnf1, cnf2, true, "9th", "");
// Changing the strict parsing value of first object; objects must not be equal
cnf1.setStrict(true);
checkEquals(cnf1, cnf2, false, "10th", "different strict parsing");
// Changing the strict parsing value of second object; objects must be equal
cnf2.setStrict(true);
checkEquals(cnf1, cnf2, true, "11th", "");
}
private void checkEquals(CompactNumberFormat cnf1, CompactNumberFormat cnf2,
boolean mustEqual, String nthComparison, String message) {
if (cnf1.equals(cnf2) != mustEqual) {
if (mustEqual) {
throw new RuntimeException("[testEquality() " + nthComparison
+ " comparison FAILED: The compared objects must be equal]");
} else {
throw new RuntimeException("[testEquality() " + nthComparison
+ " comparison FAILED: The compared objects must"
+ " not be equal because of " + message + "]");
}
}
}
@Test
void testHashCode() {
NumberFormat cnf1 = NumberFormat
.getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
NumberFormat cnf2 = NumberFormat
.getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
if (cnf1.hashCode() != cnf2.hashCode()) {
throw new RuntimeException("[testHashCode() FAILED: hashCode of the"
+ " compared objects must match]");
}
}
// Test the property of equals and hashCode i.e. two equal object must
// always have the same hashCode
@Test
void testEqualsAndHashCode() {
NumberFormat cnf1 = NumberFormat
.getCompactNumberInstance(Locale.of("hi", "IN"), NumberFormat.Style.SHORT);
cnf1.setMinimumIntegerDigits(5);
NumberFormat cnf2 = NumberFormat
.getCompactNumberInstance(Locale.of("hi", "IN"), NumberFormat.Style.SHORT);
cnf2.setMinimumIntegerDigits(5);
if (cnf1.equals(cnf2)) {
if (cnf1.hashCode() != cnf2.hashCode()) {
throw new RuntimeException("[testEqualsAndHashCode() FAILED: two"
+ " equal objects must have same hashCode]");
}
} else {
throw new RuntimeException("[testEqualsAndHashCode() FAILED: The"
+ " compared objects must be equal]");
}
}
}

View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2018, 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 8177552
* @summary Checks the functioning of
* CompactNumberFormat.formatToCharacterIterator method
* @modules jdk.localedata
* @run junit/othervm TestFormatToCharacterIterator
*/
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.AttributedCharacterIterator;
import java.text.CharacterIterator;
import java.text.Format;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestFormatToCharacterIterator {
private static final NumberFormat FORMAT_DZ = NumberFormat
.getCompactNumberInstance(Locale.of("dz"),
NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_EN_US = NumberFormat
.getCompactNumberInstance(Locale.US,
NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_EN_LONG = NumberFormat
.getCompactNumberInstance(Locale.ENGLISH,
NumberFormat.Style.LONG);
Object[][] compactFieldPositionData() {
return new Object[][]{
// compact format instance, number, resulted string, attributes/fields, attribute positions
{FORMAT_DZ, 1000.09, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F21",
new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 9, 9, 10}},
{FORMAT_DZ, -999.99, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F21",
new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER},
new int[]{0, 1, 1, 10, 10, 11}},
{FORMAT_DZ, -0.0, "-\u0F20", new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.INTEGER}, new int[]{0, 1, 1, 2}},
{FORMAT_DZ, 3000L, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F23",
new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 9, 9, 10}},
{FORMAT_DZ, new BigInteger("12345678901234567890"),
"\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27",
new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 14, 14, 20}},
{FORMAT_DZ, new BigDecimal("12345678901234567890.89"),
"\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27",
new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 14, 14, 20}},
// Zeros
{FORMAT_EN_US, 0, "0", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 1}},
{FORMAT_EN_US, 0.0, "0", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 1}},
{FORMAT_EN_US, -0.0, "-0", new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.INTEGER}, new int[]{0, 1, 1, 2}},
// Less than 1000 no suffix
{FORMAT_EN_US, 499, "499", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 3}},
// Boundary number
{FORMAT_EN_US, 1000.0, "1K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
// Long
{FORMAT_EN_US, 3000L, "3K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
{FORMAT_EN_US, 30000L, "30K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
{FORMAT_EN_US, 300000L, "300K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
{FORMAT_EN_US, 3000000L, "3M",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
{FORMAT_EN_US, 30000000L, "30M",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
{FORMAT_EN_US, 300000000L, "300M",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
{FORMAT_EN_US, 3000000000L, "3B",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
{FORMAT_EN_US, 30000000000L, "30B",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
{FORMAT_EN_US, 300000000000L, "300B",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
{FORMAT_EN_US, 3000000000000L, "3T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
{FORMAT_EN_US, 30000000000000L, "30T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
{FORMAT_EN_US, 300000000000000L, "300T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
{FORMAT_EN_US, 3000000000000000L, "3000T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 4, 4, 5}},
// Double
{FORMAT_EN_US, 3000.0, "3K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
{FORMAT_EN_US, 30000.0, "30K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
{FORMAT_EN_US, 300000.0, "300K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
{FORMAT_EN_US, 3000000000000000.0, "3000T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 4, 4, 5}},
// BigInteger
{FORMAT_EN_US, new BigInteger("12345678901234567890"), "12345679T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 9}},
// BigDecimal
{FORMAT_EN_US, new BigDecimal("12345678901234567890.89"), "12345679T",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 9}},
// Number as exponent
{FORMAT_EN_US, 9.78313E+3, "10K",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
// Less than 1000 no suffix
{FORMAT_EN_LONG, 999, "999", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 3}},
// Round the value and then format
{FORMAT_EN_LONG, 999.99, "1 thousand",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 10}},
// 10 thousand
{FORMAT_EN_LONG, 99000, "99 thousand",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 11}},
// Long path
{FORMAT_EN_LONG, 330000, "330 thousand",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 12}},
// Double path
{FORMAT_EN_LONG, 3000.90, "3 thousand",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 10}},
// BigInteger path
{FORMAT_EN_LONG, new BigInteger("12345678901234567890"), "12345679 trillion",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 17}},
// BigDecimal path
{FORMAT_EN_LONG, new BigDecimal("12345678901234567890.89"), "12345679 trillion",
new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 17}}
};
}
@ParameterizedTest
@MethodSource("compactFieldPositionData")
void testFormatToCharacterIterator(NumberFormat fmt, Object number,
String expected, Format.Field[] expectedFields, int[] positions) {
AttributedCharacterIterator iterator = fmt.formatToCharacterIterator(number);
assertEquals(expected, getText(iterator), "Incorrect formatting of the number '"
+ number + "'");
iterator.first();
// Check start and end index of the formatted string
assertEquals(0, iterator.getBeginIndex(), "Incorrect start index: "
+ iterator.getBeginIndex() + " of the formatted string: " + expected);
assertEquals(expected.length(), iterator.getEndIndex(), "Incorrect end index: "
+ iterator.getEndIndex() + " of the formatted string: " + expected);
// Check the attributes returned by the formatToCharacterIterator
assertEquals(Set.of(expectedFields), iterator.getAllAttributeKeys(),
"Attributes do not match while formatting number: " + number);
// Check the begin and end index for attributes
iterator.first();
int currentPosition = 0;
do {
int start = iterator.getRunStart();
int end = iterator.getRunLimit();
assertEquals(positions[currentPosition], start,
"Incorrect start position for the attribute(s): "
+ iterator.getAttributes().keySet());
assertEquals(positions[currentPosition + 1], end,
"Incorrect end position for the attribute(s): "
+ iterator.getAttributes().keySet());
currentPosition = currentPosition + 2;
iterator.setIndex(end);
} while (iterator.current() != CharacterIterator.DONE);
}
// Create the formatted string from returned AttributedCharacterIterator
private String getText(AttributedCharacterIterator iterator) {
StringBuffer buffer = new StringBuffer();
for (char c = iterator.first(); c != CharacterIterator.DONE;
c = iterator.next()) {
buffer.append(c);
}
return buffer.toString();
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2018, 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 8177552
* @summary Checks the functioning of compact number format by changing the
* formatting parameters. For example, min fraction digits, grouping
* size etc.
* @modules jdk.localedata
* @run junit/othervm TestMutatingInstance
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestMutatingInstance {
private static final NumberFormat FORMAT_FRACTION = NumberFormat
.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
private static final CompactNumberFormat FORMAT_GROUPING = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_MININTEGER = NumberFormat
.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_PARSEINTONLY = NumberFormat
.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
// No compact patterns are specified for this instance except at index 4.
// This is to test how the behaviour differs between compact number formatting
// and general number formatting
private static final NumberFormat FORMAT_NO_PATTERNS = new CompactNumberFormat(
"#,##0.0#", DecimalFormatSymbols.getInstance(Locale.US),
new String[]{"", "", "", "", "00K", "", "", "", "", "", "", "", "", "", ""});
@BeforeAll
void mutateInstances() {
FORMAT_FRACTION.setMinimumFractionDigits(2);
FORMAT_GROUPING.setGroupingSize(3);
FORMAT_GROUPING.setGroupingUsed(true);
FORMAT_MININTEGER.setMinimumIntegerDigits(5);
FORMAT_PARSEINTONLY.setParseIntegerOnly(true);
FORMAT_PARSEINTONLY.setGroupingUsed(true);
// Setting min fraction digits and other fields does not effect
// the general number formatting behaviour, when no compact number
// patterns are specified
FORMAT_NO_PATTERNS.setMinimumFractionDigits(2);
}
Object[][] compactFormatData() {
return new Object[][]{
{FORMAT_FRACTION, 1900, "1.90 thousand"},
{FORMAT_FRACTION, 1000, "1.00 thousand"},
{FORMAT_FRACTION, 9090.99, "9.09 thousand"},
{FORMAT_FRACTION, new BigDecimal(12346567890987654.32),
"12346.57 trillion"},
{FORMAT_FRACTION, new BigInteger("12346567890987654"),
"12346.57 trillion"},
{FORMAT_GROUPING, new BigDecimal(12346567890987654.32),
"12,347 trillion"},
{FORMAT_GROUPING, 100000, "100 thousand"},
{FORMAT_MININTEGER, 10000, "00010 thousand"},
{FORMAT_NO_PATTERNS, 100000, "100,000"},
{FORMAT_NO_PATTERNS, 1000.998, "1,001"},
{FORMAT_NO_PATTERNS, 10900, "10.90K"},
{FORMAT_NO_PATTERNS, new BigDecimal(12346567890987654.32), "12,346,567,890,987,654"},};
}
Object[][] compactParseData() {
return new Object[][]{
{FORMAT_FRACTION, "190 thousand", 190000L},
{FORMAT_FRACTION, "19.9 thousand", 19900L},
{FORMAT_GROUPING, "12,346 thousand", 12346000L},
{FORMAT_PARSEINTONLY, "12345 thousand", 12345000L},
{FORMAT_PARSEINTONLY, "12,345 thousand", 12345000L},
{FORMAT_PARSEINTONLY, "12.345 thousand", 12000L},};
}
@ParameterizedTest
@MethodSource("compactFormatData")
void formatCompactNumber(NumberFormat nf,
Object number, String expected) {
CompactFormatAndParseHelper.testFormat(nf, number, expected);
}
@ParameterizedTest
@MethodSource("compactParseData")
void parseCompactNumber(NumberFormat nf,
String parseString, Number expected) throws ParseException {
CompactFormatAndParseHelper.testParse(nf, parseString, expected, null, null);
}
}

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2018, 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 8177552 8306116 8319990
* @summary Checks CNF.parse() when parseBigDecimal is set to true
* @modules jdk.localedata
* @run junit/othervm TestParseBigDecimal
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.math.BigDecimal;
import java.text.CompactNumberFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestParseBigDecimal {
private static final CompactNumberFormat FORMAT_DZ_LONG = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.of("dz"), NumberFormat.Style.LONG);
private static final CompactNumberFormat FORMAT_EN_US_SHORT = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
private static final CompactNumberFormat FORMAT_EN_LONG = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
private static final CompactNumberFormat FORMAT_HI_IN_LONG = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.of("hi", "IN"), NumberFormat.Style.LONG);
private static final CompactNumberFormat FORMAT_JA_JP_SHORT = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
private static final CompactNumberFormat FORMAT_IT_SHORT = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.SHORT);
private static final CompactNumberFormat FORMAT_SW_LONG = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.of("sw"), NumberFormat.Style.LONG);
private static final CompactNumberFormat FORMAT_SE_SHORT = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.of("se"), NumberFormat.Style.SHORT);
@BeforeAll
void mutateInstances() {
FORMAT_DZ_LONG.setParseBigDecimal(true);
FORMAT_EN_US_SHORT.setParseBigDecimal(true);
FORMAT_EN_LONG.setParseBigDecimal(true);
FORMAT_HI_IN_LONG.setParseBigDecimal(true);
FORMAT_JA_JP_SHORT.setParseBigDecimal(true);
FORMAT_IT_SHORT.setParseBigDecimal(true);
FORMAT_SW_LONG.setParseBigDecimal(true);
FORMAT_SE_SHORT.setParseBigDecimal(true);
}
Object[][] compactParseData() {
return new Object[][]{
// compact number format instance, string to parse, parsed number
{FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+ "\u0F42 \u0F21", new BigDecimal("1000")},
{FORMAT_DZ_LONG, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+ "\u0F42 \u0F23", new BigDecimal("-3000")},
{FORMAT_DZ_LONG, "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62"
+ "\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21"
+ "\u0F22\u0F23\u0F24\u0F25\u0F27", new BigDecimal("12345700000000000000")},
{FORMAT_DZ_LONG, "-\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62"
+ "\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21"
+ "\u0F22\u0F23\u0F24\u0F25\u0F27", new BigDecimal("-12345700000000000000")},
{FORMAT_EN_US_SHORT, "-0.0", new BigDecimal("-0.0")},
{FORMAT_EN_US_SHORT, "0", new BigDecimal("0")},
{FORMAT_EN_US_SHORT, "499", new BigDecimal("499")},
{FORMAT_EN_US_SHORT, "-499", new BigDecimal("-499")},
{FORMAT_EN_US_SHORT, "499.89", new BigDecimal("499.89")},
{FORMAT_EN_US_SHORT, "-499.89", new BigDecimal("-499.89")},
{FORMAT_EN_US_SHORT, "1K", new BigDecimal("1000")},
{FORMAT_EN_US_SHORT, "-1K", new BigDecimal("-1000")},
{FORMAT_EN_US_SHORT, "3K", new BigDecimal("3000")},
{FORMAT_EN_US_SHORT, "-3K", new BigDecimal("-3000")},
{FORMAT_EN_US_SHORT, "17K", new BigDecimal("17000")},
{FORMAT_EN_US_SHORT, "-17K", new BigDecimal("-17000")},
{FORMAT_EN_US_SHORT, "12345678901234567890",
new BigDecimal("12345678901234567890")},
{FORMAT_EN_US_SHORT, "12345679T", new BigDecimal("12345679000000000000")},
{FORMAT_EN_US_SHORT, "-12345679T", new BigDecimal("-12345679000000000000")},
{FORMAT_EN_US_SHORT, "599.01K", new BigDecimal("599010.00")},
{FORMAT_EN_US_SHORT, "-599.01K", new BigDecimal("-599010.00")},
{FORMAT_EN_US_SHORT, "599444444.90T", new BigDecimal("599444444900000000000.00")},
{FORMAT_EN_US_SHORT, "-599444444.90T", new BigDecimal("-599444444900000000000.00")},
{FORMAT_EN_US_SHORT, "123456789012345.5678K",
new BigDecimal("123456789012345567.8000")},
{FORMAT_EN_US_SHORT, "17.000K", new BigDecimal("17000.000")},
{FORMAT_EN_US_SHORT, "123.56678K", new BigDecimal("123566.78000")},
{FORMAT_EN_US_SHORT, "-123.56678K", new BigDecimal("-123566.78000")},
{FORMAT_EN_LONG, "999", new BigDecimal("999")},
{FORMAT_EN_LONG, "1 thousand", new BigDecimal("1000")},
{FORMAT_EN_LONG, "3 thousand", new BigDecimal("3000")},
{FORMAT_EN_LONG, "12345679 trillion", new BigDecimal("12345679000000000000")},
{FORMAT_HI_IN_LONG, "999", new BigDecimal("999")},
{FORMAT_HI_IN_LONG, "-999", new BigDecimal("-999")},
{FORMAT_HI_IN_LONG, "1 \u0939\u091C\u093C\u093E\u0930", new BigDecimal("1000")},
{FORMAT_HI_IN_LONG, "-1 \u0939\u091C\u093C\u093E\u0930", new BigDecimal("-1000")},
{FORMAT_HI_IN_LONG, "3 \u0939\u091C\u093C\u093E\u0930", new BigDecimal("3000")},
{FORMAT_HI_IN_LONG, "12345679 \u0916\u0930\u092C", new BigDecimal("1234567900000000000")},
{FORMAT_HI_IN_LONG, "-12345679 \u0916\u0930\u092C", new BigDecimal("-1234567900000000000")},
{FORMAT_JA_JP_SHORT, "-99", new BigDecimal("-99")},
{FORMAT_JA_JP_SHORT, "1\u4E07", new BigDecimal("10000")},
{FORMAT_JA_JP_SHORT, "30\u4E07", new BigDecimal("300000")},
{FORMAT_JA_JP_SHORT, "-30\u4E07", new BigDecimal("-300000")},
{FORMAT_JA_JP_SHORT, "12345679\u5146", new BigDecimal("12345679000000000000")},
{FORMAT_JA_JP_SHORT, "-12345679\u5146", new BigDecimal("-12345679000000000000")},
{FORMAT_IT_SHORT, "-99", new BigDecimal("-99")},
{FORMAT_IT_SHORT, "1\u00a0Mln", new BigDecimal("1000000")},
{FORMAT_IT_SHORT, "30\u00a0Mln", new BigDecimal("30000000")},
{FORMAT_IT_SHORT, "-30\u00a0Mln", new BigDecimal("-30000000")},
{FORMAT_IT_SHORT, "12345679\u00a0Bln", new BigDecimal("12345679000000000000")},
{FORMAT_IT_SHORT, "-12345679\u00a0Bln", new BigDecimal("-12345679000000000000")},
{FORMAT_SW_LONG, "-0.0", new BigDecimal("-0.0")},
{FORMAT_SW_LONG, "499", new BigDecimal("499")},
{FORMAT_SW_LONG, "elfu 1", new BigDecimal("1000")},
{FORMAT_SW_LONG, "elfu 3", new BigDecimal("3000")},
{FORMAT_SW_LONG, "elfu 17", new BigDecimal("17000")},
{FORMAT_SW_LONG, "elfu -3", new BigDecimal("-3000")},
{FORMAT_SW_LONG, "-499", new BigDecimal("-499")},
{FORMAT_SW_LONG, "elfu 1", new BigDecimal("1000")},
{FORMAT_SW_LONG, "elfu 3", new BigDecimal("3000")},
{FORMAT_SW_LONG, "elfu -3", new BigDecimal("-3000")},
{FORMAT_SW_LONG, "elfu 17", new BigDecimal("17000")},
{FORMAT_SW_LONG, "trilioni 12345679", new BigDecimal("12345679000000000000")},
{FORMAT_SW_LONG, "trilioni -12345679", new BigDecimal("-12345679000000000000")},
{FORMAT_SW_LONG, "elfu 599.01", new BigDecimal("599010.00")},
{FORMAT_SW_LONG, "elfu -599.01", new BigDecimal("-599010.00")},
{FORMAT_SE_SHORT, "999", new BigDecimal("999")},
{FORMAT_SE_SHORT, "8\u00a0mn", new BigDecimal("8000000")},
{FORMAT_SE_SHORT, "8\u00a0dt", new BigDecimal("8000")},
{FORMAT_SE_SHORT, "12345679\u00a0bn", new BigDecimal("12345679000000000000")},
{FORMAT_SE_SHORT, "12345679,89\u00a0bn", new BigDecimal("12345679890000000000.00")},
{FORMAT_SE_SHORT, "\u2212999", new BigDecimal("-999")},
{FORMAT_SE_SHORT, "\u22128\u00a0mn", new BigDecimal("-8000000")},
{FORMAT_SE_SHORT, "\u22128\u00a0dt", new BigDecimal("-8000")},
{FORMAT_SE_SHORT, "\u221212345679\u00a0bn", new BigDecimal("-12345679000000000000")},
{FORMAT_SE_SHORT, "\u221212345679,89\u00a0bn", new BigDecimal("-12345679890000000000.00")},};
}
@ParameterizedTest
@MethodSource("compactParseData")
void testParse(NumberFormat cnf, String parseString,
Number expected) throws ParseException {
CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, BigDecimal.class);
}
}

View file

@ -0,0 +1,129 @@
/*
* 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 8222756
* @summary Tests plurals support in CompactNumberFormat
* @run junit/othervm TestPlurals
*/
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.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestPlurals {
private final static DecimalFormatSymbols DFS = DecimalFormatSymbols.getInstance(Locale.ROOT);
private final static String[] PATTERN = {
"{zero:0->zero one:0->one two:0->two few:0->few many:0->many other:0->other}"};
private final static String RULE_1 = "zero:n = 0; one:n = 1; two:n = 2; few:n = 3..4; many:n = 5..6,8";
private final static String RULE_2 = "one:n % 2 = 1 or n / 3 = 2;";
private final static String RULE_3 = "one:n%2=0andn/3=2;";
Object[][] pluralRules() {
return new Object[][]{
// rules, number, expected
{RULE_1, 0, "0->zero"},
{RULE_1, 1, "1->one"},
{RULE_1, 2, "2->two"},
{RULE_1, 3, "3->few"},
{RULE_1, 4, "4->few"},
{RULE_1, 5, "5->many"},
{RULE_1, 6, "6->many"},
{RULE_1, 7, "7->other"},
{RULE_1, 8, "8->many"},
{RULE_1, 9, "9->other"},
{RULE_2, 0, "0->other"},
{RULE_2, 1, "1->one"},
{RULE_2, 2, "2->other"},
{RULE_2, 3, "3->one"},
{RULE_2, 4, "4->other"},
{RULE_2, 5, "5->one"},
{RULE_2, 6, "6->one"},
{RULE_3, 0, "0->other"},
{RULE_3, 1, "1->other"},
{RULE_3, 2, "2->other"},
{RULE_3, 3, "3->other"},
{RULE_3, 4, "4->other"},
{RULE_3, 5, "5->other"},
{RULE_3, 6, "6->one"},
};
}
Object[][] invalidRules() {
return new Object [][] {
{"one:a = 1"},
{"on:n = 1"},
{"one:n = 1...2"},
{"one:n = 1.2"},
{"one:n = 1..2,"},
{"one:n = 1;one:n = 2"},
{"foo:n = 1"},
{"one:n = 1..2 andor v % 10 != 0"},
};
}
@Test
void testNullPluralRules() {
assertThrows(NullPointerException.class, () -> {
String[] pattern = {""};
new CompactNumberFormat("#", DFS, PATTERN, null);
});
}
@ParameterizedTest
@MethodSource("pluralRules")
void testPluralRules(String rules, Number n, String expected) {
var cnp = new CompactNumberFormat("#", DFS, PATTERN, rules);
assertEquals(expected, cnp.format(n));
}
@ParameterizedTest
@MethodSource("invalidRules")
void testInvalidRules(String rules) {
assertThrows(IllegalArgumentException.class,
() -> new CompactNumberFormat("#", DFS, PATTERN, rules));
}
@Test
void testLimitExceedingRules() {
assertThrows(IllegalArgumentException.class, () -> {
String andCond = " and n = 1";
String invalid = "one: n = 1" + andCond.repeat(2_048 / andCond.length());
new CompactNumberFormat("#", DFS, PATTERN, invalid);
});
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018, 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 8177552
* @summary Checks the formatting and parsing of special values
* @modules jdk.localedata
* @run junit/othervm TestSpecialValues
*/
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestSpecialValues {
private static final NumberFormat FORMAT = NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
Object[][] formatSpecialValues() {
return new Object[][]{
// number , formatted ouput
{+0.0, "0"},
{-0.0, "-0"},
{Double.MIN_VALUE, "0"},
{Double.MIN_NORMAL, "0"},
{Double.NaN, "NaN"},
{Double.POSITIVE_INFINITY, "\u221E"},
{Double.NEGATIVE_INFINITY, "-\u221E"},
{Long.MIN_VALUE, "-9223372T"},
{Long.MAX_VALUE, "9223372T"},};
}
Object[][] parseSpecialValues() {
return new Object[][]{
// parse string, parsed number
{"-0.0", -0.0},
{"" + Long.MIN_VALUE, Long.MIN_VALUE},
{"" + Long.MAX_VALUE, Long.MAX_VALUE},
{"NaN", Double.NaN},
{"\u221E", Double.POSITIVE_INFINITY},
{"-\u221E", Double.NEGATIVE_INFINITY},};
}
@ParameterizedTest
@MethodSource("formatSpecialValues")
void testFormatSpecialValues(Object number, String expected) {
CompactFormatAndParseHelper.testFormat(FORMAT, number, expected);
}
@ParameterizedTest
@MethodSource("parseSpecialValues")
void testParseSpecialValues(String parseString, Number expected)
throws ParseException {
CompactFormatAndParseHelper.testParse(FORMAT, parseString, expected, null, null);
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2018, 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 8177552 8221432
* @summary Checks the behaviour of Unicode BCP 47 U Extension with
* compact number format
* @modules jdk.localedata
* @run junit/othervm TestUExtensionOverride
*/
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestUExtensionOverride {
Object[][] compactFormatData() {
return new Object[][]{
// locale, number, formatted string
// -nu
{Locale.forLanguageTag("en-US-u-nu-deva"), 12345, "\u0967\u0968K"},
{Locale.forLanguageTag("en-US-u-nu-sinh"), 12345, "\u0de7\u0de8K"},
{Locale.forLanguageTag("en-US-u-nu-zzzz"), 12345, "12K"},
// -rg
{Locale.forLanguageTag("fr-FR-u-rg-cazzzz"), 1234567,
"1\u00a0M"},
{Locale.forLanguageTag("fr-FR-u-rg-cazzzz"), 1234567890,
"1\u00a0G"},
// -nu and -rg
{Locale.forLanguageTag("en-US-u-nu-deva-rg-dezzzz"), 12345,
"\u0967\u0968K"},
{Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-cazzzz"), 1234567890,
"1\u00a0Md"},
{Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-zzzz"), 12345,
"12\u00a0k"},
{Locale.forLanguageTag("fr-FR-u-rg-cazzzz-nu-deva"), 12345,
"\u0967\u0968\u00a0k"},};
}
Object[][] compactParseData() {
return new Object[][]{
// locale, parse string, parsed number
// -nu
{Locale.forLanguageTag("en-US-u-nu-deva"),
"\u0967\u0968K", 12000L},
{Locale.forLanguageTag("en-US-u-nu-sinh"),
"\u0de7\u0de8K", 12000L},
{Locale.forLanguageTag("en-US-u-nu-zzzz"),
"12K", 12000L},
// -rg
{Locale.forLanguageTag("fr-FR-u-rg-cazzzz"),
"1\u00a0G", 1000000000L},
// -nu and -rg
{Locale.forLanguageTag("en-US-u-nu-deva-rg-dezzzz"),
"\u0967\u0968K", 12000L},
{Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-cazzzz"),
"1\u00a0Md", 1000000000L},
{Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-zzzz"),
"12\u00a0k", 12000L},
{Locale.forLanguageTag("fr-FR-u-rg-cazzzz-nu-deva"),
"\u0967\u0968\u00a0k", 12000L},};
}
@ParameterizedTest
@MethodSource("compactFormatData")
void testFormat(Locale locale, double num,
String expected) {
NumberFormat cnf = NumberFormat.getCompactNumberInstance(locale,
NumberFormat.Style.SHORT);
CompactFormatAndParseHelper.testFormat(cnf, num, expected);
}
@ParameterizedTest
@MethodSource("compactParseData")
void testParse(Locale locale, String parseString,
Number expected) throws ParseException {
NumberFormat cnf = NumberFormat.getCompactNumberInstance(locale,
NumberFormat.Style.SHORT);
CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null);
}
}

View file

@ -0,0 +1,58 @@
/*
* 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 8321545
* @summary Ensure value returned by overridden toString method is as expected
* @run junit ToStringTest
*/
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ToStringTest {
// Check a normal expected value. There is no null test as the getInstance
// methods and constructor (which takes DFS) throw NPE if the locale param is null.
@Test
public void expectedValueTest() {
String expectedStr = "CompactNumberFormat [locale: \"English (Canada)\", decimal pattern: \"#,##0.###\", compact patterns: \"[, , , {one:0' 'thousand other:0' 'thousand}, {one:00' 'thousand other:00' 'thousand}, {one:000' 'thousand other:000' 'thousand}, {one:0' 'million other:0' 'million}, {one:00' 'million other:00' 'million}, {one:000' 'million other:000' 'million}, {one:0' 'billion other:0' 'billion}, {one:00' 'billion other:00' 'billion}, {one:000' 'billion other:000' 'billion}, {one:0' 'trillion other:0' 'trillion}, {one:00' 'trillion other:00' 'trillion}, {one:000' 'trillion other:000' 'trillion}]\"]\n";
var c = NumberFormat.getCompactNumberInstance(Locale.CANADA, NumberFormat.Style.LONG);
assertEquals(expectedStr, c.toString());
}
// Check an odd value with empty decimal pattern and compact patterns.
@Test
public void oddValueTest() {
String expectedStr = "CompactNumberFormat [locale: \"English (Canada)\", decimal pattern: \"\", compact patterns: \"[]\"]\n";
var c = new CompactNumberFormat("", new DecimalFormatSymbols(Locale.CANADA), new String[]{});
assertEquals(expectedStr, c.toString());
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2019, 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.
*/
module provider {
exports test;
provides java.text.spi.NumberFormatProvider with test.NumberFormatProviderImpl;
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2019, 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.
*/
package test;
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.spi.NumberFormatProvider;
import java.util.Locale;
public class NumberFormatProviderImpl extends NumberFormatProvider {
private static final Locale QAA = Locale.forLanguageTag("qaa");
private static final Locale QAB = Locale.forLanguageTag("qab");
private static final Locale[] locales = {QAA, QAB};
private static final String[] oldPattern = {
// old US short compact format
"",
"",
"",
"0K",
"00K",
"000K",
"0M",
"00M",
"000M",
"0B",
"00B",
"000B",
"0T",
"00T",
"000T"
};
private static final String[] newPattern = {
"",
"",
"",
"{one:0K;(0K) two:0KK few:0KKK other:0KKKK}",
"",
"",
"{one:0' 'M;(0' 'M) two:0' 'MM;(0' 'MM) few:0' 'MMM other:0' 'MMMM}"
};
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
return null;
}
@Override
public NumberFormat getIntegerInstance(Locale locale) {
return null;
}
@Override
public NumberFormat getNumberInstance(Locale locale) {
return null;
}
@Override
public NumberFormat getPercentInstance(Locale locale) {
return null;
}
@Override
public NumberFormat getCompactNumberInstance(Locale locale,
NumberFormat.Style style) {
if (locale.equals(QAB)) {
return new CompactNumberFormat(
"#",
DecimalFormatSymbols.getInstance(locale),
newPattern,
"one:v = 0 and i % 100 = 1;" +
"two:v = 0 and i % 100 = 2;" +
"few:v = 0 and i % 100 = 3..4 or v != 0;" +
"other:");
} else if (locale.equals(QAA)) {
return new CompactNumberFormat(
"#",
DecimalFormatSymbols.getInstance(locale),
oldPattern);
} else {
throw new RuntimeException("unsupported locale");
}
}
@Override
public Locale[] getAvailableLocales() {
return locales;
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2018, 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 8177552
* @modules jdk.localedata
* @summary Checks deserialization of compact number format
* @library /java/text/testlib
* @build TestDeserializeCNF HexDumpReader
* @run junit/othervm TestDeserializeCNF
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.RoundingMode;
import java.text.CompactNumberFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertEquals;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestDeserializeCNF {
// This object is serialized in cnf1.ser.txt with HALF_UP
// rounding mode, groupingsize = 3 and parseBigDecimal = true
private static final CompactNumberFormat COMPACT_FORMAT1 = new CompactNumberFormat("#,##0.###",
DecimalFormatSymbols.getInstance(Locale.US),
new String[]{"", "", "", "0K", "00K", "000K", "0M", "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"});
// This object is serialized in cnf2.ser.txt with min integer digits = 20
// and min fraction digits = 5
private static final CompactNumberFormat COMPACT_FORMAT2 = new CompactNumberFormat("#,##0.###",
DecimalFormatSymbols.getInstance(Locale.JAPAN),
new String[]{"", "", "", "0", "0\u4e07", "00\u4e07", "000\u4e07", "0000\u4e07", "0\u5104", "00\u5104", "000\u5104", "0000\u5104", "0\u5146", "00\u5146", "000\u5146"});
private static final String FILE_COMPACT_FORMAT1 = "cnf1.ser.txt";
private static final String FILE_COMPACT_FORMAT2 = "cnf2.ser.txt";
@BeforeAll
void mutateInstances() {
COMPACT_FORMAT1.setRoundingMode(RoundingMode.HALF_UP);
COMPACT_FORMAT1.setGroupingSize(3);
COMPACT_FORMAT1.setParseBigDecimal(true);
COMPACT_FORMAT2.setMinimumIntegerDigits(20);
COMPACT_FORMAT2.setMinimumFractionDigits(5);
}
@Test
void testDeserialization() throws IOException, ClassNotFoundException {
try (InputStream istream1 = HexDumpReader.getStreamFromHexDump(FILE_COMPACT_FORMAT1);
ObjectInputStream ois1 = new ObjectInputStream(istream1);
InputStream istream2 = HexDumpReader.getStreamFromHexDump(FILE_COMPACT_FORMAT2);
ObjectInputStream ois2 = new ObjectInputStream(istream2);) {
CompactNumberFormat obj1 = (CompactNumberFormat) ois1.readObject();
assertEquals(COMPACT_FORMAT1, obj1, "Deserialized instance is not"
+ " equal to the instance serialized in " + FILE_COMPACT_FORMAT1);
CompactNumberFormat obj2 = (CompactNumberFormat) ois2.readObject();
assertEquals(COMPACT_FORMAT2, obj2, "Deserialized instance is not"
+ " equal to the instance serialized in " + FILE_COMPACT_FORMAT2);
}
}
// The objects are serialized using the serialize() method, the hex
// dump printed is copied to respective object files
// void serialize(CompactNumberFormat cnf) {
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
// oos.writeObject(cnf);
// } catch (IOException ioe) {
// throw new RuntimeException(ioe);
// }
// byte[] ser = baos.toByteArray();
// for (byte b : ser) {
// System.out.print("" + String.format("%02x", b));
// }
// }
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2018, 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 8177552 8327640
* @modules jdk.localedata
* @summary Checks the serialization feature of CompactNumberFormat
* @run junit/othervm TestSerialization
*/
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.RoundingMode;
import java.text.CompactNumberFormat;
import java.text.NumberFormat;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertEquals;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestSerialization {
private static final NumberFormat FORMAT_HI = NumberFormat.getCompactNumberInstance(
Locale.of("hi"), NumberFormat.Style.SHORT);
private static final CompactNumberFormat FORMAT_EN_US = (CompactNumberFormat) NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_JA_JP = NumberFormat.getCompactNumberInstance(
Locale.JAPAN, NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_FR_FR = NumberFormat.getCompactNumberInstance(
Locale.FRANCE, NumberFormat.Style.LONG);
private static final NumberFormat FORMAT_DE_DE = NumberFormat.getCompactNumberInstance(
Locale.GERMANY, NumberFormat.Style.SHORT);
private static final NumberFormat FORMAT_KO_KR = NumberFormat.getCompactNumberInstance(
Locale.KOREA, NumberFormat.Style.SHORT);
@BeforeAll
void mutateInstances() {
FORMAT_HI.setMinimumFractionDigits(2);
FORMAT_HI.setMinimumIntegerDigits(5);
FORMAT_EN_US.setRoundingMode(RoundingMode.HALF_UP);
FORMAT_EN_US.setGroupingSize(3);
FORMAT_EN_US.setParseBigDecimal(true);
FORMAT_JA_JP.setMaximumFractionDigits(30);
FORMAT_JA_JP.setMaximumIntegerDigits(30);
FORMAT_FR_FR.setParseIntegerOnly(true);
FORMAT_FR_FR.setGroupingUsed(true);
FORMAT_FR_FR.setStrict(true);
// Setting minimum integer digits beyond the allowed range
FORMAT_DE_DE.setMinimumIntegerDigits(320);
// Setting minimum fraction digits beyond the allowed range
FORMAT_KO_KR.setMinimumFractionDigits(350);
}
@Test
void testSerialization() throws IOException, ClassNotFoundException {
// Serialize
serialize("cdf.ser", FORMAT_HI, FORMAT_EN_US, FORMAT_JA_JP, FORMAT_FR_FR, FORMAT_DE_DE, FORMAT_KO_KR);
// Deserialize
deserialize("cdf.ser", FORMAT_HI, FORMAT_EN_US, FORMAT_JA_JP, FORMAT_FR_FR, FORMAT_DE_DE, FORMAT_KO_KR);
}
private void serialize(String fileName, NumberFormat... formats)
throws IOException {
try (ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream(fileName))) {
for (NumberFormat fmt : formats) {
os.writeObject(fmt);
}
}
}
private static void deserialize(String fileName, NumberFormat... formats)
throws IOException, ClassNotFoundException {
try (ObjectInputStream os = new ObjectInputStream(
new FileInputStream(fileName))) {
for (NumberFormat fmt : formats) {
NumberFormat obj = (NumberFormat) os.readObject();
assertEquals(obj, fmt, "Serialized and deserialized"
+ " objects do not match");
long number = 123456789789L;
String expected = fmt.format(number);
String actual = obj.format(number);
assertEquals(expected, actual, "Serialized and deserialized"
+ " objects are expected to return same formatted"
+ " output for number: " + number);
}
}
}
}

View file

@ -0,0 +1,64 @@
#
# Copyright (c) 2018, 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.
#
# Hex dump of a serialized CompactNumberFormat for TestDeserializeCNF.
aced00057372001d6a6176612e746578742e436f6d706163744e756d626572466f726d
617462ed0c37b8207cf602000642000c67726f7570696e6753697a655a000f70617273
65426967446563696d616c5b000f636f6d706163745061747465726e737400135b4c6a
6176612f6c616e672f537472696e673b4c000e646563696d616c5061747465726e7400
124c6a6176612f6c616e672f537472696e673b4c000c726f756e64696e674d6f646574
00184c6a6176612f6d6174682f526f756e64696e674d6f64653b4c000773796d626f6c
737400204c6a6176612f746578742f446563696d616c466f726d617453796d626f6c73
3b787200166a6176612e746578742e4e756d626572466f726d6174dff6b3bf137d07e8
03000b5a000c67726f7570696e67557365644200116d61784672616374696f6e446967
6974734200106d6178496e74656765724469676974734900156d6178696d756d467261
6374696f6e4469676974734900146d6178696d756d496e746567657244696769747342
00116d696e4672616374696f6e4469676974734200106d696e496e7465676572446967
6974734900156d696e696d756d4672616374696f6e4469676974734900146d696e696d
756d496e74656765724469676974735a00107061727365496e74656765724f6e6c7949
001573657269616c56657273696f6e4f6e53747265616d787200106a6176612e746578
742e466f726d6174fbd8bc12e90f1843020000787000007f0000000000000135000100
000000000000010000000001780301757200135b4c6a6176612e6c616e672e53747269
6e673badd256e7e91d7b4702000078700000000f74000071007e000a71007e000a7400
02304b74000330304b7400043030304b740002304d74000330304d7400043030304d74
0002304274000330304274000430303042740002305474000330305474000430303054
740009232c2323302e2323237e7200166a6176612e6d6174682e526f756e64696e674d
6f646500000000000000001200007872000e6a6176612e6c616e672e456e756d000000
0000000000120000787074000748414c465f55507372001e6a6176612e746578742e44
6563696d616c466f726d617453796d626f6c73501d17990868939c0200114300106465
63696d616c536570617261746f72430005646967697443000b6578706f6e656e746961
6c43001167726f7570696e67536570617261746f724300096d696e75735369676e4300
116d6f6e6574617279536570617261746f724300107061747465726e53657061726174
6f724300077065724d696c6c43000770657263656e7449001573657269616c56657273
696f6e4f6e53747265616d4300097a65726f44696769744c00034e614e71007e00024c
000e63757272656e637953796d626f6c71007e00024c00146578706f6e656e7469616c
536570617261746f7271007e00024c0008696e66696e69747971007e00024c0012696e
746c43757272656e637953796d626f6c71007e00024c00066c6f63616c657400124c6a
6176612f7574696c2f4c6f63616c653b7870002e00230045002c002d002e003b203000
250000000300307400034e614e7074000145740003e2889e70737200106a6176612e75
74696c2e4c6f63616c657ef811609c30f9ec03000649000868617368636f64654c0007
636f756e74727971007e00024c000a657874656e73696f6e7371007e00024c00086c61
6e677561676571007e00024c000673637269707471007e00024c000776617269616e74
71007e00027870ffffffff740002555371007e000a740002656e71007e000a71007e00
0a78

View file

@ -0,0 +1,64 @@
#
# Copyright (c) 2018, 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.
#
# Hex dump of a serialized CompactNumberFormat for TestDeserializeCNF.
aced00057372001d6a6176612e746578742e436f6d706163744e756d626572466f726d
617462ed0c37b8207cf602000642000c67726f7570696e6753697a655a000f70617273
65426967446563696d616c5b000f636f6d706163745061747465726e737400135b4c6a
6176612f6c616e672f537472696e673b4c000e646563696d616c5061747465726e7400
124c6a6176612f6c616e672f537472696e673b4c000c726f756e64696e674d6f646574
00184c6a6176612f6d6174682f526f756e64696e674d6f64653b4c000773796d626f6c
737400204c6a6176612f746578742f446563696d616c466f726d617453796d626f6c73
3b787200166a6176612e746578742e4e756d626572466f726d6174dff6b3bf137d07e8
03000b5a000c67726f7570696e67557365644200116d61784672616374696f6e446967
6974734200106d6178496e74656765724469676974734900156d6178696d756d467261
6374696f6e4469676974734900146d6178696d756d496e746567657244696769747342
00116d696e4672616374696f6e4469676974734200106d696e496e7465676572446967
6974734900156d696e696d756d4672616374696f6e4469676974734900146d696e696d
756d496e74656765724469676974735a00107061727365496e74656765724f6e6c7949
001573657269616c56657273696f6e4f6e53747265616d787200106a6176612e746578
742e466f726d6174fbd8bc12e90f1843020000787000057f0000000500000135051400
000005000000140000000001780000757200135b4c6a6176612e6c616e672e53747269
6e673badd256e7e91d7b4702000078700000000f74000071007e000a71007e000a7400
013074000430e4b8877400053030e4b887740006303030e4b88774000730303030e4b8
8774000430e584847400053030e58484740006303030e5848474000730303030e58484
74000430e585867400053030e58586740006303030e58586740009232c2323302e2323
237e7200166a6176612e6d6174682e526f756e64696e674d6f64650000000000000000
1200007872000e6a6176612e6c616e672e456e756d0000000000000000120000787074
000948414c465f4556454e7372001e6a6176612e746578742e446563696d616c466f72
6d617453796d626f6c73501d17990868939c020011430010646563696d616c53657061
7261746f72430005646967697443000b6578706f6e656e7469616c43001167726f7570
696e67536570617261746f724300096d696e75735369676e4300116d6f6e6574617279
536570617261746f724300107061747465726e536570617261746f724300077065724d
696c6c43000770657263656e7449001573657269616c56657273696f6e4f6e53747265
616d4300097a65726f44696769744c00034e614e71007e00024c000e63757272656e63
7953796d626f6c71007e00024c00146578706f6e656e7469616c536570617261746f72
71007e00024c0008696e66696e69747971007e00024c0012696e746c43757272656e63
7953796d626f6c71007e00024c00066c6f63616c657400124c6a6176612f7574696c2f
4c6f63616c653b7870002e00230045002c002d002e003b203000250000000300307400
034e614e7074000145740003e2889e70737200106a6176612e7574696c2e4c6f63616c
657ef811609c30f9ec03000649000868617368636f64654c0007636f756e7472797100
7e00024c000a657874656e73696f6e7371007e00024c00086c616e677561676571007e
00024c000673637269707471007e00024c000776617269616e7471007e00027870ffff
ffff7400024a5071007e000a7400026a6171007e000a71007e000a78