]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/impl/PluralRulesLoader.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / impl / PluralRulesLoader.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2008-2013, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.impl;
8
9 import java.text.ParseException;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.Iterator;
13 import java.util.Map;
14 import java.util.MissingResourceException;
15 import java.util.Set;
16 import java.util.TreeMap;
17
18 import com.ibm.icu.text.PluralRules;
19 import com.ibm.icu.text.PluralRules.PluralType;
20 import com.ibm.icu.util.ULocale;
21 import com.ibm.icu.util.UResourceBundle;
22
23 /**
24  * Loader for plural rules data.
25  */
26 public class PluralRulesLoader extends PluralRules.Factory {
27     private final Map<String, PluralRules> rulesIdToRules;
28     // lazy init, use getLocaleIdToRulesIdMap to access
29     private Map<String, String> localeIdToCardinalRulesId;
30     private Map<String, String> localeIdToOrdinalRulesId;
31     private Map<String, ULocale> rulesIdToEquivalentULocale;
32
33     /**
34      * Access through singleton.
35      */
36     private PluralRulesLoader() {
37         rulesIdToRules = new HashMap<String, PluralRules>();
38     }
39
40     /**
41      * Returns the locales for which we have plurals data. Utility for testing.
42      */
43     public ULocale[] getAvailableULocales() {
44         Set<String> keys = getLocaleIdToRulesIdMap(PluralType.CARDINAL).keySet();
45         ULocale[] locales = new ULocale[keys.size()];
46         int n = 0;
47         for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
48             locales[n++] = ULocale.createCanonical(iter.next());
49         }
50         return locales;
51     }
52
53     /**
54      * Returns the functionally equivalent locale.
55      */
56     public ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
57         if (isAvailable != null && isAvailable.length > 0) {
58             String localeId = ULocale.canonicalize(locale.getBaseName());
59             Map<String, String> idMap = getLocaleIdToRulesIdMap(PluralType.CARDINAL);
60             isAvailable[0] = idMap.containsKey(localeId);
61         }
62
63         String rulesId = getRulesIdForLocale(locale, PluralType.CARDINAL);
64         if (rulesId == null || rulesId.trim().length() == 0) {
65             return ULocale.ROOT; // ultimate fallback
66         }
67
68         ULocale result = getRulesIdToEquivalentULocaleMap().get(
69                 rulesId);
70         if (result == null) {
71             return ULocale.ROOT; // ultimate fallback
72         }
73
74         return result;
75     }
76
77     /**
78      * Returns the lazily-constructed map.
79      */
80     private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
81         checkBuildRulesIdMaps();
82         return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
83     }
84
85     /**
86      * Returns the lazily-constructed map.
87      */
88     private Map<String, ULocale> getRulesIdToEquivalentULocaleMap() {
89         checkBuildRulesIdMaps();
90         return rulesIdToEquivalentULocale;
91     }
92
93     /**
94      * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
95      * maps if necessary. These exactly reflect the contents of the locales
96      * resource in plurals.res.
97      */
98     private void checkBuildRulesIdMaps() {
99         boolean haveMap;
100         synchronized (this) {
101             haveMap = localeIdToCardinalRulesId != null;
102         }
103         if (!haveMap) {
104             Map<String, String> tempLocaleIdToCardinalRulesId;
105             Map<String, String> tempLocaleIdToOrdinalRulesId;
106             Map<String, ULocale> tempRulesIdToEquivalentULocale;
107             try {
108                 UResourceBundle pluralb = getPluralBundle();
109                 // Read cardinal-number rules.
110                 UResourceBundle localeb = pluralb.get("locales");
111
112                 // sort for convenience of getAvailableULocales
113                 tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
114                 // not visible
115                 tempRulesIdToEquivalentULocale = new HashMap<String, ULocale>();
116
117                 for (int i = 0; i < localeb.getSize(); ++i) {
118                     UResourceBundle b = localeb.get(i);
119                     String id = b.getKey();
120                     String value = b.getString().intern();
121                     tempLocaleIdToCardinalRulesId.put(id, value);
122
123                     if (!tempRulesIdToEquivalentULocale.containsKey(value)) {
124                         tempRulesIdToEquivalentULocale.put(value, new ULocale(id));
125                     }
126                 }
127
128                 // Read ordinal-number rules.
129                 localeb = pluralb.get("locales_ordinals");
130                 tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
131                 for (int i = 0; i < localeb.getSize(); ++i) {
132                     UResourceBundle b = localeb.get(i);
133                     String id = b.getKey();
134                     String value = b.getString().intern();
135                     tempLocaleIdToOrdinalRulesId.put(id, value);
136                 }
137             } catch (MissingResourceException e) {
138                 // dummy so we don't try again
139                 tempLocaleIdToCardinalRulesId = Collections.emptyMap();
140                 tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
141                 tempRulesIdToEquivalentULocale = Collections.emptyMap();
142             }
143             
144             synchronized(this) {
145                 if (localeIdToCardinalRulesId == null) {
146                     localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
147                     localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
148                     rulesIdToEquivalentULocale = tempRulesIdToEquivalentULocale;
149                 }
150             }
151         }
152     }
153
154     /**
155      * Gets the rulesId from the locale,with locale fallback. If there is no
156      * rulesId, return null. The rulesId might be the empty string if the rule
157      * is the default rule.
158      */
159     public String getRulesIdForLocale(ULocale locale, PluralType type) {
160         Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
161         String localeId = ULocale.canonicalize(locale.getBaseName());
162         String rulesId = null;
163         while (null == (rulesId = idMap.get(localeId))) {
164             int ix = localeId.lastIndexOf("_");
165             if (ix == -1) {
166                 break;
167             }
168             localeId = localeId.substring(0, ix);
169         }
170         return rulesId;
171     }
172
173     /**
174      * Gets the rule from the rulesId. If there is no rule for this rulesId,
175      * return null.
176      */
177     public PluralRules getRulesForRulesId(String rulesId) {
178         // synchronize on the map.  release the lock temporarily while we build the rules.
179         PluralRules rules = null;
180         boolean hasRules;  // Separate boolean because stored rules can be null.
181         synchronized (rulesIdToRules) {
182             hasRules = rulesIdToRules.containsKey(rulesId);
183             if (hasRules) {
184                 rules = rulesIdToRules.get(rulesId);  // can be null
185             }
186         }
187         if (!hasRules) {
188             try {
189                 UResourceBundle pluralb = getPluralBundle();
190                 UResourceBundle rulesb = pluralb.get("rules");
191                 UResourceBundle setb = rulesb.get(rulesId);
192
193                 StringBuilder sb = new StringBuilder();
194                 for (int i = 0; i < setb.getSize(); ++i) {
195                     UResourceBundle b = setb.get(i);
196                     if (i > 0) {
197                         sb.append("; ");
198                     }
199                     sb.append(b.getKey());
200                     sb.append(": ");
201                     sb.append(b.getString());
202                 }
203                 rules = PluralRules.parseDescription(sb.toString());
204             } catch (ParseException e) {
205             } catch (MissingResourceException e) {
206             }
207             synchronized (rulesIdToRules) {
208                 if (rulesIdToRules.containsKey(rulesId)) {
209                     rules = rulesIdToRules.get(rulesId);
210                 } else {
211                     rulesIdToRules.put(rulesId, rules);  // can be null
212                 }
213             }
214         }
215         return rules;
216     }
217
218     /**
219      * Return the plurals resource. Note MissingResourceException is unchecked,
220      * listed here for clarity. Callers should handle this exception.
221      */
222     public UResourceBundle getPluralBundle() throws MissingResourceException {
223         return ICUResourceBundle.getBundleInstance(
224                 ICUResourceBundle.ICU_BASE_NAME, "plurals",
225                 ICUResourceBundle.ICU_DATA_CLASS_LOADER, true);
226     }
227
228     /**
229      * Returns the plural rules for the the locale. If we don't have data,
230      * com.ibm.icu.text.PluralRules.DEFAULT is returned.
231      */
232     public PluralRules forLocale(ULocale locale, PluralRules.PluralType type) {
233         String rulesId = getRulesIdForLocale(locale, type);
234         if (rulesId == null || rulesId.trim().length() == 0) {
235             return PluralRules.DEFAULT;
236         }
237         PluralRules rules = getRulesForRulesId(rulesId);
238         if (rules == null) {
239             rules = PluralRules.DEFAULT;
240         }
241         return rules;
242     }
243
244     /**
245      * The only instance of the loader.
246      */
247     public static final PluralRulesLoader loader = new PluralRulesLoader();
248
249     /* (non-Javadoc)
250      * @see com.ibm.icu.text.PluralRules.Factory#hasOverride(com.ibm.icu.util.ULocale)
251      */
252     @Override
253     public boolean hasOverride(ULocale locale) {
254         return false;
255     }
256 }