diff --git a/src/java.base/share/classes/java/util/Formatter.java b/src/java.base/share/classes/java/util/Formatter.java index 4e8119ea1f3da76ce86346b5b45360a3f5785199..d61f2774df19aabfa8d780afd6fe15981f0bc598 100644 --- a/src/java.base/share/classes/java/util/Formatter.java +++ b/src/java.base/share/classes/java/util/Formatter.java @@ -47,6 +47,7 @@ import java.text.DateFormatSymbols; 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 java.time.temporal.UnsupportedTemporalTypeException; 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 @@ public final class Formatter implements Closeable, Flushable { } 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 --git a/test/jdk/java/util/Formatter/NoGroupingUsed.java b/test/jdk/java/util/Formatter/NoGroupingUsed.java new file mode 100644 index 0000000000000000000000000000000000000000..8e229f3580ea01fe24a4e44df7c0da442f334e7f --- /dev/null +++ b/test/jdk/java/util/Formatter/NoGroupingUsed.java @@ -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 --git a/test/jdk/java/util/Formatter/spi/FormatterWithProvider.java b/test/jdk/java/util/Formatter/spi/FormatterWithProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..8a79530e02484f2f59837cb7b36178773dc5438e --- /dev/null +++ b/test/jdk/java/util/Formatter/spi/FormatterWithProvider.java @@ -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 --git a/test/jdk/java/util/Formatter/spi/provider/module-info.java b/test/jdk/java/util/Formatter/spi/provider/module-info.java new file mode 100644 index 0000000000000000000000000000000000000000..edce16eed7100c82883294dc35ea04c3ce98c6a0 --- /dev/null +++ b/test/jdk/java/util/Formatter/spi/provider/module-info.java @@ -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 --git a/test/jdk/java/util/Formatter/spi/provider/test/NumberFormatProviderImpl.java b/test/jdk/java/util/Formatter/spi/provider/test/NumberFormatProviderImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..2a68c0fd97b92d64669927df8e327f2b45c04322 --- /dev/null +++ b/test/jdk/java/util/Formatter/spi/provider/test/NumberFormatProviderImpl.java @@ -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; + } +} +