4993841: (str) java.lang.Character should have a toString(int) method
authornaoto
Mon, 05 Mar 2018 08:50:47 -0800
changeset 49129 fb9f590b9eee
parent 49128 97288886180c
child 49130 4c2e97e4975a
child 49221 d8d1061ce34c
4993841: (str) java.lang.Character should have a toString(int) method Reviewed-by: martin, rriggs, sherman, smarks
src/java.base/share/classes/java/lang/Character.java
src/java.base/share/classes/java/lang/String.java
src/java.base/share/classes/java/lang/StringUTF16.java
test/jdk/java/lang/Character/Supplementary.java
--- a/src/java.base/share/classes/java/lang/Character.java	Mon Mar 05 08:27:42 2018 -0800
+++ b/src/java.base/share/classes/java/lang/Character.java	Mon Mar 05 08:50:47 2018 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -7568,6 +7568,11 @@
      * specified {@code char}.  The result is a string of length
      * 1 consisting solely of the specified {@code char}.
      *
+     * @apiNote This method cannot handle <a
+     * href="#supplementary"> supplementary characters</a>. To support
+     * all Unicode characters, including supplementary characters, use
+     * the {@link #toString(int)} method.
+     *
      * @param c the {@code char} to be converted
      * @return the string representation of the specified {@code char}
      * @since 1.4
@@ -7577,6 +7582,22 @@
     }
 
     /**
+     * Returns a {@code String} object representing the
+     * specified character (Unicode code point).  The result is a string of
+     * length 1 or 2, consisting solely of the specified {@code codePoint}.
+     *
+     * @param codePoint the {@code codePoint} to be converted
+     * @return the string representation of the specified {@code codePoint}
+     * @exception IllegalArgumentException if the specified
+     *      {@code codePoint} is not a {@linkplain #isValidCodePoint
+     *      valid Unicode code point}.
+     * @since 11
+     */
+    public static String toString(int codePoint) {
+        return String.valueOfCodePoint(codePoint);
+    }
+
+    /**
      * Determines whether the specified code point is a valid
      * <a href="http://www.unicode.org/glossary/#code_point">
      * Unicode code point value</a>.
--- a/src/java.base/share/classes/java/lang/String.java	Mon Mar 05 08:27:42 2018 -0800
+++ b/src/java.base/share/classes/java/lang/String.java	Mon Mar 05 08:50:47 2018 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 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
@@ -3157,4 +3157,27 @@
                 "begin " + begin + ", end " + end + ", length " + length);
         }
     }
+
+    /**
+     * Returns the string representation of the {@code codePoint}
+     * argument.
+     *
+     * @param   codePoint a {@code codePoint}.
+     * @return  a string of length {@code 1} or {@code 2} containing
+     *          as its single character the argument {@code codePoint}.
+     * @throws IllegalArgumentException if the specified
+     *          {@code codePoint} is not a {@linkplain Character#isValidCodePoint
+     *          valid Unicode code point}.
+     */
+    static String valueOfCodePoint(int codePoint) {
+        if (COMPACT_STRINGS && StringLatin1.canEncode(codePoint)) {
+            return new String(StringLatin1.toBytes((char)codePoint), LATIN1);
+        } else if (Character.isBmpCodePoint(codePoint)) {
+            return new String(StringUTF16.toBytes((char)codePoint), UTF16);
+        } else if (Character.isSupplementaryCodePoint(codePoint)) {
+            return new String(StringUTF16.toBytesSupplementary(codePoint), UTF16);
+        }
+
+        throw new IllegalArgumentException("Not a valid Unicode code point");
+    }
 }
--- a/src/java.base/share/classes/java/lang/StringUTF16.java	Mon Mar 05 08:27:42 2018 -0800
+++ b/src/java.base/share/classes/java/lang/StringUTF16.java	Mon Mar 05 08:50:47 2018 -0800
@@ -235,6 +235,13 @@
         return result;
     }
 
+    static byte[] toBytesSupplementary(int cp) {
+        byte[] result = new byte[4];
+        putChar(result, 0, Character.highSurrogate(cp));
+        putChar(result, 1, Character.lowSurrogate(cp));
+        return result;
+    }
+
     @HotSpotIntrinsicCandidate
     public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
         // We need a range check here because 'getChar' has no checks
--- a/test/jdk/java/lang/Character/Supplementary.java	Mon Mar 05 08:27:42 2018 -0800
+++ b/test/jdk/java/lang/Character/Supplementary.java	Mon Mar 05 08:50:47 2018 -0800
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 4533872 4985214 4985217 5017268 5017280
+ * @bug 4533872 4985214 4985217 4993841 5017268 5017280
  * @summary Unit tests for supplementary character support (JSR-204)
  * @compile Supplementary.java
  * @run main/timeout=600 Supplementary
@@ -59,6 +59,9 @@
         test04(str);
         test05(str);
 
+        // Test for toString(int)
+        test06();
+
         // Test unpaired surrogates
         testUnpaired();
 
@@ -454,6 +457,23 @@
         checkNewIndex(a, 0, index, length);
     }
 
+    /**
+     * Test toString(int)
+     *
+     * This test case assumes that Character.toChars()/String(char[]) work
+     * correctly.
+     */
+    static void test06() {
+        for (int cp = Character.MIN_CODE_POINT; cp <= Character.MAX_CODE_POINT; cp++) {
+            String result = Character.toString(cp);
+            String expected = new String(Character.toChars(cp));
+            if (!result.equals(expected)) {
+                throw new RuntimeException("Wrong string is created. code point: " +
+                    cp + ", result: " + result + ", expected: " + expected);
+            }
+        }
+    }
+
     private static void checkNewIndex(Object data, int offset, int result, int expected) {
         String type = getType(data);
         String offsetType = (offset > 0) ? "positive" : (offset < 0) ? "negative" : "0";
@@ -580,6 +600,7 @@
 
     // Test toChar(int)
     //      toChar(int, char[], int)
+    //      toString(int)
     // for exceptions
     static void testExceptions00() {
         callToChars1(-1, IllegalArgumentException.class);
@@ -595,6 +616,9 @@
         callToChars3(MIN_SUPPLEMENTARY, new char[1],  0, IndexOutOfBoundsException.class);
         callToChars3(MIN_SUPPLEMENTARY, new char[2], -1, IndexOutOfBoundsException.class);
         callToChars3(MIN_SUPPLEMENTARY, new char[2],  1, IndexOutOfBoundsException.class);
+
+        callToString(Character.MIN_CODE_POINT - 1, IllegalArgumentException.class);
+        callToString(Character.MAX_CODE_POINT + 1, IllegalArgumentException.class);
     }
 
     static final boolean At = true, Before = false;
@@ -834,6 +858,19 @@
                                    + expectedException.getName());
     }
 
+    private static void callToString(int codePoint, Class expectedException) {
+        try {
+            String s = Character.toString(codePoint);
+        } catch (Exception e) {
+            if (expectedException.isInstance(e)) {
+                return;
+            }
+            throw new RuntimeException("Unspecified exception", e);
+        }
+        throw new RuntimeException("toString(int) didn't throw "
+                                   + expectedException.getName());
+    }
+
     private static String getType(Object data) {
         return (data instanceof CharSequence) ? "CharSequence" : "char[]";
     }