• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package ohos.global.i18n;
17 
18 import com.ibm.icu.text.DateFormatSymbols;
19 import com.ibm.icu.text.DateTimePatternGenerator;
20 import com.ibm.icu.text.DecimalFormatSymbols;
21 import com.ibm.icu.text.NumberFormat;
22 import com.ibm.icu.text.NumberingSystem;
23 import com.ibm.icu.util.ULocale;
24 import com.ibm.icu.text.DateFormat;
25 import com.ibm.icu.text.SimpleDateFormat;
26 import com.ibm.icu.util.Calendar;
27 
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Objects;
31 import java.util.concurrent.locks.ReentrantLock;
32 import java.util.Map;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35 import java.lang.reflect.Field;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.Method;
38 
39 import ohos.global.i18n.ResourceConfiguration.ConfigItem;
40 import ohos.global.i18n.ResourceConfiguration.Element;
41 
42 /**
43  * Fetcher is used to fetche a locale's specified data
44  */
45 public class Fetcher implements Runnable, Comparable<Fetcher> {
46     /** configuration extracted from resourec_items.json */
47     private static ArrayList<ConfigItem> configItems = null;
48     private static final Logger LOG = Logger.getLogger("Fetcher");
49     private static int resourceCount = 0;
50     private static HashMap<Integer, String> int2Str = new HashMap<>();
51     private static HashMap<String, Integer> str2Int = new HashMap<>();
52     private static boolean sStatusOk = true;
53 
54     static {
55         configItems = ResourceConfiguration.parse();
56         configItems.sort((ConfigItem first, ConfigItem second) -> first.getIndex() - second.getIndex());
57         resourceCount = configItems.size();
58     }
59 
60     /** Used to store data related to a locale */
61     public ArrayList<String> datas = new ArrayList<>();
62 
63     /** All non-repeated strings will be put into idMap */
64     public Map<String, Integer> idMap;
65 
66     /** Indicate whether this Fetcher is included in the final generation process of i18n.dat file */
67     public boolean included = true;
68 
69     /** LanguageTag related to the locale */
70     public String languageTag;
71 
72     private String lan; // language
73     private ReentrantLock lock; // Lock used to synchronize dump operation
74     private ULocale locale;
75     private DateFormatSymbols formatSymbols;
76     private DateTimePatternGenerator patternGenerator;
77     private int status = 0;
78     private String defaultHourString;
79     private ArrayList<Integer> reserved = new ArrayList<>();
80 
81     /**
82      * show whether resouce_items is loaded successfully
83      *
84      * @return true if status is right, otherwise false
85      */
isFetcherStatusOk()86     public static boolean isFetcherStatusOk() {
87         return sStatusOk;
88     }
89 
90     /**
91      * return the total resource number
92      *
93      * @return resourceCount
94      */
getResourceCount()95     public static int getResourceCount() {
96         return resourceCount;
97     }
98 
99     /**
100      * Methods to get int2Str
101      *
102      * @return Return int2Str
103      */
getInt2Str()104     public static HashMap<Integer, String> getInt2Str() {
105         return int2Str;
106     }
107 
108     /**
109      * Methods to get str2Int
110      *
111      * @return Return str2Int
112      */
getStr2Int()113     public static HashMap<String, Integer> getStr2Int() {
114         return str2Int;
115     }
116 
Fetcher(String tag, ReentrantLock lock, Map<String, Integer> idMap)117     public Fetcher(String tag, ReentrantLock lock, Map<String, Integer> idMap) {
118         if (!Utils.isValidLanguageTag(tag)) {
119             LOG.log(Level.SEVERE, String.format("wrong languageTag %s", tag));
120             status = 1;
121         }
122         this.languageTag = tag;
123         Objects.requireNonNull(lock);
124         this.lock = lock;
125         Objects.requireNonNull(idMap);
126         this.idMap = idMap;
127         this.lan = this.languageTag.split("-")[0];
128         this.locale = ULocale.forLanguageTag(this.languageTag);
129         formatSymbols = DateFormatSymbols.getInstance(locale);
130         patternGenerator = DateTimePatternGenerator.getInstance(locale);
131         defaultHourString = defaultHour();
132     }
133 
134     /**
135      * Check the status of the fetcher, normally a wrong language tag
136      * can make the status wrong.
137      *
138      * @return the status
139      */
checkStatus()140     public boolean checkStatus() {
141         return status == 0;
142     }
143 
144     /**
145      * Get all meta data defined in resource_items.json
146      */
getData()147     public void getData() {
148         int current = 0;
149         Method method = null;
150         for (ConfigItem item : configItems) {
151             int index = item.getIndex();
152             if (current != index) {
153                 throw new IllegalStateException();
154             }
155             String methodString = item.getMethod();
156             try {
157                 method = Fetcher.class.getDeclaredMethod(methodString, ConfigItem.class);
158                 method.setAccessible(true);
159                 method.invoke(this, item);
160             } catch(IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
161                 LOG.severe("get data failed for index " + current);
162             }
163             ++current;
164         }
165     }
166 
167     /**
168      * Dump all datas in this locale to idMap
169      */
dump()170     public void dump() {
171         try {
172             lock.lock();
173             int size = this.idMap.size();
174             for (int i = 0; i < datas.size(); i++) {
175                 String data = datas.get(i);
176                 if (!idMap.containsKey(data)) {
177                     idMap.put(data, size);
178                     size++;
179                 }
180             }
181         } finally {
182             lock.unlock();
183         }
184     }
185 
186     /**
187      * Equals function to determine whether two objs are equal
188      *
189      * @param obj Object to be compared
190      * @return Return true if obj is equals to this Fetcher object, otherwise false
191      */
equals(Object obj)192     public boolean equals(Object obj) {
193         if (!(obj instanceof Fetcher)) {
194             return false;
195         }
196         Fetcher fetcher = (Fetcher) obj;
197         if (datas.size() != fetcher.datas.size()) {
198             return false;
199         }
200         for (int i = 0; i < datas.size(); i++) {
201             if (!datas.get(i).equals(fetcher.datas.get(i))) {
202                 return false;
203             }
204         }
205         return true;
206     }
207 
208     /**
209      * Returns hashcode
210      *
211      * @return HashCode of Fetcher object
212      */
213     @Override
hashCode()214     public int hashCode() {
215         return datas.hashCode() + languageTag.hashCode();
216     }
217 
218     /**
219      * Override methods in Runnable
220      */
run()221     public void run() {
222         getData();
223         dump();
224     }
225 
226     /**
227      * the i'th value represent whether i'th resource should reserved for this locale
228      *
229      * @param index resource id
230      * @return 1 represents i'th resource reserved.
231      */
reservedGet(int index)232     public int reservedGet(int index) {
233         return reserved.get(index);
234     }
235 
236     /**
237      * the i'th value represent whether i'th resource should reserved for this locale
238      *
239      * @param val whether reserves current resource
240      */
reservedAdd(int val)241     public void reservedAdd(int val) {
242         reserved.add(val);
243     }
244 
convertNoAscii(String str)245     private String convertNoAscii(String str) {
246         return str;
247     }
248 
249     // Get month names
getMonthNames(int formatType, int lengthType)250     private void getMonthNames(int formatType, int lengthType) {
251         StringBuilder sb = new StringBuilder();
252         String[] months = formatSymbols.getMonths(formatType, lengthType);
253         for (int i = 0; i < months.length; i++) {
254             sb.append(months[i]);
255             if (i != months.length - 1) {
256                 sb.append(FileConfig.SEP);
257             }
258         }
259         datas.add(sb.toString());
260     }
261 
262     // Get weekday names
getWeekDayNames(int formatType, int lengthType)263     private void getWeekDayNames(int formatType, int lengthType) {
264         StringBuilder sb = new StringBuilder();
265         String[] weekdays = formatSymbols.getWeekdays(formatType, lengthType);
266         String[] adjustWeekdays = new String[(weekdays.length - 1)];
267         for (int i = 0; i < adjustWeekdays.length; i++) {
268             adjustWeekdays[i] = weekdays[i + 1];
269         }
270         for (int i = 0; i < adjustWeekdays.length; i++) {
271             sb.append(adjustWeekdays[i]);
272             if (i != adjustWeekdays.length - 1) {
273                 sb.append(FileConfig.SEP);
274             }
275         }
276         this.datas.add(sb.toString());
277     }
278 
getPatterns(ConfigItem config)279     private void getPatterns(ConfigItem config) {
280         if (config.elements == null) {
281             throw new IllegalArgumentException("no patterns defined in resource_items.json for index: " + config.index);
282         }
283         Element[] elements = config.elements;
284         int current = 0;
285         ArrayList<String> skeletons = new ArrayList<String>();
286         for (Element ele : elements) {
287             int index = ele.index;
288             if (current != index) {
289                 throw new IllegalStateException("wrong index order in patterns for index: " + config.index);
290             }
291             ++current;
292             skeletons.add(ele.skeleton);
293         }
294         StringBuilder sb = new StringBuilder();
295         String[] outPatterns = new String[skeletons.size()];
296         processPatterns(outPatterns, skeletons);
297         for (int i = 0; i < skeletons.size(); i++) {
298             sb.append(outPatterns[i]);
299             if (i != outPatterns.length - 1) {
300                 sb.append(FileConfig.SEP);
301             }
302         }
303         datas.add(sb.toString());
304     }
305 
processPatterns(String[] outPatterns, ArrayList<String> skeletons)306     private void processPatterns(String[] outPatterns, ArrayList<String> skeletons) {
307         for (int i = 0; i < skeletons.size(); ++i) {
308             switch (skeletons.get(i)) {
309                 case "FULL":
310                 case "MEDIUM":
311                 case "SHORT": {
312                     outPatterns[i] = getFMSPattern(skeletons.get(i));
313                     break;
314                 }
315                 default: {
316                     processSpecialPattern(outPatterns, skeletons, i);
317                 }
318             }
319         }
320     }
321 
processSpecialPattern(String[] outPatterns, ArrayList<String> skeletons, int i)322     private void processSpecialPattern(String[] outPatterns, ArrayList<String> skeletons, int i) {
323         if ("en-US".equals(languageTag) && ("Ed".equals(skeletons.get(i)))) {
324             outPatterns[i] = "EEE d";
325             return;
326         }
327         if ("jm".equals(skeletons.get(i))) {
328             if ("h".equals(defaultHourString)) {
329                 outPatterns[i] = patternGenerator.getBestPattern("hm");
330             } else {
331                 outPatterns[i] = patternGenerator.getBestPattern("Hm");
332             }
333             return;
334         }
335         if ("jms".equals(skeletons.get(i))) {
336             if ("h".equals(defaultHourString)) {
337                 outPatterns[i] = patternGenerator.getBestPattern("hms");
338             } else {
339                 outPatterns[i] = patternGenerator.getBestPattern("Hms");
340             }
341             return;
342         }
343         outPatterns[i] = patternGenerator.getBestPattern(skeletons.get(i));
344     }
345 
346     // Get FULL-MEDIUM_SHORT pattern
getFMSPattern(String skeleton)347     private String getFMSPattern(String skeleton) {
348         DateFormat formatter = null;
349         try {
350             Field patternField = DateFormat.class.getField(skeleton);
351             int patternIndex = patternField.getInt(null);
352             formatter = DateFormat.getDateInstance(patternIndex, locale);
353         } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e ) {
354             LOG.log(Level.SEVERE, "cannot get field " + skeleton);
355         }
356         if (formatter instanceof SimpleDateFormat) {
357             return ((SimpleDateFormat)formatter).toPattern();
358         } else {
359             LOG.log(Level.SEVERE, "wrong type in getFMSPattern");
360             return "";
361         }
362     }
363 
364     // 0. get format abbreviated month names
getFormatAbbrMonthNames(ConfigItem config)365     private void getFormatAbbrMonthNames(ConfigItem config) {
366         getMonthNames(DateFormatSymbols.FORMAT, DateFormatSymbols.ABBREVIATED);
367     }
368 
369     // 1. get format abbreviated day names
getFormatAbbrDayNames(ConfigItem config)370     private void getFormatAbbrDayNames(ConfigItem config) {
371         getWeekDayNames(DateFormatSymbols.FORMAT, DateFormatSymbols.ABBREVIATED);
372     }
373 
374     // 4. get am pm markser
getAmPmMarkers(ConfigItem config)375     private void getAmPmMarkers(ConfigItem config) {
376         StringBuilder sb = new StringBuilder();
377         String[] amPmStrings = formatSymbols.getAmPmStrings();
378         for (int i = 0; i < amPmStrings.length; ++i) {
379             sb.append(amPmStrings[i]);
380             if (i != amPmStrings.length - 1) {
381                 sb.append(FileConfig.SEP);
382             }
383         }
384         this.datas.add(sb.toString());
385     }
386 
387     // 5. get plural data
getPluralRules(ConfigItem config)388     private void getPluralRules(ConfigItem config) {
389         String str = PluralFetcher.getInstance().get(this.lan);
390         if (str == null) {
391             str = "";
392         }
393         this.datas.add(str);
394     }
395 
getDecimalPluralRules(ConfigItem config)396     private void getDecimalPluralRules(ConfigItem config) {
397         String str = PluralFetcher.getInstance().getDecimal(this.lan);
398         if (str == null) {
399             str = "";
400         }
401         this.datas.add(str);
402     }
403 
404     // 6. get number format data
405     @SuppressWarnings("Deprecation")
getNumberFormat(ConfigItem config)406     private void getNumberFormat(ConfigItem config) {
407         String pattern = NumberFormat.getPatternForStyle(locale, NumberFormat.NUMBERSTYLE);
408         String percentPattern = NumberFormat.getPatternForStyle(locale, NumberFormat.PERCENTSTYLE);
409         DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
410         String percent = decimalFormatSymbols.getPercentString();
411         String  groupingSeparator = decimalFormatSymbols.getGroupingSeparatorString();
412         String decimalSeparator = decimalFormatSymbols.getDecimalSeparatorString();
413         StringBuilder sb = new StringBuilder();
414         sb.append(pattern);
415         sb.append(FileConfig.SEP);
416         sb.append(percentPattern);
417         sb.append(FileConfig.SEP);
418         sb.append(convertNoAscii(decimalSeparator));
419         sb.append(FileConfig.SEP);
420         sb.append(convertNoAscii(groupingSeparator));
421         sb.append(FileConfig.SEP);
422         sb.append(convertNoAscii(percent));
423         datas.add(sb.toString());
424     }
425 
426     // 7. get number digits
getNumberDigits(ConfigItem config)427     private void getNumberDigits(ConfigItem config) {
428         NumberingSystem numberSystem = NumberingSystem.getInstance(locale);
429         String description = numberSystem.getDescription();
430         StringBuilder sb = new StringBuilder();
431         for (int i = 0; i < description.length(); i++) {
432             sb.append(String.valueOf(description.charAt(i)));
433             if (i != description.length() - 1) {
434                 sb.append(FileConfig.NUMBER_SEP);
435             }
436         }
437         datas.add(sb.toString());
438     }
439 
440     // 8. get time separtor
441     @SuppressWarnings("Deprecation")
getTimeSeparator(ConfigItem config)442     private void getTimeSeparator(ConfigItem config) {
443         datas.add(formatSymbols.getTimeSeparatorString());
444     }
445 
446     // 9. get default hour
getDefaultHour(ConfigItem config)447     private void getDefaultHour(ConfigItem config) {
448         datas.add(defaultHourString);
449     }
450 
451     // 10.get standalone abbreviated month
getStandAloneAbbrMonthNames(ConfigItem config)452     private void getStandAloneAbbrMonthNames(ConfigItem config) {
453         getMonthNames(DateFormatSymbols.STANDALONE, DateFormatSymbols.ABBREVIATED);
454     }
455 
456     // 11. get standalone abbreviated weekday
getStandAloneAbbrWeekDayNames(ConfigItem config)457     private void getStandAloneAbbrWeekDayNames(ConfigItem config) {
458         getWeekDayNames(DateFormatSymbols.STANDALONE, DateFormatSymbols.ABBREVIATED);
459     }
460 
461     // 12. get format wide month
getFormatWideMonthNames(ConfigItem config)462     private void getFormatWideMonthNames(ConfigItem config) {
463         getMonthNames(DateFormatSymbols.FORMAT, DateFormatSymbols.WIDE);
464     }
465 
466     // 13. get format wide days
getFormatWideWeekDayNames(ConfigItem config)467     private void getFormatWideWeekDayNames(ConfigItem config) {
468         getWeekDayNames(DateFormatSymbols.FORMAT, DateFormatSymbols.WIDE);
469     }
470 
471     // 14. get standalone wide days
getStandAloneWideWeekDayNames(ConfigItem config)472     private void getStandAloneWideWeekDayNames(ConfigItem config) {
473         getWeekDayNames(DateFormatSymbols.STANDALONE, DateFormatSymbols.WIDE);
474     }
475 
476     // 15. get standalone wide month
getStandAloneWideMonthNames(ConfigItem config)477     private void getStandAloneWideMonthNames(ConfigItem config) {
478         getMonthNames(DateFormatSymbols.STANDALONE, DateFormatSymbols.WIDE);
479     }
480 
481     // 16. get measure format pattern
getMeasureFormatPatterns(ConfigItem config)482     private void getMeasureFormatPatterns(ConfigItem config) {
483         String str = MeasureFormatPatternFetcher.getInstance().get(this.languageTag);
484         if (str == null) {
485             str = "";
486         }
487         this.datas.add(str);
488     }
489 
defaultHour()490     private String defaultHour() {
491         DateFormat tempFormat = DateFormat
492             .getTimeInstance(DateFormat.SHORT, ULocale.forLanguageTag(languageTag));
493         SimpleDateFormat timeInstance = null;
494         if (tempFormat instanceof SimpleDateFormat) {
495             timeInstance = (SimpleDateFormat) tempFormat;
496         }
497         String shortDateTimePattern = (timeInstance == null) ? "" : timeInstance.toPattern();
498         if (shortDateTimePattern.contains("H")) {
499             return "H";
500         } else {
501             return "h";
502         }
503     }
504 
getWeekdata(ConfigItem config)505     private void getWeekdata(ConfigItem config) {
506         Calendar cal = Calendar.getInstance(ULocale.forLanguageTag(languageTag));
507         Calendar.WeekData weekdata = cal.getWeekData();
508         StringBuilder sb = new StringBuilder();
509         sb.append(weekdata.firstDayOfWeek);
510         sb.append(FileConfig.NUMBER_SEP);
511         sb.append(weekdata.minimalDaysInFirstWeek);
512         sb.append(FileConfig.NUMBER_SEP);
513         sb.append(weekdata.weekendOnset);
514         sb.append(FileConfig.NUMBER_SEP);
515         sb.append(weekdata.weekendCease);
516         datas.add(sb.toString());
517     }
518 
getMinusSign(ConfigItem config)519     private void getMinusSign(ConfigItem config) {
520         NumberFormat formatter = NumberFormat.getNumberInstance(locale);
521         String formatValue = formatter.format(-1);
522         NumberingSystem numberSystem = NumberingSystem.getInstance(locale);
523         String description = numberSystem.getDescription();
524         if (formatValue.length() > 0) {
525             String temp = formatValue.substring(0, formatValue.indexOf(description.charAt(1)));
526             datas.add(temp);
527         }
528     }
529 
compareTo(Fetcher other)530     public @Override int compareTo(Fetcher other) {
531         if (languageTag == null && other.languageTag == null) {
532             return 0;
533         }
534         if (languageTag == null) {
535             return -1;
536         } else if (other.languageTag == null) {
537             return 1;
538         } else {
539             return languageTag.compareTo(other.languageTag);
540         }
541     }
542 }
543