/* ******************************************************************************* * 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 rulesIdToRules; // lazy init, use getLocaleIdToRulesIdMap to access private Map localeIdToCardinalRulesId; private Map localeIdToOrdinalRulesId; /** * Access through singleton. */ private PluralRulesLoader() { rulesIdToRules = new HashMap(); } /** * Returns the lazily-constructed map. */ private Map 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 tempLocaleIdToCardinalRulesId; Map tempLocaleIdToOrdinalRulesId; try { ResourceBundle pluralb = DATA_RB; // Read cardinal-number rules. Object[][] localeb = (Object[][]) pluralb.getObject("locales"); // sort for convenience of getAvailableULocales tempLocaleIdToCardinalRulesId = new TreeMap(); 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(); 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 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(); }