Home | History | Annotate | Download | only in simple
      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      */
     36     private PluralRulesLoader() {
     37         rulesIdToRules = new HashMap<String, PluralRules>();
     38     }
     39 
     40    /**
     41      * Returns the lazily-constructed map.
     42      */
     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      */
     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      */
    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      */
    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      */
    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