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 {
locales.add(new Locale(R, R))50         locales.add(new Locale("az", ""));
java.util.Arrays.asList()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 
main(String[] args)118     public static void main (String[] args) {
119         SpecialCasingTest specialCasingTest = new SpecialCasingTest();
120         specialCasingTest.test();
121     }
122 
test()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 
test(String line)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 
testLowerCase(String orig, String expected, String lang, String condition)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 
testUpperCase(String orig, String expected, String lang, String condition)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 
convert(String str)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 
toString(String str)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 }
356