• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *******************************************************************************
3  * Copyright (C) 2008-2013, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.simple;
8 
9 import java.text.ParseException;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.MissingResourceException;
15 import java.util.ResourceBundle;
16 import java.util.TreeMap;
17 
18 import com.ibm.icu.simple.PluralRules.PluralType;
19 
20 /**
21  * Loader for plural rules data.
22  */
23 public class PluralRulesLoader extends PluralRules.Factory {
24     // Data created from ICU4C with the command
25     // ~/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
26     private static final ResourceBundle DATA_RB = new LocaleElements_plurals();
27 
28     private final Map<String, PluralRules> rulesIdToRules;
29     // lazy init, use getLocaleIdToRulesIdMap to access
30     private Map<String, String> localeIdToCardinalRulesId;
31     private Map<String, String> localeIdToOrdinalRulesId;
32 
33     /**
34      * Access through singleton.
35      */
PluralRulesLoader()36     private PluralRulesLoader() {
37         rulesIdToRules = new HashMap<String, PluralRules>();
38     }
39 
40    /**
41      * Returns the lazily-constructed map.
42      */
getLocaleIdToRulesIdMap(PluralType type)43     private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
44         checkBuildRulesIdMaps();
45         return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
46     }
47 
48     /**
49      * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
50      * maps if necessary. These exactly reflect the contents of the locales
51      * resource in plurals.res.
52      */
checkBuildRulesIdMaps()53     private void checkBuildRulesIdMaps() {
54         boolean haveMap;
55         synchronized (this) {
56             haveMap = localeIdToCardinalRulesId != null;
57         }
58         if (!haveMap) {
59             Map<String, String> tempLocaleIdToCardinalRulesId;
60             Map<String, String> tempLocaleIdToOrdinalRulesId;
61             try {
62                 ResourceBundle pluralb = DATA_RB;
63                 // Read cardinal-number rules.
64                 Object[][] localeb = (Object[][]) pluralb.getObject("locales");
65 
66                 // sort for convenience of getAvailableULocales
67                 tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
68 
69                 for (Object[] langAndId : localeb) {
70                     String id = (String) langAndId[0];
71                     String value = (String) langAndId[1];
72                     tempLocaleIdToCardinalRulesId.put(id, value);
73                 }
74 
75                 // Read ordinal-number rules.
76                 localeb = (Object[][]) pluralb.getObject("locales_ordinals");
77                 tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
78                 for (Object[] langAndId : localeb) {
79                     String id = (String) langAndId[0];
80                     String value = (String) langAndId[1];
81                     tempLocaleIdToOrdinalRulesId.put(id, value);
82                 }
83             } catch (MissingResourceException e) {
84                 // dummy so we don't try again
85                 tempLocaleIdToCardinalRulesId = Collections.emptyMap();
86                 tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
87             }
88 
89             synchronized(this) {
90                 if (localeIdToCardinalRulesId == null) {
91                     localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
92                     localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
93                 }
94             }
95         }
96     }
97 
98     /**
99      * Gets the rulesId from the locale,with locale fallback. If there is no
100      * rulesId, return null. The rulesId might be the empty string if the rule
101      * is the default rule.
102      */
getRulesIdForLocale(Locale locale, PluralType type)103     public String getRulesIdForLocale(Locale locale, PluralType type) {
104         Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
105         String lang = locale.getLanguage();
106         String rulesId = idMap.get(lang);
107         return rulesId;
108     }
109 
110     /**
111      * Gets the rule from the rulesId. If there is no rule for this rulesId,
112      * return null.
113      */
getRulesForRulesId(String rulesId)114     public PluralRules getRulesForRulesId(String rulesId) {
115         // synchronize on the map.  release the lock temporarily while we build the rules.
116         PluralRules rules = null;
117         boolean hasRules;  // Separate boolean because stored rules can be null.
118         synchronized (rulesIdToRules) {
119             hasRules = rulesIdToRules.containsKey(rulesId);
120             if (hasRules) {
121                 rules = rulesIdToRules.get(rulesId);  // can be null
122             }
123         }
124         if (!hasRules) {
125             try {
126                 ResourceBundle pluralb = DATA_RB;
127                 Object[][] rulesb = (Object[][]) pluralb.getObject("rules");
128                 Object[][] setb = null;
129                 for (Object[] idAndRule : rulesb) {  // Unbounded loop: We must find the rulesId.
130                     if (rulesId.equals(idAndRule[0])) {
131                         setb = (Object[][]) idAndRule[1];
132                         break;
133                     }
134                 }
135 
136                 StringBuilder sb = new StringBuilder();
137                 for (Object[] keywordAndRule : setb) {
138                     if (sb.length() > 0) {
139                         sb.append("; ");
140                     }
141                     sb.append((String) keywordAndRule[0]);
142                     sb.append(": ");
143                     sb.append((String) keywordAndRule[1]);
144                 }
145                 rules = PluralRules.parseDescription(sb.toString());
146             } catch (ParseException e) {
147             } catch (MissingResourceException e) {
148             }
149             synchronized (rulesIdToRules) {
150                 if (rulesIdToRules.containsKey(rulesId)) {
151                     rules = rulesIdToRules.get(rulesId);
152                 } else {
153                     rulesIdToRules.put(rulesId, rules);  // can be null
154                 }
155             }
156         }
157         return rules;
158     }
159 
160     /**
161      * Returns the plural rules for the the locale. If we don't have data,
162      * com.ibm.icu.text.PluralRules.DEFAULT is returned.
163      */
forLocale(Locale locale, PluralRules.PluralType type)164     public PluralRules forLocale(Locale locale, PluralRules.PluralType type) {
165         String rulesId = getRulesIdForLocale(locale, type);
166         if (rulesId == null || rulesId.trim().length() == 0) {
167             return PluralRules.DEFAULT;
168         }
169         PluralRules rules = getRulesForRulesId(rulesId);
170         if (rules == null) {
171             rules = PluralRules.DEFAULT;
172         }
173         return rules;
174     }
175 
176     /**
177      * The only instance of the loader.
178      */
179     public static final PluralRulesLoader loader = new PluralRulesLoader();
180 }
181