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