changeset 49277:cf9e3c8607b7

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
author nishjain
date Thu, 22 Mar 2018 12:59:58 +0530
parents 3acb379b8672
children 31c5e0d5f4c3
files src/java.base/share/classes/java/util/Formatter.java test/jdk/java/util/Formatter/NoGroupingUsed.java test/jdk/java/util/Formatter/spi/FormatterWithProvider.java test/jdk/java/util/Formatter/spi/provider/module-info.java test/jdk/java/util/Formatter/spi/provider/test/NumberFormatProviderImpl.java
diffstat 5 files changed, 263 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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';
+                    }
                 }
             }
 
--- /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 + "]");
+        }
+    }
+
+}
--- /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);
+    }
+
+}
+
--- /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;
+}
--- /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;
+    }
+}
+