# HG changeset patch # User nishjain # Date 1521703798 -19800 # Node ID cf9e3c8607b76bd9e80528197ea7270f16ab284b # Parent 3acb379b86725c47e7f33358cb22efa8752ae532 8196399: Formatting a decimal using locale-specific grouping separators causes ArithmeticException (division by zero). 8199672: ClassCastException is thrown by java.util.Formatter when an NumberFormatProvider SPI is used. Reviewed-by: naoto diff -r 3acb379b8672 -r cf9e3c8607b7 src/java.base/share/classes/java/util/Formatter.java --- a/src/java.base/share/classes/java/util/Formatter.java Thu Mar 22 08:09:51 2018 +0800 +++ b/src/java.base/share/classes/java/util/Formatter.java Thu Mar 22 12:59:58 2018 +0530 @@ -47,6 +47,7 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; +import java.text.spi.NumberFormatProvider; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.Objects; @@ -62,6 +63,8 @@ import jdk.internal.math.DoubleConsts; import jdk.internal.math.FormattedFloatingDecimal; +import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * An interpreter for printf-style format strings. This class provides support @@ -4476,8 +4479,33 @@ } else { DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l); grpSep = dfs.getGroupingSeparator(); - DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l); + DecimalFormat df = null; + NumberFormat nf = NumberFormat.getNumberInstance(l); + if (nf instanceof DecimalFormat) { + df = (DecimalFormat) nf; + } else { + + // Use DecimalFormat constructor to obtain the instance, + // in case NumberFormat.getNumberInstance(l) + // returns instance other than DecimalFormat + LocaleProviderAdapter adapter = LocaleProviderAdapter + .getAdapter(NumberFormatProvider.class, l); + if (!(adapter instanceof ResourceBundleBasedAdapter)) { + adapter = LocaleProviderAdapter.getResourceBundleBased(); + } + String[] all = adapter.getLocaleResources(l) + .getNumberPatterns(); + df = new DecimalFormat(all[0], dfs); + } grpSize = df.getGroupingSize(); + // Some locales do not use grouping (the number + // pattern for these locales does not contain group, e.g. + // ("#0.###")), but specify a grouping separator. + // To avoid unnecessary identification of the position of + // grouping separator, reset its value with null character + if (!df.isGroupingUsed() || grpSize == 0) { + grpSep = '\0'; + } } } diff -r 3acb379b8672 -r cf9e3c8607b7 test/jdk/java/util/Formatter/NoGroupingUsed.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/util/Formatter/NoGroupingUsed.java Thu Mar 22 12:59:58 2018 +0530 @@ -0,0 +1,78 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8196399 + * @summary test Formatter if any ArithmeticException is thrown while + * formatting a number in the locale which does not use any + * grouping, but specifies a grouping separator e.g. hy_AM. + * @modules jdk.localedata + */ +import java.util.Formatter; +import java.util.Locale; + +public class NoGroupingUsed { + + public static void main(String[] args) { + + Locale locale = new Locale("hy", "AM"); + String number = "1234567"; + String formatString = "%,d"; + + try { + testGrouping(locale, formatString, number); + } catch (ArithmeticException ex) { + throw new RuntimeException("[FAILED: ArithmeticException occurred" + + " while formatting the number: " + number + ", with" + + " format string: " + formatString + ", in locale: " + + locale, ex); + } + + } + + private static void testGrouping(Locale locale, String formatString, String number) { + + // test using String.format + String result = String.format(locale, formatString, Integer.parseInt(number)); + if (!number.equals(result)) { + throw new RuntimeException("[FAILED: Incorrect formatting" + + " of number: " + number + " using String.format with format" + + " string: " + formatString + " in locale: " + locale + + ". Actual: " + result + ", Expected: " + number + "]"); + } + + // test using Formatter's format + StringBuilder sb = new StringBuilder(); + Formatter formatter = new Formatter(sb, locale); + formatter.format(formatString, Integer.parseInt(number)); + if (!number.equals(sb.toString())) { + throw new RuntimeException("[FAILED: Incorrect formatting" + + " of number: " + number + "using Formatter.format with" + + " format string: " + formatString + " in locale: " + locale + + ". Actual: " + sb.toString() + ", Expected: " + number + "]"); + } + } + +} diff -r 3acb379b8672 -r cf9e3c8607b7 test/jdk/java/util/Formatter/spi/FormatterWithProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/util/Formatter/spi/FormatterWithProvider.java Thu Mar 22 12:59:58 2018 +0530 @@ -0,0 +1,69 @@ +/* + * 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. + */ +/* + * @test + * @bug 8199672 + * @summary test the Formatter.format() method with java.locale.providers=SPI, + * COMPAT. It should not throw ClassCastException if an SPI is + * used and NumberFormat.getInstance() does not return a + * DecimalFormat object. + * @modules jdk.localedata + * @library provider + * @build provider/module-info provider/test.NumberFormatProviderImpl + * @run main/othervm -Djava.locale.providers=SPI,COMPAT FormatterWithProvider + */ + +import java.util.Formatter; +import java.util.Locale; + +public class FormatterWithProvider { + + public static void main(String[] args) { + + Integer number = 1234567; + String formatString = "%,d"; + + try { + testFormatter(Locale.JAPANESE, formatString, number); + testFormatter(Locale.FRENCH, formatString, number); + testFormatter(new Locale("hi", "IN"), formatString, number); + + } catch (ClassCastException ex) { + throw new RuntimeException("[FAILED: A ClassCastException is" + + " thrown while using Formatter.format() with VM" + + " argument java.locale.providers=SPI,COMPAT]", ex); + } + } + + private static void testFormatter(Locale locale, String formatString, + Integer number) { + + // test using String.format + String.format(locale, formatString, number); + // test using Formatter's format + Formatter formatter = new Formatter(locale); + formatter.format(formatString, number); + } + +} + diff -r 3acb379b8672 -r cf9e3c8607b7 test/jdk/java/util/Formatter/spi/provider/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/util/Formatter/spi/provider/module-info.java Thu Mar 22 12:59:58 2018 +0530 @@ -0,0 +1,26 @@ +/* + * 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. +*/ +module provider { + exports test; + provides java.text.spi.NumberFormatProvider with test.NumberFormatProviderImpl; +} diff -r 3acb379b8672 -r cf9e3c8607b7 test/jdk/java/util/Formatter/spi/provider/test/NumberFormatProviderImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/util/Formatter/spi/provider/test/NumberFormatProviderImpl.java Thu Mar 22 12:59:58 2018 +0530 @@ -0,0 +1,61 @@ +/* + * 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. + */ +package test; + +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.text.spi.NumberFormatProvider; +import java.util.Locale; + +public class NumberFormatProviderImpl extends NumberFormatProvider { + + private static final Locale[] locales = {Locale.FRENCH, Locale.JAPANESE, + new Locale("hi", "IN")}; + + @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 Locale[] getAvailableLocales() { + return locales; + } +} +