| /* GENERATED SOURCE. DO NOT MODIFY. */ |
| // © 2020 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| package android.icu.impl.units; |
| |
| import java.math.BigDecimal; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import android.icu.impl.ICUData; |
| import android.icu.impl.ICUResourceBundle; |
| import android.icu.impl.UResource; |
| import android.icu.util.ULocale; |
| import android.icu.util.UResourceBundle; |
| |
| /** |
| * @hide Only a subset of ICU is exposed in Android |
| */ |
| public class UnitPreferences { |
| private static final Map<String, String> measurementSystem; |
| |
| static { |
| Map<String, String> tempMS = new HashMap<>(); |
| tempMS.put("metric", "001"); |
| tempMS.put("ussystem", "US"); |
| tempMS.put("uksystem", "GB"); |
| measurementSystem = Collections.unmodifiableMap(tempMS); |
| } |
| |
| |
| private HashMap<String, HashMap<String, UnitPreference[]>> mapToUnitPreferences = new HashMap<>(); |
| |
| public UnitPreferences() { |
| // Read unit preferences |
| ICUResourceBundle resource; |
| resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "units"); |
| UnitPreferencesSink sink = new UnitPreferencesSink(); |
| resource.getAllItemsWithFallback(UnitsData.Constants.UNIT_PREFERENCE_TABLE_NAME, sink); |
| this.mapToUnitPreferences = sink.getMapToUnitPreferences(); |
| } |
| |
| public static String formMapKey(String category, String usage) { |
| return category + "++" + usage; |
| } |
| |
| /** |
| * Extracts all the sub-usages from a usage including the default one in the end. |
| * The usages will be in order starting with the longest matching one. |
| * For example: |
| * if usage : "person-height-child" |
| * the function will return: "person-height-child" |
| * "person-height" |
| * "person" |
| * "default" |
| * |
| * @param usage |
| * @return |
| */ |
| private static String[] getAllUsages(String usage) { |
| ArrayList<String> result = new ArrayList<>(); |
| result.add(usage); |
| for (int i = usage.length() - 1; i >= 0; --i) { |
| if (usage.charAt(i) == '-') { |
| result.add(usage.substring(0, i)); |
| } |
| } |
| |
| if (!usage.equals(UnitsData.Constants.DEFAULT_USAGE)) { // Do not add default usage twice. |
| result.add(UnitsData.Constants.DEFAULT_USAGE); |
| } |
| return result.toArray(new String[0]); |
| } |
| |
| public UnitPreference[] getPreferencesFor(String category, String usage, ULocale locale, UnitsData data) { |
| // TODO: remove this condition when all the categories are allowed. |
| // WARNING: when this is removed please make sure to keep the "fahrenhe" => "fahrenheit" mapping |
| if ("temperature".equals(category)) { |
| String localeUnit = locale.getKeywordValue("mu"); |
| // The value for -u-mu- is `fahrenhe`, but CLDR and everything else uses `fahrenheit` |
| if ("fahrenhe".equals(localeUnit)) { |
| localeUnit = "fahrenheit"; |
| } |
| String localeUnitCategory; |
| try { |
| localeUnitCategory = localeUnit == null ? null : data.getCategory(MeasureUnitImpl.forIdentifier(localeUnit)); |
| } catch (Exception e) { |
| localeUnitCategory = null; |
| } |
| |
| if (localeUnitCategory != null && category.equals(localeUnitCategory)) { |
| UnitPreference[] preferences = {new UnitPreference(localeUnit, null, null)}; |
| return preferences; |
| } |
| } |
| |
| String region = locale.getCountry(); |
| |
| // Check the locale system tag, e.g `ms=metric`. |
| String localeSystem = locale.getKeywordValue("measure"); |
| boolean isLocaleSystem = false; |
| if (measurementSystem.containsKey(localeSystem)) { |
| isLocaleSystem = true; |
| region = measurementSystem.get(localeSystem); |
| } |
| |
| // Check the region tag, e.g. `rg=uszzz`. |
| if (!isLocaleSystem) { |
| String localeRegion = locale.getKeywordValue("rg"); |
| if (localeRegion != null && localeRegion.length() >= 3) { |
| if (localeRegion.equals("default")) { |
| region = localeRegion; |
| } else if (Character.isDigit(localeRegion.charAt(0))) { |
| region = localeRegion.substring(0, 3); // e.g. 001 |
| } else { |
| // Capitalize the first two character of the region, e.g. ukzzzz or usca |
| region = localeRegion.substring(0, 2).toUpperCase(Locale.ROOT); |
| } |
| } |
| } |
| |
| String[] subUsages = getAllUsages(usage); |
| UnitPreference[] result = null; |
| for (String subUsage : |
| subUsages) { |
| result = getUnitPreferences(category, subUsage, region); |
| if (result != null) break; |
| } |
| |
| // TODO: if a category is missing, we get an assertion failure, or we |
| // return null, causing a NullPointerException. In C++, we return an |
| // U_MISSING_RESOURCE_ERROR error. |
| assert (result != null) : "At least the category must be exist"; |
| return result; |
| } |
| |
| /** |
| * @param category |
| * @param usage |
| * @param region |
| * @return null if there is no entry associated to the category and usage. O.W. returns the corresponding UnitPreference[] |
| */ |
| private UnitPreference[] getUnitPreferences(String category, String usage, String region) { |
| String key = formMapKey(category, usage); |
| if (this.mapToUnitPreferences.containsKey(key)) { |
| HashMap<String, UnitPreference[]> unitPreferencesMap = this.mapToUnitPreferences.get(key); |
| UnitPreference[] result = |
| unitPreferencesMap.containsKey(region) ? |
| unitPreferencesMap.get(region) : |
| unitPreferencesMap.get(UnitsData.Constants.DEFAULT_REGION); |
| |
| assert (result != null); |
| return result; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @hide Only a subset of ICU is exposed in Android |
| */ |
| public static class UnitPreference { |
| private final String unit; |
| private final BigDecimal geq; |
| private final String skeleton; |
| |
| |
| public UnitPreference(String unit, String geq, String skeleton) { |
| this.unit = unit; |
| this.geq = geq == null ? BigDecimal.valueOf( Double.MIN_VALUE) /* -inf */ : new BigDecimal(geq); |
| this.skeleton = skeleton == null? "" : skeleton; |
| } |
| |
| public String getUnit() { |
| return this.unit; |
| } |
| |
| public BigDecimal getGeq() { |
| return geq; |
| } |
| |
| public String getSkeleton() { |
| return skeleton; |
| } |
| } |
| |
| /** |
| * @hide Only a subset of ICU is exposed in Android |
| */ |
| public static class UnitPreferencesSink extends UResource.Sink { |
| |
| private HashMap<String, HashMap<String, UnitPreference[]>> mapToUnitPreferences; |
| |
| public UnitPreferencesSink() { |
| this.mapToUnitPreferences = new HashMap<>(); |
| } |
| |
| public HashMap<String, HashMap<String, UnitPreference[]>> getMapToUnitPreferences() { |
| return mapToUnitPreferences; |
| } |
| |
| /** |
| * The unitPreferenceData structure (see icu4c/source/data/misc/units.txt) contains a |
| * hierarchy of category/usage/region, within which are a set of |
| * preferences. Hence three for-loops and another loop for the |
| * preferences themselves. |
| */ |
| @Override |
| public void put(UResource.Key key, UResource.Value value, boolean noFallback) { |
| assert (UnitsData.Constants.UNIT_PREFERENCE_TABLE_NAME.equals(key.toString())); |
| |
| UResource.Table categoryTable = value.getTable(); |
| for (int i = 0; categoryTable.getKeyAndValue(i, key, value); i++) { |
| assert (value.getType() == UResourceBundle.TABLE); |
| |
| String category = key.toString(); |
| UResource.Table usageTable = value.getTable(); |
| for (int j = 0; usageTable.getKeyAndValue(j, key, value); j++) { |
| assert (value.getType() == UResourceBundle.TABLE); |
| |
| String usage = key.toString(); |
| UResource.Table regionTable = value.getTable(); |
| for (int k = 0; regionTable.getKeyAndValue(k, key, value); k++) { |
| assert (value.getType() == UResourceBundle.ARRAY); |
| |
| String region = key.toString(); |
| UResource.Array preferencesTable = value.getArray(); |
| ArrayList<UnitPreference> unitPreferences = new ArrayList<>(); |
| for (int l = 0; preferencesTable.getValue(l, value); l++) { |
| assert (value.getType() == UResourceBundle.TABLE); |
| |
| UResource.Table singlePrefTable = value.getTable(); |
| // TODO collect the data |
| String unit = null; |
| String geq = "1"; |
| String skeleton = ""; |
| for (int m = 0; singlePrefTable.getKeyAndValue(m, key, value); m++) { |
| assert (value.getType() == UResourceBundle.STRING); |
| String keyString = key.toString(); |
| if ("unit".equals(keyString)) { |
| unit = value.getString(); |
| } else if ("geq".equals(keyString)) { |
| geq = value.getString(); |
| } else if ("skeleton".equals(keyString)) { |
| skeleton = value.getString(); |
| } else { |
| assert false : "key must be unit, geq or skeleton"; |
| } |
| } |
| assert (unit != null); |
| unitPreferences.add(new UnitPreference(unit, geq, skeleton)); |
| } |
| |
| assert (!unitPreferences.isEmpty()); |
| this.insertUnitPreferences( |
| category, |
| usage, |
| region, |
| unitPreferences.toArray(new UnitPreference[0]) |
| ); |
| } |
| } |
| } |
| } |
| |
| private void insertUnitPreferences(String category, String usage, String region, UnitPreference[] unitPreferences) { |
| String key = formMapKey(category, usage); |
| HashMap<String, UnitPreference[]> shouldInsert; |
| if (this.mapToUnitPreferences.containsKey(key)) { |
| shouldInsert = this.mapToUnitPreferences.get(key); |
| } else { |
| shouldInsert = new HashMap<>(); |
| this.mapToUnitPreferences.put(key, shouldInsert); |
| } |
| |
| shouldInsert.put(region, unitPreferences); |
| } |
| } |
| } |