1 /*
   2  * Copyright (c) 2018, 2019, 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 8202771 8221431
  27  * @summary Check j.l.Character.isDigit/isLetter/isLetterOrDigit/isSpaceChar
  28  * /isWhitespace/isTitleCase/isISOControl/isIdentifierIgnorable
  29  * /isJavaIdentifierStart/isJavaIdentifierPart/isUnicodeIdentifierStart
  30  * /isUnicodeIdentifierPart
  31  * @library /lib/testlibrary/java/lang
  32  * @run main CharPropTest
  33  */
  34 
  35 import java.nio.file.Files;
  36 import java.util.stream.Stream;
  37 
  38 public class CharPropTest {
  39     private static int diffs = 0;
  40     private static int rangeStart = 0x0000;
  41     private static boolean isRange = false;
  42 
  43     public static void main(String[] args) throws Exception {
  44         try (Stream<String> lines = Files.lines(UCDFiles.UNICODE_DATA)) {
  45             lines.map(String::trim)
  46                  .filter(line -> line.length() != 0 && line.charAt(0) != '#')
  47                  .forEach(line -> handleOneLine(line));
  48 
  49             if (diffs != 0) {
  50                 throw new RuntimeException("Total differences: " + diffs);
  51             }
  52         }
  53     }
  54 
  55     private static void handleOneLine(String line) {
  56         String[] fields = line.split(";");
  57         int currentCp = Integer.parseInt(fields[0], 16);
  58         String name = fields[1];
  59         String category = fields[2];
  60 
  61         // Except single code point, also handle ranges like the following:
  62         // 3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
  63         // 4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;
  64         if (isRange) {
  65             if (name.endsWith("Last>")) {
  66                 for (int cp = rangeStart; cp <= currentCp; cp++) {
  67                     testCodePoint(cp, category);
  68                 }
  69             } else {
  70                 throw new RuntimeException("Not a valid range, first range <"
  71                         + Integer.toHexString(rangeStart) + "> without last.");
  72             }
  73             isRange = false;
  74         } else {
  75             if (name.endsWith("First>")) {
  76                 rangeStart = currentCp;
  77                 isRange = true;
  78             } else {
  79                 testCodePoint(currentCp, category);
  80             }
  81         }
  82     }
  83 
  84     private static void testCodePoint(int codePoint, String category) {
  85         isDigitTest(codePoint, category);
  86         isLetterTest(codePoint, category);
  87         isLetterOrDigitTest(codePoint, category);
  88 
  89         isSpaceCharTest(codePoint, category);
  90         isWhitespaceTest(codePoint, category);
  91 
  92         isTitleCaseTest(codePoint, category);
  93 
  94         isISOControlTest(codePoint);
  95 
  96         isIdentifierIgnorableTest(codePoint, category);
  97         isJavaIdentifierStartTest(codePoint, category);
  98         isJavaIdentifierPartTest(codePoint, category);
  99         isUnicodeIdentifierStartTest(codePoint, category);
 100         isUnicodeIdentifierPartTest(codePoint, category);
 101     }
 102 
 103     private static void isDigitTest(int codePoint, String category) {
 104         boolean actual = Character.isDigit(codePoint);
 105         boolean expected = category.equals("Nd");
 106         if (actual != expected) {
 107             printDiff(codePoint, "isDigit", actual, expected);
 108         }
 109     }
 110 
 111     private static void isLetterTest(int codePoint, String category) {
 112         boolean actual = Character.isLetter(codePoint);
 113         boolean expected = isLetter(category);
 114         if (actual != expected) {
 115             printDiff(codePoint, "isLetter", actual, expected);
 116         }
 117     }
 118 
 119     private static void isLetterOrDigitTest(int codePoint, String category) {
 120         boolean actual = Character.isLetterOrDigit(codePoint);
 121         boolean expected = isLetter(category) || category.equals("Nd");
 122         if (actual != expected) {
 123             printDiff(codePoint, "isLetterOrDigit", actual, expected);
 124         }
 125     }
 126 
 127     private static void isSpaceCharTest(int codePoint, String category) {
 128         boolean actual = Character.isSpaceChar(codePoint);
 129         boolean expected = isSpaceChar(category);
 130         if (actual != expected) {
 131             printDiff(codePoint, "isSpaceChar", actual, expected);
 132         }
 133     }
 134 
 135     private static void isWhitespaceTest(int codePoint, String category) {
 136         boolean actual = Character.isWhitespace(codePoint);
 137         boolean expected = isWhitespace(codePoint, category);
 138         if (actual != expected) {
 139             printDiff(codePoint, "isWhitespace", actual, expected);
 140         }
 141     }
 142 
 143     private static void isTitleCaseTest(int codePoint, String category) {
 144         boolean actual = Character.isTitleCase(codePoint);
 145         boolean expected = category.equals("Lt");
 146         if (actual != expected) {
 147             printDiff(codePoint, "isTitleCase", actual, expected);
 148         }
 149     }
 150 
 151     private static void isISOControlTest(int codePoint) {
 152         boolean actual = Character.isISOControl(codePoint);
 153         boolean expected = isISOControl(codePoint);
 154         if (actual != expected) {
 155             printDiff(codePoint, "isISOControl", actual, expected);
 156         }
 157     }
 158 
 159     private static void isIdentifierIgnorableTest(int codePoint, String category) {
 160         boolean actual = Character.isIdentifierIgnorable(codePoint);
 161         boolean expected = isIdentifierIgnorable(codePoint, category);
 162         if (actual != expected) {
 163             printDiff(codePoint, "isIdentifierIgnorable", actual, expected);
 164         }
 165     }
 166 
 167     private static void isJavaIdentifierStartTest(int codePoint, String category) {
 168         boolean actual = Character.isJavaIdentifierStart(codePoint);
 169         boolean expected = isJavaIdentifierStart(category);
 170         if (actual != expected) {
 171             printDiff(codePoint, "isJavaIdentifierStart", actual, expected);
 172         }
 173     }
 174 
 175     private static void isJavaIdentifierPartTest(int codePoint, String category) {
 176         boolean actual = Character.isJavaIdentifierPart(codePoint);
 177         boolean expected = isJavaIdentifierPart(codePoint, category);
 178         if (actual != expected) {
 179             printDiff(codePoint, "isJavaIdentifierPart", actual, expected);
 180         }
 181     }
 182 
 183     private static void isUnicodeIdentifierStartTest(int codePoint, String category) {
 184         boolean actual = Character.isUnicodeIdentifierStart(codePoint);
 185         boolean expected = isUnicodeIdentifierStart(category);
 186         if (actual != expected) {
 187             printDiff(codePoint, "isUnicodeIdentifierStart", actual, expected);
 188         }
 189     }
 190 
 191     private static void isUnicodeIdentifierPartTest(int codePoint, String category) {
 192         boolean actual = Character.isUnicodeIdentifierPart(codePoint);
 193         boolean expected = isUnicodeIdentifierPart(codePoint, category);
 194         if (actual != expected) {
 195             printDiff(codePoint, "isUnicodeIdentifierPart", actual, expected);
 196         }
 197     }
 198 
 199     private static boolean isLetter(String category) {
 200         return category.equals("Lu") || category.equals("Ll")
 201                || category.equals("Lt") || category.equals("Lm")
 202                || category.equals("Lo");
 203     }
 204 
 205     private static boolean isSpaceChar(String category) {
 206         return category.equals("Zs") || category.equals("Zl")
 207                || category.equals("Zp");
 208     }
 209 
 210     private static boolean isWhitespace(int codePoint, String category) {
 211         if (isSpaceChar(category) && codePoint != Integer.parseInt("00A0", 16)
 212                 && codePoint != Integer.parseInt("2007", 16)
 213                 && codePoint != Integer.parseInt("202F", 16)) {
 214             return true;
 215         } else {
 216             if (codePoint == Integer.parseInt("0009", 16)
 217                     || codePoint == Integer.parseInt("000A", 16)
 218                     || codePoint == Integer.parseInt("000B", 16)
 219                     || codePoint == Integer.parseInt("000C", 16)
 220                     || codePoint == Integer.parseInt("000D", 16)
 221                     || codePoint == Integer.parseInt("001C", 16)
 222                     || codePoint == Integer.parseInt("001D", 16)
 223                     || codePoint == Integer.parseInt("001E", 16)
 224                     || codePoint == Integer.parseInt("001F", 16)) {
 225                 return true;
 226             }
 227         }
 228         return false;
 229     }
 230 
 231     private static boolean isISOControl(int codePoint) {
 232         return (codePoint > 0x00 && codePoint < 0x1f)
 233                || (codePoint > 0x7f && codePoint < 0x9f)
 234                || (codePoint == 0x00 || codePoint == 0x1f || codePoint == 0x7f || codePoint == 0x9f);
 235     }
 236 
 237     private static boolean isIdentifierIgnorable(int codePoint, String category) {
 238         if (category.equals("Cf")) {
 239             return true;
 240         } else {
 241             int a1 = Integer.parseInt("0000", 16);
 242             int a2 = Integer.parseInt("0008", 16);
 243             int b1 = Integer.parseInt("000E", 16);
 244             int b2 = Integer.parseInt("001B", 16);
 245             int c1 = Integer.parseInt("007F", 16);
 246             int c2 = Integer.parseInt("009F", 16);
 247 
 248             if ((codePoint > a1 && codePoint < a2) || (codePoint > b1 && codePoint < b2)
 249                     || (codePoint > c1 && codePoint < c2) || (codePoint == a1 || codePoint == a2
 250                     || codePoint == b1 || codePoint == b2 || codePoint == c1 || codePoint == c2)) {
 251                 return true;
 252             }
 253         }
 254         return false;
 255     }
 256 
 257     private static boolean isJavaIdentifierStart(String category) {
 258         return isLetter(category) || category.equals("Nl") || category.equals("Sc")
 259                || category.equals("Pc");
 260     }
 261 
 262     private static boolean isJavaIdentifierPart(int codePoint, String category) {
 263         return isLetter(category) || category.equals("Sc") || category.equals("Pc")
 264                || category.equals("Nd") || category.equals("Nl")
 265                || category.equals("Mc") || category.equals("Mn")
 266                || isIdentifierIgnorable(codePoint, category);
 267     }
 268 
 269     private static boolean isUnicodeIdentifierStart(String category) {
 270         return isLetter(category) || category.equals("Nl");
 271     }
 272 
 273     private static boolean isUnicodeIdentifierPart(int codePoint, String category) {
 274         return isLetter(category) || category.equals("Pc") || category.equals("Nd")
 275                || category.equals("Nl") || category.equals("Mc") || category.equals("Mn")
 276                || isIdentifierIgnorable(codePoint, category);
 277     }
 278 
 279     private static void printDiff(int codePoint, String method, boolean actual, boolean expected) {
 280         System.out.println("Not equal at codePoint <" + Integer.toHexString(codePoint)
 281                 + ">, method: " + method
 282                 + ", actual: " + actual + ", expected: " + expected);
 283         diffs++;
 284     }
 285 }