1 /*
   2  * Copyright (c) 2007, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @test
  28  * @bug 4830803 4886934 6565620 6959267 7070436 7198195 8032446 8072600 8202771
  29  *      8221431
  30  * @summary  Check that the UnicodeBlock forName() method works as expected
  31  *      and block ranges are correct for all Unicode characters.
  32  * @library /lib/testlibrary/java/lang
  33  * @run main CheckBlocks
  34  * @author John O'Conner
  35  */
  36 
  37 import java.lang.Character.UnicodeBlock;
  38 import java.lang.reflect.Field;
  39 import java.io.BufferedReader;
  40 import java.io.File;
  41 import java.io.FileReader;
  42 import java.util.HashSet;
  43 import java.util.Locale;
  44 
  45 public class CheckBlocks {
  46 
  47     static boolean err = false;
  48     static Class<?> clazzUnicodeBlock;
  49 
  50     public static void main(String[] args) throws Exception {
  51         generateBlockList();
  52 
  53         try {
  54             clazzUnicodeBlock = Class.forName("java.lang.Character$UnicodeBlock");
  55         } catch (ClassNotFoundException e) {
  56             throw new RuntimeException("Class.forName(\"java.lang.Character$UnicodeBlock\") failed.");
  57         }
  58 
  59         for (Block blk : blocks) {
  60             test4830803_1(blk);
  61             test4830803_2();
  62             test4886934(blk);
  63         }
  64 
  65         test8202771();
  66 
  67         if (err) {
  68             throw new RuntimeException("Failed");
  69         } else {
  70             System.out.println("Passed");
  71         }
  72     }
  73 
  74     /**
  75      * Check that the UnicodeBlock forName() method works as expected.
  76      */
  77     private static void test4830803_1(Block blk) throws Exception {
  78 
  79         /*
  80          * Try 3 forms of block name in the forName() method. Each form should
  81          * produce the same expected block.
  82          */
  83         String blkName = blk.getName();
  84 
  85         // For backward compatibility
  86         switch (blkName) {
  87             case "COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS":
  88                 blkName = "COMBINING_MARKS_FOR_SYMBOLS";
  89                 System.out.println("*** COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS"
  90                         + " is replaced with COMBINING_MARKS_FOR_SYMBOLS"
  91                         + " for backward compatibility.");
  92                 break;
  93             case "GREEK_AND_COPTIC":
  94                 blkName = "GREEK";
  95                 System.out.println("*** GREEK_AND_COPTIC is replaced with GREEK"
  96                         + " for backward compatibility.");
  97                 break;
  98             case "CYRILLIC_SUPPLEMENT":
  99                 blkName = "CYRILLIC_SUPPLEMENTARY";
 100                 System.out.println("*** CYRILLIC_SUPPLEMENT is replaced with"
 101                         + " CYRILLIC_SUPPLEMENTARY for backward compatibility.");
 102                 break;
 103             default:
 104                 break;
 105         }
 106 
 107         String expectedBlock = null;
 108         try {
 109             expectedBlock = clazzUnicodeBlock.getField(blkName).getName();
 110         } catch (NoSuchFieldException | SecurityException e) {
 111             System.err.println("Error: " + blkName + " was not found.");
 112             err = true;
 113             return;
 114         }
 115 
 116         String canonicalBlockName = blk.getOriginalName();
 117         String idBlockName = expectedBlock;
 118         String regexBlockName = toRegExString(canonicalBlockName);
 119 
 120         if (regexBlockName == null) {
 121             System.err.println("Error: Block name which was processed with regex was null.");
 122             err = true;
 123             return;
 124         }
 125 
 126         if (!expectedBlock.equals(UnicodeBlock.forName(canonicalBlockName).toString())) {
 127             System.err.println("Error #1: UnicodeBlock.forName(\"" +
 128                     canonicalBlockName + "\") returned wrong value.\n\tGot: " +
 129                     UnicodeBlock.forName(canonicalBlockName) +
 130                     "\n\tExpected: " + expectedBlock);
 131             err = true;
 132         }
 133 
 134         if (!expectedBlock.equals(UnicodeBlock.forName(idBlockName).toString())) {
 135             System.err.println("Error #2: UnicodeBlock.forName(\"" +
 136                     idBlockName + "\") returned wrong value.\n\tGot: " +
 137                     UnicodeBlock.forName(idBlockName) +
 138                     "\n\tExpected: " + expectedBlock);
 139             err = true;
 140         }
 141 
 142         if (!expectedBlock.equals(UnicodeBlock.forName(regexBlockName).toString())) {
 143             System.err.println("Error #3: UnicodeBlock.forName(\"" +
 144                     regexBlockName + "\") returned wrong value.\n\tGot: " +
 145                     UnicodeBlock.forName(regexBlockName) +
 146                     "\n\tExpected: " + expectedBlock);
 147             err = true;
 148         }
 149     }
 150 
 151     /**
 152      * now try a bad block name. This should produce an IAE.
 153      */
 154     private static void test4830803_2() {
 155         boolean threwExpected = false;
 156 
 157         try {
 158             UnicodeBlock block = UnicodeBlock.forName("notdefined");
 159         }
 160         catch(IllegalArgumentException e) {
 161             threwExpected = true;
 162         }
 163 
 164         if (threwExpected == false) {
 165             System.err.println("Error: UnicodeBlock.forName(\"notdefined\") should throw IllegalArgumentException.");
 166             err = true;
 167         }
 168     }
 169 
 170     /**
 171      * Convert the argument to a block name form used by the regex package.
 172      * That is, remove all spaces.
 173      */
 174     private static String toRegExString(String str) {
 175         String[] tokens = null;
 176         StringBuilder retStr = new StringBuilder();
 177         try {
 178             tokens = str.split(" ");
 179         }
 180         catch(java.util.regex.PatternSyntaxException e) {
 181             return null;
 182         }
 183         for(int x=0; x < tokens.length; ++x) {
 184             retStr.append(tokens[x]);
 185         }
 186         return retStr.toString();
 187     }
 188 
 189     private static void test4886934(Block blk) {
 190         String blkName = blk.getName();
 191         String blkOrigName = blk.getOriginalName();
 192         UnicodeBlock block;
 193         String blockName;
 194 
 195         // For backward compatibility
 196         switch (blkName) {
 197             case "COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS":
 198                 blkName = "COMBINING_MARKS_FOR_SYMBOLS";
 199                 System.out.println("*** COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS"
 200                         + " is replaced with COMBINING_MARKS_FOR_SYMBOLS"
 201                         + " for backward compatibility.");
 202                 break;
 203             case "GREEK_AND_COPTIC":
 204                 blkName = "GREEK";
 205                 System.out.println("*** GREEK_AND_COPTIC is replaced with GREEK"
 206                         + " for backward compatibility.");
 207                 break;
 208             case "CYRILLIC_SUPPLEMENT":
 209                 blkName = "CYRILLIC_SUPPLEMENTARY";
 210                 System.out.println("*** CYRILLIC_SUPPLEMENT is replaced with"
 211                         + " CYRILLIC_SUPPLEMENTARY for backward compatibility.");
 212                 break;
 213             default:
 214                 break;
 215         }
 216 
 217         for (int ch = blk.getBegin(); ch <= blk.getEnd(); ch++) {
 218             block = UnicodeBlock.of(ch);
 219             if (block == null) {
 220                 System.err.println("Error: The block for " + blkName
 221                         + " is missing. Please check java.lang.Character.UnicodeBlock.");
 222                 err = true;
 223                 break;
 224             }
 225             blockName = block.toString();
 226             if (!blockName.equals(blkName)) {
 227                 System.err.println("Error: Character(0x"
 228                         + Integer.toHexString(ch).toUpperCase()
 229                         + ") should be in \"" + blkName + "\" block "
 230                         + "(Block name is \"" + blkOrigName + "\")"
 231                         + " but found in \"" + blockName + "\" block.");
 232                 err = true;
 233             }
 234         }
 235     }
 236 
 237     /**
 238      * Check if every Field of Character.UnicodeBlock is a valid Unicode Block.
 239      */
 240     private static void test8202771() {
 241         Field[] fields = clazzUnicodeBlock.getFields();
 242 
 243         for (Field f : fields) {
 244             // Handle Deprecated field "SURROGATES_AREA".
 245             if (f.getAnnotation(Deprecated.class) != null) {
 246                 continue;
 247             }
 248 
 249             String blkName = f.getName();
 250             switch (blkName) {
 251                 case "COMBINING_MARKS_FOR_SYMBOLS":
 252                     validateBlock("COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS");
 253                     break;
 254                 case "GREEK":
 255                     validateBlock("GREEK_AND_COPTIC");
 256                     break;
 257                 case "CYRILLIC_SUPPLEMENTARY":
 258                     validateBlock("CYRILLIC_SUPPLEMENT");
 259                     break;
 260                 default:
 261                     validateBlock(blkName);
 262                     break;
 263             }
 264         }
 265     }
 266 
 267     private static void validateBlock(String blkName) {
 268         for (Block block : blocks) {
 269             String blockName = block.getName();
 270             if (blockName.equals(blkName)) {
 271                 return;
 272             }
 273         }
 274         err = true;
 275         System.err.println(blkName + " is not a valid Unicode Block.");
 276     }
 277 
 278     // List of all Unicode blocks, their start, and end codepoints.
 279     public static HashSet<Block> blocks = new HashSet<>();
 280 
 281     private static void generateBlockList() throws Exception {
 282         File blockData = UCDFiles.BLOCKS.toFile();
 283         try (BufferedReader f = new BufferedReader(new FileReader(blockData))) {
 284             String line;
 285             while ((line = f.readLine()) != null) {
 286                 if (line.length() == 0 || line.charAt(0) == '#') {
 287                     continue;
 288                 }
 289 
 290                 int index1 = line.indexOf('.');
 291                 int begin = Integer.parseInt(line.substring(0, index1), 16);
 292                 int index2 = line.indexOf(';');
 293                 int end = Integer.parseInt(line.substring(index1 + 2, index2), 16);
 294                 String name = line.substring(index2 + 1).trim();
 295 
 296                 System.out.println("  Adding a Block(" + Integer.toHexString(begin) + ", " + Integer.toHexString(end)
 297                         + ", " + name + ")");
 298                 blocks.add(new Block(begin, end, name));
 299             }
 300         }
 301     }
 302 }
 303 
 304 class Block {
 305 
 306     public Block() {
 307         blockBegin = 0;
 308         blockEnd = 0;
 309         blockName = null;
 310     }
 311 
 312     public Block(int begin, int end, String name) {
 313         blockBegin = begin;
 314         blockEnd = end;
 315         blockName = name.replaceAll("[ -]", "_").toUpperCase(Locale.ENGLISH);
 316         originalBlockName = name;
 317     }
 318 
 319     public int getBegin() {
 320         return blockBegin;
 321     }
 322 
 323     public int getEnd() {
 324         return blockEnd;
 325     }
 326 
 327     public String getName() {
 328         return blockName;
 329     }
 330 
 331     public String getOriginalName() {
 332         return originalBlockName;
 333     }
 334 
 335     @Override
 336     public boolean equals(Object obj) {
 337         if (obj == null) return false;
 338         if (!(obj instanceof Block)) return false;
 339 
 340         Block other = (Block)obj;
 341         return other.blockBegin == blockBegin &&
 342                 other.blockEnd == blockEnd &&
 343                 other.blockName.equals(blockName) &&
 344                 other.originalBlockName.equals(originalBlockName);
 345     }
 346     int blockBegin, blockEnd;
 347     String blockName, originalBlockName;
 348 }