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.  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 4397357 6565620 6959267 7070436 7198195 8041791 8032446 8072600
  29  *      8221431
  30  * @summary Confirm special case mappings are handled correctly.
  31  * @library /lib/testlibrary/java/lang
  32  */
  33 
  34 import java.io.BufferedReader;
  35 import java.io.IOException;
  36 import java.nio.file.Files;
  37 import java.nio.file.Paths;
  38 import java.util.ArrayList;
  39 import java.util.List;
  40 import java.util.Locale;
  41 import java.util.StringTokenizer;
  42 
  43 public class SpecialCasingTest {
  44 
  45     private static boolean err = false;
  46 
  47     // Locales which are used for testing
  48     private static List<Locale> locales = new ArrayList<>();
  49     static {
  50         locales.add(new Locale("az", ""));
  51         locales.addAll(java.util.Arrays.asList(Locale.getAvailableLocales()));
  52     }
  53 
  54     // Default locale
  55     private static String defaultLang;
  56 
  57     // True if the default language is az, lt, or tr which has locale-specific
  58     // mappings.
  59     private static boolean specificLocale;
  60 
  61     // Additional test cases
  62     //   Pseudo-locales which are used here:
  63     //     L1: locales other than lt
  64     //     L2: locales other than az and tr
  65     //     L3: locales other than az, lt and tr
  66     private static final String[] additionalTestData = {
  67         // Format:
  68         //   <code>; <lower>; <title>; <upper>; (<condition_list>)
  69 
  70         // Counterpart of Final_Sigma test case
  71         //   03A3; 03C2; 03A3; 03A3; Final_Sigma
  72             "03A3; 03C3; 03A3; 03A3; SunSpecific_Not_Final_Sigma1",
  73             "03A3; 03C3; 03A3; 03A3; SunSpecific_Not_Final_Sigma2",
  74 
  75         // Counterpart of After_Soft_Dotted test case
  76         //   0307; 0307;     ;     ; lt After_Soft_Dotted
  77             "0307; 0307; 0307; 0307; L1 After_Soft_Dotted",
  78             "0307; 0307; 0307; 0307; lt SunSpecific_Not_After_Soft_Dotted",
  79             "0307; 0307; 0307; 0307; L1 SunSpecific_Not_After_Soft_Dotted",
  80 
  81         // Counterpart of More_Above test cases
  82         //   0049; 0069 0307; 0049; 0049; lt More_Above
  83             "0049; 0131     ; 0049; 0049; az More_Above",
  84             "0049; 0131     ; 0049; 0049; tr More_Above",
  85             "0049; 0069     ; 0049; 0049; L3 More_Above",
  86             "0049; 0069     ; 0049; 0049; lt SunSpecific_Not_More_Above",
  87             "0049; 0131     ; 0049; 0049; az SunSpecific_Not_More_Above",
  88             "0049; 0131     ; 0049; 0049; tr SunSpecific_Not_More_Above",
  89             "0049; 0069     ; 0049; 0049; L3 SunSpecific_Not_More_Above",
  90         //   004A; 006A 0307; 004A; 004A; lt More_Above
  91             "004A; 006A     ; 004A; 004A; L1 More_Above",
  92             "004A; 006A     ; 004A; 004A; lt SunSpecific_Not_More_Above",
  93             "004A; 006A     ; 004A; 004A; L1 SunSpecific_Not_More_Above",
  94         //   012E; 012F 0307; 012E; 012E; lt More_Above
  95             "012E; 012F     ; 012E; 012E; L1 More_Above",
  96             "012E; 012F     ; 012E; 012E; lt SunSpecific_Not_More_Above",
  97             "012E; 012F     ; 012E; 012E; L1 SunSpecific_Not_More_Above",
  98 
  99         // Counterpart of After_I test cases
 100         //   0307;          ; 0307; 0307; tr After_I
 101         //   0307;          ; 0307; 0307; az After_I
 102             "0307; 0307 0307; 0307; 0307; lt After_I",
 103             "0307; 0307     ; 0307; 0307; L3 After_I",
 104             "0307; 0307     ; 0307; 0307; tr SunSpecific_Not_After_I",
 105             "0307; 0307     ; 0307; 0307; az SunSpecific_Not_After_I",
 106             "0307; 0307     ; 0307; 0307; L2 SunSpecific_Not_After_I",
 107 
 108         // Counterpart of Not_Before_Dot test cases
 109         //   0049; 0131          ; 0049; 0049; tr Not_Before_Dot
 110         //   0049; 0131          ; 0049; 0049; az Not_Before_Dot
 111             "0049; 0069          ; 0049; 0049; L2 Not_Before_Dot",
 112             "0049; 0069          ; 0049; 0049; tr SunSpecific_Before_Dot",
 113             "0049; 0069          ; 0049; 0049; az SunSpecific_Before_Dot",
 114             "0049; 0069 0307 0307; 0049; 0049; lt SunSpecific_Before_Dot",
 115             "0049; 0069 0307     ; 0049; 0049; L3 SunSpecific_Before_Dot",
 116     };
 117 
 118     public static void main (String[] args) {
 119         SpecialCasingTest specialCasingTest = new SpecialCasingTest();
 120         specialCasingTest.test();
 121     }
 122 
 123     private void test ()  {
 124         Locale defaultLocale = Locale.getDefault();
 125         BufferedReader in = null;
 126 
 127         try {
 128             int locale_num = locales.size();
 129             for (int l = 0; l < locale_num; l++) {
 130                 Locale locale = locales.get(l);
 131                 Locale.setDefault(locale);
 132                 System.out.println("Testing on " + locale + " locale....");
 133 
 134                 defaultLang = locale.getLanguage();
 135                 if (defaultLang.equals("az") ||
 136                     defaultLang.equals("lt") ||
 137                     defaultLang.equals("tr")) {
 138                     specificLocale = true;
 139                 } else {
 140                     specificLocale = false;
 141                 }
 142                 in = Files.newBufferedReader(UCDFiles.SPECIAL_CASING.toRealPath());
 143                 String line;
 144                 while ((line = in.readLine()) != null) {
 145                     if (line.length() == 0 || line.charAt(0) == '#') {
 146                         continue;
 147                     }
 148                     test(line);
 149                 }
 150                 in.close();
 151                 in = null;
 152                 System.out.println("Testing with Sun original data....");
 153                 for (String additionalTestData1 : additionalTestData) {
 154                     test(additionalTestData1);
 155                 }
 156             }
 157         }
 158         catch (IOException e) {
 159             err = true;
 160             e.printStackTrace();
 161         }
 162         finally {
 163             if (in != null) {
 164                 try {
 165                     in.close();
 166                 }
 167                 catch (IOException e) {
 168                 }
 169             }
 170             Locale.setDefault(defaultLocale);
 171             if (err) {
 172                 throw new RuntimeException("SpecialCasingTest failed.");
 173             } else {
 174                 System.out.println("*** SpecialCasingTest passed.");
 175             }
 176         }
 177     }
 178 
 179     private void test(String line) {
 180         int index = line.indexOf('#');
 181         if (index != -1) {
 182             line = line.substring(0, index);
 183         }
 184 
 185         String lang = null;
 186         String condition = null;
 187         String[] fields = line.split("; ");
 188 
 189         for (int i = 0; i < 4; i++) {
 190             if (fields[i].length() != 0) {
 191                 fields[i] = convert(fields[i]);
 192             }
 193         }
 194         if (fields.length != 4) {
 195             StringTokenizer st = new StringTokenizer(fields[4]);
 196 
 197             while (st.hasMoreTokens()) {
 198                 String token = st.nextToken();
 199 
 200                 if (token.equals("Final_Sigma")) {
 201                     condition = "Final Sigma";
 202                     fields[0] = "Abc" + fields[0];
 203                     fields[1] = "abc" + fields[1];
 204                     fields[3] = "ABC" + fields[3];
 205                 } else if (token.equals("SunSpecific_Not_Final_Sigma1")) {
 206                     condition = "*Sun Specific* Not Final Sigma 1";
 207                     fields[0] = "Abc" + fields[0] + "xyz";
 208                     fields[1] = "abc" + fields[1] + "xyz";
 209                     fields[3] = "ABC" + fields[3] + "XYZ";
 210                 } else if (token.equals("SunSpecific_Not_Final_Sigma2")) {
 211                     condition = "*Sun Specific* Not Final Sigma 2";
 212                 } else if (token.equals("After_Soft_Dotted")) {
 213                     condition = "After Soft-Dotted";
 214                     fields[0] = "\u1E2D" + fields[0];
 215                     fields[1] = "\u1E2D" + fields[1];
 216                     fields[3] = "\u1E2C" + fields[3];
 217                 } else if (token.equals("SunSpecific_Not_After_Soft_Dotted")) {
 218                     condition = "*Sun Specific* Not After Soft-Dotted";
 219                     fields[0] = "Dot" + fields[0];
 220                     fields[1] = "dot" + fields[1];
 221                     fields[3] = "DOT" + fields[3];
 222                 } else if (token.equals("More_Above")) {
 223                     condition = "More Above";
 224                     fields[0] = fields[0] + "\u0306";
 225                     fields[1] = fields[1] + "\u0306";
 226                     fields[3] = fields[3] + "\u0306";
 227                 } else if (token.equals("SunSpecific_Not_More_Above")) {
 228                     condition = "*Sun Specific* Not More Above";
 229                     fields[0] = fields[0] + "breve";
 230                     fields[1] = fields[1] + "breve";
 231                     fields[3] = fields[3] + "BREVE";
 232                 } else if (token.equals("After_I")) {
 233                     condition = "After I";
 234                     fields[0] = "I" + fields[0];
 235                     fields[1] = "i" + fields[1];
 236                     fields[3] = "I" + fields[3];
 237                 } else if (token.equals("SunSpecific_Not_After_I")) {
 238                     condition = "*Sun Specific* Not After I";
 239                     fields[0] = "A" + fields[0];
 240                     fields[1] = "a" + fields[1];
 241                     fields[3] = "A" + fields[3];
 242                 } else if (token.equals("Not_Before_Dot")) {
 243                     condition = "Not Before Dot";
 244                     fields[0] = fields[0] + "Z";
 245                     fields[1] = fields[1] + "z";
 246                     fields[3] = fields[3] + "Z";
 247                 } else if (token.equals("SunSpecific_Before_Dot")) {
 248                     condition = "*Sun Specific* Before Dot";
 249                     fields[0] = fields[0] + "\u0307";
 250                     fields[3] = fields[3] + "\u0307";
 251                 } else if (token.length() == 2) {
 252                     lang = token;
 253 
 254                     if (lang.equals("L1")) {
 255                         if (defaultLang.equals("lt")) {
 256                             lang = "en";
 257                         } else {
 258                             lang = defaultLang;
 259                         }
 260                     } else if (lang.equals("L2")) {
 261                         if (defaultLang.equals("az") ||
 262                             defaultLang.equals("tr")) {
 263                             lang = "en";
 264                         } else {
 265                             lang = defaultLang;
 266                         }
 267                     } else if (lang.equals("L3")) {
 268                         if (defaultLang.equals("az") ||
 269                             defaultLang.equals("lt") ||
 270                             defaultLang.equals("tr")) {
 271                             lang = "en";
 272                         } else {
 273                             lang = defaultLang;
 274                         }
 275                     // I want to have another test case here for double-check.
 276                     // Current implementation for Character and String considers
 277                     // only az, lt, and tr locales. I want to detect if other
 278                     // locales are specified.
 279                     } else if (!lang.equals("az") &&
 280                                !lang.equals("lt") &&
 281                                !lang.equals("tr")) {
 282                         throw new RuntimeException("Unsupported locale: " +
 283                             lang + ". It may need to be considered in ConditionalSpecialCasing.java. Please confirm.");
 284                     }
 285                 } else {
 286                     throw new RuntimeException("Unknown condition: " + token);
 287                 }
 288             }
 289         } else if (fields[0].equals("\u0130")) {
 290             // special case for \u0130
 291             if (defaultLang.equals("az") ||
 292                 defaultLang.equals("tr")) {
 293                 lang = "en";
 294             } else {
 295                 lang = defaultLang;
 296             }
 297         }
 298         testLowerCase(fields[0], fields[1], lang, condition);
 299         testUpperCase(fields[0], fields[3], lang, condition);
 300     }
 301 
 302     private void testLowerCase(String orig, String expected,
 303                                String lang, String condition) {
 304         String got = (lang == null) ?
 305             orig.toLowerCase() : orig.toLowerCase(new Locale(lang, ""));
 306 
 307         if (!expected.equals(got)) {
 308             err = true;
 309             System.err.println("toLowerCase(lang=" + lang +
 310                 ") failed.\n\tOriginal: " + toString(orig) +
 311                 "\n\tGot:      " + toString(got) +
 312                 "\n\tExpected: " + toString(expected) +
 313                 ((condition == null) ? "" : ("\n    under condition(" +
 314                 condition + ")")));
 315         }
 316     }
 317 
 318     private void testUpperCase(String orig, String expected,
 319                                String lang, String condition) {
 320         String got = (lang == null) ?
 321             orig.toUpperCase() : orig.toUpperCase(new Locale(lang, ""));
 322 
 323         if (!expected.equals(got)) {
 324             err = true;
 325             System.err.println("toUpperCase(lang=" + lang +
 326                 ") failed.\n\tOriginal: " + toString(orig) +
 327                 "\n\tGot:      " + toString(got) +
 328                 "\n\tExpected: " + toString(expected) +
 329                 ((condition == null) ? "" : ("\n    under condition(" +
 330                 condition + ")")));
 331         }
 332     }
 333     StringBuilder sb = new StringBuilder();
 334 
 335     private String convert(String str) {
 336         sb.setLength(0);
 337 
 338         String[] tokens = str.split(" ");
 339         for (String token : tokens) {
 340             sb.append((char) Integer.parseInt(token, 16));
 341         }
 342         return sb.toString();
 343     }
 344 
 345     private String toString(String str) {
 346         sb.setLength(0);
 347 
 348         int len = str.length();
 349         for (int i = 0; i < len; i++) {
 350             sb.append("0x").append(Integer.toHexString(str.charAt(i)).toUpperCase()).append(" ");
 351         }
 352         return sb.toString();
 353     }
 354 
 355 }