• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2020 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 package com.ibm.icu.impl.units;
4 
5 import java.math.BigDecimal;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 
9 import com.ibm.icu.impl.ICUData;
10 import com.ibm.icu.impl.ICUResourceBundle;
11 import com.ibm.icu.impl.UResource;
12 import com.ibm.icu.util.UResourceBundle;
13 
14 public class UnitPreferences {
15 
16     private HashMap<String, HashMap<String, UnitPreference[]>> mapToUnitPreferences = new HashMap<>();
17 
UnitPreferences()18     public UnitPreferences() {
19         // Read unit preferences
20         ICUResourceBundle resource;
21         resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "units");
22         UnitPreferencesSink sink = new UnitPreferencesSink();
23         resource.getAllItemsWithFallback(UnitsData.Constants.UNIT_PREFERENCE_TABLE_NAME, sink);
24         this.mapToUnitPreferences = sink.getMapToUnitPreferences();
25     }
26 
formMapKey(String category, String usage)27     public static String formMapKey(String category, String usage) {
28         return category + "++" + usage;
29     }
30 
31     /**
32      * Extracts all the sub-usages from a usage including the default one in the end.
33      * The usages will be in order starting with the longest matching one.
34      * For example:
35      * if usage                :   "person-height-child"
36      * the function will return:   "person-height-child"
37      * "person-height"
38      * "person"
39      * "default"
40      *
41      * @param usage
42      * @return
43      */
getAllUsages(String usage)44     private static String[] getAllUsages(String usage) {
45         ArrayList<String> result = new ArrayList<>();
46         result.add(usage);
47         for (int i = usage.length() - 1; i >= 0; --i) {
48             if (usage.charAt(i) == '-') {
49                 result.add(usage.substring(0, i));
50             }
51         }
52 
53         if (!usage.equals(UnitsData.Constants.DEFAULT_USAGE)) { // Do not add default usage twice.
54             result.add(UnitsData.Constants.DEFAULT_USAGE);
55         }
56         return result.toArray(new String[0]);
57     }
58 
getPreferencesFor(String category, String usage, String region)59     public UnitPreference[] getPreferencesFor(String category, String usage, String region) {
60         String[] subUsages = getAllUsages(usage);
61         UnitPreference[] result = null;
62         for (String subUsage :
63                 subUsages) {
64             result = getUnitPreferences(category, subUsage, region);
65             if (result != null) break;
66         }
67         // TODO: if a category is missing, we get an assertion failure, or we
68         // return null, causing a NullPointerException. In C++, we return an
69         // U_MISSING_RESOURCE_ERROR error.
70         assert (result != null) : "At least the category must be exist";
71         return result;
72     }
73 
74     /**
75      * @param category
76      * @param usage
77      * @param region
78      * @return null if there is no entry associated to the category and usage. O.W. returns the corresponding UnitPreference[]
79      */
getUnitPreferences(String category, String usage, String region)80     private UnitPreference[] getUnitPreferences(String category, String usage, String region) {
81         String key = formMapKey(category, usage);
82         if (this.mapToUnitPreferences.containsKey(key)) {
83             HashMap<String, UnitPreference[]> unitPreferencesMap = this.mapToUnitPreferences.get(key);
84             UnitPreference[] result =
85                     unitPreferencesMap.containsKey(region) ?
86                             unitPreferencesMap.get(region) :
87                             unitPreferencesMap.get(UnitsData.Constants.DEFAULT_REGION);
88 
89             assert (result != null);
90             return result;
91         }
92 
93         return null;
94     }
95 
96     public static class UnitPreference {
97         private final String unit;
98         private final BigDecimal geq;
99         private final String skeleton;
100 
101 
UnitPreference(String unit, String geq, String skeleton)102         public UnitPreference(String unit, String geq, String skeleton) {
103             this.unit = unit;
104             this.geq = new BigDecimal(geq);
105             this.skeleton = skeleton;
106         }
107 
getUnit()108         public String getUnit() {
109             return this.unit;
110         }
111 
getGeq()112         public BigDecimal getGeq() {
113             return geq;
114         }
115 
getSkeleton()116         public String getSkeleton() {
117             return skeleton;
118         }
119     }
120 
121     public static class UnitPreferencesSink extends UResource.Sink {
122 
123         private HashMap<String, HashMap<String, UnitPreference[]>> mapToUnitPreferences;
124 
UnitPreferencesSink()125         public UnitPreferencesSink() {
126             this.mapToUnitPreferences = new HashMap<>();
127         }
128 
getMapToUnitPreferences()129         public HashMap<String, HashMap<String, UnitPreference[]>> getMapToUnitPreferences() {
130             return mapToUnitPreferences;
131         }
132 
133         /**
134          * The unitPreferenceData structure (see icu4c/source/data/misc/units.txt) contains a
135          * hierarchy of category/usage/region, within which are a set of
136          * preferences. Hence three for-loops and another loop for the
137          * preferences themselves.
138          */
139         @Override
put(UResource.Key key, UResource.Value value, boolean noFallback)140         public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
141             assert (UnitsData.Constants.UNIT_PREFERENCE_TABLE_NAME.equals(key.toString()));
142 
143             UResource.Table categoryTable = value.getTable();
144             for (int i = 0; categoryTable.getKeyAndValue(i, key, value); i++) {
145                 assert (value.getType() == UResourceBundle.TABLE);
146 
147                 String category = key.toString();
148                 UResource.Table usageTable = value.getTable();
149                 for (int j = 0; usageTable.getKeyAndValue(j, key, value); j++) {
150                     assert (value.getType() == UResourceBundle.TABLE);
151 
152                     String usage = key.toString();
153                     UResource.Table regionTable = value.getTable();
154                     for (int k = 0; regionTable.getKeyAndValue(k, key, value); k++) {
155                         assert (value.getType() == UResourceBundle.ARRAY);
156 
157                         String region = key.toString();
158                         UResource.Array preferencesTable = value.getArray();
159                         ArrayList<UnitPreference> unitPreferences = new ArrayList<>();
160                         for (int l = 0; preferencesTable.getValue(l, value); l++) {
161                             assert (value.getType() == UResourceBundle.TABLE);
162 
163                             UResource.Table singlePrefTable = value.getTable();
164                             // TODO collect the data
165                             String unit = null;
166                             String geq = "1";
167                             String skeleton = "";
168                             for (int m = 0; singlePrefTable.getKeyAndValue(m, key, value); m++) {
169                                 assert (value.getType() == UResourceBundle.STRING);
170                                 String keyString = key.toString();
171                                 if ("unit".equals(keyString)) {
172                                     unit = value.getString();
173                                 } else if ("geq".equals(keyString)) {
174                                     geq = value.getString();
175                                 } else if ("skeleton".equals(keyString)) {
176                                     skeleton = value.getString();
177                                 } else {
178                                     assert false : "key must be unit, geq or skeleton";
179                                 }
180                             }
181                             assert (unit != null);
182                             unitPreferences.add(new UnitPreference(unit, geq, skeleton));
183                         }
184 
185                         assert (!unitPreferences.isEmpty());
186                         this.insertUnitPreferences(
187                                 category,
188                                 usage,
189                                 region,
190                                 unitPreferences.toArray(new UnitPreference[0])
191                         );
192                     }
193                 }
194             }
195         }
196 
insertUnitPreferences(String category, String usage, String region, UnitPreference[] unitPreferences)197         private void insertUnitPreferences(String category, String usage, String region, UnitPreference[] unitPreferences) {
198             String key = formMapKey(category, usage);
199             HashMap<String, UnitPreference[]> shouldInsert;
200             if (this.mapToUnitPreferences.containsKey(key)) {
201                 shouldInsert = this.mapToUnitPreferences.get(key);
202             } else {
203                 shouldInsert = new HashMap<>();
204                 this.mapToUnitPreferences.put(key, shouldInsert);
205             }
206 
207             shouldInsert.put(region, unitPreferences);
208         }
209     }
210 }
211