blob: ceb1382914af9d2eca7fca41eee1262def501afa [file] [log] [blame]
/*
**********************************************************************
* 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 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;
public class ULocaleCollationTest extends TestFmwk {
public static void main(String[] args) throws Exception {
new ULocaleCollationTest().run(args);
}
public void TestCollator() {
checkService("ja_JP_YOKOHAMA", new ServiceFacade() {
public Object create(ULocale req) {
return Collator.getInstance(req);
}
}, null, new Registrar() {
public Object register(ULocale loc, Object prototype) {
return Collator.registerInstance((Collator) prototype, loc);
}
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");
}
}
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)", "Bahasa Indonesia (Indonesia)", "in", "in_ID"},
{"Indonésien (Serbie)", "Bahasa 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);
}
}
}
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());
}
}
}
}