blob: ac5d9a7d8ee8ffc5ab7416ab324b8467c353266d [file] [log] [blame]
// © 2020 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.impl.units;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.UResource;
import com.ibm.icu.util.UResourceBundle;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
public class UnitPreferences {
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, String region) {
String[] subUsages = getAllUsages(usage);
UnitPreference[] result = null;
for (String subUsage :
subUsages) {
result = getUnitPreferences(category, subUsage, region);
if (result != null) break;
}
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;
}
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 = new BigDecimal(geq);
this.skeleton = skeleton;
}
public String getUnit() {
return this.unit;
}
public BigDecimal getGeq() {
return geq;
}
public String getSkeleton() {
return skeleton;
}
}
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);
}
}
}