• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 /*
5  *******************************************************************************
6  * Copyright (C) 2008-2016, International Business Machines Corporation and
7  * others. All Rights Reserved.
8  *******************************************************************************
9  */
10 package ohos.global.icu.impl;
11 
12 import java.text.ParseException;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.LinkedHashSet;
17 import java.util.Map;
18 import java.util.MissingResourceException;
19 import java.util.Set;
20 import java.util.TreeMap;
21 
22 import ohos.global.icu.text.PluralRanges;
23 import ohos.global.icu.text.PluralRules;
24 import ohos.global.icu.text.PluralRules.PluralType;
25 import ohos.global.icu.util.ULocale;
26 import ohos.global.icu.util.UResourceBundle;
27 
28 /**
29  * Loader for plural rules data.
30  * @hide exposed on OHOS
31  */
32 public class PluralRulesLoader extends PluralRules.Factory {
33     private final Map<String, PluralRules> rulesIdToRules;
34     // lazy init, use getLocaleIdToRulesIdMap to access
35     private Map<String, String> localeIdToCardinalRulesId;
36     private Map<String, String> localeIdToOrdinalRulesId;
37     private Map<String, ULocale> rulesIdToEquivalentULocale;
38     private static Map<String, PluralRanges> localeIdToPluralRanges;
39 
40 
41     /**
42      * Access through singleton.
43      */
PluralRulesLoader()44     private PluralRulesLoader() {
45         rulesIdToRules = new HashMap<String, PluralRules>();
46     }
47 
48     /**
49      * Returns the locales for which we have plurals data. Utility for testing.
50      */
getAvailableULocales()51     public ULocale[] getAvailableULocales() {
52         Set<String> keys = getLocaleIdToRulesIdMap(PluralType.CARDINAL).keySet();
53         Set<ULocale> locales = new LinkedHashSet<ULocale>(keys.size());
54         for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
55             locales.add(ULocale.createCanonical(iter.next()));
56         }
57         return locales.toArray(new ULocale[0]);
58     }
59 
60     /**
61      * Returns the functionally equivalent locale.
62      */
getFunctionalEquivalent(ULocale locale, boolean[] isAvailable)63     public ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
64         if (isAvailable != null && isAvailable.length > 0) {
65             String localeId = ULocale.canonicalize(locale.getBaseName());
66             Map<String, String> idMap = getLocaleIdToRulesIdMap(PluralType.CARDINAL);
67             isAvailable[0] = idMap.containsKey(localeId);
68         }
69 
70         String rulesId = getRulesIdForLocale(locale, PluralType.CARDINAL);
71         if (rulesId == null || rulesId.trim().length() == 0) {
72             return ULocale.ROOT; // ultimate fallback
73         }
74 
75         ULocale result = getRulesIdToEquivalentULocaleMap().get(
76                 rulesId);
77         if (result == null) {
78             return ULocale.ROOT; // ultimate fallback
79         }
80 
81         return result;
82     }
83 
84     /**
85      * Returns the lazily-constructed map.
86      */
getLocaleIdToRulesIdMap(PluralType type)87     private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
88         checkBuildRulesIdMaps();
89         return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
90     }
91 
92     /**
93      * Returns the lazily-constructed map.
94      */
getRulesIdToEquivalentULocaleMap()95     private Map<String, ULocale> getRulesIdToEquivalentULocaleMap() {
96         checkBuildRulesIdMaps();
97         return rulesIdToEquivalentULocale;
98     }
99 
100     /**
101      * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
102      * maps if necessary. These exactly reflect the contents of the locales
103      * resource in plurals.res.
104      */
checkBuildRulesIdMaps()105     private void checkBuildRulesIdMaps() {
106         boolean haveMap;
107         synchronized (this) {
108             haveMap = localeIdToCardinalRulesId != null;
109         }
110         if (!haveMap) {
111             Map<String, String> tempLocaleIdToCardinalRulesId;
112             Map<String, String> tempLocaleIdToOrdinalRulesId;
113             Map<String, ULocale> tempRulesIdToEquivalentULocale;
114             try {
115                 UResourceBundle pluralb = getPluralBundle();
116                 // Read cardinal-number rules.
117                 UResourceBundle localeb = pluralb.get("locales");
118 
119                 // sort for convenience of getAvailableULocales
120                 tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
121                 // not visible
122                 tempRulesIdToEquivalentULocale = new HashMap<String, ULocale>();
123 
124                 for (int i = 0; i < localeb.getSize(); ++i) {
125                     UResourceBundle b = localeb.get(i);
126                     String id = b.getKey();
127                     String value = b.getString().intern();
128                     tempLocaleIdToCardinalRulesId.put(id, value);
129 
130                     if (!tempRulesIdToEquivalentULocale.containsKey(value)) {
131                         tempRulesIdToEquivalentULocale.put(value, new ULocale(id));
132                     }
133                 }
134 
135                 // Read ordinal-number rules.
136                 localeb = pluralb.get("locales_ordinals");
137                 tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
138                 for (int i = 0; i < localeb.getSize(); ++i) {
139                     UResourceBundle b = localeb.get(i);
140                     String id = b.getKey();
141                     String value = b.getString().intern();
142                     tempLocaleIdToOrdinalRulesId.put(id, value);
143                 }
144             } catch (MissingResourceException e) {
145                 // dummy so we don't try again
146                 tempLocaleIdToCardinalRulesId = Collections.emptyMap();
147                 tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
148                 tempRulesIdToEquivalentULocale = Collections.emptyMap();
149             }
150 
151             synchronized(this) {
152                 if (localeIdToCardinalRulesId == null) {
153                     localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
154                     localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
155                     rulesIdToEquivalentULocale = tempRulesIdToEquivalentULocale;
156                 }
157             }
158         }
159     }
160 
161     /**
162      * Gets the rulesId from the locale,with locale fallback. If there is no
163      * rulesId, return null. The rulesId might be the empty string if the rule
164      * is the default rule.
165      */
getRulesIdForLocale(ULocale locale, PluralType type)166     public String getRulesIdForLocale(ULocale locale, PluralType type) {
167         Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
168         String localeId = ULocale.canonicalize(locale.getBaseName());
169         String rulesId = null;
170         while (null == (rulesId = idMap.get(localeId))) {
171             int ix = localeId.lastIndexOf("_");
172             if (ix == -1) {
173                 break;
174             }
175             localeId = localeId.substring(0, ix);
176         }
177         return rulesId;
178     }
179 
180     /**
181      * Gets the rule from the rulesId. If there is no rule for this rulesId,
182      * return null.
183      */
getRulesForRulesId(String rulesId)184     public PluralRules getRulesForRulesId(String rulesId) {
185         // synchronize on the map.  release the lock temporarily while we build the rules.
186         PluralRules rules = null;
187         boolean hasRules;  // Separate boolean because stored rules can be null.
188         synchronized (rulesIdToRules) {
189             hasRules = rulesIdToRules.containsKey(rulesId);
190             if (hasRules) {
191                 rules = rulesIdToRules.get(rulesId);  // can be null
192             }
193         }
194         if (!hasRules) {
195             try {
196                 UResourceBundle pluralb = getPluralBundle();
197                 UResourceBundle rulesb = pluralb.get("rules");
198                 UResourceBundle setb = rulesb.get(rulesId);
199 
200                 StringBuilder sb = new StringBuilder();
201                 for (int i = 0; i < setb.getSize(); ++i) {
202                     UResourceBundle b = setb.get(i);
203                     if (i > 0) {
204                         sb.append("; ");
205                     }
206                     sb.append(b.getKey());
207                     sb.append(": ");
208                     sb.append(b.getString());
209                 }
210                 rules = PluralRules.parseDescription(sb.toString());
211             } catch (ParseException e) {
212             } catch (MissingResourceException e) {
213             }
214             synchronized (rulesIdToRules) {
215                 if (rulesIdToRules.containsKey(rulesId)) {
216                     rules = rulesIdToRules.get(rulesId);
217                 } else {
218                     rulesIdToRules.put(rulesId, rules);  // can be null
219                 }
220             }
221         }
222         return rules;
223     }
224 
225     /**
226      * Return the plurals resource. Note MissingResourceException is unchecked,
227      * listed here for clarity. Callers should handle this exception.
228      */
getPluralBundle()229     public UResourceBundle getPluralBundle() throws MissingResourceException {
230         return ICUResourceBundle.getBundleInstance(
231                 ICUData.ICU_BASE_NAME, "plurals",
232                 ICUResourceBundle.ICU_DATA_CLASS_LOADER, true);
233     }
234 
235     /**
236      * Returns the plural rules for the the locale. If we don't have data,
237      * ohos.global.icu.text.PluralRules.DEFAULT is returned.
238      */
forLocale(ULocale locale, PluralRules.PluralType type)239     public PluralRules forLocale(ULocale locale, PluralRules.PluralType type) {
240         String rulesId = getRulesIdForLocale(locale, type);
241         if (rulesId == null || rulesId.trim().length() == 0) {
242             return PluralRules.DEFAULT;
243         }
244         PluralRules rules = getRulesForRulesId(rulesId);
245         if (rules == null) {
246             rules = PluralRules.DEFAULT;
247         }
248         return rules;
249     }
250 
251     /**
252      * The only instance of the loader.
253      */
254     public static final PluralRulesLoader loader = new PluralRulesLoader();
255 
256     /* (non-Javadoc)
257      * @see ohos.global.icu.text.PluralRules.Factory#hasOverride(ohos.global.icu.util.ULocale)
258      */
259     @Override
hasOverride(ULocale locale)260     public boolean hasOverride(ULocale locale) {
261         return false;
262     }
263 
264     private static final PluralRanges UNKNOWN_RANGE = new PluralRanges().freeze();
265 
getPluralRanges(ULocale locale)266     public PluralRanges getPluralRanges(ULocale locale) {
267         // TODO markdavis Fix the bad fallback, here and elsewhere in this file.
268         String localeId = ULocale.canonicalize(locale.getBaseName());
269         PluralRanges result;
270         while (null == (result = localeIdToPluralRanges.get(localeId))) {
271             int ix = localeId.lastIndexOf("_");
272             if (ix == -1) {
273                 result = UNKNOWN_RANGE;
274                 break;
275             }
276             localeId = localeId.substring(0, ix);
277         }
278         return result;
279     }
280 
isPluralRangesAvailable(ULocale locale)281     public boolean isPluralRangesAvailable(ULocale locale) {
282         return getPluralRanges(locale) == UNKNOWN_RANGE;
283     }
284 
285     // TODO markdavis FIX HARD-CODED HACK once we have data from CLDR in the bundles
286     static {
287         String[][] pluralRangeData = {
288                 {"locales", "id ja km ko lo ms my th vi zh"},
289                 {"other", "other", "other"},
290 
291                 {"locales", "am bn fr gu hi hy kn mr pa zu"},
292                 {"one", "one", "one"},
293                 {"one", "other", "other"},
294                 {"other", "other", "other"},
295 
296                 {"locales", "fa"},
297                 {"one", "one", "other"},
298                 {"one", "other", "other"},
299                 {"other", "other", "other"},
300 
301                 {"locales", "ka"},
302                 {"one", "other", "one"},
303                 {"other", "one", "other"},
304                 {"other", "other", "other"},
305 
306                 {"locales", "az de el gl hu it kk ky ml mn ne nl pt sq sw ta te tr ug uz"},
307                 {"one", "other", "other"},
308                 {"other", "one", "one"},
309                 {"other", "other", "other"},
310 
311                 {"locales", "af bg ca en es et eu fi nb sv ur"},
312                 {"one", "other", "other"},
313                 {"other", "one", "other"},
314                 {"other", "other", "other"},
315 
316                 {"locales", "da fil is"},
317                 {"one", "one", "one"},
318                 {"one", "other", "other"},
319                 {"other", "one", "one"},
320                 {"other", "other", "other"},
321 
322                 {"locales", "si"},
323                 {"one", "one", "one"},
324                 {"one", "other", "other"},
325                 {"other", "one", "other"},
326                 {"other", "other", "other"},
327 
328                 {"locales", "mk"},
329                 {"one", "one", "other"},
330                 {"one", "other", "other"},
331                 {"other", "one", "other"},
332                 {"other", "other", "other"},
333 
334                 {"locales", "lv"},
335                 {"zero", "zero", "other"},
336                 {"zero", "one", "one"},
337                 {"zero", "other", "other"},
338                 {"one", "zero", "other"},
339                 {"one", "one", "one"},
340                 {"one", "other", "other"},
341                 {"other", "zero", "other"},
342                 {"other", "one", "one"},
343                 {"other", "other", "other"},
344 
345                 {"locales", "ro"},
346                 {"one", "few", "few"},
347                 {"one", "other", "other"},
348                 {"few", "one", "few"},
349                 {"few", "few", "few"},
350                 {"few", "other", "other"},
351                 {"other", "few", "few"},
352                 {"other", "other", "other"},
353 
354                 {"locales", "hr sr bs"},
355                 {"one", "one", "one"},
356                 {"one", "few", "few"},
357                 {"one", "other", "other"},
358                 {"few", "one", "one"},
359                 {"few", "few", "few"},
360                 {"few", "other", "other"},
361                 {"other", "one", "one"},
362                 {"other", "few", "few"},
363                 {"other", "other", "other"},
364 
365                 {"locales", "sl"},
366                 {"one", "one", "few"},
367                 {"one", "two", "two"},
368                 {"one", "few", "few"},
369                 {"one", "other", "other"},
370                 {"two", "one", "few"},
371                 {"two", "two", "two"},
372                 {"two", "few", "few"},
373                 {"two", "other", "other"},
374                 {"few", "one", "few"},
375                 {"few", "two", "two"},
376                 {"few", "few", "few"},
377                 {"few", "other", "other"},
378                 {"other", "one", "few"},
379                 {"other", "two", "two"},
380                 {"other", "few", "few"},
381                 {"other", "other", "other"},
382 
383                 {"locales", "he"},
384                 {"one", "two", "other"},
385                 {"one", "many", "many"},
386                 {"one", "other", "other"},
387                 {"two", "many", "other"},
388                 {"two", "other", "other"},
389                 {"many", "many", "many"},
390                 {"many", "other", "many"},
391                 {"other", "one", "other"},
392                 {"other", "two", "other"},
393                 {"other", "many", "many"},
394                 {"other", "other", "other"},
395 
396                 {"locales", "cs pl sk"},
397                 {"one", "few", "few"},
398                 {"one", "many", "many"},
399                 {"one", "other", "other"},
400                 {"few", "few", "few"},
401                 {"few", "many", "many"},
402                 {"few", "other", "other"},
403                 {"many", "one", "one"},
404                 {"many", "few", "few"},
405                 {"many", "many", "many"},
406                 {"many", "other", "other"},
407                 {"other", "one", "one"},
408                 {"other", "few", "few"},
409                 {"other", "many", "many"},
410                 {"other", "other", "other"},
411 
412                 {"locales", "lt ru uk"},
413                 {"one", "one", "one"},
414                 {"one", "few", "few"},
415                 {"one", "many", "many"},
416                 {"one", "other", "other"},
417                 {"few", "one", "one"},
418                 {"few", "few", "few"},
419                 {"few", "many", "many"},
420                 {"few", "other", "other"},
421                 {"many", "one", "one"},
422                 {"many", "few", "few"},
423                 {"many", "many", "many"},
424                 {"many", "other", "other"},
425                 {"other", "one", "one"},
426                 {"other", "few", "few"},
427                 {"other", "many", "many"},
428                 {"other", "other", "other"},
429 
430                 {"locales", "cy"},
431                 {"zero", "one", "one"},
432                 {"zero", "two", "two"},
433                 {"zero", "few", "few"},
434                 {"zero", "many", "many"},
435                 {"zero", "other", "other"},
436                 {"one", "two", "two"},
437                 {"one", "few", "few"},
438                 {"one", "many", "many"},
439                 {"one", "other", "other"},
440                 {"two", "few", "few"},
441                 {"two", "many", "many"},
442                 {"two", "other", "other"},
443                 {"few", "many", "many"},
444                 {"few", "other", "other"},
445                 {"many", "other", "other"},
446                 {"other", "one", "one"},
447                 {"other", "two", "two"},
448                 {"other", "few", "few"},
449                 {"other", "many", "many"},
450                 {"other", "other", "other"},
451 
452                 {"locales", "ar"},
453                 {"zero", "one", "zero"},
454                 {"zero", "two", "zero"},
455                 {"zero", "few", "few"},
456                 {"zero", "many", "many"},
457                 {"zero", "other", "other"},
458                 {"one", "two", "other"},
459                 {"one", "few", "few"},
460                 {"one", "many", "many"},
461                 {"one", "other", "other"},
462                 {"two", "few", "few"},
463                 {"two", "many", "many"},
464                 {"two", "other", "other"},
465                 {"few", "few", "few"},
466                 {"few", "many", "many"},
467                 {"few", "other", "other"},
468                 {"many", "few", "few"},
469                 {"many", "many", "many"},
470                 {"many", "other", "other"},
471                 {"other", "one", "other"},
472                 {"other", "two", "other"},
473                 {"other", "few", "few"},
474                 {"other", "many", "many"},
475                 {"other", "other", "other"},
476         };
477         PluralRanges pr = null;
478         String[] locales = null;
479         HashMap<String, PluralRanges> tempLocaleIdToPluralRanges = new HashMap<String, PluralRanges>();
480         for (String[] row : pluralRangeData) {
481             if (row[0].equals("locales")) {
482                 if (pr != null) {
pr.freeze()483                     pr.freeze();
484                     for (String locale : locales) {
tempLocaleIdToPluralRanges.put(locale, pr)485                         tempLocaleIdToPluralRanges.put(locale, pr);
486                     }
487                 }
488                 locales = row[1].split(" ");
489                 pr = new PluralRanges();
490             } else {
491                 pr.add(
StandardPlural.fromString(row[0])492                         StandardPlural.fromString(row[0]),
493                         StandardPlural.fromString(row[1]),
494                         StandardPlural.fromString(row[2]));
495             }
496         }
497         // do last one
498         for (String locale : locales) {
tempLocaleIdToPluralRanges.put(locale, pr)499             tempLocaleIdToPluralRanges.put(locale, pr);
500         }
501         // now make whole thing immutable
502         localeIdToPluralRanges = Collections.unmodifiableMap(tempLocaleIdToPluralRanges);
503     }
504 }
505