| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ********************************************************************** |
| * Copyright (c) 2015, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ********************************************************************** |
| * Author: Alan Liu |
| * Created: January 14 2004 |
| * Since: ICU 2.8 |
| ********************************************************************** |
| */ |
| package com.ibm.icu.dev.test.util; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Set; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| import com.ibm.icu.dev.test.TestFmwk; |
| import com.ibm.icu.text.Collator; |
| import com.ibm.icu.text.DisplayContext; |
| import com.ibm.icu.text.DisplayContext.Type; |
| import com.ibm.icu.text.LocaleDisplayNames; |
| import com.ibm.icu.text.LocaleDisplayNames.UiListItem; |
| import com.ibm.icu.util.IllformedLocaleException; |
| import com.ibm.icu.util.ULocale; |
| |
| @RunWith(JUnit4.class) |
| public class ULocaleCollationTest extends TestFmwk { |
| @Test |
| public void TestCollator() { |
| checkService("ja_JP_YOKOHAMA", new ServiceFacade() { |
| @Override |
| public Object create(ULocale req) { |
| return Collator.getInstance(req); |
| } |
| }, null, new Registrar() { |
| @Override |
| public Object register(ULocale loc, Object prototype) { |
| return Collator.registerInstance((Collator) prototype, loc); |
| } |
| @Override |
| public boolean unregister(Object key) { |
| return Collator.unregister(key); |
| } |
| }); |
| } |
| |
| |
| /** |
| * Interface used by checkService defining a protocol to create an |
| * object, given a requested locale. |
| */ |
| interface ServiceFacade { |
| Object create(ULocale requestedLocale); |
| } |
| |
| /** |
| * Interface used by checkService defining a protocol to get a |
| * contained subobject, given its parent object. |
| */ |
| interface Subobject { |
| Object get(Object parent); |
| } |
| |
| /** |
| * Interface used by checkService defining a protocol to register |
| * and unregister a service object prototype. |
| */ |
| interface Registrar { |
| Object register(ULocale loc, Object prototype); |
| boolean unregister(Object key); |
| } |
| |
| |
| |
| /** |
| * Compare two locale IDs. If they are equal, return 0. If `string' |
| * starts with `prefix' plus an additional element, that is, string == |
| * prefix + '_' + x, then return 1. Otherwise return a value < 0. |
| */ |
| static int loccmp(String string, String prefix) { |
| int slen = string.length(), |
| plen = prefix.length(); |
| /* 'root' is "less than" everything */ |
| if (prefix.equals("root")) { |
| return string.equals("root") ? 0 : 1; |
| } |
| // ON JAVA (only -- not on C -- someone correct me if I'm wrong) |
| // consider "" to be an alternate name for "root". |
| if (plen == 0) { |
| return slen == 0 ? 0 : 1; |
| } |
| if (!string.startsWith(prefix)) return -1; /* mismatch */ |
| if (slen == plen) return 0; |
| if (string.charAt(plen) == '_') return 1; |
| return -2; /* false match, e.g. "en_USX" cmp "en_US" */ |
| } |
| |
| /** |
| * Check the relationship between requested locales, and report problems. |
| * The caller specifies the expected relationships between requested |
| * and valid (expReqValid) and between valid and actual (expValidActual). |
| * Possible values are: |
| * "gt" strictly greater than, e.g., en_US > en |
| * "ge" greater or equal, e.g., en >= en |
| * "eq" equal, e.g., en == en |
| */ |
| void checklocs(String label, |
| String req, |
| Locale validLoc, |
| Locale actualLoc, |
| String expReqValid, |
| String expValidActual) { |
| String valid = validLoc.toString(); |
| String actual = actualLoc.toString(); |
| int reqValid = loccmp(req, valid); |
| int validActual = loccmp(valid, actual); |
| boolean reqOK = (expReqValid.equals("gt") && reqValid > 0) || |
| (expReqValid.equals("ge") && reqValid >= 0) || |
| (expReqValid.equals("eq") && reqValid == 0); |
| boolean valOK = (expValidActual.equals("gt") && validActual > 0) || |
| (expValidActual.equals("ge") && validActual >= 0) || |
| (expValidActual.equals("eq") && validActual == 0); |
| if (reqOK && valOK) { |
| logln("Ok: " + label + "; req=" + req + ", valid=" + valid + |
| ", actual=" + actual); |
| } else { |
| errln("FAIL: " + label + "; req=" + req + ", valid=" + valid + |
| ", actual=" + actual + |
| (reqOK ? "" : "\n req !" + expReqValid + " valid") + |
| (valOK ? "" : "\n val !" + expValidActual + " actual")); |
| } |
| } |
| |
| /** |
| * Use reflection to call getLocale() on the given object to |
| * determine both the valid and the actual locale. Verify these |
| * for correctness. |
| */ |
| void checkObject(String requestedLocale, Object obj, |
| String expReqValid, String expValidActual) { |
| Class[] getLocaleParams = new Class[] { ULocale.Type.class }; |
| try { |
| Class cls = obj.getClass(); |
| Method getLocale = cls.getMethod("getLocale", getLocaleParams); |
| ULocale valid = (ULocale) getLocale.invoke(obj, new Object[] { |
| ULocale.VALID_LOCALE }); |
| ULocale actual = (ULocale) getLocale.invoke(obj, new Object[] { |
| ULocale.ACTUAL_LOCALE }); |
| checklocs(cls.getName(), requestedLocale, |
| valid.toLocale(), actual.toLocale(), |
| expReqValid, expValidActual); |
| } |
| |
| // Make the following exceptions _specific_ -- do not |
| // catch(Exception), since that will catch the exception |
| // that errln throws. |
| catch(NoSuchMethodException e1) { |
| // no longer an error, Currency has no getLocale |
| // errln("FAIL: reflection failed: " + e1); |
| } catch(SecurityException e2) { |
| errln("FAIL: reflection failed: " + e2); |
| } catch(IllegalAccessException e3) { |
| errln("FAIL: reflection failed: " + e3); |
| } catch(IllegalArgumentException e4) { |
| errln("FAIL: reflection failed: " + e4); |
| } catch(InvocationTargetException e5) { |
| // no longer an error, Currency has no getLocale |
| // errln("FAIL: reflection failed: " + e5); |
| } |
| } |
| |
| /** |
| * Verify the correct getLocale() behavior for the given service. |
| * @param requestedLocale the locale to request. This MUST BE |
| * FAKE. In other words, it should be something like |
| * en_US_FAKEVARIANT so this method can verify correct fallback |
| * behavior. |
| * @param svc a factory object that can create the object to be |
| * tested. This isn't necessary here (one could just pass in the |
| * object) but is required for the overload of this method that |
| * takes a Registrar. |
| */ |
| void checkService(String requestedLocale, ServiceFacade svc) { |
| checkService(requestedLocale, svc, null, null); |
| } |
| |
| /** |
| * Verify the correct getLocale() behavior for the given service. |
| * @param requestedLocale the locale to request. This MUST BE |
| * FAKE. In other words, it should be something like |
| * en_US_FAKEVARIANT so this method can verify correct fallback |
| * behavior. |
| * @param svc a factory object that can create the object to be |
| * tested. |
| * @param sub an object that can be used to retrieve a subobject |
| * which should also be tested. May be null. |
| * @param reg an object that supplies the registration and |
| * unregistration functionality to be tested. May be null. |
| */ |
| void checkService(String requestedLocale, ServiceFacade svc, |
| Subobject sub, Registrar reg) { |
| ULocale req = new ULocale(requestedLocale); |
| Object obj = svc.create(req); |
| checkObject(requestedLocale, obj, "gt", "ge"); |
| if (sub != null) { |
| Object subobj = sub.get(obj); |
| checkObject(requestedLocale, subobj, "gt", "ge"); |
| } |
| if (reg != null) { |
| logln("Info: Registering service"); |
| Object key = reg.register(req, obj); |
| Object objReg = svc.create(req); |
| checkObject(requestedLocale, objReg, "eq", "eq"); |
| if (sub != null) { |
| Object subobj = sub.get(obj); |
| // Assume subobjects don't come from services, so |
| // their metadata should be structured normally. |
| checkObject(requestedLocale, subobj, "gt", "ge"); |
| } |
| logln("Info: Unregistering service"); |
| if (!reg.unregister(key)) { |
| errln("FAIL: unregister failed"); |
| } |
| Object objUnreg = svc.create(req); |
| checkObject(requestedLocale, objUnreg, "gt", "ge"); |
| } |
| } |
| |
| @Test |
| public void TestNameList() { |
| String[][][] tests = { |
| /* name in French, name in self, minimized, modified */ |
| {{"fr-Cyrl-BE", "fr-Cyrl-CA"}, |
| {"Français (cyrillique, Belgique)", "Français (cyrillique, Belgique)", "fr_Cyrl_BE", "fr_Cyrl_BE"}, |
| {"Français (cyrillique, Canada)", "Français (cyrillique, Canada)", "fr_Cyrl_CA", "fr_Cyrl_CA"}, |
| }, |
| {{"en", "de", "fr", "zh"}, |
| {"Allemand", "Deutsch", "de", "de"}, |
| {"Anglais", "English", "en", "en"}, |
| {"Chinois", "中文", "zh", "zh"}, |
| {"Français", "Français", "fr", "fr"}, |
| }, |
| // some non-canonical names |
| {{"iw", "iw-US", "no", "no-Cyrl", "in", "in-YU"}, |
| {"Hébreu (États-Unis)", "עברית (ארצות הברית)", "iw_US", "iw_US"}, |
| {"Hébreu (Israël)", "עברית (ישראל)", "iw", "iw_IL"}, |
| {"Indonésien (Indonésie)", "Indonesia (Indonesia)", "in", "in_ID"}, |
| {"Indonésien (Serbie)", "Indonesia (Serbia)", "in_YU", "in_YU"}, |
| {"Norvégien (cyrillique)", "Norsk (kyrillisk)", "no_Cyrl", "no_Cyrl"}, |
| {"Norvégien (latin)", "Norsk (latinsk)", "no", "no_Latn"}, |
| }, |
| {{"zh-Hant-TW", "en", "en-gb", "fr", "zh-Hant", "de", "de-CH", "zh-TW"}, |
| {"Allemand (Allemagne)", "Deutsch (Deutschland)", "de", "de_DE"}, |
| {"Allemand (Suisse)", "Deutsch (Schweiz)", "de_CH", "de_CH"}, |
| {"Anglais (États-Unis)", "English (United States)", "en", "en_US"}, |
| {"Anglais (Royaume-Uni)", "English (United Kingdom)", "en_GB", "en_GB"}, |
| {"Chinois (traditionnel)", "中文(繁體)", "zh_Hant", "zh_Hant"}, |
| {"Français", "Français", "fr", "fr"}, |
| }, |
| {{"zh", "en-gb", "en-CA", "fr-Latn-FR"}, |
| {"Anglais (Canada)", "English (Canada)", "en_CA", "en_CA"}, |
| {"Anglais (Royaume-Uni)", "English (United Kingdom)", "en_GB", "en_GB"}, |
| {"Chinois", "中文", "zh", "zh"}, |
| {"Français", "Français", "fr", "fr"}, |
| }, |
| {{"en-gb", "fr", "zh-Hant", "zh-SG", "sr", "sr-Latn"}, |
| {"Anglais (Royaume-Uni)", "English (United Kingdom)", "en_GB", "en_GB"}, |
| {"Chinois (simplifié, Singapour)", "中文(简体,新加坡)", "zh_SG", "zh_Hans_SG"}, |
| {"Chinois (traditionnel, Taïwan)", "中文(繁體,台灣)", "zh_Hant", "zh_Hant_TW"}, |
| {"Français", "Français", "fr", "fr"}, |
| {"Serbe (cyrillique)", "Српски (ћирилица)", "sr", "sr_Cyrl"}, |
| {"Serbe (latin)", "Srpski (latinica)", "sr_Latn", "sr_Latn"}, |
| }, |
| {{"fr-Cyrl", "fr-Arab"}, |
| {"Français (arabe)", "Français (arabe)", "fr_Arab", "fr_Arab"}, |
| {"Français (cyrillique)", "Français (cyrillique)", "fr_Cyrl", "fr_Cyrl"}, |
| }, |
| {{"fr-Cyrl-BE", "fr-Arab-CA"}, |
| {"Français (arabe, Canada)", "Français (arabe, Canada)", "fr_Arab_CA", "fr_Arab_CA"}, |
| {"Français (cyrillique, Belgique)", "Français (cyrillique, Belgique)", "fr_Cyrl_BE", "fr_Cyrl_BE"}, |
| } |
| }; |
| ULocale french = ULocale.FRENCH; |
| LocaleDisplayNames names = LocaleDisplayNames.getInstance(french, |
| DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU); |
| for (Type type : DisplayContext.Type.values()) { |
| logln("Contexts: " + names.getContext(type).toString()); |
| } |
| Collator collator = Collator.getInstance(french); |
| |
| for (String[][] test : tests) { |
| Set<ULocale> list = new LinkedHashSet<ULocale>(); |
| List<UiListItem> expected = new ArrayList<UiListItem>(); |
| for (String item : test[0]) { |
| list.add(new ULocale(item)); |
| } |
| for (int i = 1; i < test.length; ++i) { |
| String[] rawRow = test[i]; |
| expected.add(new UiListItem(new ULocale(rawRow[2]), new ULocale(rawRow[3]), rawRow[0], rawRow[1])); |
| } |
| List<UiListItem> newList = names.getUiList(list, false, collator); |
| if (!expected.equals(newList)) { |
| if (expected.size() != newList.size()) { |
| errln(list.toString() + ": wrong size" + expected + ", " + newList); |
| } else { |
| errln(list.toString()); |
| for (int i = 0; i < expected.size(); ++i) { |
| assertEquals(i+"", expected.get(i), newList.get(i)); |
| } |
| } |
| } else { |
| assertEquals(list.toString(), expected, newList); |
| } |
| } |
| } |
| |
| @Test |
| public void TestIllformedLocale() { |
| ULocale french = ULocale.FRENCH; |
| Collator collator = Collator.getInstance(french); |
| LocaleDisplayNames names = LocaleDisplayNames.getInstance(french, |
| DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU); |
| for (String malformed : Arrays.asList("en-a", "$", "ü--a", "en--US")) { |
| try { |
| Set<ULocale> supported = Collections.singleton(new ULocale(malformed)); |
| names.getUiList(supported, false, collator); |
| assertNull("Failed to detect bogus locale «" + malformed + "»", supported); |
| } catch (IllformedLocaleException e) { |
| logln("Successfully detected ill-formed locale «" + malformed + "»:" + e.getMessage()); |
| } |
| } |
| } |
| } |