ICU-6373 use plural rules data from resource, update tests to match new data

X-SVN-Rev: 24266
This commit is contained in:
Doug Felt 2008-06-21 23:43:51 +00:00
parent 9993b72bc3
commit 942954d066
4 changed files with 203 additions and 69 deletions

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2007, International Business Machines Corporation and *
* Copyright (C) 2007-2008, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -48,15 +48,19 @@ public class PluralFormatTest extends TestFmwk {
*/
log("test pattern: '" + testPattern + "'");
for (int i = 0; i < locales.length; ++i) {
PluralFormat plf = new PluralFormat(new ULocale(locales[i]), testPattern);
log("plf: " + plf);
String expected = (String) changes.get(new Integer(0));
for (int n = 0; n < 200; ++n) {
if (changes.get(new Integer(n)) != null) {
expected = (String) changes.get(new Integer(n));
try {
PluralFormat plf = new PluralFormat(new ULocale(locales[i]), testPattern);
log("plf: " + plf);
String expected = (String) changes.get(new Integer(0));
for (int n = 0; n < 200; ++n) {
if (changes.get(new Integer(n)) != null) {
expected = (String) changes.get(new Integer(n));
}
assertEquals("Locale: " + locales[i] + ", number: " + n,
expected, plf.format(n));
}
assertEquals("Locale: " + locales[i] + ", number: " + n,
expected, plf.format(n));
} catch (IllegalArgumentException e) {
errln(e.getMessage() + " locale: " + locales[i] + " pattern: '" + testPattern + "' " + System.currentTimeMillis());
}
}
}
@ -70,7 +74,7 @@ public class PluralFormatTest extends TestFmwk {
}
public void TestSingular1Locales() {
String localeIDs = "da,de,el,en,eo,es,et,fi,fo,he,hu,it,nb,nl,nn,no,pt,sv";
String localeIDs = "da,de,el,en,eo,es,et,fi,fo,he,it,nb,nl,nn,no,pt_PT,sv";
String testPattern = "one{one} other{other}";
Map changes = new HashMap();
changes.put(new Integer(0), "other");
@ -118,13 +122,13 @@ public class PluralFormatTest extends TestFmwk {
public void TestSingularZeroSome() {
String localeIDs = "ro";
String testPattern = "zero{zero} one{one} other{other}";
String testPattern = "few{few} one{one} other{other}";
Map changes = new HashMap();
changes.put(new Integer(0), "zero");
changes.put(new Integer(0), "few");
changes.put(new Integer(1), "one");
changes.put(new Integer(2), "zero");
changes.put(new Integer(2), "few");
changes.put(new Integer(20), "other");
changes.put(new Integer(101), "zero");
changes.put(new Integer(101), "few");
changes.put(new Integer(120), "other");
helperTestRules(localeIDs, testPattern, changes);
}
@ -187,7 +191,7 @@ public class PluralFormatTest extends TestFmwk {
changes.put(new Integer(2), "few");
changes.put(new Integer(5), "other");
for (int i = 2; i < 20; ++i) {
if (i == 11) {
if (i == 2 || i == 11 || i == 12) {
continue;
}
changes.put(new Integer(i*10 + 2), "few");

View file

@ -9,6 +9,7 @@ package com.ibm.icu.dev.test.format;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.ULocale;
import java.text.ParseException;
import java.util.ArrayList;
@ -131,4 +132,22 @@ public class PluralRulesTest extends TestFmwk {
compareEquality(rules);
}
}
public void testBuiltInRules() {
// spot check
PluralRules rules = PluralRules.forLocale(ULocale.US);
assertEquals("us 0", PluralRules.KEYWORD_OTHER, rules.select(0));
assertEquals("us 1", PluralRules.KEYWORD_ONE, rules.select(1));
assertEquals("us 2", PluralRules.KEYWORD_OTHER, rules.select(2));
rules = PluralRules.forLocale(ULocale.JAPAN);
assertEquals("ja 0", PluralRules.KEYWORD_OTHER, rules.select(0));
assertEquals("ja 1", PluralRules.KEYWORD_OTHER, rules.select(1));
assertEquals("ja 2", PluralRules.KEYWORD_OTHER, rules.select(2));
rules = PluralRules.forLocale(ULocale.createCanonical("ru"));
assertEquals("ru 0", PluralRules.KEYWORD_MANY, rules.select(0));
assertEquals("ru 1", PluralRules.KEYWORD_ONE, rules.select(1));
assertEquals("ru 2", PluralRules.KEYWORD_FEW, rules.select(2));
}
}

View file

@ -0,0 +1,161 @@
/*
*******************************************************************************
* Copyright (C) 2008, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.impl;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.TreeMap;
/**
* Loader for plural rules data.
*/
public class PluralRulesLoader {
private final Map rulesIdToRules;
private Map localeIdToRulesId; // lazy init, use getLocaleIdToRulesIdMap to access
/**
* Access through singleton.
*/
private PluralRulesLoader() {
rulesIdToRules = new HashMap();
}
/**
* Returns the locales for which we have plurals data.
* Utility for testing.
*/
public ULocale[] getAvailableULocales() {
Set keys = getLocaleIdToRulesIdMap().keySet();
ULocale[] locales = new ULocale[keys.size()];
int n = 0;
for (Iterator iter = keys.iterator(); iter.hasNext();) {
locales[n++] = ULocale.createCanonical((String) iter.next());
}
return locales;
}
/**
* Lazily construct the map from localeIds to rulesIds. This
* map exactly reflects the contents of the locales resource
* in plurals.res.
*/
private Map getLocaleIdToRulesIdMap() {
if (localeIdToRulesId == null) {
try {
UResourceBundle pluralb = getPluralBundle();
UResourceBundle localeb = pluralb.get("locales");
localeIdToRulesId = new TreeMap(); // sort for convenience of getAvailableULocales
for (int i = 0; i < localeb.getSize(); ++i) {
UResourceBundle b = localeb.get(i);
String id = b.getKey();
String value = b.getString().intern();
localeIdToRulesId.put(id, value);
}
}
catch (MissingResourceException e) {
localeIdToRulesId = new HashMap(); // dummy so we don't try again
}
}
return localeIdToRulesId;
}
/**
* Gets the rulesId from the locale,with locale fallback. If there is no
* rulesId, return null. The rulesId might be the empty string if the
* rule is the default rule.
*/
public String getRulesIdForLocale(ULocale locale) {
Map idMap = getLocaleIdToRulesIdMap();
String localeId = ULocale.canonicalize(locale.getBaseName());
String rulesId = null;
while (null == (rulesId = (String) idMap.get(localeId))) {
int ix = localeId.lastIndexOf("_");
if (ix == -1) {
break;
}
localeId = localeId.substring(0, ix);
}
return rulesId;
}
/**
* Gets the rule from the rulesId. If there is no rule for this rulesId,
* return null.
*/
public PluralRules getRulesForRulesId(String rulesId) {
PluralRules rules = (PluralRules) rulesIdToRules.get(rulesId);
if (rules == null) {
try {
UResourceBundle pluralb = getPluralBundle();
UResourceBundle rulesb = pluralb.get("rules");
UResourceBundle setb = rulesb.get(rulesId);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < setb.getSize(); ++i) {
UResourceBundle b = setb.get(i);
if (i > 0) {
sb.append("; ");
}
sb.append(b.getKey());
sb.append(": ");
sb.append(b.getString());
}
rules = PluralRules.parseDescription(sb.toString());
} catch (ParseException e) {
} catch (MissingResourceException e) {
}
rulesIdToRules.put(rulesId, rules); // put even if null
}
return rules;
}
/**
* Return the plurals resource.
* Note MissingResourceException is unchecked, listed here for clarity.
* Callers should handle this exception.
*/
public UResourceBundle getPluralBundle() throws MissingResourceException {
return ICUResourceBundle.getBundleInstance(
ICUResourceBundle.ICU_BASE_NAME,
"plurals",
ICUResourceBundle.ICU_DATA_CLASS_LOADER,
true);
}
/**
* Returns the plural rules for the the locale.
* If we don't have data,
* com.ibm.icu.text.PluralRules.DEFAULT is returned.
*/
public PluralRules forLocale(ULocale locale) {
String rulesId = getRulesIdForLocale(locale);
if (rulesId == null || rulesId.trim().length() == 0) {
return PluralRules.DEFAULT;
}
PluralRules rules = getRulesForRulesId(rulesId);
if (rules == null) {
rules = PluralRules.DEFAULT;
}
return rules;
}
/**
* The only instance of the loader.
*/
public static final PluralRulesLoader loader = new PluralRulesLoader();
}

View file

@ -7,17 +7,16 @@
package com.ibm.icu.text;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.impl.PluralRulesLoader;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.util.ULocale;
import java.io.Serializable;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
@ -78,8 +77,6 @@ import java.util.Set;
public class PluralRules implements Serializable {
private static final long serialVersionUID = 1;
private static final Map ruleMap; // from locale string to PluralRules
private final RuleList rules;
private final Set keywords;
private int repeatLimit; // for equality test
@ -276,46 +273,6 @@ public class PluralRules implements Serializable {
int getRepeatLimit();
}
// default data
static {
String[] ruledata = {
"other: n/ja,ko,tr,vi", // not strictly necessary, default for all
"zero: n is 0; one: n is 1; two: n is 2; few: n in 3..10; " +
"many: n in 11..99/ar",
"one: n is 1/da,de,el,en,eo,es,et,fi,fo,he,hu,it,nb,nl,nn,no,pt,sv",
"one: n in 0..1/fr,pt_BR",
"zero: n is 0; one: n mod 10 is 1 and n mod 100 is not 11/lv",
"one: n is 1; two: n is 2/ga",
"zero: n is 0; one: n is 1; zero: n mod 100 in 1..19/ro",
"other: n mod 100 in 11..19; one: n mod 10 is 1; " +
"few: n mod 10 in 2..9/lt",
"one: n mod 10 is 1 and n mod 100 is not 11; " +
"few: n mod 10 in 2..4 " +
"and n mod 100 not in 12..14/hr,ru,sr,uk",
"one: n is 1; few: n in 2..4/cs,sk",
"one: n is 1; few: n mod 10 in 2..4 and n mod 100 not in 12..14/pl",
"one: n mod 100 is 1; two: n mod 100 is 2; " +
"few: n mod 100 in 3..4/sl",
};
HashMap map = new HashMap();
for (int i = 0; i < ruledata.length; ++i) {
String[] data = Utility.split(ruledata[i], '/');
try {
PluralRules pluralRules = parseDescription(data[0]);
String[] locales = Utility.split(data[1], ',');
for (int j = 0; j < locales.length; ++j) {
map.put(locales[j].trim(), pluralRules);
}
} catch (Exception e) {
System.err.println("PluralRules init failure, " +
e.getMessage() + " at line " + i);
}
}
ruleMap = map;
}
/**
* syntax:
* condition : or_condition
@ -724,14 +681,7 @@ public class PluralRules implements Serializable {
* @provisional This API might change or be removed in a future release.
*/
public static PluralRules forLocale(ULocale locale) {
PluralRules result = null;
while (null == (result = (PluralRules) ruleMap.get(locale.getName()))) {
locale = locale.getFallback();
if (locale == null) {
return DEFAULT;
}
}
return result;
return PluralRulesLoader.loader.forLocale(locale);
}
/**
@ -846,4 +796,4 @@ public class PluralRules implements Serializable {
}
return repeatLimit;
}
}
}