1 /*
  2  * Copyright (c) 2016, 2023, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 8040211 8191404 8203872 8222980 8225435 8241082 8242010 8247432
 27  *      8258795 8267038 8287180 8302512 8304761 8306031 8308021
 28  * @summary Checks the IANA language subtag registry data update
 29  *          (LSR Revision: 2023-05-11) with Locale and Locale.LanguageRange
 30  *          class methods.
 31  * @run main LanguageSubtagRegistryTest
 32  */
 33 
 34 import java.util.ArrayList;
 35 import java.util.Iterator;
 36 import java.util.Locale;
 37 import java.util.List;
 38 import java.util.Locale.LanguageRange;
 39 import java.util.Locale.FilteringMode;
 40 import static java.util.Locale.FilteringMode.EXTENDED_FILTERING;
 41 
 42 public class LanguageSubtagRegistryTest {
 43 
 44     static boolean err = false;
 45 
 46     private static final String ACCEPT_LANGUAGE =
 47         "Accept-Language: aam, adp, aeb, ajs, aog, apc, ajp, aue, bcg, bic, bpp, cey, cbr, cnp, cqu, crr, csp, csx, dif, dmw, dsz, ehs, ema,"
 48         + " en-gb-oed, gti, iba, jks, kdz, kjh, kmb, koj, kru, ksp, kwq, kxe, kzk, lgs, lii, lmm, lsb, lsc, lsn, lsv, lsw, lvi, mtm,"
 49         + " ngv, nns, ola, oyb, pat, phr, plu, pnd, pub, rib, rnb, rsn, scv, snz, sqx, suj, szy, taj, tdg, tjj, tjp, tpn, tvx,"
 50         + " umi, uss, uth, ysm, zko, wkr;q=0.9, ar-hyw;q=0.8, yug;q=0.5, gfx;q=0.4";
 51     private static final List<LanguageRange> EXPECTED_RANGE_LIST = List.of(
 52             new LanguageRange("aam", 1.0),
 53             new LanguageRange("aas", 1.0),
 54             new LanguageRange("adp", 1.0),
 55             new LanguageRange("dz", 1.0),
 56             new LanguageRange("aeb", 1.0),
 57             new LanguageRange("ar-aeb", 1.0),
 58             new LanguageRange("ajt", 1.0),
 59             new LanguageRange("ajs", 1.0),
 60             new LanguageRange("sgn-ajs", 1.0),
 61             new LanguageRange("aog", 1.0),
 62             new LanguageRange("myd", 1.0),
 63             new LanguageRange("apc", 1.0),
 64             new LanguageRange("ar-apc", 1.0),
 65             new LanguageRange("ar-ajp", 1.0),
 66             new LanguageRange("ajp", 1.0),
 67             new LanguageRange("aue", 1.0),
 68             new LanguageRange("ktz", 1.0),
 69             new LanguageRange("bcg", 1.0),
 70             new LanguageRange("bgm", 1.0),
 71             new LanguageRange("bic", 1.0),
 72             new LanguageRange("bir", 1.0),
 73             new LanguageRange("bpp", 1.0),
 74             new LanguageRange("nxu", 1.0),
 75             new LanguageRange("cey", 1.0),
 76             new LanguageRange("cbr", 1.0),
 77             new LanguageRange("nom", 1.0),
 78             new LanguageRange("cnp", 1.0),
 79             new LanguageRange("zh-cnp", 1.0),
 80             new LanguageRange("cqu", 1.0),
 81             new LanguageRange("quh", 1.0),
 82             new LanguageRange("crr", 1.0),
 83             new LanguageRange("pmk", 1.0),
 84             new LanguageRange("csp", 1.0),
 85             new LanguageRange("zh-csp", 1.0),
 86             new LanguageRange("csx", 1.0),
 87             new LanguageRange("sgn-csx", 1.0),
 88             new LanguageRange("dif", 1.0),
 89             new LanguageRange("dit", 1.0),
 90             new LanguageRange("dmw", 1.0),
 91             new LanguageRange("xrq", 1.0),
 92             new LanguageRange("dsz", 1.0),
 93             new LanguageRange("sgn-dsz", 1.0),
 94             new LanguageRange("ehs", 1.0),
 95             new LanguageRange("sgn-ehs", 1.0),
 96             new LanguageRange("ema", 1.0),
 97             new LanguageRange("uok", 1.0),
 98             new LanguageRange("en-gb-oed", 1.0),
 99             new LanguageRange("en-gb-oxendict", 1.0),
100             new LanguageRange("gti", 1.0),
101             new LanguageRange("nyc", 1.0),
102             new LanguageRange("iba", 1.0),
103             new LanguageRange("snb", 1.0),
104             new LanguageRange("blg", 1.0),
105             new LanguageRange("jks", 1.0),
106             new LanguageRange("sgn-jks", 1.0),
107             new LanguageRange("kdz", 1.0),
108             new LanguageRange("ncp", 1.0),
109             new LanguageRange("kjh", 1.0),
110             new LanguageRange("zkb", 1.0),
111             new LanguageRange("kmb", 1.0),
112             new LanguageRange("smd", 1.0),
113             new LanguageRange("koj", 1.0),
114             new LanguageRange("kwv", 1.0),
115             new LanguageRange("kru", 1.0),
116             new LanguageRange("kxl", 1.0),
117             new LanguageRange("ksp", 1.0),
118             new LanguageRange("lak", 1.0),
119             new LanguageRange("kwq", 1.0),
120             new LanguageRange("yam", 1.0),
121             new LanguageRange("kxe", 1.0),
122             new LanguageRange("tvd", 1.0),
123             new LanguageRange("kzk", 1.0),
124             new LanguageRange("gli", 1.0),
125             new LanguageRange("drr", 1.0),
126             new LanguageRange("lgs", 1.0),
127             new LanguageRange("sgn-lgs", 1.0),
128             new LanguageRange("lii", 1.0),
129             new LanguageRange("raq", 1.0),
130             new LanguageRange("lmm", 1.0),
131             new LanguageRange("rmx", 1.0),
132             new LanguageRange("lsb", 1.0),
133             new LanguageRange("sgn-lsb", 1.0),
134             new LanguageRange("lsc", 1.0),
135             new LanguageRange("sgn-lsc", 1.0),
136             new LanguageRange("lsn", 1.0),
137             new LanguageRange("sgn-lsn", 1.0),
138             new LanguageRange("lsv", 1.0),
139             new LanguageRange("sgn-lsv", 1.0),
140             new LanguageRange("lsw", 1.0),
141             new LanguageRange("sgn-lsw", 1.0),
142             new LanguageRange("lvi", 1.0),
143             new LanguageRange("mtm", 1.0),
144             new LanguageRange("ymt", 1.0),
145             new LanguageRange("ngv", 1.0),
146             new LanguageRange("nnx", 1.0),
147             new LanguageRange("nns", 1.0),
148             new LanguageRange("nbr", 1.0),
149             new LanguageRange("ola", 1.0),
150             new LanguageRange("thw", 1.0),
151             new LanguageRange("oyb", 1.0),
152             new LanguageRange("thx", 1.0),
153             new LanguageRange("skk", 1.0),
154             new LanguageRange("jeg", 1.0),
155             new LanguageRange("pat", 1.0),
156             new LanguageRange("kxr", 1.0),
157             new LanguageRange("phr", 1.0),
158             new LanguageRange("pmu", 1.0),
159             new LanguageRange("plu", 1.0),
160             new LanguageRange("kgm", 1.0),
161             new LanguageRange("pnd", 1.0),
162             new LanguageRange("pub", 1.0),
163             new LanguageRange("puz", 1.0),
164             new LanguageRange("rib", 1.0),
165             new LanguageRange("sgn-rib", 1.0),
166             new LanguageRange("rnb", 1.0),
167             new LanguageRange("sgn-rnb", 1.0),
168             new LanguageRange("rsn", 1.0),
169             new LanguageRange("sgn-rsn", 1.0),
170             new LanguageRange("scv", 1.0),
171             new LanguageRange("zir", 1.0),
172             new LanguageRange("snz", 1.0),
173             new LanguageRange("asd", 1.0),
174             new LanguageRange("sqx", 1.0),
175             new LanguageRange("sgn-sqx", 1.0),
176             new LanguageRange("suj", 1.0),
177             new LanguageRange("szy", 1.0),
178             new LanguageRange("taj", 1.0),
179             new LanguageRange("tsf", 1.0),
180             new LanguageRange("tdg", 1.0),
181             new LanguageRange("tmk", 1.0),
182             new LanguageRange("tjj", 1.0),
183             new LanguageRange("tjp", 1.0),
184             new LanguageRange("tpn", 1.0),
185             new LanguageRange("tpw", 1.0),
186             new LanguageRange("tvx", 1.0),
187             new LanguageRange("umi", 1.0),
188             new LanguageRange("szd", 1.0),
189             new LanguageRange("uss", 1.0),
190             new LanguageRange("uth", 1.0),
191             new LanguageRange("ysm", 1.0),
192             new LanguageRange("sgn-ysm", 1.0),
193             new LanguageRange("zko", 1.0),
194             new LanguageRange("xss", 1.0),
195             new LanguageRange("wkr", 0.9),
196             new LanguageRange("ar-hyw", 0.8),
197             new LanguageRange("yug", 0.5),
198             new LanguageRange("yuu", 0.5),
199             new LanguageRange("gfx", 0.4),
200             new LanguageRange("oun", 0.4),
201             new LanguageRange("mwj", 0.4),
202             new LanguageRange("vaj", 0.4)
203         );
204 
205     public static void main(String[] args) {
206         testLanguageRange();
207         testLocale();
208 
209         if (err) {
210             throw new RuntimeException("Failed.");
211         }
212     }
213 
214     private static void testLanguageRange() {
215         System.out.println("Test LanguageRange class parse method...");
216         test_parse();
217     }
218 
219     private static void testLocale() {
220         System.out.println("Test Locale class methods...");
221         test_filter();
222         test_filterTags();
223         test_lookup();
224         test_lookupTag();
225     }
226 
227     private static void test_parse() {
228         boolean error = false;
229         List<LanguageRange> got = LanguageRange.parse(ACCEPT_LANGUAGE);
230         if (!areEqual(EXPECTED_RANGE_LIST, got)) {
231             error = true;
232             System.err.println("    language parse() test failed.");
233         }
234 
235         if (error) {
236             err = true;
237             System.out.println("  test_parse() failed.");
238         } else {
239             System.out.println("  test_parse() passed.");
240         }
241 
242     }
243 
244     private static boolean areEqual(List<LanguageRange> expected,
245             List<LanguageRange> got) {
246         boolean error = false;
247 
248         int expectedSize = expected.size();
249         int actualSize = got.size();
250 
251         if (expectedSize != actualSize) {
252             error = true;
253 
254             System.err.println("  Expected size=" + expectedSize);
255             for (LanguageRange lr : expected) {
256                 System.err.println("    range=" + lr.getRange()
257                         + ", weight=" + lr.getWeight());
258             }
259 
260             System.err.println("  Actual size=" + actualSize);
261             for (LanguageRange lr : got) {
262                 System.err.println("    range=" + lr.getRange()
263                         + ", weight=" + lr.getWeight());
264             }
265         } else {
266             for (int i = 0; i < expectedSize; i++) {
267                 LanguageRange lr1 = expected.get(i);
268                 LanguageRange lr2 = got.get(i);
269 
270                 if (!lr1.getRange().equals(lr2.getRange())
271                         || lr1.getWeight() != lr2.getWeight()) {
272                     error = true;
273                     System.err.println("  " + i + ": Expected: range=" + lr1.getRange()
274                             + ", weight=" + lr1.getWeight());
275                     System.err.println("  " + i + ": Actual:   range=" + lr2.getRange()
276                             + ", weight=" + lr2.getWeight());
277                 }
278             }
279         }
280 
281         return !error;
282     }
283 
284     private static void test_filter() {
285         boolean error = false;
286 
287         String ranges = "mtm-RU, en-gb-oed, coy, ar-HY";
288         String tags = "de-DE, en, mtm-RU, ymt-RU, en-gb-oxendict, ja-JP, pij, nts, ar-arevela";
289         FilteringMode mode = EXTENDED_FILTERING;
290 
291         List<LanguageRange> priorityList = LanguageRange.parse(ranges);
292         List<Locale> tagList = generateLocales(tags);
293         String actualLocales
294                 = showLocales(Locale.filter(priorityList, tagList, mode));
295         String expectedLocales = "mtm-RU, ymt-RU, en-GB-oxendict, nts, pij";
296 
297         if (!expectedLocales.equals(actualLocales)) {
298             error = true;
299             showErrorMessage("#1 filter(" + mode + ")",
300                     ranges, tags, expectedLocales, actualLocales);
301         }
302 
303         ranges = "phr-*-IN, ja-JP";
304         tags = "en, pmu-Guru-IN, ja-Latn-JP, iw";
305         mode = EXTENDED_FILTERING;
306 
307         priorityList = LanguageRange.parse(ranges);
308         tagList = generateLocales(tags);
309         actualLocales = showLocales(Locale.filter(priorityList, tagList, mode));
310         expectedLocales = "pmu-Guru-IN, ja-Latn-JP";
311 
312         if (!expectedLocales.equals(actualLocales)) {
313             error = true;
314             showErrorMessage("#2 filter(" + mode + ")",
315                     ranges, tags, expectedLocales, actualLocales);
316         }
317 
318         if (error) {
319             err = true;
320             System.out.println("  test_filter() failed.");
321         } else {
322             System.out.println("  test_filter() passed.");
323         }
324     }
325 
326     private static void test_filterTags() {
327         boolean error = false;
328 
329         String ranges = "gti;q=0.2, gfx, kzj";
330         String tags = "de-DE, gti, he, nyc, mwj, vaj, ktr, dtp";
331 
332         List<LanguageRange> priorityList = LanguageRange.parse(ranges);
333         List<String> tagList = generateLanguageTags(tags);
334         String actualTags
335                 = showLanguageTags(Locale.filterTags(priorityList, tagList));
336         String expectedTags = "mwj, vaj, ktr, dtp, gti, nyc";
337 
338         if (!expectedTags.equals(actualTags)) {
339             error = true;
340             showErrorMessage("filterTags()",
341                     ranges, tags, expectedTags, actualTags);
342         }
343 
344         if (error) {
345             err = true;
346             System.out.println("  test_filterTags() failed.");
347         } else {
348             System.out.println("  test_filterTags() passed.");
349         }
350     }
351 
352     private static void test_lookup() {
353         boolean error = false;
354 
355         String ranges = "en;q=0.2, yam, rmx;q=0.9";
356         String tags = "de-DE, en, kwq, lmm";
357         List<LanguageRange> priorityList = LanguageRange.parse(ranges);
358         List<Locale> localeList = generateLocales(tags);
359         String actualLocale
360                 = Locale.lookup(priorityList, localeList).toLanguageTag();
361         String expectedLocale = "kwq";
362 
363         if (!expectedLocale.equals(actualLocale)) {
364             error = true;
365             showErrorMessage("lookup()", ranges, tags, expectedLocale, actualLocale);
366         }
367 
368         if (error) {
369             err = true;
370             System.out.println("  test_lookup() failed.");
371         } else {
372             System.out.println("  test_lookup() passed.");
373         }
374     }
375 
376     private static void test_lookupTag() {
377         boolean error = false;
378 
379         String ranges = "en, tsf;q=0.2";
380         String tags = "es, ja-JP, taj";
381         List<LanguageRange> priorityList = LanguageRange.parse(ranges);
382         List<String> tagList = generateLanguageTags(tags);
383         String actualTag = Locale.lookupTag(priorityList, tagList);
384         String expectedTag = "taj";
385 
386         if (!expectedTag.equals(actualTag)) {
387             error = true;
388             showErrorMessage("lookupTag()", ranges, tags, expectedTag, actualTag);
389         }
390 
391         if (error) {
392             err = true;
393             System.out.println("  test_lookupTag() failed.");
394         } else {
395             System.out.println("  test_lookupTag() passed.");
396         }
397     }
398 
399     private static List<Locale> generateLocales(String tags) {
400         if (tags == null) {
401             return null;
402         }
403 
404         List<Locale> localeList = new ArrayList<>();
405         if (tags.equals("")) {
406             return localeList;
407         }
408         String[] t = tags.split(", ");
409         for (String tag : t) {
410             localeList.add(Locale.forLanguageTag(tag));
411         }
412         return localeList;
413     }
414 
415     private static List<String> generateLanguageTags(String tags) {
416         List<String> tagList = new ArrayList<>();
417         String[] t = tags.split(", ");
418         for (String tag : t) {
419             tagList.add(tag);
420         }
421         return tagList;
422     }
423 
424     private static String showLanguageTags(List<String> tags) {
425         StringBuilder sb = new StringBuilder();
426 
427         Iterator<String> itr = tags.iterator();
428         if (itr.hasNext()) {
429             sb.append(itr.next());
430         }
431         while (itr.hasNext()) {
432             sb.append(", ");
433             sb.append(itr.next());
434         }
435 
436         return sb.toString().trim();
437     }
438 
439     private static String showLocales(List<Locale> locales) {
440         StringBuilder sb = new StringBuilder();
441 
442         java.util.Iterator<Locale> itr = locales.iterator();
443         if (itr.hasNext()) {
444             sb.append(itr.next().toLanguageTag());
445         }
446         while (itr.hasNext()) {
447             sb.append(", ");
448             sb.append(itr.next().toLanguageTag());
449         }
450 
451         return sb.toString().trim();
452     }
453 
454     private static void showErrorMessage(String methodName,
455             String priorityList,
456             String tags,
457             String expectedTags,
458             String actualTags) {
459         System.err.println("\nIncorrect " + methodName + " result.");
460         System.err.println("  Priority list  :  " + priorityList);
461         System.err.println("  Language tags  :  " + tags);
462         System.err.println("  Expected value : " + expectedTags);
463         System.err.println("  Actual value   : " + actualTags);
464     }
465 
466 }
467