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 }