/* 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.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import android.icu.impl.ICUData; import android.icu.impl.ICUResourceBundle; import android.icu.impl.IllegalIcuArgumentException; import android.icu.impl.UResource; import android.icu.util.ULocale; import android.icu.util.UResourceBundle; /** * Responsible for all units data operations (retriever, analysis, extraction certain data ... etc.). * @hide Only a subset of ICU is exposed in Android */ public class UnitsData { // TODO(icu-units#122): this class can use static initialization to load the // data once, and provide access to it via static methods. (Partial change // has been done already.) // Array of simple unit IDs. private static String[] simpleUnits = null; // Maps from the value associated with each simple unit ID to a category // index number. private static int[] simpleUnitCategories = null; private ConversionRates conversionRates; private UnitPreferences unitPreferences; public UnitsData() { this.conversionRates = new ConversionRates(); this.unitPreferences = new UnitPreferences(); } public static String[] getSimpleUnits() { return simpleUnits; } static { // Read simple units ICUResourceBundle resource; resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "units"); SimpleUnitIdentifiersSink sink = new SimpleUnitIdentifiersSink(); resource.getAllItemsWithFallback("convertUnits", sink); simpleUnits = sink.simpleUnits; simpleUnitCategories = sink.simpleUnitCategories; } public ConversionRates getConversionRates() { return conversionRates; } public UnitPreferences getUnitPreferences() { return unitPreferences; } public static int getCategoryIndexOfSimpleUnit(int simpleUnitIndex) { return simpleUnitCategories[simpleUnitIndex]; } /** * @param measureUnit An instance of MeasureUnitImpl. * @return the corresponding category. */ public String getCategory(MeasureUnitImpl measureUnit) { MeasureUnitImpl baseMeasureUnitImpl = this.getConversionRates().extractCompoundBaseUnit(measureUnit); baseMeasureUnitImpl.serialize(); String identifier = baseMeasureUnitImpl.getIdentifier(); Integer index = Categories.baseUnitToIndex.get(identifier); // In case the base unit identifier did not match any entry. if (index == null) { baseMeasureUnitImpl.takeReciprocal(); baseMeasureUnitImpl.serialize(); identifier = baseMeasureUnitImpl.getIdentifier(); index = Categories.baseUnitToIndex.get(identifier); } // In case the reciprocal of the base unit identifier did not match any entry. baseMeasureUnitImpl.takeReciprocal(); // return to original form MeasureUnitImpl simplifiedUnit = baseMeasureUnitImpl.copyAndSimplify(); if (index == null) { simplifiedUnit.serialize(); identifier = simplifiedUnit.getIdentifier(); index = Categories.baseUnitToIndex.get(identifier); } // In case the simplified base unit identifier did not match any entry. if (index == null) { simplifiedUnit.takeReciprocal(); simplifiedUnit.serialize(); identifier = simplifiedUnit.getIdentifier(); index = Categories.baseUnitToIndex.get(identifier); } // If there is no match at all, throw an exception. if (index == null) { throw new IllegalIcuArgumentException("This unit does not has a category" + measureUnit.getIdentifier()); } return Categories.indexToCategory[index]; } public UnitPreferences.UnitPreference[] getPreferencesFor(String category, String usage, ULocale locale) { return this.unitPreferences.getPreferencesFor(category, usage, locale, this); } /** * @hide Only a subset of ICU is exposed in Android */ public static class SimpleUnitIdentifiersSink extends UResource.Sink { String[] simpleUnits = null; int[] simpleUnitCategories = null; @Override public void put(UResource.Key key, UResource.Value value, boolean noFallback) { assert key.toString().equals(Constants.CONVERSION_UNIT_TABLE_NAME); assert value.getType() == UResourceBundle.TABLE; UResource.Table simpleUnitsTable = value.getTable(); ArrayList simpleUnits = new ArrayList<>(); ArrayList simpleUnitCategories = new ArrayList<>(); for (int i = 0; simpleUnitsTable.getKeyAndValue(i, key, value); i++) { if (key.toString().equals("kilogram")) { // For parsing, we use "gram", the prefixless metric mass unit. We // thus ignore the SI Base Unit of Mass: it exists due to being the // mass conversion target unit, but not needed for MeasureUnit // parsing. continue; } // Find the base target unit for this simple unit UResource.Table table = value.getTable(); if (!table.findValue("target", value)) { // TODO: is there a more idiomatic way to deal with Resource // Sink data errors in ICU4J? For now we just assert-fail, // and otherwise skip bad data: assert false : "Could not find \"target\" for simple unit: " + key; continue; } String target = value.getString(); simpleUnits.add(key.toString()); simpleUnitCategories.add(Categories.baseUnitToIndex.get(target)); } this.simpleUnits = simpleUnits.toArray(new String[0]); this.simpleUnitCategories = new int[simpleUnitCategories.size()]; Iterator iter = simpleUnitCategories.iterator(); for (int i = 0; i < this.simpleUnitCategories.length; i++) { this.simpleUnitCategories[i] = iter.next().intValue(); } } } /** * Contains all the needed constants. * @hide Only a subset of ICU is exposed in Android */ public static class Constants { // TODO: consider moving the Trie-offset-related constants into // MeasureUnitImpl.java, the only place they're being used? // Trie value offset for simple units, e.g. "gram", "nautical-mile", // "fluid-ounce-imperial". public static final int kSimpleUnitOffset = 512; // Trie value offset for powers like "square-", "cubic-", "pow2-" etc. public static final int kPowerPartOffset = 256; // Trie value offset for "per-". public final static int kInitialCompoundPartOffset = 192; // Trie value offset for compound parts, e.g. "-per-", "-", "-and-". public final static int kCompoundPartOffset = 128; // Trie value offset for SI or binary prefixes. This is big enough to // ensure we only insert positive integers into the trie. public static final int kPrefixOffset = 64; /* Tables Names*/ public static final String CONVERSION_UNIT_TABLE_NAME = "convertUnits"; public static final String UNIT_PREFERENCE_TABLE_NAME = "unitPreferenceData"; public static final String CATEGORY_TABLE_NAME = "unitQuantities"; public static final String DEFAULT_REGION = "001"; public static final String DEFAULT_USAGE = "default"; } // Deals with base units and categories, e.g. "meter-per-second" --> "speed". /** * @hide Only a subset of ICU is exposed in Android */ public static class Categories { /** * Maps from base unit to an index value: an index into the * indexToCategory array. */ static HashMap baseUnitToIndex; /** * Our official array of category strings - categories are identified by * indeces into this array. */ static String[] indexToCategory; static { // Read unit Categories ICUResourceBundle resource; resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "units"); CategoriesSink sink = new CategoriesSink(); resource.getAllItemsWithFallback(Constants.CATEGORY_TABLE_NAME, sink); baseUnitToIndex = sink.mapFromUnitToIndex; indexToCategory = sink.categories.toArray(new String[0]); } } /** * A Resource Sink that collects information from {@code unitQuantities} in the * {@code units} resource to provide key->value lookups from base unit to * category, as well as preserving ordering information for these * categories. See {@code units.txt}. * * For example: "kilogram" -> "mass", "meter-per-second" -> "speed". * * In Java unitQuantity values are collected in order into an ArrayList, * while unitQuantity key-to-index lookups are handled with a HashMap. * @hide Only a subset of ICU is exposed in Android */ public static class CategoriesSink extends UResource.Sink { /** * Contains the map between units in their base units into their category. * For example: meter-per-second --> "speed" */ HashMap mapFromUnitToIndex; ArrayList categories; public CategoriesSink() { mapFromUnitToIndex = new HashMap<>(); categories = new ArrayList<>(); } @Override public void put(UResource.Key key, UResource.Value value, boolean noFallback) { assert (key.toString().equals(Constants.CATEGORY_TABLE_NAME)); assert (value.getType() == UResourceBundle.ARRAY); UResource.Array categoryArray = value.getArray(); for (int i=0; categoryArray.getValue(i, value); i++) { assert (value.getType() == UResourceBundle.TABLE); UResource.Table table = value.getTable(); assert (table.getSize() == 1) : "expecting single-entry table, got size: " + table.getSize(); table.getKeyAndValue(0, key, value); assert value.getType() == UResourceBundle.STRING : "expecting category string"; mapFromUnitToIndex.put(key.toString(), categories.size()); categories.add(value.toString()); } } } }