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