267 lines
10 KiB
Java
267 lines
10 KiB
Java
/* 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<String, PluralRules> pluralRulesCache;
|
|
// lazy init, use getLocaleIdToRulesIdMap to access
|
|
private Map<String, String> localeIdToCardinalRulesId;
|
|
private Map<String, String> localeIdToOrdinalRulesId;
|
|
private Map<String, ULocale> rulesIdToEquivalentULocale;
|
|
|
|
|
|
/**
|
|
* Access through singleton.
|
|
*/
|
|
private PluralRulesLoader() {
|
|
pluralRulesCache = new HashMap<String, PluralRules>();
|
|
}
|
|
|
|
/**
|
|
* Returns the locales for which we have plurals data. Utility for testing.
|
|
*/
|
|
public ULocale[] getAvailableULocales() {
|
|
Set<String> keys = getLocaleIdToRulesIdMap(PluralType.CARDINAL).keySet();
|
|
Set<ULocale> locales = new LinkedHashSet<ULocale>(keys.size());
|
|
for (Iterator<String> 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<String, String> 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<String, String> getLocaleIdToRulesIdMap(PluralType type) {
|
|
checkBuildRulesIdMaps();
|
|
return (type == PluralType.CARDINAL) ? localeIdToCardinalRulesId : localeIdToOrdinalRulesId;
|
|
}
|
|
|
|
/**
|
|
* Returns the lazily-constructed map.
|
|
*/
|
|
private Map<String, ULocale> 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<String, String> tempLocaleIdToCardinalRulesId;
|
|
Map<String, String> tempLocaleIdToOrdinalRulesId;
|
|
Map<String, ULocale> tempRulesIdToEquivalentULocale;
|
|
try {
|
|
UResourceBundle pluralb = getPluralBundle();
|
|
// Read cardinal-number rules.
|
|
UResourceBundle localeb = pluralb.get("locales");
|
|
|
|
// sort for convenience of getAvailableULocales
|
|
tempLocaleIdToCardinalRulesId = new TreeMap<String, String>();
|
|
// not visible
|
|
tempRulesIdToEquivalentULocale = new HashMap<String, ULocale>();
|
|
|
|
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<String, String>();
|
|
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<String, String> 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;
|
|
}
|
|
}
|