1/* 2 ********************************************************************** 3 * Copyright (c) 2015, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ********************************************************************** 6 * Author: Alan Liu 7 * Created: January 14 2004 8 * Since: ICU 2.8 9 ********************************************************************** 10 */ 11package com.ibm.icu.dev.test.util; 12 13import java.lang.reflect.InvocationTargetException; 14import java.lang.reflect.Method; 15import java.util.ArrayList; 16import java.util.Arrays; 17import java.util.Collections; 18import java.util.LinkedHashSet; 19import java.util.List; 20import java.util.Locale; 21import java.util.Set; 22 23import com.ibm.icu.dev.test.TestFmwk; 24import com.ibm.icu.text.Collator; 25import com.ibm.icu.text.DisplayContext; 26import com.ibm.icu.text.DisplayContext.Type; 27import com.ibm.icu.text.LocaleDisplayNames; 28import com.ibm.icu.text.LocaleDisplayNames.UiListItem; 29import com.ibm.icu.util.IllformedLocaleException; 30import com.ibm.icu.util.ULocale; 31 32public class ULocaleCollationTest extends TestFmwk { 33 34 public static void main(String[] args) throws Exception { 35 new ULocaleCollationTest().run(args); 36 } 37 38 public void TestCollator() { 39 checkService("ja_JP_YOKOHAMA", new ServiceFacade() { 40 public Object create(ULocale req) { 41 return Collator.getInstance(req); 42 } 43 }, null, new Registrar() { 44 public Object register(ULocale loc, Object prototype) { 45 return Collator.registerInstance((Collator) prototype, loc); 46 } 47 public boolean unregister(Object key) { 48 return Collator.unregister(key); 49 } 50 }); 51 } 52 53 54 /** 55 * Interface used by checkService defining a protocol to create an 56 * object, given a requested locale. 57 */ 58 interface ServiceFacade { 59 Object create(ULocale requestedLocale); 60 } 61 62 /** 63 * Interface used by checkService defining a protocol to get a 64 * contained subobject, given its parent object. 65 */ 66 interface Subobject { 67 Object get(Object parent); 68 } 69 70 /** 71 * Interface used by checkService defining a protocol to register 72 * and unregister a service object prototype. 73 */ 74 interface Registrar { 75 Object register(ULocale loc, Object prototype); 76 boolean unregister(Object key); 77 } 78 79 80 81 /** 82 * Compare two locale IDs. If they are equal, return 0. If `string' 83 * starts with `prefix' plus an additional element, that is, string == 84 * prefix + '_' + x, then return 1. Otherwise return a value < 0. 85 */ 86 static int loccmp(String string, String prefix) { 87 int slen = string.length(), 88 plen = prefix.length(); 89 /* 'root' is "less than" everything */ 90 if (prefix.equals("root")) { 91 return string.equals("root") ? 0 : 1; 92 } 93 // ON JAVA (only -- not on C -- someone correct me if I'm wrong) 94 // consider "" to be an alternate name for "root". 95 if (plen == 0) { 96 return slen == 0 ? 0 : 1; 97 } 98 if (!string.startsWith(prefix)) return -1; /* mismatch */ 99 if (slen == plen) return 0; 100 if (string.charAt(plen) == '_') return 1; 101 return -2; /* false match, e.g. "en_USX" cmp "en_US" */ 102 } 103 104 /** 105 * Check the relationship between requested locales, and report problems. 106 * The caller specifies the expected relationships between requested 107 * and valid (expReqValid) and between valid and actual (expValidActual). 108 * Possible values are: 109 * "gt" strictly greater than, e.g., en_US > en 110 * "ge" greater or equal, e.g., en >= en 111 * "eq" equal, e.g., en == en 112 */ 113 void checklocs(String label, 114 String req, 115 Locale validLoc, 116 Locale actualLoc, 117 String expReqValid, 118 String expValidActual) { 119 String valid = validLoc.toString(); 120 String actual = actualLoc.toString(); 121 int reqValid = loccmp(req, valid); 122 int validActual = loccmp(valid, actual); 123 boolean reqOK = (expReqValid.equals("gt") && reqValid > 0) || 124 (expReqValid.equals("ge") && reqValid >= 0) || 125 (expReqValid.equals("eq") && reqValid == 0); 126 boolean valOK = (expValidActual.equals("gt") && validActual > 0) || 127 (expValidActual.equals("ge") && validActual >= 0) || 128 (expValidActual.equals("eq") && validActual == 0); 129 if (reqOK && valOK) { 130 logln("Ok: " + label + "; req=" + req + ", valid=" + valid + 131 ", actual=" + actual); 132 } else { 133 errln("FAIL: " + label + "; req=" + req + ", valid=" + valid + 134 ", actual=" + actual + 135 (reqOK ? "" : "\n req !" + expReqValid + " valid") + 136 (valOK ? "" : "\n val !" + expValidActual + " actual")); 137 } 138 } 139 140 /** 141 * Use reflection to call getLocale() on the given object to 142 * determine both the valid and the actual locale. Verify these 143 * for correctness. 144 */ 145 void checkObject(String requestedLocale, Object obj, 146 String expReqValid, String expValidActual) { 147 Class[] getLocaleParams = new Class[] { ULocale.Type.class }; 148 try { 149 Class cls = obj.getClass(); 150 Method getLocale = cls.getMethod("getLocale", getLocaleParams); 151 ULocale valid = (ULocale) getLocale.invoke(obj, new Object[] { 152 ULocale.VALID_LOCALE }); 153 ULocale actual = (ULocale) getLocale.invoke(obj, new Object[] { 154 ULocale.ACTUAL_LOCALE }); 155 checklocs(cls.getName(), requestedLocale, 156 valid.toLocale(), actual.toLocale(), 157 expReqValid, expValidActual); 158 } 159 160 // Make the following exceptions _specific_ -- do not 161 // catch(Exception), since that will catch the exception 162 // that errln throws. 163 catch(NoSuchMethodException e1) { 164 // no longer an error, Currency has no getLocale 165 // errln("FAIL: reflection failed: " + e1); 166 } catch(SecurityException e2) { 167 errln("FAIL: reflection failed: " + e2); 168 } catch(IllegalAccessException e3) { 169 errln("FAIL: reflection failed: " + e3); 170 } catch(IllegalArgumentException e4) { 171 errln("FAIL: reflection failed: " + e4); 172 } catch(InvocationTargetException e5) { 173 // no longer an error, Currency has no getLocale 174 // errln("FAIL: reflection failed: " + e5); 175 } 176 } 177 178 /** 179 * Verify the correct getLocale() behavior for the given service. 180 * @param requestedLocale the locale to request. This MUST BE 181 * FAKE. In other words, it should be something like 182 * en_US_FAKEVARIANT so this method can verify correct fallback 183 * behavior. 184 * @param svc a factory object that can create the object to be 185 * tested. This isn't necessary here (one could just pass in the 186 * object) but is required for the overload of this method that 187 * takes a Registrar. 188 */ 189 void checkService(String requestedLocale, ServiceFacade svc) { 190 checkService(requestedLocale, svc, null, null); 191 } 192 193 /** 194 * Verify the correct getLocale() behavior for the given service. 195 * @param requestedLocale the locale to request. This MUST BE 196 * FAKE. In other words, it should be something like 197 * en_US_FAKEVARIANT so this method can verify correct fallback 198 * behavior. 199 * @param svc a factory object that can create the object to be 200 * tested. 201 * @param sub an object that can be used to retrieve a subobject 202 * which should also be tested. May be null. 203 * @param reg an object that supplies the registration and 204 * unregistration functionality to be tested. May be null. 205 */ 206 void checkService(String requestedLocale, ServiceFacade svc, 207 Subobject sub, Registrar reg) { 208 ULocale req = new ULocale(requestedLocale); 209 Object obj = svc.create(req); 210 checkObject(requestedLocale, obj, "gt", "ge"); 211 if (sub != null) { 212 Object subobj = sub.get(obj); 213 checkObject(requestedLocale, subobj, "gt", "ge"); 214 } 215 if (reg != null) { 216 logln("Info: Registering service"); 217 Object key = reg.register(req, obj); 218 Object objReg = svc.create(req); 219 checkObject(requestedLocale, objReg, "eq", "eq"); 220 if (sub != null) { 221 Object subobj = sub.get(obj); 222 // Assume subobjects don't come from services, so 223 // their metadata should be structured normally. 224 checkObject(requestedLocale, subobj, "gt", "ge"); 225 } 226 logln("Info: Unregistering service"); 227 if (!reg.unregister(key)) { 228 errln("FAIL: unregister failed"); 229 } 230 Object objUnreg = svc.create(req); 231 checkObject(requestedLocale, objUnreg, "gt", "ge"); 232 } 233 } 234 235 public void TestNameList() { 236 String[][][] tests = { 237 /* name in French, name in self, minimized, modified */ 238 {{"fr-Cyrl-BE", "fr-Cyrl-CA"}, 239 {"Français (cyrillique, Belgique)", "Français (cyrillique, Belgique)", "fr_Cyrl_BE", "fr_Cyrl_BE"}, 240 {"Français (cyrillique, Canada)", "Français (cyrillique, Canada)", "fr_Cyrl_CA", "fr_Cyrl_CA"}, 241 }, 242 {{"en", "de", "fr", "zh"}, 243 {"Allemand", "Deutsch", "de", "de"}, 244 {"Anglais", "English", "en", "en"}, 245 {"Chinois", "中文", "zh", "zh"}, 246 {"Français", "Français", "fr", "fr"}, 247 }, 248 // some non-canonical names 249 {{"iw", "iw-US", "no", "no-Cyrl", "in", "in-YU"}, 250 {"Hébreu (États-Unis)", "עברית (ארצות הברית)", "iw_US", "iw_US"}, 251 {"Hébreu (Israël)", "עברית (ישראל)", "iw", "iw_IL"}, 252 {"Indonésien (Indonésie)", "Bahasa Indonesia (Indonesia)", "in", "in_ID"}, 253 {"Indonésien (Serbie)", "Bahasa Indonesia (Serbia)", "in_YU", "in_YU"}, 254 {"Norvégien (cyrillique)", "Norsk (kyrillisk)", "no_Cyrl", "no_Cyrl"}, 255 {"Norvégien (latin)", "Norsk (latinsk)", "no", "no_Latn"}, 256 }, 257 {{"zh-Hant-TW", "en", "en-gb", "fr", "zh-Hant", "de", "de-CH", "zh-TW"}, 258 {"Allemand (Allemagne)", "Deutsch (Deutschland)", "de", "de_DE"}, 259 {"Allemand (Suisse)", "Deutsch (Schweiz)", "de_CH", "de_CH"}, 260 {"Anglais (États-Unis)", "English (United States)", "en", "en_US"}, 261 {"Anglais (Royaume-Uni)", "English (United Kingdom)", "en_GB", "en_GB"}, 262 {"Chinois (traditionnel)", "中文(繁體)", "zh_Hant", "zh_Hant"}, 263 {"Français", "Français", "fr", "fr"}, 264 }, 265 {{"zh", "en-gb", "en-CA", "fr-Latn-FR"}, 266 {"Anglais (Canada)", "English (Canada)", "en_CA", "en_CA"}, 267 {"Anglais (Royaume-Uni)", "English (United Kingdom)", "en_GB", "en_GB"}, 268 {"Chinois", "中文", "zh", "zh"}, 269 {"Français", "Français", "fr", "fr"}, 270 }, 271 {{"en-gb", "fr", "zh-Hant", "zh-SG", "sr", "sr-Latn"}, 272 {"Anglais (Royaume-Uni)", "English (United Kingdom)", "en_GB", "en_GB"}, 273 {"Chinois (simplifié, Singapour)", "中文(简体、新加坡)", "zh_SG", "zh_Hans_SG"}, 274 {"Chinois (traditionnel, Taïwan)", "中文(繁體,台灣)", "zh_Hant", "zh_Hant_TW"}, 275 {"Français", "Français", "fr", "fr"}, 276 {"Serbe (cyrillique)", "Српски (ћирилица)", "sr", "sr_Cyrl"}, 277 {"Serbe (latin)", "Srpski (latinica)", "sr_Latn", "sr_Latn"}, 278 }, 279 {{"fr-Cyrl", "fr-Arab"}, 280 {"Français (arabe)", "Français (arabe)", "fr_Arab", "fr_Arab"}, 281 {"Français (cyrillique)", "Français (cyrillique)", "fr_Cyrl", "fr_Cyrl"}, 282 }, 283 {{"fr-Cyrl-BE", "fr-Arab-CA"}, 284 {"Français (arabe, Canada)", "Français (arabe, Canada)", "fr_Arab_CA", "fr_Arab_CA"}, 285 {"Français (cyrillique, Belgique)", "Français (cyrillique, Belgique)", "fr_Cyrl_BE", "fr_Cyrl_BE"}, 286 } 287 }; 288 ULocale french = ULocale.FRENCH; 289 LocaleDisplayNames names = LocaleDisplayNames.getInstance(french, 290 DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU); 291 for (Type type : DisplayContext.Type.values()) { 292 logln("Contexts: " + names.getContext(type).toString()); 293 } 294 Collator collator = Collator.getInstance(french); 295 296 for (String[][] test : tests) { 297 Set<ULocale> list = new LinkedHashSet<ULocale>(); 298 List<UiListItem> expected = new ArrayList<UiListItem>(); 299 for (String item : test[0]) { 300 list.add(new ULocale(item)); 301 } 302 for (int i = 1; i < test.length; ++i) { 303 String[] rawRow = test[i]; 304 expected.add(new UiListItem(new ULocale(rawRow[2]), new ULocale(rawRow[3]), rawRow[0], rawRow[1])); 305 } 306 List<UiListItem> newList = names.getUiList(list, false, collator); 307 if (!expected.equals(newList)) { 308 if (expected.size() != newList.size()) { 309 errln(list.toString() + ": wrong size" + expected + ", " + newList); 310 } else { 311 errln(list.toString()); 312 for (int i = 0; i < expected.size(); ++i) { 313 assertEquals(i+"", expected.get(i), newList.get(i)); 314 } 315 } 316 } else { 317 assertEquals(list.toString(), expected, newList); 318 } 319 } 320 } 321 322 public void TestIllformedLocale() { 323 ULocale french = ULocale.FRENCH; 324 Collator collator = Collator.getInstance(french); 325 LocaleDisplayNames names = LocaleDisplayNames.getInstance(french, 326 DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU); 327 for (String malformed : Arrays.asList("en-a", "$", "ü--a", "en--US")) { 328 try { 329 Set<ULocale> supported = Collections.singleton(new ULocale(malformed)); 330 names.getUiList(supported, false, collator); 331 assertNull("Failed to detect bogus locale «" + malformed + "»", supported); 332 } catch (IllformedLocaleException e) { 333 logln("Successfully detected ill-formed locale «" + malformed + "»:" + e.getMessage()); 334 } 335 } 336 } 337} 338