1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4533872 4985214 4985217 5017268 5017280
  27  * @summary Unit tests for supplementary character support (JSR-204)
  28  * @compile Supplementary.java
  29  * @run main/timeout=600 Supplementary
  30  */
  31 
  32 public class Supplementary {
  33     private static final char MIN_HIGH = '\uD800';
  34     private static final char MAX_HIGH = '\uDBFF';
  35     private static final char MIN_LOW = MAX_HIGH + 1;
  36     private static final char MAX_LOW = '\uDFFF';
  37     private static final int MIN_CODE_POINT = 0x000000;
  38     private static final int MIN_SUPPLEMENTARY = 0x010000;
  39     private static final int MAX_SUPPLEMENTARY = 0x10ffff;
  40 
  41     public static void main(String[] args) {
  42         // Do not change the order of test method calls since there
  43         // are some interdependencies.
  44 
  45         testConstants();
  46 
  47         test00();
  48 
  49         // Store all Unicode code points, except for surrogate code
  50         // points, in cu[] through the loops below. Then, use the data
  51         // for code point/code unit conversion and other tests later.
  52         char[] cu = new char[(MAX_SUPPLEMENTARY+1) * 2];
  53         int length = test01(cu);
  54 
  55         String str = new String(cu, 0, length);
  56         cu = null;
  57         test02(str);
  58         test03(str.toCharArray());
  59         test04(str);
  60         test05(str);
  61 
  62         // Test unpaired surrogates
  63         testUnpaired();
  64 
  65         // Test exceptions
  66         testExceptions00();
  67         testExceptions01(str);
  68         testExceptions02(str.toCharArray());
  69     }
  70 
  71     static void testConstants() {
  72         if (Character.MIN_HIGH_SURROGATE != MIN_HIGH) {
  73             constantError("MIN_HIGH_SURROGATE", Character.MIN_HIGH_SURROGATE, MIN_HIGH);
  74         }
  75         if (Character.MAX_HIGH_SURROGATE != MAX_HIGH) {
  76             constantError("MAX_HIGH_SURROGATE", Character.MAX_HIGH_SURROGATE, MAX_HIGH);
  77         }
  78         if (Character.MIN_LOW_SURROGATE != MIN_LOW) {
  79             constantError("MIN_LOW_SURROGATE", Character.MIN_LOW_SURROGATE, MIN_LOW);
  80         }
  81         if (Character.MAX_LOW_SURROGATE != MAX_LOW) {
  82             constantError("MAX_LOW_SURROGATE", Character.MAX_LOW_SURROGATE, MAX_LOW);
  83         }
  84         if (Character.MIN_SURROGATE != MIN_HIGH) {
  85             constantError("MIN_SURROGATE", Character.MIN_SURROGATE, MIN_HIGH);
  86         }
  87         if (Character.MAX_SURROGATE != MAX_LOW) {
  88             constantError("MAX_SURROGATE", Character.MAX_SURROGATE, MAX_LOW);
  89         }
  90         if (Character.MIN_SUPPLEMENTARY_CODE_POINT != MIN_SUPPLEMENTARY) {
  91             constantError("MIN_SUPPLEMENTARY_CODE_POINT",
  92                           Character.MIN_SUPPLEMENTARY_CODE_POINT, MIN_SUPPLEMENTARY);
  93         }
  94         if (Character.MIN_CODE_POINT != MIN_CODE_POINT) {
  95             constantError("MIN_CODE_POINT", Character.MIN_CODE_POINT, MIN_CODE_POINT);
  96         }
  97         if (Character.MAX_CODE_POINT != MAX_SUPPLEMENTARY) {
  98             constantError("MAX_CODE_POINT", Character.MAX_CODE_POINT, MAX_SUPPLEMENTARY);
  99         }
 100     }
 101 
 102     static void constantError(String name, int value, int expectedValue) {
 103         throw new RuntimeException("Character." + name + " has a wrong value: got "
 104                                    + toHexString(value)
 105                                    + ", expected " + toHexString(expectedValue));
 106     }
 107 
 108     /*
 109      * Test isValidCodePoint(int)
 110      *      isSupplementaryCodePoint(int)
 111      *      charCount(int)
 112      */
 113     static void test00() {
 114         for (int cp = -MAX_SUPPLEMENTARY; cp <= MAX_SUPPLEMENTARY*2; cp++) {
 115             boolean isValid = cp >= 0 && cp <= MAX_SUPPLEMENTARY;
 116             if (Character.isValidCodePoint(cp) != isValid) {
 117                 throw new RuntimeException("isValidCodePoint failed with "
 118                                            + toHexString(cp));
 119             }
 120             boolean isSupplementary = cp >= MIN_SUPPLEMENTARY && cp <= MAX_SUPPLEMENTARY;
 121             if (Character.isSupplementaryCodePoint(cp) != isSupplementary) {
 122                 throw new RuntimeException("isSupplementaryCodePoint failed with "
 123                                            + toHexString(cp));
 124             }
 125             int len = Character.charCount(cp);
 126             if (isValid) {
 127                 if ((isSupplementary && len != 2)
 128                     || (!isSupplementary && len != 1)) {
 129                     throw new RuntimeException("wrong character length "+len+" for "
 130                                                + toHexString(cp));
 131                 }
 132             } else if (len != 1 && len != 2) {
 133                 throw new RuntimeException("wrong character length "+len+" for "
 134                                            + toHexString(cp));
 135             }
 136         }
 137     }
 138 
 139     /**
 140      * Test toChar(int)
 141      *      toChar(int, char[], int)
 142      *      isHighSurrogate(char)
 143      *      isLowSurrogate(char)
 144      *      isSurrogatePair(int, int)
 145      *
 146      * While testing those methods, this method generates all Unicode
 147      * code points (except for surrogate code points) and store them
 148      * in cu.
 149      *
 150      * @return the number of code units generated in cu
 151      */
 152     static int test01(char[] cu) {
 153         int index = 0;
 154         // Test toChar(int)
 155         //      toChar(int, char[], int)
 156         //      isHighSurrogate(char)
 157         //      isLowSurrogate(char)
 158         // with BMP code points
 159         for (int i = 0; i <= Character.MAX_VALUE; i++) {
 160             char[] u = Character.toChars(i);
 161             if (u.length != 1 || u[0] != i) {
 162                 throw new RuntimeException("wrong toChars(int) result for BMP: "
 163                                            + toHexString("u", u));
 164             }
 165             int n = Character.toChars(i, cu, index);
 166             if (n != 1 || cu[index] != i) {
 167                 throw new RuntimeException("wrong toChars(int, char[], int) result for BMP:"
 168                                            + " len=" + n
 169                                            + ", cu["+index+"]="+toHexString(cu[index]));
 170             }
 171             boolean isHigh = i >= MIN_HIGH && i <= MAX_HIGH;
 172             if (Character.isHighSurrogate((char) i) != isHigh) {
 173                 throw new RuntimeException("wrong high-surrogate test for "
 174                                            + toHexString(i));
 175             }
 176             boolean isLow = i >= MIN_LOW && i <= MAX_LOW;
 177             if (Character.isLowSurrogate((char)i) != isLow) {
 178                 throw new RuntimeException("wrong low-surrogate test for "
 179                                            + toHexString(i));
 180             }
 181             if (!isHigh && !isLow) {
 182                 index++;
 183             }
 184         }
 185 
 186         // Test isSurrogatePair with all surrogate pairs
 187         // Test toChars(int)
 188         //      toChars(int, char[], int)
 189         // with all supplementary characters
 190         int supplementary = MIN_SUPPLEMENTARY;
 191         for (int i = Character.MAX_VALUE/2; i <= Character.MAX_VALUE; i++) {
 192             char hi = (char) i;
 193             boolean isHigh = Character.isHighSurrogate(hi);
 194 
 195             for (int j = Character.MAX_VALUE/2; j <= Character.MAX_VALUE; j++) {
 196                 char lo = (char) j;
 197                 boolean isLow = Character.isLowSurrogate(lo);
 198                 boolean isSurrogatePair = isHigh && isLow;
 199                 if (Character.isSurrogatePair(hi, lo) != isSurrogatePair) {
 200                     throw new RuntimeException("wrong surrogate pair test for hi="
 201                                                + toHexString(hi)
 202                                                + ", lo="+toHexString(lo));
 203                 }
 204                 if (isSurrogatePair) {
 205                     int cp = Character.toCodePoint(hi, lo);
 206                     if (cp != supplementary) {
 207                         throw new RuntimeException("wrong code point: got "
 208                                                    + toHexString(cp)
 209                                                    + ", expected="
 210                                                    + toHexString(supplementary));
 211                     }
 212                     char[] u = Character.toChars(cp);
 213                     if (u.length != 2 || u[0] != hi || u[1] != lo) {
 214                         throw new RuntimeException("wrong toChars(int) result for supplementary: "+
 215                                                    toHexString("u", u));
 216                     }
 217                     int n = Character.toChars(cp, cu, index);
 218                     if (n != 2 || cu[index] != hi || cu[index+1] != lo) {
 219                         throw new RuntimeException("wrong toChars(int, char[], int) result "
 220                                            + "for supplementary: len=" + n
 221                                            + ", cu["+index+"]=" + toHexString(cu[index])
 222                                            + ", cu["+(index+1)+"]=" + toHexString(cu[index+1]));
 223                     }
 224                     index += n;
 225                     supplementary++;
 226                 }
 227             }
 228         }
 229         if (supplementary != MAX_SUPPLEMENTARY + 1) {
 230             throw new RuntimeException("wrong supplementary count (supplementary="
 231                                        + toHexString(supplementary)+")");
 232         }
 233 
 234         int nCodeUnits = Character.MAX_VALUE + 1 - (MAX_LOW - MIN_HIGH + 1)
 235                          + ((MAX_SUPPLEMENTARY - MIN_SUPPLEMENTARY + 1) * 2);
 236         if (index != nCodeUnits) {
 237             throw new RuntimeException("wrong number of code units: " + index
 238                                        + ", expected " + nCodeUnits);
 239         }
 240         return index;
 241     }
 242 
 243     /**
 244      * Test codePointAt(CharSequence, int)
 245      *      codePointBefore(CharSequence, int)
 246      */
 247     static void test02(CharSequence cs) {
 248         int cp = 0;
 249         int ch;
 250         for (int i = 0; i < cs.length(); i += Character.charCount(ch)) {
 251             ch = Character.codePointAt(cs, i);
 252             if (ch != cp) {
 253                 throw new RuntimeException("wrong codePointAt(CharSequence, "+i+") value: got "
 254                                            + toHexString(ch)
 255                                            + ", expected "+toHexString(cp));
 256             }
 257             cp++;
 258             // Skip surrogates
 259             if (cp == MIN_HIGH) {
 260                 cp = MAX_LOW + 1;
 261             }
 262         }
 263 
 264         cp--;
 265         for (int i = cs.length(); i > 0; i -= Character.charCount(ch)) {
 266             ch = Character.codePointBefore(cs, i);
 267             if (ch != cp) {
 268                 throw new RuntimeException("codePointBefore(CharSequence, "+i+") returned "
 269                                            + toHexString(ch)
 270                                            + ", expected " + toHexString(cp));
 271             }
 272             cp--;
 273             // Skip surrogates
 274             if (cp == MAX_LOW) {
 275                 cp = MIN_HIGH - 1;
 276             }
 277         }
 278     }
 279 
 280     /**
 281      * Test codePointAt(char[], int)
 282      *      codePointAt(char[], int, int)
 283      *      codePointBefore(char[], int)
 284      *      codePointBefore(char[], int, int)
 285      */
 286     static void test03(char[] a) {
 287         int cp = 0;
 288         int ch;
 289         for (int i = 0; i < a.length; i += Character.charCount(ch)) {
 290             ch = Character.codePointAt(a, i);
 291             if (ch != cp) {
 292                 throw new RuntimeException("codePointAt(char[], "+i+") returned "
 293                                            + toHexString(ch)
 294                                            + ", expected "+toHexString(cp));
 295             }
 296             int x = Character.codePointAt(a, i, i+1);
 297             if (x != a[i]) {
 298                 throw new RuntimeException(String.format(
 299                                  "codePointAt(char[], %d, %d) returned 0x%04x, expected 0x%04x\n",
 300                                  i, i+1, x, (int)a[i]));
 301             }
 302             cp++;
 303             // Skip surrogates
 304             if (cp == MIN_HIGH) {
 305                 cp = MAX_LOW + 1;
 306             }
 307         }
 308 
 309         cp--;
 310         for (int i = a.length; i > 0; i -= Character.charCount(ch)) {
 311             ch = Character.codePointBefore(a, i);
 312             if (ch != cp) {
 313                 throw new RuntimeException("codePointBefore(char[], "+i+") returned "
 314                                            + toHexString(ch)
 315                                            + ", expected " + toHexString(cp));
 316             }
 317             int x = Character.codePointBefore(a, i, i-1);
 318             if (x != a[i-1]) {
 319                 throw new RuntimeException(String.format(
 320                                  "codePointAt(char[], %d, %d) returned 0x%04x, expected 0x%04x\n",
 321                                  i, i-1, x, (int)a[i-1]));
 322             }
 323             cp--;
 324             // Skip surrogates
 325             if (cp == MAX_LOW) {
 326                 cp = MIN_HIGH - 1;
 327             }
 328         }
 329     }
 330 
 331     /**
 332      * Test codePointCount(CharSequence, int, int)
 333      *      codePointCount(char[], int, int, int, int)
 334      */
 335     static void test04(String str) {
 336         int length = str.length();
 337         char[] a = str.toCharArray();
 338 
 339         for (int i = 0; i <= length; i += 99, length -= 29999) {
 340             int n = Character.codePointCount(str, i, length);
 341             int m = codePointCount(str.substring(i, length));
 342             checkCodePointCount(str, n, m);
 343             n = Character.codePointCount(a, i, length - i);
 344             checkCodePointCount(a, n, m);
 345         }
 346 
 347         // test special cases
 348         length = str.length();
 349         int n = Character.codePointCount(str, 0, 0);
 350         checkCodePointCount(str, n, 0);
 351         n = Character.codePointCount(str, length, length);
 352         checkCodePointCount(str, n, 0);
 353         n = Character.codePointCount(a, 0, 0);
 354         checkCodePointCount(a, n, 0);
 355         n = Character.codePointCount(a, length, 0);
 356         checkCodePointCount(a, n, 0);
 357     }
 358 
 359     // This method assumes that Character.codePointAt() and
 360     // Character.charCount() work correctly.
 361     private static int codePointCount(CharSequence seq) {
 362         int n = 0, len;
 363         for (int i = 0; i < seq.length(); i += len) {
 364             int codepoint = Character.codePointAt(seq, i);
 365             n++;
 366             len = Character.charCount(codepoint);
 367         }
 368         return n;
 369     }
 370 
 371     private static void checkCodePointCount(Object data, int n, int expected) {
 372         String type = getType(data);
 373         if (n != expected) {
 374             throw new RuntimeException("codePointCount(" + type + "...) returned " + n
 375                                        + ", expected " + expected);
 376         }
 377     }
 378 
 379     /**
 380      * Test offsetByCodePoints(CharSequence, int, int)
 381      *      offsetByCodePoints(char[], int, int, int, int)
 382      *
 383      * This test case assumes that Character.codePointCount()s work
 384      * correctly.
 385      */
 386     static void test05(String str) {
 387         int length = str.length();
 388         char[] a = str.toCharArray();
 389 
 390         for (int i = 0; i <= length; i += 99, length -= 29999) {
 391             int nCodePoints = Character.codePointCount(a, i, length - i);
 392             int index;
 393 
 394             // offsetByCodePoints(CharSequence, int, int)
 395 
 396             int expectedHighIndex = length;
 397             // For forward CharSequence scan, we need to adjust the
 398             // expected index in case the last char in the text range
 399             // is a high surrogate and forms a valid supplementary
 400             // code point with the next char.
 401             if (length < a.length) {
 402                 int cp = Character.codePointAt(a, length - 1);
 403                 if (Character.isSupplementaryCodePoint(cp)) {
 404                     expectedHighIndex++;
 405                 }
 406             }
 407             index = Character.offsetByCodePoints(str, i, nCodePoints);
 408             checkNewIndex(str, nCodePoints, index, expectedHighIndex);
 409             int expectedLowIndex = i;
 410             if (i > 0) {
 411                 int cp = Character.codePointBefore(a, i + 1);
 412                 if (Character.isSupplementaryCodePoint(cp)) {
 413                     expectedLowIndex--;
 414                 }
 415             }
 416             index = Character.offsetByCodePoints(str, length, -nCodePoints);
 417             checkNewIndex(str, -nCodePoints, index, expectedLowIndex);
 418 
 419             // offsetByCodePoints(char[], int, int, int, int)
 420 
 421             int start = Math.max(0, i-1);
 422             int limit = Math.min(a.length, length+1);
 423             index = Character.offsetByCodePoints(a, start, limit - start,
 424                                                  i, nCodePoints);
 425             checkNewIndex(a, nCodePoints, index, expectedHighIndex);
 426             if (length != expectedHighIndex) {
 427                 index = Character.offsetByCodePoints(a, start, length - start,
 428                                                      i, nCodePoints);
 429                 checkNewIndex(a, nCodePoints, index, length);
 430             }
 431             index = Character.offsetByCodePoints(a, start, limit - start,
 432                                                  length, -nCodePoints);
 433             checkNewIndex(a, -nCodePoints, index, expectedLowIndex);
 434             if (i != expectedLowIndex) {
 435                 index = Character.offsetByCodePoints(a, i, limit - i,
 436                                                      length, -nCodePoints);
 437                 checkNewIndex(a, -nCodePoints, index, i);
 438             }
 439         }
 440 
 441         // test special cases for 0-length text ranges.
 442         length = str.length();
 443         int index = Character.offsetByCodePoints(str, 0, 0);
 444         checkNewIndex(str, 0, index, 0);
 445         index = Character.offsetByCodePoints(str, length, 0);
 446         checkNewIndex(str, 0, index, length);
 447         index = Character.offsetByCodePoints(a, 0, 0, 0, 0);
 448         checkNewIndex(a, 0, index, 0);
 449         index = Character.offsetByCodePoints(a, 0, length, 0, 0);
 450         checkNewIndex(a, 0, index, 0);
 451         index = Character.offsetByCodePoints(a, 0, length, length, 0);
 452         checkNewIndex(a, 0, index, length);
 453         index = Character.offsetByCodePoints(a, length, 0, length, 0);
 454         checkNewIndex(a, 0, index, length);
 455     }
 456 
 457     private static void checkNewIndex(Object data, int offset, int result, int expected) {
 458         String type = getType(data);
 459         String offsetType = (offset > 0) ? "positive" : (offset < 0) ? "negative" : "0";
 460         if (result != expected) {
 461             throw new RuntimeException("offsetByCodePoints(" + type + ", ...) ["
 462                                        + offsetType + " offset]"
 463                                        + " returned " + result
 464                                        + ", expected " + expected);
 465         }
 466     }
 467 
 468     // Test codePointAt(CharSequence, int)
 469     //      codePointBefore(CharSequence, int)
 470     //      codePointAt(char[], int)
 471     //      codePointBefore(char[], int)
 472     //      toChar(int)
 473     //      toChar(int, char[], int)
 474     // with unpaired surrogates
 475     static void testUnpaired() {
 476         testCodePoint("\uD800", new int[] { 0xD800 });
 477         testCodePoint("\uDC00", new int[] { 0xDC00 });
 478         testCodePoint("a\uD800", new int[] { 'a', 0xD800 });
 479         testCodePoint("a\uDC00", new int[] { 'a', 0xDC00 });
 480         testCodePoint("\uD800a", new int[] { 0xD800, 'a' });
 481         testCodePoint("\uDBFFa", new int[] { 0xDBFF, 'a' });
 482         testCodePoint("a\uD800\uD801", new int[] { 'a', 0xD800, 0xD801 });
 483         testCodePoint("a\uD800x\uDC00", new int[] { 'a', 0xD800, 'x', 0xDC00 });
 484         testCodePoint("\uDC00\uD800", new int[] { 0xDC00, 0xD800 });
 485         testCodePoint("\uD800\uDC00\uDC00", new int[] { 0x10000, 0xDC00 });
 486         testCodePoint("\uD800\uD800\uDC00", new int[] { 0xD800, 0x10000 });
 487         testCodePoint("\uD800\uD800\uD800\uD800\uDC00\uDC00\uDC00\uDC00",
 488                       new int[] { 0xD800, 0xD800, 0xD800, 0x10000, 0xDC00, 0xDC00, 0xDC00});
 489     }
 490 
 491     static void testCodePoint(String str, int[] codepoints) {
 492         int c;
 493         // Test Character.codePointAt/Before(CharSequence, int)
 494         int j = 0;
 495         for (int i = 0; i < str.length(); i += Character.charCount(c)) {
 496             c = Character.codePointAt(str, i);
 497             if (c != codepoints[j++]) {
 498                 throw new RuntimeException("codePointAt(CharSequence, " + i + ") returned "
 499                                            + toHexString(c)
 500                                            + ", expected " + toHexString(codepoints[j-1]));
 501             }
 502         }
 503         if (j != codepoints.length) {
 504             throw new RuntimeException("j != codepoints.length after codePointAt(CharSequence, int)"
 505                                        + " (j=" + j + ")"
 506                                        + ", expected: " + codepoints.length);
 507         }
 508 
 509         j = codepoints.length;
 510         for (int i = str.length(); i > 0 ; i -= Character.charCount(c)) {
 511             c = Character.codePointBefore(str, i);
 512             if (c != codepoints[--j]) {
 513                 throw new RuntimeException("codePointBefore(CharSequence, " + i + ") returned "
 514                                            + toHexString(c)
 515                                            + ", expected " + toHexString(codepoints[j]));
 516             }
 517         }
 518         if (j != 0) {
 519             throw new RuntimeException("j != 0 after codePointBefore(CharSequence, int)"
 520                                        + " (j=" + j + ")");
 521         }
 522 
 523         // Test Character.codePointAt/Before(char[], int)
 524         char[] a = str.toCharArray();
 525         j = 0;
 526         for (int i = 0; i < a.length; i += Character.charCount(c)) {
 527             c = Character.codePointAt(a, i);
 528             if (c != codepoints[j++]) {
 529                 throw new RuntimeException("codePointAt(char[], " + i + ") returned "
 530                                            + toHexString(c)
 531                                            + ", expected " + toHexString(codepoints[j-1]));
 532             }
 533         }
 534         if (j != codepoints.length) {
 535             throw new RuntimeException("j != codepoints.length after codePointAt(char[], int)"
 536                                        + " (j=" + j + ")"
 537                                        + ", expected: " + codepoints.length);
 538         }
 539 
 540         j = codepoints.length;
 541         for (int i = a.length; i > 0 ; i -= Character.charCount(c)) {
 542             c = Character.codePointBefore(a, i);
 543             if (c != codepoints[--j]) {
 544                 throw new RuntimeException("codePointBefore(char[], " + i + ") returned "
 545                                            + toHexString(c)
 546                                            + ", expected " + toHexString(codepoints[j]));
 547             }
 548         }
 549         if (j != 0) {
 550             throw new RuntimeException("j != 0 after codePointBefore(char[], int)"
 551                                        + " (j=" + j + ")");
 552         }
 553 
 554         // Test toChar(int)
 555         j = 0;
 556         for (int i = 0; i < codepoints.length; i++) {
 557             a = Character.toChars(codepoints[i]);
 558             for (int k = 0; k < a.length; k++) {
 559                 if (str.charAt(j++) != a[k]) {
 560                     throw new RuntimeException("toChars(int) returned " + toHexString("result", a)
 561                                                + " from codepoint=" + toHexString(codepoints[i]));
 562                 }
 563             }
 564         }
 565 
 566         // Test toChars(int, char[], int)
 567         a = new char[codepoints.length * 2];
 568         j = 0;
 569         for (int i = 0; i < codepoints.length; i++) {
 570             int n = Character.toChars(codepoints[i], a, j);
 571             j += n;
 572         }
 573         String s = new String(a, 0, j);
 574         if (!str.equals(s)) {
 575             throw new RuntimeException("toChars(int, char[], int) returned "
 576                                        + toHexString("dst", s.toCharArray())
 577                                        + ", expected " + toHexString("data", str.toCharArray()));
 578         }
 579     }
 580 
 581     // Test toChar(int)
 582     //      toChar(int, char[], int)
 583     // for exceptions
 584     static void testExceptions00() {
 585         callToChars1(-1, IllegalArgumentException.class);
 586         callToChars1(MAX_SUPPLEMENTARY + 1, IllegalArgumentException.class);
 587 
 588         callToChars3(MAX_SUPPLEMENTARY, null, 0, NullPointerException.class);
 589         callToChars3(-MIN_SUPPLEMENTARY,    new char[2], 0, IllegalArgumentException.class);
 590         callToChars3(MAX_SUPPLEMENTARY + 1, new char[2], 0, IllegalArgumentException.class);
 591         callToChars3('A', new char[0],  0, IndexOutOfBoundsException.class);
 592         callToChars3('A', new char[1], -1, IndexOutOfBoundsException.class);
 593         callToChars3('A', new char[1],  1, IndexOutOfBoundsException.class);
 594         callToChars3(MIN_SUPPLEMENTARY, new char[0],  0, IndexOutOfBoundsException.class);
 595         callToChars3(MIN_SUPPLEMENTARY, new char[1],  0, IndexOutOfBoundsException.class);
 596         callToChars3(MIN_SUPPLEMENTARY, new char[2], -1, IndexOutOfBoundsException.class);
 597         callToChars3(MIN_SUPPLEMENTARY, new char[2],  1, IndexOutOfBoundsException.class);
 598     }
 599 
 600     static final boolean At = true, Before = false;
 601 
 602     /**
 603      * Test codePointAt(CharSequence, int)
 604      *      codePointBefore(CharSequence, int)
 605      *      codePointCount(CharSequence, int, int)
 606      *      offsetByCodePoints(CharSequence, int, int)
 607      * for exceptions
 608      */
 609     static void testExceptions01(CharSequence cs) {
 610         CharSequence nullSeq = null;
 611         // codePointAt
 612         callCodePoint(At, nullSeq, 0, NullPointerException.class);
 613         callCodePoint(At, cs, -1, IndexOutOfBoundsException.class);
 614         callCodePoint(At, cs, cs.length(), IndexOutOfBoundsException.class);
 615         callCodePoint(At, cs, cs.length()*3, IndexOutOfBoundsException.class);
 616 
 617         // codePointBefore
 618         callCodePoint(Before, nullSeq, 0, NullPointerException.class);
 619         callCodePoint(Before, cs, -1, IndexOutOfBoundsException.class);
 620         callCodePoint(Before, cs, 0, IndexOutOfBoundsException.class);
 621         callCodePoint(Before, cs, cs.length()+1, IndexOutOfBoundsException.class);
 622 
 623         // codePointCount
 624         callCodePointCount(nullSeq, 0, 0, NullPointerException.class);
 625         callCodePointCount(cs, -1, 1, IndexOutOfBoundsException.class);
 626         callCodePointCount(cs, 0, cs.length()+1, IndexOutOfBoundsException.class);
 627         callCodePointCount(cs, 3, 1, IndexOutOfBoundsException.class);
 628 
 629         // offsetByCodePoints
 630         callOffsetByCodePoints(nullSeq, 0, 0, NullPointerException.class);
 631         callOffsetByCodePoints(cs, -1, 1, IndexOutOfBoundsException.class);
 632         callOffsetByCodePoints(cs, cs.length()+1, 1, IndexOutOfBoundsException.class);
 633         callOffsetByCodePoints(cs, 0, cs.length()*2, IndexOutOfBoundsException.class);
 634         callOffsetByCodePoints(cs, cs.length(), 1, IndexOutOfBoundsException.class);
 635         callOffsetByCodePoints(cs, 0, -1, IndexOutOfBoundsException.class);
 636         callOffsetByCodePoints(cs, cs.length(), -cs.length()*2,
 637                                IndexOutOfBoundsException.class);
 638         callOffsetByCodePoints(cs, cs.length(), Integer.MIN_VALUE,
 639                                IndexOutOfBoundsException.class);
 640         callOffsetByCodePoints(cs, 0, Integer.MAX_VALUE, IndexOutOfBoundsException.class);
 641     }
 642 
 643     /**
 644      * Test codePointAt(char[], int)
 645      *      codePointAt(char[], int, int)
 646      *      codePointBefore(char[], int)
 647      *      codePointBefore(char[], int, int)
 648      *      codePointCount(char[], int, int)
 649      *      offsetByCodePoints(char[], int, int, int, int)
 650      * for exceptions
 651      */
 652     static void testExceptions02(char[] a) {
 653         char[] nullArray = null;
 654         callCodePoint(At, nullArray, 0, NullPointerException.class);
 655         callCodePoint(At, a, -1, IndexOutOfBoundsException.class);
 656         callCodePoint(At, a, a.length, IndexOutOfBoundsException.class);
 657         callCodePoint(At, a, a.length*3, IndexOutOfBoundsException.class);
 658         callCodePoint(Before, nullArray, 0, NullPointerException.class);
 659         callCodePoint(Before, a, -1, IndexOutOfBoundsException.class);
 660         callCodePoint(Before, a, 0, IndexOutOfBoundsException.class);
 661         callCodePoint(Before, a, a.length+1, IndexOutOfBoundsException.class);
 662 
 663         // tests for the methods with limit
 664         callCodePoint(At, nullArray, 0, 1, NullPointerException.class);
 665         callCodePoint(At, a, 0, -1, IndexOutOfBoundsException.class);
 666         callCodePoint(At, a, 0, 0, IndexOutOfBoundsException.class);
 667         callCodePoint(At, a, 0, a.length+1, IndexOutOfBoundsException.class);
 668         callCodePoint(At, a, 2, 1, IndexOutOfBoundsException.class);
 669         callCodePoint(At, a, -1, 1, IndexOutOfBoundsException.class);
 670         callCodePoint(At, a, a.length, 1, IndexOutOfBoundsException.class);
 671         callCodePoint(At, a, a.length*3, 1, IndexOutOfBoundsException.class);
 672         callCodePoint(Before, nullArray, 1, 0, NullPointerException.class);
 673         callCodePoint(Before, a, 2, -1, IndexOutOfBoundsException.class);
 674         callCodePoint(Before, a, 2, 2, IndexOutOfBoundsException.class);
 675         callCodePoint(Before, a, 2, 3, IndexOutOfBoundsException.class);
 676         callCodePoint(Before, a, 2, a.length, IndexOutOfBoundsException.class);
 677         callCodePoint(Before, a, -1, -1, IndexOutOfBoundsException.class);
 678         callCodePoint(Before, a, 0, 0, IndexOutOfBoundsException.class);
 679         callCodePoint(Before, a, a.length+1, a.length-1, IndexOutOfBoundsException.class);
 680 
 681         // codePointCount
 682         callCodePointCount(nullArray, 0, 0, NullPointerException.class);
 683         callCodePointCount(a, -1, 1, IndexOutOfBoundsException.class);
 684         callCodePointCount(a, 0, -1, IndexOutOfBoundsException.class);
 685         callCodePointCount(a, 0, a.length+1, IndexOutOfBoundsException.class);
 686         callCodePointCount(a, 1, a.length, IndexOutOfBoundsException.class);
 687         callCodePointCount(a, a.length, 1, IndexOutOfBoundsException.class);
 688         callCodePointCount(a, a.length+1, -1, IndexOutOfBoundsException.class);
 689 
 690         // offsetByCodePoints
 691         callOffsetByCodePoints(nullArray, 0, 0, 0, 0,  NullPointerException.class);
 692         callOffsetByCodePoints(a, -1, a.length, 1, 1, IndexOutOfBoundsException.class);
 693         callOffsetByCodePoints(a, 0, a.length+1, 1, 1, IndexOutOfBoundsException.class);
 694         callOffsetByCodePoints(a, 10, a.length, 1, 1, IndexOutOfBoundsException.class);
 695         callOffsetByCodePoints(a, 10, a.length-10, 1, 1, IndexOutOfBoundsException.class);
 696         callOffsetByCodePoints(a, 10, 10, 21, 1, IndexOutOfBoundsException.class);
 697         callOffsetByCodePoints(a, 20, -10, 15, 1, IndexOutOfBoundsException.class);
 698         callOffsetByCodePoints(a, 10, 10, 15, 20, IndexOutOfBoundsException.class);
 699         callOffsetByCodePoints(a, 10, 10, 15, -20, IndexOutOfBoundsException.class);
 700         callOffsetByCodePoints(a, 0, a.length, -1, 1, IndexOutOfBoundsException.class);
 701         callOffsetByCodePoints(a, 0, a.length, a.length+1, 1, IndexOutOfBoundsException.class);
 702         callOffsetByCodePoints(a, 0, a.length, 0, a.length*2, IndexOutOfBoundsException.class);
 703         callOffsetByCodePoints(a, 0, a.length, a.length, 1, IndexOutOfBoundsException.class);
 704         callOffsetByCodePoints(a, 0, a.length, 0, -1, IndexOutOfBoundsException.class);
 705         callOffsetByCodePoints(a, 0, a.length, a.length, -a.length*2,
 706                                IndexOutOfBoundsException.class);
 707         callOffsetByCodePoints(a, 0, a.length, a.length, Integer.MIN_VALUE,
 708                                IndexOutOfBoundsException.class);
 709         callOffsetByCodePoints(a, 0, a.length, 0, Integer.MAX_VALUE,
 710                                IndexOutOfBoundsException.class);
 711     }
 712 
 713     /**
 714      * Test the 1-arg toChars(int) for exceptions
 715      */
 716     private static void callToChars1(int codePoint, Class expectedException) {
 717         try {
 718             char[] a = Character.toChars(codePoint);
 719         } catch (Exception e) {
 720             if (expectedException.isInstance(e)) {
 721                 return;
 722             }
 723             throw new RuntimeException("Unspecified exception", e);
 724         }
 725         throw new RuntimeException("toChars(int) didn't throw " + expectedException.getName());
 726     }
 727 
 728     /**
 729      * Test the 3-arg toChars(int, char[], int) for exceptions
 730      */
 731     private static void callToChars3(int codePoint, char[] dst, int index,
 732                                      Class expectedException) {
 733         try {
 734             int n = Character.toChars(codePoint, dst, index);
 735         } catch (Exception e) {
 736             if (expectedException.isInstance(e)) {
 737                 return;
 738             }
 739             throw new RuntimeException("Unspecified exception", e);
 740         }
 741         throw new RuntimeException("toChars(int,char[],int) didn't throw "
 742                                    + expectedException.getName());
 743     }
 744 
 745     private static void callCodePoint(boolean isAt, CharSequence cs, int index,
 746                                       Class expectedException) {
 747         try {
 748             int c = isAt ? Character.codePointAt(cs, index)
 749                          : Character.codePointBefore(cs, index);
 750         } catch (Exception e) {
 751             if (expectedException.isInstance(e)) {
 752                 return;
 753             }
 754             throw new RuntimeException("Unspecified exception", e);
 755         }
 756         throw new RuntimeException("codePoint" + (isAt ? "At" : "Before")
 757                                    + " didn't throw " + expectedException.getName());
 758     }
 759 
 760     private static void callCodePoint(boolean isAt, char[] a, int index,
 761                                       Class expectedException) {
 762         try {
 763             int c = isAt ? Character.codePointAt(a, index)
 764                          : Character.codePointBefore(a, index);
 765         } catch (Exception e) {
 766             if (expectedException.isInstance(e)) {
 767                 return;
 768             }
 769             throw new RuntimeException("Unspecified exception", e);
 770         }
 771         throw new RuntimeException("codePoint" + (isAt ? "At" : "Before")
 772                                    + " didn't throw " + expectedException.getName());
 773     }
 774 
 775     private static void callCodePoint(boolean isAt, char[] a, int index, int limit,
 776                                       Class expectedException) {
 777         try {
 778             int c = isAt ? Character.codePointAt(a, index, limit)
 779                          : Character.codePointBefore(a, index, limit);
 780         } catch (Exception e) {
 781             if (expectedException.isInstance(e)) {
 782                 return;
 783             }
 784             throw new RuntimeException("Unspecified exception", e);
 785         }
 786         throw new RuntimeException("codePoint" + (isAt ? "At" : "Before")
 787                                    + " didn't throw " + expectedException.getName());
 788     }
 789 
 790     private static void callCodePointCount(Object data, int beginIndex, int endIndex,
 791                                            Class expectedException) {
 792         String type = getType(data);
 793         try {
 794             int n = (data instanceof CharSequence) ?
 795                   Character.codePointCount((CharSequence) data, beginIndex, endIndex)
 796                 : Character.codePointCount((char[]) data, beginIndex, endIndex);
 797         } catch (Exception e) {
 798             if (expectedException.isInstance(e)) {
 799                 return;
 800             }
 801             throw new RuntimeException("Unspecified exception", e);
 802         }
 803         throw new RuntimeException("codePointCount(" + type + "...) didn't throw "
 804                                    + expectedException.getName());
 805     }
 806 
 807     private static void callOffsetByCodePoints(CharSequence seq, int index, int offset,
 808                                                Class expectedException) {
 809         try {
 810             int n = Character.offsetByCodePoints(seq, index, offset);
 811         } catch (Exception e) {
 812             if (expectedException.isInstance(e)) {
 813                 return;
 814             }
 815             throw new RuntimeException("Unspecified exception", e);
 816         }
 817         throw new RuntimeException("offsetCodePointCounts(CharSequnce...) didn't throw "
 818                                    + expectedException.getName());
 819     }
 820 
 821 
 822     private static void callOffsetByCodePoints(char[] a, int start, int count,
 823                                                int index, int offset,
 824                                                Class expectedException) {
 825         try {
 826             int n = Character.offsetByCodePoints(a, start, count, index, offset);
 827         } catch (Exception e) {
 828             if (expectedException.isInstance(e)) {
 829                 return;
 830             }
 831             throw new RuntimeException("Unspecified exception", e);
 832         }
 833         throw new RuntimeException("offsetCodePointCounts(char[]...) didn't throw "
 834                                    + expectedException.getName());
 835     }
 836 
 837     private static String getType(Object data) {
 838         return (data instanceof CharSequence) ? "CharSequence" : "char[]";
 839     }
 840 
 841     private static String toHexString(int c) {
 842         return "0x" + Integer.toHexString(c);
 843     }
 844 
 845     private static String toHexString(String name, char[] a) {
 846         StringBuffer sb = new StringBuffer();
 847         for (int i = 0; i < a.length; i++) {
 848             if (i > 0) {
 849                 sb.append(", ");
 850             }
 851             sb.append(name).append('[').append(i).append("]=");
 852             sb.append(toHexString(a[i]));
 853         }
 854         return sb.toString();
 855     }
 856 }