/* GENERATED SOURCE. DO NOT MODIFY. */ // © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2008-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ package android.icu.impl; import java.text.ParseException; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.MissingResourceException; import java.util.Set; import java.util.TreeMap; import android.icu.impl.number.range.StandardPluralRanges; import android.icu.text.PluralRules; import android.icu.text.PluralRules.PluralType; import android.icu.util.ULocale; import android.icu.util.UResourceBundle; /** * Loader for plural rules data. * @hide Only a subset of ICU is exposed in Android */ public class PluralRulesLoader extends PluralRules.Factory { // Key is rules set + ranges set private final Map pluralRulesCache; // lazy init, use getLocaleIdToRulesIdMap to access private Map localeIdToCardinalRulesId; private Map localeIdToOrdinalRulesId; private Map rulesIdToEquivalentULocale; /** * Access through singleton. */ private PluralRulesLoader() { pluralRulesCache = new HashMap(); } /** * Returns the locales for which we have plurals data. Utility for testing. */ public ULocale[] getAvailableULocales() { Set keys = getLocaleIdToRulesIdMap(PluralType.CARDINAL).keySet(); Set locales = new LinkedHashSet(keys.size()); for (Iterator iter = keys.iterator(); iter.hasNext();) { locales.add(ULocale.createCanonical(iter.next())); } return locales.toArray(new ULocale[0]); } /** * Returns the functionally equivalent locale. */ public ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) { if (isAvailable != null && isAvailable.length > 0) { String localeId = ULocale.canonicalize(locale.getBaseName()); Map idMap = getLocaleIdToRulesIdMap(PluralType.CARDINAL); isAvailable[0] = idMap.containsKey(localeId); } String rulesId = getRulesIdForLocale(locale, PluralType.CARDINAL); if (rulesId == null || rulesId.trim().length() == 0) { return ULocale.ROOT; // ultimate fallback } ULocale result = getRulesIdToEquivalentULocaleMap().get( rulesId); if (result == null) { return ULocale.ROOT; // ultimate fallback } return result; } /** * Returns the lazily-constructed map. */ private Map getLocaleIdToRulesIdMap(PluralType type) { checkBuildRulesIdMaps(); return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId; } /** * Returns the lazily-constructed map. */ private Map getRulesIdToEquivalentULocaleMap() { checkBuildRulesIdMaps(); return rulesIdToEquivalentULocale; } /** * Lazily constructs the localeIdToRulesId and rulesIdToEquivalentULocale * maps if necessary. These exactly reflect the contents of the locales * resource in plurals.res. */ private void checkBuildRulesIdMaps() { boolean haveMap; synchronized (this) { haveMap = localeIdToCardinalRulesId != null; } if (!haveMap) { Map tempLocaleIdToCardinalRulesId; Map tempLocaleIdToOrdinalRulesId; Map tempRulesIdToEquivalentULocale; try { UResourceBundle pluralb = getPluralBundle(); // Read cardinal-number rules. UResourceBundle localeb = pluralb.get("locales"); // sort for convenience of getAvailableULocales tempLocaleIdToCardinalRulesId = new TreeMap(); // not visible tempRulesIdToEquivalentULocale = new HashMap(); for (int i = 0; i < localeb.getSize(); ++i) { UResourceBundle b = localeb.get(i); String id = b.getKey(); String value = b.getString().intern(); tempLocaleIdToCardinalRulesId.put(id, value); if (!tempRulesIdToEquivalentULocale.containsKey(value)) { tempRulesIdToEquivalentULocale.put(value, new ULocale(id)); } } // Read ordinal-number rules. localeb = pluralb.get("locales_ordinals"); tempLocaleIdToOrdinalRulesId = new TreeMap(); for (int i = 0; i < localeb.getSize(); ++i) { UResourceBundle b = localeb.get(i); String id = b.getKey(); String value = b.getString().intern(); tempLocaleIdToOrdinalRulesId.put(id, value); } } catch (MissingResourceException e) { // dummy so we don't try again tempLocaleIdToCardinalRulesId = Collections.emptyMap(); tempLocaleIdToOrdinalRulesId = Collections.emptyMap(); tempRulesIdToEquivalentULocale = Collections.emptyMap(); } synchronized(this) { if (localeIdToCardinalRulesId == null) { localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId; localeIdToOrdinalRulesId = tempLocaleIdToOrdinalRulesId; rulesIdToEquivalentULocale = tempRulesIdToEquivalentULocale; } } } } /** * Gets the rulesId from the locale,with locale fallback. If there is no * rulesId, return null. The rulesId might be the empty string if the rule * is the default rule. */ public String getRulesIdForLocale(ULocale locale, PluralType type) { Map idMap = getLocaleIdToRulesIdMap(type); String localeId = ULocale.canonicalize(locale.getBaseName()); String rulesId = null; while (null == (rulesId = idMap.get(localeId))) { int ix = localeId.lastIndexOf("_"); if (ix == -1) { break; } localeId = localeId.substring(0, ix); } return rulesId; } /** * Gets the rule from the rulesId. If there is no rule for this rulesId, * return null. */ public PluralRules getOrCreateRulesForLocale(ULocale locale, PluralRules.PluralType type) { String rulesId = getRulesIdForLocale(locale, type); if (rulesId == null || rulesId.trim().length() == 0) { return null; } String rangesId = StandardPluralRanges.getSetForLocale(locale); String cacheKey = rulesId + "/" + rangesId; // could end with "/null" (this is OK) // synchronize on the map. release the lock temporarily while we build the rules. PluralRules rules = null; boolean hasRules; // Separate boolean because stored rules can be null. synchronized (pluralRulesCache) { hasRules = pluralRulesCache.containsKey(cacheKey); if (hasRules) { rules = pluralRulesCache.get(cacheKey); // can be null } } if (!hasRules) { try { UResourceBundle pluralb = getPluralBundle(); UResourceBundle rulesb = pluralb.get("rules"); UResourceBundle setb = rulesb.get(rulesId); StringBuilder sb = new StringBuilder(); for (int i = 0; i < setb.getSize(); ++i) { UResourceBundle b = setb.get(i); if (i > 0) { sb.append("; "); } sb.append(b.getKey()); sb.append(": "); sb.append(b.getString()); } StandardPluralRanges ranges = StandardPluralRanges.forSet(rangesId); rules = PluralRules.newInternal(sb.toString(), ranges); } catch (ParseException e) { } catch (MissingResourceException e) { } synchronized (pluralRulesCache) { if (pluralRulesCache.containsKey(cacheKey)) { rules = pluralRulesCache.get(cacheKey); } else { pluralRulesCache.put(cacheKey, rules); // can be null } } } return rules; } /** * Return the plurals resource. Note MissingResourceException is unchecked, * listed here for clarity. Callers should handle this exception. */ public UResourceBundle getPluralBundle() throws MissingResourceException { return ICUResourceBundle.getBundleInstance( ICUData.ICU_BASE_NAME, "plurals", ICUResourceBundle.ICU_DATA_CLASS_LOADER, true); } /** * Returns the plural rules for the the locale. If we don't have data, * android.icu.text.PluralRules.DEFAULT is returned. */ public PluralRules forLocale(ULocale locale, PluralRules.PluralType type) { PluralRules rules = getOrCreateRulesForLocale(locale, type); if (rules == null) { rules = PluralRules.DEFAULT; } return rules; } /** * The only instance of the loader. */ public static final PluralRulesLoader loader = new PluralRulesLoader(); /* (non-Javadoc) * @see android.icu.text.PluralRules.Factory#hasOverride(android.icu.util.ULocale) */ @Override public boolean hasOverride(ULocale locale) { return false; } }