blob: 23383ea18d6d19dc910f310e7d32a702ff91253d [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2008-2013, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.simple;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TreeMap;
import com.ibm.icu.simple.PluralRules.PluralType;
/**
* Loader for plural rules data.
*/
public class PluralRulesLoader extends PluralRules.Factory {
// Data created from ICU4C with the command
// ~/svn.icu/trunk/bld$ LD_LIBRARY_PATH=lib bin/genrb --write-java UTF-8 --java-package com.ibm.icu.simple -s ../src/source/data/misc/ plurals.txt -d /tmp/icu
private static final ResourceBundle DATA_RB = new LocaleElements_plurals();
private final Map<String, PluralRules> rulesIdToRules;
// lazy init, use getLocaleIdToRulesIdMap to access
private Map<String, String> localeIdToCardinalRulesId;
private Map<String, String> localeIdToOrdinalRulesId;
/**
* Access through singleton.
*/
private PluralRulesLoader() {
rulesIdToRules = new HashMap<String, PluralRules>();
}
/**
* Returns the lazily-constructed map.
*/
private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
checkBuildRulesIdMaps();
return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
}
/**
* Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
* maps if necessary. These exactly reflect the contents of the locales
* resource in plurals.res.
*/
private void checkBuildRulesIdMaps() {
boolean haveMap;
synchronized (this) {
haveMap = localeIdToCardinalRulesId != null;
}
if (!haveMap) {
Map<String, String> tempLocaleIdToCardinalRulesId;
Map<String, String> tempLocaleIdToOrdinalRulesId;
try {
ResourceBundle pluralb = DATA_RB;
// Read cardinal-number rules.
Object[][] localeb = (Object[][]) pluralb.getObject("locales");
// sort for convenience of getAvailableULocales
tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
for (Object[] langAndId : localeb) {
String id = (String) langAndId[0];
String value = (String) langAndId[1];
tempLocaleIdToCardinalRulesId.put(id, value);
}
// Read ordinal-number rules.
localeb = (Object[][]) pluralb.getObject("locales_ordinals");
tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
for (Object[] langAndId : localeb) {
String id = (String) langAndId[0];
String value = (String) langAndId[1];
tempLocaleIdToOrdinalRulesId.put(id, value);
}
} catch (MissingResourceException e) {
// dummy so we don't try again
tempLocaleIdToCardinalRulesId = Collections.emptyMap();
tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
}
synchronized(this) {
if (localeIdToCardinalRulesId == null) {
localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
}
}
}
}
/**
* 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(Locale locale, PluralType type) {
Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
String lang = locale.getLanguage();
String rulesId = idMap.get(lang);
return rulesId;
}
/**
* Gets the rule from the rulesId. If there is no rule for this rulesId,
* return null.
*/
public PluralRules getRulesForRulesId(String rulesId) {
// synchronize on the map. release the lock temporarily while we build the rules.
PluralRules rules = null;
boolean hasRules; // Separate boolean because stored rules can be null.
synchronized (rulesIdToRules) {
hasRules = rulesIdToRules.containsKey(rulesId);
if (hasRules) {
rules = rulesIdToRules.get(rulesId); // can be null
}
}
if (!hasRules) {
try {
ResourceBundle pluralb = DATA_RB;
Object[][] rulesb = (Object[][]) pluralb.getObject("rules");
Object[][] setb = null;
for (Object[] idAndRule : rulesb) { // Unbounded loop: We must find the rulesId.
if (rulesId.equals(idAndRule[0])) {
setb = (Object[][]) idAndRule[1];
break;
}
}
StringBuilder sb = new StringBuilder();
for (Object[] keywordAndRule : setb) {
if (sb.length() > 0) {
sb.append("; ");
}
sb.append((String) keywordAndRule[0]);
sb.append(": ");
sb.append((String) keywordAndRule[1]);
}
rules = PluralRules.parseDescription(sb.toString());
} catch (ParseException e) {
} catch (MissingResourceException e) {
}
synchronized (rulesIdToRules) {
if (rulesIdToRules.containsKey(rulesId)) {
rules = rulesIdToRules.get(rulesId);
} else {
rulesIdToRules.put(rulesId, rules); // can be null
}
}
}
return rules;
}
/**
* 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(Locale locale, PluralRules.PluralType type) {
String rulesId = getRulesIdForLocale(locale, type);
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();
}