changeset 14687:6767ed99ea10

8146156: Inconsistent default locale in string formatter methods Reviewed-by: sherman
author naoto
date Mon, 06 Jun 2016 10:52:21 -0700
parents 5be0d816aff5
children 981ae344923f
files src/java.base/share/classes/java/io/PrintStream.java src/java.base/share/classes/java/lang/String.java src/java.base/share/classes/java/util/Formatter.java test/java/io/PrintStream/FormatLocale.java test/java/util/Formatter/FormatLocale.java
diffstat 5 files changed, 175 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/io/PrintStream.java	Mon Jun 06 09:13:35 2016 +0200
+++ b/src/java.base/share/classes/java/io/PrintStream.java	Mon Jun 06 10:52:21 2016 -0700
@@ -930,8 +930,10 @@
      * format string and arguments.
      *
      * <p> The locale always used is the one returned by {@link
-     * java.util.Locale#getDefault() Locale.getDefault()}, regardless of any
-     * previous invocations of other formatting methods on this object.
+     * java.util.Locale#getDefault(Locale.Category)} with
+     * {@link java.util.Locale.Category#FORMAT FORMAT} category specified,
+     * regardless of any previous invocations of other formatting methods on
+     * this object.
      *
      * @param  format
      *         A format string as described in <a
@@ -969,9 +971,11 @@
             synchronized (this) {
                 ensureOpen();
                 if ((formatter == null)
-                    || (formatter.locale() != Locale.getDefault()))
+                    || (formatter.locale() !=
+                        Locale.getDefault(Locale.Category.FORMAT)))
                     formatter = new Formatter((Appendable) this);
-                formatter.format(Locale.getDefault(), format, args);
+                formatter.format(Locale.getDefault(Locale.Category.FORMAT),
+                                 format, args);
             }
         } catch (InterruptedIOException x) {
             Thread.currentThread().interrupt();
--- a/src/java.base/share/classes/java/lang/String.java	Mon Jun 06 09:13:35 2016 +0200
+++ b/src/java.base/share/classes/java/lang/String.java	Mon Jun 06 10:52:21 2016 -0700
@@ -2718,7 +2718,9 @@
      * arguments.
      *
      * <p> The locale always used is the one returned by {@link
-     * java.util.Locale#getDefault() Locale.getDefault()}.
+     * java.util.Locale#getDefault(java.util.Locale.Category)
+     * Locale.getDefault(Locale.Category)} with
+     * {@link java.util.Locale.Category#FORMAT FORMAT} category specified.
      *
      * @param  format
      *         A <a href="../util/Formatter.html#syntax">format string</a>
--- a/src/java.base/share/classes/java/util/Formatter.java	Mon Jun 06 09:13:35 2016 +0200
+++ b/src/java.base/share/classes/java/util/Formatter.java	Mon Jun 06 10:52:21 2016 -0700
@@ -285,10 +285,10 @@
  * lower-case conversion characters except that the result is converted to
  * upper case according to the rules of the prevailing {@link java.util.Locale
  * Locale}.  The result is equivalent to the following invocation of {@link
- * String#toUpperCase()}
+ * String#toUpperCase(Locale)}
  *
  * <pre>
- *    out.toUpperCase() </pre>
+ *    out.toUpperCase(Locale.getDefault(Locale.Category.FORMAT)) </pre>
  *
  * <table cellpadding=5 summary="genConv">
  *
@@ -696,10 +696,10 @@
  * corresponding lower-case conversion characters except that the result is
  * converted to upper case according to the rules of the prevailing {@link
  * java.util.Locale Locale}.  The result is equivalent to the following
- * invocation of {@link String#toUpperCase()}
+ * invocation of {@link String#toUpperCase(Locale)}
  *
  * <pre>
- *    out.toUpperCase() </pre>
+ *    out.toUpperCase(Locale.getDefault(Locale.Category.FORMAT)) </pre>
  *
  * <h4><a name="dgen">General</a></h4>
  *
@@ -2906,7 +2906,7 @@
             if (precision != -1 && precision < s.length())
                 s = s.substring(0, precision);
             if (f.contains(Flags.UPPERCASE))
-                s = s.toUpperCase();
+                s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
             appendJustified(a, s);
         }
 
@@ -3130,7 +3130,7 @@
                     trailingZeros(sb, width - len);
                 }
                 if (f.contains(Flags.UPPERCASE))
-                    s = s.toUpperCase();
+                    s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
                 sb.append(s);
             }
 
@@ -3205,7 +3205,7 @@
                     trailingZeros(sb, width - len);
                 }
                 if (f.contains(Flags.UPPERCASE))
-                    s = s.toUpperCase();
+                    s = s.toUpperCase(Locale.getDefault(Locale.Category.FORMAT));
                 sb.append(s);
             }
 
@@ -3383,7 +3383,7 @@
                 if (upper) {
                     String tmp = s.substring(0, idx);
                     // don't localize hex
-                    tmp = tmp.toUpperCase(Locale.US);
+                    tmp = tmp.toUpperCase(Locale.ROOT);
                     va.append(tmp);
                 } else {
                     va.append(s, 0, idx);
@@ -3804,7 +3804,7 @@
 
             // justify based on width
             if (f.contains(Flags.UPPERCASE)) {
-                appendJustified(a, sb.toString().toUpperCase());
+                appendJustified(a, sb.toString().toUpperCase(Locale.getDefault(Locale.Category.FORMAT)));
             } else {
                 appendJustified(a, sb);
             }
@@ -3861,7 +3861,8 @@
                     ampm = dfs.getAmPmStrings();
                 }
                 String s = ampm[t.get(Calendar.AM_PM)];
-                sb.append(s.toLowerCase(Objects.requireNonNullElse(l, Locale.US)));
+                sb.append(s.toLowerCase(Objects.requireNonNullElse(l,
+                            Locale.getDefault(Locale.Category.FORMAT))));
                 break;
             }
             case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
@@ -3894,7 +3895,8 @@
                 TimeZone tz = t.getTimeZone();
                 sb.append(tz.getDisplayName((t.get(Calendar.DST_OFFSET) != 0),
                                            TimeZone.SHORT,
-                                           Objects.requireNonNullElse(l, Locale.US)));
+                                           Objects.requireNonNullElse(l,
+                                               Locale.getDefault(Locale.Category.FORMAT))));
                 break;
             }
 
@@ -3902,7 +3904,8 @@
             case DateTime.NAME_OF_DAY_ABBREV:     // 'a'
             case DateTime.NAME_OF_DAY:          { // 'A'
                 int i = t.get(Calendar.DAY_OF_WEEK);
-                Locale lt = Objects.requireNonNullElse(l, Locale.US);
+                Locale lt = Objects.requireNonNullElse(l,
+                                               Locale.getDefault(Locale.Category.FORMAT));
                 DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
                 if (c == DateTime.NAME_OF_DAY)
                     sb.append(dfs.getWeekdays()[i]);
@@ -3914,7 +3917,8 @@
             case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
             case DateTime.NAME_OF_MONTH:        { // 'B'
                 int i = t.get(Calendar.MONTH);
-                Locale lt = Objects.requireNonNullElse(l, Locale.US);
+                Locale lt = Objects.requireNonNullElse(l,
+                                               Locale.getDefault(Locale.Category.FORMAT));
                 DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
                 if (c == DateTime.NAME_OF_MONTH)
                     sb.append(dfs.getMonths()[i]);
@@ -3985,7 +3989,8 @@
                 StringBuilder tsb = new StringBuilder();
                 print(tsb, t, DateTime.AM_PM, l);
 
-                sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l, Locale.US)));
+                sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l,
+                                               Locale.getDefault(Locale.Category.FORMAT))));
                 break;
             }
             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
@@ -4023,7 +4028,7 @@
             print(sb, t, c, l);
             // justify based on width
             if (f.contains(Flags.UPPERCASE)) {
-                appendJustified(a, sb.toString().toUpperCase());
+                appendJustified(a, sb.toString().toUpperCase(Locale.getDefault(Locale.Category.FORMAT)));
             } else {
                 appendJustified(a, sb);
             }
@@ -4093,7 +4098,8 @@
                         ampm = dfs.getAmPmStrings();
                     }
                     String s = ampm[t.get(ChronoField.AMPM_OF_DAY)];
-                    sb.append(s.toLowerCase(Objects.requireNonNullElse(l, Locale.US)));
+                    sb.append(s.toLowerCase(Objects.requireNonNullElse(l,
+                            Locale.getDefault(Locale.Category.FORMAT))));
                     break;
                 }
                 case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
@@ -4224,7 +4230,8 @@
                     // this may be in wrong place for some locales
                     StringBuilder tsb = new StringBuilder();
                     print(tsb, t, DateTime.AM_PM, l);
-                    sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l, Locale.US)));
+                    sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l,
+                                        Locale.getDefault(Locale.Category.FORMAT))));
                     break;
                 }
                 case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/io/PrintStream/FormatLocale.java	Mon Jun 06 10:52:21 2016 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, 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 8146156
+ * @summary test whether conversion follows Locale.Category.FORMAT locale.
+ * @run main/othervm FormatLocale
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Locale;
+import java.util.Locale.Category;
+import java.util.stream.IntStream;
+
+public class FormatLocale {
+
+    static final float src = 3.14f;
+    static final List<Locale> formatLocale = List.of(Locale.US, Locale.FRANCE);
+    static final List<String> expected = List.of("3.14", "3,14");
+
+    public static void main(String [] args) {
+        IntStream.range(0, formatLocale.size()).forEach(i -> {
+            Locale.setDefault(Locale.Category.FORMAT, formatLocale.get(i));
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            new PrintStream(baos).format("%.2f", src);
+            if (!baos.toString().equals(expected.get(i))) {
+                throw new RuntimeException(
+                    "Wrong conversion with PrintStream.format() in locale "
+                    + formatLocale.get(i) +
+                    ". Expected: " + expected.get(i) +
+                    " Returned: " + baos.toString());
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/Formatter/FormatLocale.java	Mon Jun 06 10:52:21 2016 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, 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 8146156
+ * @summary test whether uppercasing follows Locale.Category.FORMAT locale.
+ * @run main/othervm FormatLocale
+ */
+
+import java.time.LocalDate;
+import java.time.Month;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Locale;
+import java.util.Locale.Category;
+import java.util.stream.IntStream;
+
+public class FormatLocale {
+
+    static final Locale TURKISH = new Locale("tr");
+
+    static final List<String> conversions = List.of(
+        "%S",
+        "%S",
+        "%TB",
+        "%G");
+    static final List<Object> src = List.of(
+        "Turkish",
+        "Turkish",
+        LocalDate.of(2016, Month.APRIL, 1),
+        Float.valueOf(100_000_000));
+    static final List<Locale> formatLocale = List.of(
+        Locale.ENGLISH,
+        TURKISH,
+        TURKISH,
+        Locale.FRANCE);
+    static final List<String> expected = List.of(
+        "TURKISH",
+        "TURK\u0130SH",
+        "N\u0130SAN",
+        "1,00000E+08");
+
+    public static void main(String [] args) {
+        StringBuilder sb = new StringBuilder();
+
+        IntStream.range(0, src.size()).forEach(i -> {
+            sb.setLength(0);
+            Locale.setDefault(Locale.Category.FORMAT, formatLocale.get(i));
+            new Formatter(sb).format(conversions.get(i), src.get(i));
+            if (!sb.toString().equals(expected.get(i))) {
+                throw new RuntimeException(
+                    "Wrong uppercasing with Formatter.format(" +
+                    "\"" + conversions.get(i) + "\"" +
+                    ") in locale "
+                    + formatLocale.get(i) +
+                    ". Expected: " + expected.get(i) +
+                    " Returned: " + sb.toString());
+            }
+        });
+    }
+}