17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (C) 2008-2014, International Business Machines Corporation and    *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.                                                *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParseException;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Iterator;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeMap;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRanges;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules.PluralType;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.PluralRules.StandardPluralCategories;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.UResourceBundle;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Loader for plural rules data.
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class PluralRulesLoader extends PluralRules.Factory {
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final Map<String, PluralRules> rulesIdToRules;
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // lazy init, use getLocaleIdToRulesIdMap to access
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, String> localeIdToCardinalRulesId;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, String> localeIdToOrdinalRulesId;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, ULocale> rulesIdToEquivalentULocale;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Map<String, PluralRanges> localeIdToPluralRanges;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Access through singleton.
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private PluralRulesLoader() {
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        rulesIdToRules = new HashMap<String, PluralRules>();
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the locales for which we have plurals data. Utility for testing.
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale[] getAvailableULocales() {
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<String> keys = getLocaleIdToRulesIdMap(PluralType.CARDINAL).keySet();
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale[] locales = new ULocale[keys.size()];
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int n = 0;
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            locales[n++] = ULocale.createCanonical(iter.next());
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return locales;
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the functionally equivalent locale.
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) {
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isAvailable != null && isAvailable.length > 0) {
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String localeId = ULocale.canonicalize(locale.getBaseName());
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, String> idMap = getLocaleIdToRulesIdMap(PluralType.CARDINAL);
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            isAvailable[0] = idMap.containsKey(localeId);
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String rulesId = getRulesIdForLocale(locale, PluralType.CARDINAL);
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (rulesId == null || rulesId.trim().length() == 0) {
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return ULocale.ROOT; // ultimate fallback
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale result = getRulesIdToEquivalentULocaleMap().get(
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rulesId);
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (result == null) {
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return ULocale.ROOT; // ultimate fallback
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the lazily-constructed map.
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, String> getLocaleIdToRulesIdMap(PluralType type) {
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkBuildRulesIdMaps();
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the lazily-constructed map.
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, ULocale> getRulesIdToEquivalentULocaleMap() {
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        checkBuildRulesIdMaps();
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rulesIdToEquivalentULocale;
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * maps if necessary. These exactly reflect the contents of the locales
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * resource in plurals.res.
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void checkBuildRulesIdMaps() {
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean haveMap;
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (this) {
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            haveMap = localeIdToCardinalRulesId != null;
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!haveMap) {
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, String> tempLocaleIdToCardinalRulesId;
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, String> tempLocaleIdToOrdinalRulesId;
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, ULocale> tempRulesIdToEquivalentULocale;
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                UResourceBundle pluralb = getPluralBundle();
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Read cardinal-number rules.
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                UResourceBundle localeb = pluralb.get("locales");
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // sort for convenience of getAvailableULocales
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // not visible
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tempRulesIdToEquivalentULocale = new HashMap<String, ULocale>();
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < localeb.getSize(); ++i) {
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    UResourceBundle b = localeb.get(i);
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String id = b.getKey();
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String value = b.getString().intern();
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    tempLocaleIdToCardinalRulesId.put(id, value);
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (!tempRulesIdToEquivalentULocale.containsKey(value)) {
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        tempRulesIdToEquivalentULocale.put(value, new ULocale(id));
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Read ordinal-number rules.
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                localeb = pluralb.get("locales_ordinals");
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tempLocaleIdToOrdinalRulesId = new TreeMap<String, String>();
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < localeb.getSize(); ++i) {
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    UResourceBundle b = localeb.get(i);
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String id = b.getKey();
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String value = b.getString().intern();
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    tempLocaleIdToOrdinalRulesId.put(id, value);
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (MissingResourceException e) {
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // dummy so we don't try again
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tempLocaleIdToCardinalRulesId = Collections.emptyMap();
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                tempRulesIdToEquivalentULocale = Collections.emptyMap();
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized(this) {
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (localeIdToCardinalRulesId == null) {
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId;
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rulesIdToEquivalentULocale = tempRulesIdToEquivalentULocale;
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the rulesId from the locale,with locale fallback. If there is no
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * rulesId, return null. The rulesId might be the empty string if the rule
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is the default rule.
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getRulesIdForLocale(ULocale locale, PluralType type) {
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, String> idMap = getLocaleIdToRulesIdMap(type);
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String localeId = ULocale.canonicalize(locale.getBaseName());
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String rulesId = null;
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (null == (rulesId = idMap.get(localeId))) {
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ix = localeId.lastIndexOf("_");
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ix == -1) {
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localeId = localeId.substring(0, ix);
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rulesId;
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the rule from the rulesId. If there is no rule for this rulesId,
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * return null.
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralRules getRulesForRulesId(String rulesId) {
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // synchronize on the map.  release the lock temporarily while we build the rules.
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PluralRules rules = null;
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean hasRules;  // Separate boolean because stored rules can be null.
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        synchronized (rulesIdToRules) {
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            hasRules = rulesIdToRules.containsKey(rulesId);
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (hasRules) {
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rules = rulesIdToRules.get(rulesId);  // can be null
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!hasRules) {
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            try {
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                UResourceBundle pluralb = getPluralBundle();
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                UResourceBundle rulesb = pluralb.get("rules");
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                UResourceBundle setb = rulesb.get(rulesId);
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                StringBuilder sb = new StringBuilder();
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < setb.getSize(); ++i) {
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    UResourceBundle b = setb.get(i);
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (i > 0) {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        sb.append("; ");
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    sb.append(b.getKey());
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    sb.append(": ");
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    sb.append(b.getString());
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rules = PluralRules.parseDescription(sb.toString());
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (ParseException e) {
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } catch (MissingResourceException e) {
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized (rulesIdToRules) {
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (rulesIdToRules.containsKey(rulesId)) {
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rules = rulesIdToRules.get(rulesId);
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rulesIdToRules.put(rulesId, rules);  // can be null
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules;
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return the plurals resource. Note MissingResourceException is unchecked,
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * listed here for clarity. Callers should handle this exception.
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public UResourceBundle getPluralBundle() throws MissingResourceException {
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ICUResourceBundle.getBundleInstance(
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle.ICU_BASE_NAME, "plurals",
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle.ICU_DATA_CLASS_LOADER, true);
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the plural rules for the the locale. If we don't have data,
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * com.ibm.icu.text.PluralRules.DEFAULT is returned.
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralRules forLocale(ULocale locale, PluralRules.PluralType type) {
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String rulesId = getRulesIdForLocale(locale, type);
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (rulesId == null || rulesId.trim().length() == 0) {
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return PluralRules.DEFAULT;
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PluralRules rules = getRulesForRulesId(rulesId);
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (rules == null) {
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            rules = PluralRules.DEFAULT;
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules;
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The only instance of the loader.
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final PluralRulesLoader loader = new PluralRulesLoader();
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.text.PluralRules.Factory#hasOverride(com.ibm.icu.util.ULocale)
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean hasOverride(ULocale locale) {
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final PluralRanges UNKNOWN_RANGE = new PluralRanges().freeze();
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PluralRanges getPluralRanges(ULocale locale) {
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // TODO markdavis Fix the bad fallback, here and elsewhere in this file.
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String localeId = ULocale.canonicalize(locale.getBaseName());
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PluralRanges result;
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (null == (result = localeIdToPluralRanges.get(localeId))) {
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ix = localeId.lastIndexOf("_");
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ix == -1) {
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result = UNKNOWN_RANGE;
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            localeId = localeId.substring(0, ix);
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isPluralRangesAvailable(ULocale locale) {
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getPluralRanges(locale) == UNKNOWN_RANGE;
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // TODO markdavis FIX HARD-CODED HACK once we have data from CLDR in the bundles
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static {
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[][] pluralRangeData = {
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "id ja km ko lo ms my th vi zh"},
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "am bn fr gu hi hy kn mr pa zu"},
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "one"},
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "fa"},
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "other"},
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "ka"},
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "one"},
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "other"},
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "az de el gl hu it kk ky ml mn ne nl pt sq sw ta te tr ug uz"},
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "af bg ca en es et eu fi nb sv ur"},
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "other"},
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "da fil is"},
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "one"},
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "si"},
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "one"},
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "other"},
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "mk"},
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "other"},
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "other"},
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "lv"},
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "zero", "other"},
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "one", "one"},
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "other", "other"},
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "zero", "other"},
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "one"},
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "zero", "other"},
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "ro"},
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "one", "few"},
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "few", "few"},
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "hr sr bs"},
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "one"},
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "one", "one"},
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "few", "few"},
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "sl"},
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "few"},
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "two", "two"},
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "one", "few"},
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "two", "two"},
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "few", "few"},
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "other", "other"},
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "one", "few"},
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "two", "two"},
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "few", "few"},
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "few"},
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "two", "two"},
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "he"},
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "two", "other"},
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "many", "many"},
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "many", "other"},
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "other", "other"},
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "many", "many"},
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "other", "many"},
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "other"},
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "two", "other"},
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "many", "many"},
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "cs pl sk"},
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "many", "many"},
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "few", "few"},
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "many", "many"},
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "one", "one"},
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "few", "few"},
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "many", "many"},
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "other", "other"},
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "many", "many"},
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "lt ru uk"},
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "one", "one"},
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "many", "many"},
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "one", "one"},
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "few", "few"},
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "many", "many"},
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "one", "one"},
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "few", "few"},
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "many", "many"},
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "other", "other"},
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "many", "many"},
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "cy"},
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "one", "one"},
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "two", "two"},
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "few", "few"},
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "many", "many"},
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "other", "other"},
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "two", "two"},
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "many", "many"},
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "few", "few"},
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "many", "many"},
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "other", "other"},
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "many", "many"},
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "other", "other"},
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "one"},
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "two", "two"},
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "many", "many"},
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"locales", "ar"},
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "one", "zero"},
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "two", "zero"},
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "few", "few"},
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "many", "many"},
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"zero", "other", "other"},
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "two", "other"},
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "few", "few"},
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "many", "many"},
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"one", "other", "other"},
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "few", "few"},
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "many", "many"},
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"two", "other", "other"},
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "few", "few"},
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "many", "many"},
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"few", "other", "other"},
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "few", "few"},
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "many", "many"},
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"many", "other", "other"},
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "one", "other"},
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "two", "other"},
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "few", "few"},
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "many", "many"},
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                {"other", "other", "other"},
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PluralRanges pr = null;
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] locales = null;
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        HashMap<String, PluralRanges> tempLocaleIdToPluralRanges = new HashMap<String, PluralRanges>();
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String[] row : pluralRangeData) {
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (row[0].equals("locales")) {
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (pr != null) {
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    pr.freeze();
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (String locale : locales) {
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        tempLocaleIdToPluralRanges.put(locale, pr);
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                locales = row[1].split(" ");
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                pr = new PluralRanges();
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                pr.add(
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        StandardPluralCategories.valueOf(row[0]),
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        StandardPluralCategories.valueOf(row[1]),
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        StandardPluralCategories.valueOf(row[2]));
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // do last one
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String locale : locales) {
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tempLocaleIdToPluralRanges.put(locale, pr);
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // now make whole thing immutable
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        localeIdToPluralRanges = Collections.unmodifiableMap(tempLocaleIdToPluralRanges);
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}