2 *******************************************************************************
3 * Copyright (C) 2008-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl;
9 import java.text.ParseException;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.Iterator;
14 import java.util.MissingResourceException;
16 import java.util.TreeMap;
18 import com.ibm.icu.text.PluralRules;
19 import com.ibm.icu.util.ULocale;
20 import com.ibm.icu.util.UResourceBundle;
23 * Loader for plural rules data.
25 public class PluralRulesLoader {
26 private final Map<String, PluralRules> rulesIdToRules;
27 private Map<String, String> localeIdToRulesId; // lazy init, use
28 // getLocaleIdToRulesIdMap to
30 private Map<String, ULocale> rulesIdToEquivalentULocale; // lazy init, use
31 // getRulesIdToEquivalentULocaleMap
35 * Access through singleton.
37 private PluralRulesLoader() {
38 rulesIdToRules = new HashMap<String, PluralRules>();
42 * Returns the locales for which we have plurals data. Utility for testing.
44 public ULocale[] getAvailableULocales() {
45 Set<String> keys = getLocaleIdToRulesIdMap().keySet();
46 ULocale[] locales = new ULocale[keys.size()];
48 for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
49 locales[n++] = ULocale.createCanonical(iter.next());
55 * Returns the functionally equivalent locale.
57 public ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
58 if (isAvailable != null && isAvailable.length > 0) {
59 String localeId = ULocale.canonicalize(locale.getBaseName());
60 Map<String, String> idMap = getLocaleIdToRulesIdMap();
61 isAvailable[0] = idMap.containsKey(localeId);
64 String rulesId = getRulesIdForLocale(locale);
65 if (rulesId == null || rulesId.trim().length() == 0) {
66 return ULocale.ROOT; // ultimate fallback
69 ULocale result = getRulesIdToEquivalentULocaleMap().get(
72 return ULocale.ROOT; // ultimate fallback
79 * Returns the lazily-constructed map.
81 private Map<String, String> getLocaleIdToRulesIdMap() {
82 checkBuildRulesIdMaps();
83 return localeIdToRulesId;
87 * Returns the lazily-constructed map.
89 private Map<String, ULocale> getRulesIdToEquivalentULocaleMap() {
90 checkBuildRulesIdMaps();
91 return rulesIdToEquivalentULocale;
95 * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
96 * maps if necessary. These exactly reflect the contents of the locales
97 * resource in plurals.res.
99 private void checkBuildRulesIdMaps() {
100 if (localeIdToRulesId == null) {
102 UResourceBundle pluralb = getPluralBundle();
103 UResourceBundle localeb = pluralb.get("locales");
104 localeIdToRulesId = new TreeMap<String, String>(); // sort for
107 // getAvailableULocales
108 rulesIdToEquivalentULocale = new HashMap<String, ULocale>(); // not
110 for (int i = 0; i < localeb.getSize(); ++i) {
111 UResourceBundle b = localeb.get(i);
112 String id = b.getKey();
113 String value = b.getString().intern();
114 localeIdToRulesId.put(id, value);
116 if (!rulesIdToEquivalentULocale.containsKey(value)) {
117 rulesIdToEquivalentULocale.put(value, new ULocale(id));
120 } catch (MissingResourceException e) {
121 localeIdToRulesId = Collections.emptyMap(); // dummy so we don't
124 rulesIdToEquivalentULocale = Collections.emptyMap();
130 * Gets the rulesId from the locale,with locale fallback. If there is no
131 * rulesId, return null. The rulesId might be the empty string if the rule
132 * is the default rule.
134 public String getRulesIdForLocale(ULocale locale) {
135 Map<String, String> idMap = getLocaleIdToRulesIdMap();
136 String localeId = ULocale.canonicalize(locale.getBaseName());
137 String rulesId = null;
138 while (null == (rulesId = idMap.get(localeId))) {
139 int ix = localeId.lastIndexOf("_");
143 localeId = localeId.substring(0, ix);
149 * Gets the rule from the rulesId. If there is no rule for this rulesId,
152 public PluralRules getRulesForRulesId(String rulesId) {
153 PluralRules rules = rulesIdToRules.get(rulesId);
156 UResourceBundle pluralb = getPluralBundle();
157 UResourceBundle rulesb = pluralb.get("rules");
158 UResourceBundle setb = rulesb.get(rulesId);
160 StringBuilder sb = new StringBuilder();
161 for (int i = 0; i < setb.getSize(); ++i) {
162 UResourceBundle b = setb.get(i);
166 sb.append(b.getKey());
168 sb.append(b.getString());
170 rules = PluralRules.parseDescription(sb.toString());
171 } catch (ParseException e) {
172 } catch (MissingResourceException e) {
174 rulesIdToRules.put(rulesId, rules); // put even if null
180 * Return the plurals resource. Note MissingResourceException is unchecked,
181 * listed here for clarity. Callers should handle this exception.
183 public UResourceBundle getPluralBundle() throws MissingResourceException {
184 return ICUResourceBundle.getBundleInstance(
185 ICUResourceBundle.ICU_BASE_NAME, "plurals",
186 ICUResourceBundle.ICU_DATA_CLASS_LOADER, true);
190 * Returns the plural rules for the the locale. If we don't have data,
191 * com.ibm.icu.text.PluralRules.DEFAULT is returned.
193 public PluralRules forLocale(ULocale locale) {
194 String rulesId = getRulesIdForLocale(locale);
195 if (rulesId == null || rulesId.trim().length() == 0) {
196 return PluralRules.DEFAULT;
198 PluralRules rules = getRulesForRulesId(rulesId);
200 rules = PluralRules.DEFAULT;
206 * The only instance of the loader.
208 public static final PluralRulesLoader loader = new PluralRulesLoader();