• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 /*
5  *******************************************************************************
6  * Copyright (C) 2004-2014, International Business Machines Corporation and
7  * others. All Rights Reserved.
8  *******************************************************************************
9 */
10 package ohos.global.icu.util;
11 
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.BitSet;
15 import java.util.Date;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.MissingResourceException;
20 import java.util.ResourceBundle;
21 import java.util.Set;
22 
23 import ohos.global.icu.impl.Utility;
24 import ohos.global.icu.text.BreakIterator;
25 import ohos.global.icu.text.Collator;
26 import ohos.global.icu.text.DateFormat;
27 import ohos.global.icu.text.NumberFormat;
28 import ohos.global.icu.text.SimpleDateFormat;
29 
30 /**
31  * This convenience class provides a mechanism for bundling together different
32  * globalization preferences. It includes:
33  * <ul>
34  * <li>A list of locales/languages in preference order</li>
35  * <li>A territory</li>
36  * <li>A currency</li>
37  * <li>A timezone</li>
38  * <li>A calendar</li>
39  * <li>A collator (for language-sensitive sorting, searching, and matching).</li>
40  * <li>Explicit overrides for date/time formats, etc.</li>
41  * </ul>
42  * The class will heuristically compute implicit, heuristic values for the above
43  * based on available data if explicit values are not supplied. These implicit
44  * values can be presented to users for confirmation, or replacement if the
45  * values are incorrect.
46  * <p>
47  * To reset any explicit field so that it will get heuristic values, pass in
48  * null. For example, myPreferences.setLocale(null);
49  * <p>
50  * All of the heuristics can be customized by subclasses, by overriding
51  * getTerritory(), guessCollator(), etc.
52  * <p>
53  * The class also supplies display names for languages, scripts, territories,
54  * currencies, timezones, etc. These are computed according to the
55  * locale/language preference list. Thus, if the preference is Breton; French;
56  * English, then the display name for a language will be returned in Breton if
57  * available, otherwise in French if available, otherwise in English.
58  * <p>
59  * The codes used to reference territory, currency, etc. are as defined elsewhere
60  * in ICU, and are taken from CLDR (which reflects RFC 3066bis usage, ISO 4217,
61  * and the TZ Timezone database identifiers).
62  * <p>
63  * <b>This is at a prototype stage, and has not incorporated all the design
64  * changes that we would like yet; further feedback is welcome.</b></p>
65  * Note:
66  * <ul>
67  * <li>to get the display name for the first day of the week, use the calendar +
68  * display names.</li>
69  * <li>to get the work days, ask the calendar (when that is available).</li>
70  * <li>to get papersize / measurement system/bidi-orientation, ask the locale
71  * (when that is available there)</li>
72  * <li>to get the field order in a date, and whether a time is 24hour or not,
73  * ask the DateFormat (when that is available there)</li>
74  * <li>it will support HOST locale when it becomes available (it is a special
75  * locale that will ask the services to use the host platform's values).</li>
76  * </ul>
77  *
78  * @hide exposed on OHOS
79  * @hide draft / provisional / internal are hidden on OHOS
80  */
81 
82 //TODO:
83 // - Add Holidays
84 // - Add convenience to get/take Locale as well as ULocale.
85 // - Add Lenient datetime formatting when that is available.
86 // - Should this be serializable?
87 // - Other utilities?
88 
89 public class GlobalizationPreferences implements Freezable<GlobalizationPreferences> {
90 
91     /**
92      * Default constructor
93      * @hide draft / provisional / internal are hidden on OHOS
94      */
GlobalizationPreferences()95     public GlobalizationPreferences(){}
96     /**
97      * Number Format type
98      * @hide draft / provisional / internal are hidden on OHOS
99      */
100     public static final int
101         NF_NUMBER = 0,      // NumberFormat.NUMBERSTYLE
102         NF_CURRENCY = 1,    // NumberFormat.CURRENCYSTYLE
103         NF_PERCENT = 2,     // NumberFormat.PERCENTSTYLE
104         NF_SCIENTIFIC = 3,  // NumberFormat.SCIENTIFICSTYLE
105         NF_INTEGER = 4;     // NumberFormat.INTEGERSTYLE
106 
107     private static final int NF_LIMIT = NF_INTEGER + 1;
108 
109     /**
110      * Date Format type
111      * @hide draft / provisional / internal are hidden on OHOS
112      */
113     public static final int
114         DF_FULL = DateFormat.FULL,      // 0
115         DF_LONG = DateFormat.LONG,      // 1
116         DF_MEDIUM = DateFormat.MEDIUM,  // 2
117         DF_SHORT = DateFormat.SHORT,    // 3
118         DF_NONE = 4;
119 
120     private static final int DF_LIMIT = DF_NONE + 1;
121 
122     /**
123      * For selecting a choice of display names
124      * @hide draft / provisional / internal are hidden on OHOS
125      */
126     public static final int
127         ID_LOCALE = 0,
128         ID_LANGUAGE = 1,
129         ID_SCRIPT = 2,
130         ID_TERRITORY = 3,
131         ID_VARIANT = 4,
132         ID_KEYWORD = 5,
133         ID_KEYWORD_VALUE = 6,
134         ID_CURRENCY = 7,
135         ID_CURRENCY_SYMBOL = 8,
136         ID_TIMEZONE = 9;
137 
138     //private static final int ID_LIMIT = ID_TIMEZONE + 1;
139 
140     /**
141      * Break iterator type
142      * @hide draft / provisional / internal are hidden on OHOS
143      */
144     public static final int
145         BI_CHARACTER = BreakIterator.KIND_CHARACTER,    // 0
146         BI_WORD = BreakIterator.KIND_WORD,              // 1
147         BI_LINE = BreakIterator.KIND_LINE,              // 2
148         BI_SENTENCE = BreakIterator.KIND_SENTENCE,      // 3
149         BI_TITLE = BreakIterator.KIND_TITLE;            // 4
150 
151     private static final int BI_LIMIT = BI_TITLE + 1;
152 
153     /**
154      * Sets the language/locale priority list. If other information is
155      * not (yet) available, this is used to to produce a default value
156      * for the appropriate territory, currency, timezone, etc.  The
157      * user should be given the opportunity to correct those defaults
158      * in case they are incorrect.
159      *
160      * @param inputLocales list of locales in priority order, eg {"be", "fr"}
161      *     for Breton first, then French if that fails.
162      * @return this, for chaining
163      * @hide draft / provisional / internal are hidden on OHOS
164      */
setLocales(List<ULocale> inputLocales)165     public GlobalizationPreferences setLocales(List<ULocale> inputLocales) {
166         if (isFrozen()) {
167             throw new UnsupportedOperationException("Attempt to modify immutable object");
168         }
169         locales = processLocales(inputLocales);
170         return this;
171     }
172 
173     /**
174      * Get a copy of the language/locale priority list
175      *
176      * @return a copy of the language/locale priority list.
177      * @hide draft / provisional / internal are hidden on OHOS
178      */
getLocales()179     public List<ULocale> getLocales() {
180         List<ULocale> result;
181         if (locales == null) {
182             result = guessLocales();
183         } else {
184             result = new ArrayList<>();
185             result.addAll(locales);
186         }
187         return result;
188     }
189 
190     /**
191      * Convenience function for getting the locales in priority order
192      * @param index The index (0..n) of the desired item.
193      * @return desired item. null if index is out of range
194      * @hide draft / provisional / internal are hidden on OHOS
195      */
getLocale(int index)196     public ULocale getLocale(int index) {
197         List<ULocale> lcls = locales;
198         if (lcls == null) {
199             lcls = guessLocales();
200         }
201         if (index >= 0 && index < lcls.size()) {
202             return lcls.get(index);
203         }
204         return null;
205     }
206 
207     /**
208      * Convenience routine for setting the language/locale priority
209      * list from an array.
210      *
211      * @see #setLocales(List locales)
212      * @param uLocales list of locales in an array
213      * @return this, for chaining
214      * @hide draft / provisional / internal are hidden on OHOS
215      */
setLocales(ULocale[] uLocales)216     public GlobalizationPreferences setLocales(ULocale[] uLocales) {
217         if (isFrozen()) {
218             throw new UnsupportedOperationException("Attempt to modify immutable object");
219         }
220         return setLocales(Arrays.asList(uLocales));
221     }
222 
223     /**
224      * Convenience routine for setting the language/locale priority
225      * list from a single locale/language.
226      *
227      * @see #setLocales(List locales)
228      * @param uLocale single locale
229      * @return this, for chaining
230      * @hide draft / provisional / internal are hidden on OHOS
231      */
setLocale(ULocale uLocale)232     public GlobalizationPreferences setLocale(ULocale uLocale) {
233         if (isFrozen()) {
234             throw new UnsupportedOperationException("Attempt to modify immutable object");
235         }
236         return setLocales(new ULocale[]{uLocale});
237     }
238 
239     /**
240      * Convenience routine for setting the locale priority list from
241      * an Accept-Language string.
242      * @see #setLocales(List locales)
243      * @param acceptLanguageString Accept-Language list, as defined by
244      *     Section 14.4 of the RFC 2616 (HTTP 1.1)
245      * @return this, for chaining
246      * @hide draft / provisional / internal are hidden on OHOS
247      */
setLocales(String acceptLanguageString)248     public GlobalizationPreferences setLocales(String acceptLanguageString) {
249         if (isFrozen()) {
250             throw new UnsupportedOperationException("Attempt to modify immutable object");
251         }
252         Set<ULocale> acceptSet = LocalePriorityList.add(acceptLanguageString).build().getULocales();
253         // processLocales() wants a List even though it only iterates front-to-back.
254         locales = processLocales(new ArrayList<>(acceptSet));
255         return this;
256     }
257 
258     /**
259      * Convenience function to get a ResourceBundle instance using
260      * the specified base name based on the language/locale priority list
261      * stored in this object.
262      *
263      * @param baseName the base name of the resource bundle, a fully qualified
264      * class name
265      * @return a resource bundle for the given base name and locale based on the
266      * language/locale priority list stored in this object
267      * @hide draft / provisional / internal are hidden on OHOS
268      */
getResourceBundle(String baseName)269     public ResourceBundle getResourceBundle(String baseName) {
270         return getResourceBundle(baseName, null);
271     }
272 
273     /**
274      * Convenience function to get a ResourceBundle instance using
275      * the specified base name and class loader based on the language/locale
276      * priority list stored in this object.
277      *
278      * @param baseName the base name of the resource bundle, a fully qualified
279      * class name
280      * @param loader the class object from which to load the resource bundle
281      * @return a resource bundle for the given base name and locale based on the
282      * language/locale priority list stored in this object
283      * @hide draft / provisional / internal are hidden on OHOS
284      */
getResourceBundle(String baseName, ClassLoader loader)285     public ResourceBundle getResourceBundle(String baseName, ClassLoader loader) {
286         UResourceBundle urb = null;
287         UResourceBundle candidate = null;
288         String actualLocaleName = null;
289         List<ULocale> fallbacks = getLocales();
290         for (int i = 0; i < fallbacks.size(); i++) {
291             String localeName = (fallbacks.get(i)).toString();
292             if (actualLocaleName != null && localeName.equals(actualLocaleName)) {
293                 // Actual locale name in the previous round may exactly matches
294                 // with the next fallback locale
295                 urb = candidate;
296                 break;
297             }
298             try {
299                 if (loader == null) {
300                     candidate = UResourceBundle.getBundleInstance(baseName, localeName);
301                 }
302                 else {
303                     candidate = UResourceBundle.getBundleInstance(baseName, localeName, loader);
304                 }
305                 if (candidate != null) {
306                     actualLocaleName = candidate.getULocale().getName();
307                     if (actualLocaleName.equals(localeName)) {
308                         urb = candidate;
309                         break;
310                     }
311                     if (urb == null) {
312                         // Preserve the available bundle as the last resort
313                         urb = candidate;
314                     }
315                 }
316             } catch (MissingResourceException mre) {
317                 actualLocaleName = null;
318                 continue;
319             }
320         }
321         if (urb == null) {
322             throw new MissingResourceException("Can't find bundle for base name "
323                     + baseName, baseName, "");
324         }
325         return urb;
326     }
327 
328     /**
329      * Sets the territory, which is a valid territory according to for
330      * RFC 3066 (or successor).  If not otherwise set, default
331      * currency and timezone values will be set from this.  The user
332      * should be given the opportunity to correct those defaults in
333      * case they are incorrect.
334      *
335      * @param territory code
336      * @return this, for chaining
337      * @hide draft / provisional / internal are hidden on OHOS
338      */
setTerritory(String territory)339     public GlobalizationPreferences setTerritory(String territory) {
340         if (isFrozen()) {
341             throw new UnsupportedOperationException("Attempt to modify immutable object");
342         }
343         this.territory = territory; // immutable, so don't need to clone
344         return this;
345     }
346 
347     /**
348      * Gets the territory setting. If it wasn't explicitly set, it is
349      * computed from the general locale setting.
350      *
351      * @return territory code, explicit or implicit.
352      * @hide draft / provisional / internal are hidden on OHOS
353      */
getTerritory()354     public String getTerritory() {
355         if (territory == null) {
356             return guessTerritory();
357         }
358         return territory; // immutable, so don't need to clone
359     }
360 
361     /**
362      * Sets the currency code. If this has not been set, uses default for territory.
363      *
364      * @param currency Valid ISO 4217 currency code.
365      * @return this, for chaining
366      * @hide draft / provisional / internal are hidden on OHOS
367      */
setCurrency(Currency currency)368     public GlobalizationPreferences setCurrency(Currency currency) {
369         if (isFrozen()) {
370             throw new UnsupportedOperationException("Attempt to modify immutable object");
371         }
372         this.currency = currency; // immutable, so don't need to clone
373         return this;
374     }
375 
376     /**
377      * Get a copy of the currency computed according to the settings.
378      *
379      * @return currency code, explicit or implicit.
380      * @hide draft / provisional / internal are hidden on OHOS
381      */
getCurrency()382     public Currency getCurrency() {
383         if (currency == null) {
384             return guessCurrency();
385         }
386         return currency; // immutable, so don't have to clone
387     }
388 
389     /**
390      * Sets the calendar. If this has not been set, uses default for territory.
391      *
392      * @param calendar arbitrary calendar
393      * @return this, for chaining
394      * @hide draft / provisional / internal are hidden on OHOS
395      */
setCalendar(Calendar calendar)396     public GlobalizationPreferences setCalendar(Calendar calendar) {
397         if (isFrozen()) {
398             throw new UnsupportedOperationException("Attempt to modify immutable object");
399         }
400         this.calendar = (Calendar) calendar.clone(); // clone for safety
401         return this;
402     }
403 
404     /**
405      * Get a copy of the calendar according to the settings.
406      *
407      * @return calendar explicit or implicit.
408      * @hide draft / provisional / internal are hidden on OHOS
409      */
getCalendar()410     public Calendar getCalendar() {
411         if (calendar == null) {
412             return guessCalendar();
413         }
414         Calendar temp = (Calendar) calendar.clone(); // clone for safety
415         temp.setTimeZone(getTimeZone());
416         temp.setTimeInMillis(System.currentTimeMillis());
417         return temp;
418     }
419 
420     /**
421      * Sets the timezone ID.  If this has not been set, uses default for territory.
422      *
423      * @param timezone a valid TZID (see UTS#35).
424      * @return this, for chaining
425      * @hide draft / provisional / internal are hidden on OHOS
426      */
setTimeZone(TimeZone timezone)427     public GlobalizationPreferences setTimeZone(TimeZone timezone) {
428         if (isFrozen()) {
429             throw new UnsupportedOperationException("Attempt to modify immutable object");
430         }
431         this.timezone = (TimeZone) timezone.clone(); // clone for safety;
432         return this;
433     }
434 
435     /**
436      * Get the timezone. It was either explicitly set, or is
437      * heuristically computed from other settings.
438      *
439      * @return timezone, either implicitly or explicitly set
440      * @hide draft / provisional / internal are hidden on OHOS
441      */
getTimeZone()442     public TimeZone getTimeZone() {
443         if (timezone == null) {
444             return guessTimeZone();
445         }
446         return timezone.cloneAsThawed(); // clone for safety
447     }
448 
449     /**
450      * Get a copy of the collator according to the settings.
451      *
452      * @return collator explicit or implicit.
453      * @hide draft / provisional / internal are hidden on OHOS
454      */
getCollator()455     public Collator getCollator() {
456         if (collator == null) {
457             return guessCollator();
458         }
459         try {
460             return (Collator) collator.clone();  // clone for safety
461         } catch (CloneNotSupportedException e) {
462             throw new ICUCloneNotSupportedException("Error in cloning collator", e);
463         }
464     }
465 
466     /**
467      * Explicitly set the collator for this object.
468      * @param collator The collator object to be passed.
469      * @return this, for chaining
470      * @hide draft / provisional / internal are hidden on OHOS
471      */
setCollator(Collator collator)472     public GlobalizationPreferences setCollator(Collator collator) {
473         if (isFrozen()) {
474             throw new UnsupportedOperationException("Attempt to modify immutable object");
475         }
476         try {
477             this.collator = (Collator) collator.clone(); // clone for safety
478         } catch (CloneNotSupportedException e) {
479                 throw new ICUCloneNotSupportedException("Error in cloning collator", e);
480         }
481         return this;
482     }
483 
484     /**
485      * Get a copy of the break iterator for the specified type according to the
486      * settings.
487      *
488      * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE
489      * @return break iterator explicit or implicit
490      * @hide draft / provisional / internal are hidden on OHOS
491      */
getBreakIterator(int type)492     public BreakIterator getBreakIterator(int type) {
493         if (type < BI_CHARACTER || type >= BI_LIMIT) {
494             throw new IllegalArgumentException("Illegal break iterator type");
495         }
496         if (breakIterators == null || breakIterators[type] == null) {
497             return guessBreakIterator(type);
498         }
499         return (BreakIterator) breakIterators[type].clone(); // clone for safety
500     }
501 
502     /**
503      * Explicitly set the break iterator for this object.
504      *
505      * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE
506      * @param iterator a break iterator
507      * @return this, for chaining
508      * @hide draft / provisional / internal are hidden on OHOS
509      */
setBreakIterator(int type, BreakIterator iterator)510     public GlobalizationPreferences setBreakIterator(int type, BreakIterator iterator) {
511         if (type < BI_CHARACTER || type >= BI_LIMIT) {
512             throw new IllegalArgumentException("Illegal break iterator type");
513         }
514         if (isFrozen()) {
515             throw new UnsupportedOperationException("Attempt to modify immutable object");
516         }
517         if (breakIterators == null)
518             breakIterators = new BreakIterator[BI_LIMIT];
519         breakIterators[type] = (BreakIterator) iterator.clone(); // clone for safety
520         return this;
521     }
522 
523     /**
524      * Get the display name for an ID: language, script, territory, currency, timezone...
525      * Uses the language priority list to do so.
526      *
527      * @param id language code, script code, ...
528      * @param type specifies the type of the ID: ID_LANGUAGE, etc.
529      * @return the display name
530      * @hide draft / provisional / internal are hidden on OHOS
531      */
getDisplayName(String id, int type)532     public String getDisplayName(String id, int type) {
533         String result = id;
534         for (ULocale locale : getLocales()) {
535             if (!isAvailableLocale(locale, TYPE_GENERIC)) {
536                 continue;
537             }
538             switch (type) {
539             case ID_LOCALE:
540                 result = ULocale.getDisplayName(id, locale);
541                 break;
542             case ID_LANGUAGE:
543                 result = ULocale.getDisplayLanguage(id, locale);
544                 break;
545             case ID_SCRIPT:
546                 result = ULocale.getDisplayScript("und-" + id, locale);
547                 break;
548             case ID_TERRITORY:
549                 result = ULocale.getDisplayCountry("und-" + id, locale);
550                 break;
551             case ID_VARIANT:
552                 // TODO fix variant parsing
553                 result = ULocale.getDisplayVariant("und-QQ-" + id, locale);
554                 break;
555             case ID_KEYWORD:
556                 result = ULocale.getDisplayKeyword(id, locale);
557                 break;
558             case ID_KEYWORD_VALUE:
559                 String[] parts = new String[2];
560                 Utility.split(id,'=',parts);
561                 result = ULocale.getDisplayKeywordValue("und@"+id, parts[0], locale);
562                 // TODO fix to tell when successful
563                 if (result.equals(parts[1])) {
564                     continue;
565                 }
566                 break;
567             case ID_CURRENCY_SYMBOL:
568             case ID_CURRENCY:
569                 Currency temp = new Currency(id);
570                 result =temp.getName(locale, type==ID_CURRENCY
571                                      ? Currency.LONG_NAME
572                                      : Currency.SYMBOL_NAME, null /* isChoiceFormat */);
573                 // TODO: have method that doesn't take parameter. Add
574                 // function to determine whether string is choice
575                 // format.
576                 // TODO: have method that doesn't require us
577                 // to create a currency
578                 break;
579             case ID_TIMEZONE:
580                 SimpleDateFormat dtf = new SimpleDateFormat("vvvv",locale);
581                 dtf.setTimeZone(TimeZone.getFrozenTimeZone(id));
582                 result = dtf.format(new Date());
583                 // TODO, have method that doesn't require us to create a timezone
584                 // fix other hacks
585                 // hack for couldn't match
586 
587                 boolean isBadStr = false;
588                 // Matcher badTimeZone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher("");
589                 // badtzstr = badTimeZone.reset(result).matches();
590                 String teststr = result;
591                 int sidx = result.indexOf('(');
592                 int eidx = result.indexOf(')');
593                 if (sidx != -1 && eidx != -1 && (eidx - sidx) == 3) {
594                     teststr = result.substring(sidx+1, eidx);
595                 }
596                 if (teststr.length() == 2) {
597                     isBadStr = true;
598                     for (int i = 0; i < 2; i++) {
599                         char c = teststr.charAt(i);
600                         if (c < 'A' || 'Z' < c) {
601                             isBadStr = false;
602                             break;
603                         }
604                     }
605                 }
606                 if (isBadStr) {
607                     continue;
608                 }
609                 break;
610             default:
611                 throw new IllegalArgumentException("Unknown type: " + type);
612             }
613 
614             // TODO need better way of seeing if we fell back to root!!
615             // This will not work at all for lots of stuff
616             if (!id.equals(result)) {
617                 return result;
618             }
619         }
620         return result;
621     }
622 
623     /**
624      * Set an explicit date format. Overrides the locale priority list for
625      * a particular combination of dateStyle and timeStyle. DF_NONE should
626      * be used if for the style, where only the date or time format individually
627      * is being set.
628      *
629      * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
630      * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
631      * @param format The date format
632      * @return this, for chaining
633      * @hide draft / provisional / internal are hidden on OHOS
634      */
setDateFormat(int dateStyle, int timeStyle, DateFormat format)635     public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) {
636         if (isFrozen()) {
637             throw new UnsupportedOperationException("Attempt to modify immutable object");
638         }
639         if (dateFormats == null) {
640             dateFormats = new DateFormat[DF_LIMIT][DF_LIMIT];
641         }
642         dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety
643         return this;
644     }
645 
646     /**
647      * Gets a date format according to the current settings. If there
648      * is an explicit (non-null) date/time format set, a copy of that
649      * is returned. Otherwise, the language priority list is used.
650      * DF_NONE should be used for the style, where only the date or
651      * time format individually is being gotten.
652      *
653      * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
654      * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
655      * @return a DateFormat, according to the above description
656      * @hide draft / provisional / internal are hidden on OHOS
657      */
getDateFormat(int dateStyle, int timeStyle)658     public DateFormat getDateFormat(int dateStyle, int timeStyle) {
659         if (dateStyle == DF_NONE && timeStyle == DF_NONE
660                 || dateStyle < 0 || dateStyle >= DF_LIMIT
661                 || timeStyle < 0 || timeStyle >= DF_LIMIT) {
662             throw new IllegalArgumentException("Illegal date format style arguments");
663         }
664         DateFormat result = null;
665         if (dateFormats != null) {
666             result = dateFormats[dateStyle][timeStyle];
667         }
668         if (result != null) {
669             result = (DateFormat) result.clone(); // clone for safety
670             // Not sure overriding configuration is what we really want...
671             result.setTimeZone(getTimeZone());
672         } else {
673             result = guessDateFormat(dateStyle, timeStyle);
674         }
675         return result;
676     }
677 
678     /**
679      * Gets a number format according to the current settings.  If
680      * there is an explicit (non-null) number format set, a copy of
681      * that is returned.  Otherwise, the language priority list is
682      * used.
683      *
684      * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER
685      * @hide draft / provisional / internal are hidden on OHOS
686      */
getNumberFormat(int style)687     public NumberFormat getNumberFormat(int style) {
688         if (style < 0 || style >= NF_LIMIT) {
689             throw new IllegalArgumentException("Illegal number format type");
690         }
691         NumberFormat result = null;
692         if (numberFormats != null) {
693             result = numberFormats[style];
694         }
695         if (result != null) {
696             result = (NumberFormat) result.clone(); // clone for safety (later optimize)
697         } else {
698             result = guessNumberFormat(style);
699         }
700         return result;
701     }
702 
703     /**
704      * Sets a number format explicitly. Overrides the general locale settings.
705      *
706      * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER
707      * @param format The number format
708      * @return this, for chaining
709      * @hide draft / provisional / internal are hidden on OHOS
710      */
setNumberFormat(int style, NumberFormat format)711     public GlobalizationPreferences setNumberFormat(int style, NumberFormat format) {
712         if (isFrozen()) {
713             throw new UnsupportedOperationException("Attempt to modify immutable object");
714         }
715         if (numberFormats == null) {
716             numberFormats = new NumberFormat[NF_LIMIT];
717         }
718         numberFormats[style] = (NumberFormat) format.clone(); // for safety
719         return this;
720     }
721 
722     /**
723      * Restore the object to the initial state.
724      *
725      * @return this, for chaining
726      * @hide draft / provisional / internal are hidden on OHOS
727      */
reset()728     public GlobalizationPreferences reset() {
729         if (isFrozen()) {
730             throw new UnsupportedOperationException("Attempt to modify immutable object");
731         }
732         locales = null;
733         territory = null;
734         calendar = null;
735         collator = null;
736         breakIterators = null;
737         timezone = null;
738         currency = null;
739         dateFormats = null;
740         numberFormats = null;
741         implicitLocales = null;
742         return this;
743     }
744 
745     /**
746      * Process a language/locale priority list specified via <code>setLocales</code>.
747      * The input locale list may be expanded or re-ordered to represent the prioritized
748      * language/locale order actually used by this object by the algorithm explained
749      * below.
750      * <br>
751      * <br>
752      * <b>Step 1</b>: Move later occurrence of more specific locale before earlier
753      * occurrence of less specific locale.
754      * <br>
755      * Before: en, fr_FR, en_US, en_GB
756      * <br>
757      * After: en_US, en_GB, en, fr_FR
758      * <br>
759      * <br>
760      * <b>Step 2</b>: Append a fallback locale to each locale.
761      * <br>
762      * Before: en_US, en_GB, en, fr_FR
763      * <br>
764      * After: en_US, en, en_GB, en, en, fr_FR, fr
765      * <br>
766      * <br>
767      * <b>Step 3</b>: Remove earlier occurrence of duplicated locale entries.
768      * <br>
769      * Before: en_US, en, en_GB, en, en, fr_FR, fr
770      * <br>
771      * After: en_US, en_GB, en, fr_FR, fr
772      * <br>
773      * <br>
774      * The final locale list is used to produce a default value for the appropriate territory,
775      * currency, timezone, etc.  The list also represents the lookup order used in
776      * <code>getResourceBundle</code> for this object.  A subclass may override this method
777      * to customize the algorithm used for populating the locale list.
778      *
779      * @param inputLocales The list of input locales
780      * @hide draft / provisional / internal are hidden on OHOS
781      */
processLocales(List<ULocale> inputLocales)782     protected List<ULocale> processLocales(List<ULocale> inputLocales) {
783         // Note: Some of the callers, and non-ICU call sites, could be simpler/more efficient
784         // if this method took a Collection or even an Iterable.
785         // Maybe we can change it since this is still @draft and probably not widely overridden.
786         List<ULocale> result = new ArrayList<>();
787         /*
788          * Step 1: Relocate later occurrence of more specific locale
789          * before earlier occurrence of less specific locale.
790          *
791          * Example:
792          *   Before - en_US, fr_FR, zh, en_US_Boston, zh_TW, zh_Hant, fr_CA
793          *   After  - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
794          */
795         for (ULocale uloc : inputLocales) {
796             String language = uloc.getLanguage();
797             String script = uloc.getScript();
798             String country = uloc.getCountry();
799             String variant = uloc.getVariant();
800 
801             boolean bInserted = false;
802             for (int j = 0; j < result.size(); j++) {
803                 // Check if this locale is more specific
804                 // than existing locale entries already inserted
805                 // in the destination list
806                 ULocale u = result.get(j);
807                 if (!u.getLanguage().equals(language)) {
808                     continue;
809                 }
810                 String s = u.getScript();
811                 String c = u.getCountry();
812                 String v = u.getVariant();
813                 if (!s.equals(script)) {
814                     if (s.length() == 0 && c.length() == 0 && v.length() == 0) {
815                         result.add(j, uloc);
816                         bInserted = true;
817                         break;
818                     } else if (s.length() == 0 && c.equals(country)) {
819                         // We want to see zh_Hant_HK before zh_HK
820                         result.add(j, uloc);
821                         bInserted = true;
822                         break;
823                     } else if (script.length() == 0 && country.length() > 0 && c.length() == 0) {
824                         // We want to see zh_HK before zh_Hant
825                         result.add(j, uloc);
826                         bInserted = true;
827                         break;
828                     }
829                     continue;
830                 }
831                 if (!c.equals(country)) {
832                     if (c.length() == 0 && v.length() == 0) {
833                         result.add(j, uloc);
834                         bInserted = true;
835                         break;
836                     }
837                 }
838                 if (!v.equals(variant) && v.length() == 0) {
839                     result.add(j, uloc);
840                     bInserted = true;
841                     break;
842                 }
843             }
844             if (!bInserted) {
845                 // Add this locale at the end of the list
846                 result.add(uloc);
847             }
848         }
849 
850         // TODO: Locale aliases might be resolved here
851         // For example, zh_Hant_TW = zh_TW
852 
853         /*
854          * Step 2: Append fallback locales for each entry
855          *
856          * Example:
857          *   Before - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
858          *   After  - en_US_Boston, en_US, en, en_US, en, fr_FR, fr,
859          *            zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr
860          */
861         int index = 0;
862         while (index < result.size()) {
863             ULocale uloc = result.get(index);
864             while ((uloc = uloc.getFallback()) != null) {
865                 if (uloc.getLanguage().length() == 0) {
866                     break;
867                 }
868                 index++;
869                 result.add(index, uloc);
870             }
871             index++;
872         }
873 
874         /*
875          * Step 3: Remove earlier occurrence of duplicated locales
876          *
877          * Example:
878          *   Before - en_US_Boston, en_US, en, en_US, en, fr_FR, fr,
879          *            zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr
880          *   After  - en_US_Boston, en_US, en, fr_FR, zh_TW, zh_Hant,
881          *            zh, fr_CA, fr
882          */
883         index = 0;
884         while (index < result.size() - 1) {
885             ULocale uloc = result.get(index);
886             boolean bRemoved = false;
887             for (int i = index + 1; i < result.size(); i++) {
888                 if (uloc.equals(result.get(i))) {
889                     // Remove earlier one
890                     result.remove(index);
891                     bRemoved = true;
892                     break;
893                 }
894             }
895             if (!bRemoved) {
896                 index++;
897             }
898         }
899         return result;
900     }
901 
902 
903     /**
904      * This function can be overridden by subclasses to use different heuristics.
905      * <b>It MUST return a 'safe' value,
906      * one whose modification will not affect this object.</b>
907      *
908      * @param dateStyle
909      * @param timeStyle
910      * @hide draft / provisional / internal are hidden on OHOS
911      */
guessDateFormat(int dateStyle, int timeStyle)912     protected DateFormat guessDateFormat(int dateStyle, int timeStyle) {
913         DateFormat result;
914         ULocale dfLocale = getAvailableLocale(TYPE_DATEFORMAT);
915         if (dfLocale == null) {
916             dfLocale = ULocale.ROOT;
917         }
918         if (timeStyle == DF_NONE) {
919             result = DateFormat.getDateInstance(getCalendar(), dateStyle, dfLocale);
920         } else if (dateStyle == DF_NONE) {
921             result = DateFormat.getTimeInstance(getCalendar(), timeStyle, dfLocale);
922         } else {
923             result = DateFormat.getDateTimeInstance(getCalendar(), dateStyle, timeStyle, dfLocale);
924         }
925         return result;
926     }
927 
928     /**
929      * This function can be overridden by subclasses to use different heuristics.
930      * <b>It MUST return a 'safe' value,
931      * one whose modification will not affect this object.</b>
932      *
933      * @param style
934      * @hide draft / provisional / internal are hidden on OHOS
935      */
guessNumberFormat(int style)936     protected NumberFormat guessNumberFormat(int style) {
937         NumberFormat result;
938         ULocale nfLocale = getAvailableLocale(TYPE_NUMBERFORMAT);
939         if (nfLocale == null) {
940             nfLocale = ULocale.ROOT;
941         }
942         switch (style) {
943         case NF_NUMBER:
944             result = NumberFormat.getInstance(nfLocale);
945             break;
946         case NF_SCIENTIFIC:
947             result = NumberFormat.getScientificInstance(nfLocale);
948             break;
949         case NF_INTEGER:
950             result = NumberFormat.getIntegerInstance(nfLocale);
951             break;
952         case NF_PERCENT:
953             result = NumberFormat.getPercentInstance(nfLocale);
954             break;
955         case NF_CURRENCY:
956             result = NumberFormat.getCurrencyInstance(nfLocale);
957             result.setCurrency(getCurrency());
958             break;
959         default:
960             throw new IllegalArgumentException("Unknown number format style");
961         }
962         return result;
963     }
964 
965     /**
966      * This function can be overridden by subclasses to use different heuristics.
967      *
968      * @hide draft / provisional / internal are hidden on OHOS
969      */
guessTerritory()970     protected String guessTerritory() {
971         String result;
972         // pass through locales to see if there is a territory.
973         for (ULocale locale : getLocales()) {
974             result = locale.getCountry();
975             if (result.length() != 0) {
976                 return result;
977             }
978         }
979         // if not, guess from the first language tag, or maybe from
980         // intersection of languages, eg nl + fr => BE
981         // TODO: fix using real data
982         // for now, just use fixed values
983         ULocale firstLocale = getLocale(0);
984         String language = firstLocale.getLanguage();
985         String script = firstLocale.getScript();
986         result = null;
987         if (script.length() != 0) {
988             result = language_territory_hack_map.get(language + "_" + script);
989         }
990         if (result == null) {
991             result = language_territory_hack_map.get(language);
992         }
993         if (result == null) {
994             result = "US"; // need *some* default
995         }
996         return result;
997     }
998 
999     /**
1000      * This function can be overridden by subclasses to use different heuristics
1001      *
1002      * @hide draft / provisional / internal are hidden on OHOS
1003      */
guessCurrency()1004     protected Currency guessCurrency() {
1005         return Currency.getInstance(new ULocale("und-" + getTerritory()));
1006     }
1007 
1008     /**
1009      * This function can be overridden by subclasses to use different heuristics
1010      * <b>It MUST return a 'safe' value,
1011      * one whose modification will not affect this object.</b>
1012      *
1013      * @hide draft / provisional / internal are hidden on OHOS
1014      */
guessLocales()1015     protected List<ULocale> guessLocales() {
1016         if (implicitLocales == null) {
1017             List<ULocale> result = new ArrayList<>(1);
1018             result.add(ULocale.getDefault());
1019             implicitLocales = processLocales(result);
1020         }
1021         return implicitLocales;
1022     }
1023 
1024     /**
1025      * This function can be overridden by subclasses to use different heuristics.
1026      * <b>It MUST return a 'safe' value,
1027      * one whose modification will not affect this object.</b>
1028      *
1029      * @hide draft / provisional / internal are hidden on OHOS
1030      */
guessCollator()1031     protected Collator guessCollator() {
1032         ULocale collLocale = getAvailableLocale(TYPE_COLLATOR);
1033         if (collLocale == null) {
1034             collLocale = ULocale.ROOT;
1035         }
1036         return Collator.getInstance(collLocale);
1037     }
1038 
1039     /**
1040      * This function can be overridden by subclasses to use different heuristics.
1041      * <b>It MUST return a 'safe' value,
1042      * one whose modification will not affect this object.</b>
1043      *
1044      * @param type
1045      * @hide draft / provisional / internal are hidden on OHOS
1046      */
guessBreakIterator(int type)1047     protected BreakIterator guessBreakIterator(int type) {
1048         BreakIterator bitr = null;
1049         ULocale brkLocale = getAvailableLocale(TYPE_BREAKITERATOR);
1050         if (brkLocale == null) {
1051             brkLocale = ULocale.ROOT;
1052         }
1053         switch (type) {
1054         case BI_CHARACTER:
1055             bitr = BreakIterator.getCharacterInstance(brkLocale);
1056             break;
1057         case BI_TITLE:
1058             bitr = BreakIterator.getTitleInstance(brkLocale);
1059             break;
1060         case BI_WORD:
1061             bitr = BreakIterator.getWordInstance(brkLocale);
1062             break;
1063         case BI_LINE:
1064             bitr = BreakIterator.getLineInstance(brkLocale);
1065             break;
1066         case BI_SENTENCE:
1067             bitr = BreakIterator.getSentenceInstance(brkLocale);
1068             break;
1069         default:
1070             throw new IllegalArgumentException("Unknown break iterator type");
1071         }
1072         return bitr;
1073     }
1074 
1075     /**
1076      * This function can be overridden by subclasses to use different heuristics.
1077      * <b>It MUST return a 'safe' value,
1078      * one whose modification will not affect this object.</b>
1079      *
1080      * @hide draft / provisional / internal are hidden on OHOS
1081      */
guessTimeZone()1082     protected TimeZone guessTimeZone() {
1083         // TODO fix using real data
1084         // for single-zone countries, pick that zone
1085         // for others, pick the most populous zone
1086         // for now, just use fixed value
1087         // NOTE: in a few cases can do better by looking at language.
1088         // Eg haw+US should go to Pacific/Honolulu
1089         // fr+CA should go to America/Montreal
1090         String timezoneString = territory_tzid_hack_map.get(getTerritory());
1091         if (timezoneString == null) {
1092             String[] attempt = TimeZone.getAvailableIDs(getTerritory());
1093             if (attempt.length == 0) {
1094                 timezoneString = "Etc/GMT"; // gotta do something
1095             } else {
1096                 int i;
1097                 // this all needs to be fixed to use real data. But for now, do slightly better by skipping cruft
1098                 for (i = 0; i < attempt.length; ++i) {
1099                     if (attempt[i].indexOf("/") >= 0) break;
1100                 }
1101                 if (i > attempt.length) i = 0;
1102                 timezoneString = attempt[i];
1103             }
1104         }
1105         return TimeZone.getTimeZone(timezoneString);
1106     }
1107 
1108     /**
1109      * This function can be overridden by subclasses to use different heuristics.
1110      * <b>It MUST return a 'safe' value,
1111      * one whose modification will not affect this object.</b>
1112      *
1113      * @hide draft / provisional / internal are hidden on OHOS
1114      */
guessCalendar()1115     protected Calendar guessCalendar() {
1116         ULocale calLocale = getAvailableLocale(TYPE_CALENDAR);
1117         if (calLocale == null) {
1118             calLocale = ULocale.US;
1119         }
1120         return Calendar.getInstance(getTimeZone(), calLocale);
1121     }
1122 
1123     // PRIVATES
1124 
1125     private List<ULocale> locales;
1126     private String territory;
1127     private Currency currency;
1128     private TimeZone timezone;
1129     private Calendar calendar;
1130     private Collator collator;
1131     private BreakIterator[] breakIterators;
1132     private DateFormat[][] dateFormats;
1133     private NumberFormat[] numberFormats;
1134     private List<ULocale> implicitLocales;
1135 
1136     {
reset()1137         reset();
1138     }
1139 
1140 
getAvailableLocale(int type)1141     private ULocale getAvailableLocale(int type) {
1142         List<ULocale> locs = getLocales();
1143         ULocale result = null;
1144         for (int i = 0; i < locs.size(); i++) {
1145             ULocale l = locs.get(i);
1146             if (isAvailableLocale(l, type)) {
1147                 result = l;
1148                 break;
1149             }
1150         }
1151         return result;
1152     }
1153 
isAvailableLocale(ULocale loc, int type)1154     private boolean isAvailableLocale(ULocale loc, int type) {
1155         BitSet bits = available_locales.get(loc);
1156         if (bits != null && bits.get(type)) {
1157             return true;
1158         }
1159         return false;
1160     }
1161 
1162     /*
1163      * Available locales for service types
1164      */
1165     private static final HashMap<ULocale, BitSet> available_locales = new HashMap<>();
1166     private static final int
1167         TYPE_GENERIC = 0,
1168         TYPE_CALENDAR = 1,
1169         TYPE_DATEFORMAT= 2,
1170         TYPE_NUMBERFORMAT = 3,
1171         TYPE_COLLATOR = 4,
1172         TYPE_BREAKITERATOR = 5,
1173         TYPE_LIMIT = TYPE_BREAKITERATOR + 1;
1174 
1175     static {
1176         BitSet bits;
1177         ULocale[] allLocales = ULocale.getAvailableLocales();
1178         for (int i = 0; i < allLocales.length; i++) {
1179             bits = new BitSet(TYPE_LIMIT);
available_locales.put(allLocales[i], bits)1180             available_locales.put(allLocales[i], bits);
1181             bits.set(TYPE_GENERIC);
1182         }
1183 
1184         ULocale[] calLocales = Calendar.getAvailableULocales();
1185         for (int i = 0; i < calLocales.length; i++) {
1186             bits = available_locales.get(calLocales[i]);
1187             if (bits == null) {
1188                 bits = new BitSet(TYPE_LIMIT);
available_locales.put(allLocales[i], bits)1189                 available_locales.put(allLocales[i], bits);
1190             }
1191             bits.set(TYPE_CALENDAR);
1192         }
1193 
1194         ULocale[] dateLocales = DateFormat.getAvailableULocales();
1195         for (int i = 0; i < dateLocales.length; i++) {
1196             bits = available_locales.get(dateLocales[i]);
1197             if (bits == null) {
1198                 bits = new BitSet(TYPE_LIMIT);
available_locales.put(allLocales[i], bits)1199                 available_locales.put(allLocales[i], bits);
1200             }
1201             bits.set(TYPE_DATEFORMAT);
1202         }
1203 
1204         ULocale[] numLocales = NumberFormat.getAvailableULocales();
1205         for (int i = 0; i < numLocales.length; i++) {
1206             bits = available_locales.get(numLocales[i]);
1207             if (bits == null) {
1208                 bits = new BitSet(TYPE_LIMIT);
available_locales.put(allLocales[i], bits)1209                 available_locales.put(allLocales[i], bits);
1210             }
1211             bits.set(TYPE_NUMBERFORMAT);
1212         }
1213 
1214         ULocale[] collLocales = Collator.getAvailableULocales();
1215         for (int i = 0; i < collLocales.length; i++) {
1216             bits = available_locales.get(collLocales[i]);
1217             if (bits == null) {
1218                 bits = new BitSet(TYPE_LIMIT);
available_locales.put(allLocales[i], bits)1219                 available_locales.put(allLocales[i], bits);
1220             }
1221             bits.set(TYPE_COLLATOR);
1222         }
1223 
1224         ULocale[] brkLocales = BreakIterator.getAvailableULocales();
1225         for (int i = 0; i < brkLocales.length; i++) {
1226             bits = available_locales.get(brkLocales[i]);
1227             bits.set(TYPE_BREAKITERATOR);
1228         }
1229     }
1230 
1231     /** WARNING: All of this data is temporary, until we start importing from CLDR!!!
1232      *
1233      */
1234     private static final Map<String, String> language_territory_hack_map = new HashMap<>();
1235     private static final String[][] language_territory_hack = {
1236         {"af", "ZA"},
1237         {"am", "ET"},
1238         {"ar", "SA"},
1239         {"as", "IN"},
1240         {"ay", "PE"},
1241         {"az", "AZ"},
1242         {"bal", "PK"},
1243         {"be", "BY"},
1244         {"bg", "BG"},
1245         {"bn", "IN"},
1246         {"bs", "BA"},
1247         {"ca", "ES"},
1248         {"ch", "MP"},
1249         {"cpe", "SL"},
1250         {"cs", "CZ"},
1251         {"cy", "GB"},
1252         {"da", "DK"},
1253         {"de", "DE"},
1254         {"dv", "MV"},
1255         {"dz", "BT"},
1256         {"el", "GR"},
1257         {"en", "US"},
1258         {"es", "ES"},
1259         {"et", "EE"},
1260         {"eu", "ES"},
1261         {"fa", "IR"},
1262         {"fi", "FI"},
1263         {"fil", "PH"},
1264         {"fj", "FJ"},
1265         {"fo", "FO"},
1266         {"fr", "FR"},
1267         {"ga", "IE"},
1268         {"gd", "GB"},
1269         {"gl", "ES"},
1270         {"gn", "PY"},
1271         {"gu", "IN"},
1272         {"gv", "GB"},
1273         {"ha", "NG"},
1274         {"he", "IL"},
1275         {"hi", "IN"},
1276         {"ho", "PG"},
1277         {"hr", "HR"},
1278         {"ht", "HT"},
1279         {"hu", "HU"},
1280         {"hy", "AM"},
1281         {"id", "ID"},
1282         {"is", "IS"},
1283         {"it", "IT"},
1284         {"ja", "JP"},
1285         {"ka", "GE"},
1286         {"kk", "KZ"},
1287         {"kl", "GL"},
1288         {"km", "KH"},
1289         {"kn", "IN"},
1290         {"ko", "KR"},
1291         {"kok", "IN"},
1292         {"ks", "IN"},
1293         {"ku", "TR"},
1294         {"ky", "KG"},
1295         {"la", "VA"},
1296         {"lb", "LU"},
1297         {"ln", "CG"},
1298         {"lo", "LA"},
1299         {"lt", "LT"},
1300         {"lv", "LV"},
1301         {"mai", "IN"},
1302         {"men", "GN"},
1303         {"mg", "MG"},
1304         {"mh", "MH"},
1305         {"mk", "MK"},
1306         {"ml", "IN"},
1307         {"mn", "MN"},
1308         {"mni", "IN"},
1309         {"mo", "MD"},
1310         {"mr", "IN"},
1311         {"ms", "MY"},
1312         {"mt", "MT"},
1313         {"my", "MM"},
1314         {"na", "NR"},
1315         {"nb", "NO"},
1316         {"nd", "ZA"},
1317         {"ne", "NP"},
1318         {"niu", "NU"},
1319         {"nl", "NL"},
1320         {"nn", "NO"},
1321         {"no", "NO"},
1322         {"nr", "ZA"},
1323         {"nso", "ZA"},
1324         {"ny", "MW"},
1325         {"om", "KE"},
1326         {"or", "IN"},
1327         {"pa", "IN"},
1328         {"pau", "PW"},
1329         {"pl", "PL"},
1330         {"ps", "PK"},
1331         {"pt", "BR"},
1332         {"qu", "PE"},
1333         {"rn", "BI"},
1334         {"ro", "RO"},
1335         {"ru", "RU"},
1336         {"rw", "RW"},
1337         {"sd", "IN"},
1338         {"sg", "CF"},
1339         {"si", "LK"},
1340         {"sk", "SK"},
1341         {"sl", "SI"},
1342         {"sm", "WS"},
1343         {"so", "DJ"},
1344         {"sq", "CS"},
1345         {"sr", "CS"},
1346         {"ss", "ZA"},
1347         {"st", "ZA"},
1348         {"sv", "SE"},
1349         {"sw", "KE"},
1350         {"ta", "IN"},
1351         {"te", "IN"},
1352         {"tem", "SL"},
1353         {"tet", "TL"},
1354         {"th", "TH"},
1355         {"ti", "ET"},
1356         {"tg", "TJ"},
1357         {"tk", "TM"},
1358         {"tkl", "TK"},
1359         {"tvl", "TV"},
1360         {"tl", "PH"},
1361         {"tn", "ZA"},
1362         {"to", "TO"},
1363         {"tpi", "PG"},
1364         {"tr", "TR"},
1365         {"ts", "ZA"},
1366         {"uk", "UA"},
1367         {"ur", "IN"},
1368         {"uz", "UZ"},
1369         {"ve", "ZA"},
1370         {"vi", "VN"},
1371         {"wo", "SN"},
1372         {"xh", "ZA"},
1373         {"zh", "CN"},
1374         {"zh_Hant", "TW"},
1375         {"zu", "ZA"},
1376         {"aa", "ET"},
1377         {"byn", "ER"},
1378         {"eo", "DE"},
1379         {"gez", "ET"},
1380         {"haw", "US"},
1381         {"iu", "CA"},
1382         {"kw", "GB"},
1383         {"sa", "IN"},
1384         {"sh", "HR"},
1385         {"sid", "ET"},
1386         {"syr", "SY"},
1387         {"tig", "ER"},
1388         {"tt", "RU"},
1389         {"wal", "ET"},  };
1390     static {
1391         for (int i = 0; i < language_territory_hack.length; ++i) {
language_territory_hack_map.put(language_territory_hack[i][0],language_territory_hack[i][1])1392             language_territory_hack_map.put(language_territory_hack[i][0],language_territory_hack[i][1]);
1393         }
1394     }
1395 
1396     static final Map<String, String> territory_tzid_hack_map = new HashMap<>();
1397     static final String[][] territory_tzid_hack = {
1398         {"AQ", "Antarctica/McMurdo"},
1399         {"AR", "America/Buenos_Aires"},
1400         {"AU", "Australia/Sydney"},
1401         {"BR", "America/Sao_Paulo"},
1402         {"CA", "America/Toronto"},
1403         {"CD", "Africa/Kinshasa"},
1404         {"CL", "America/Santiago"},
1405         {"CN", "Asia/Shanghai"},
1406         {"EC", "America/Guayaquil"},
1407         {"ES", "Europe/Madrid"},
1408         {"GB", "Europe/London"},
1409         {"GL", "America/Godthab"},
1410         {"ID", "Asia/Jakarta"},
1411         {"ML", "Africa/Bamako"},
1412         {"MX", "America/Mexico_City"},
1413         {"MY", "Asia/Kuala_Lumpur"},
1414         {"NZ", "Pacific/Auckland"},
1415         {"PT", "Europe/Lisbon"},
1416         {"RU", "Europe/Moscow"},
1417         {"UA", "Europe/Kiev"},
1418         {"US", "America/New_York"},
1419         {"UZ", "Asia/Tashkent"},
1420         {"PF", "Pacific/Tahiti"},
1421         {"FM", "Pacific/Kosrae"},
1422         {"KI", "Pacific/Tarawa"},
1423         {"KZ", "Asia/Almaty"},
1424         {"MH", "Pacific/Majuro"},
1425         {"MN", "Asia/Ulaanbaatar"},
1426         {"SJ", "Arctic/Longyearbyen"},
1427         {"UM", "Pacific/Midway"},
1428     };
1429     static {
1430         for (int i = 0; i < territory_tzid_hack.length; ++i) {
territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1])1431             territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1]);
1432         }
1433     }
1434 
1435     // Freezable implementation
1436 
1437     private volatile boolean frozen;
1438 
1439     /**
1440      * @hide draft / provisional / internal are hidden on OHOS
1441      */
1442     @Override
isFrozen()1443     public boolean isFrozen() {
1444         return frozen;
1445     }
1446 
1447     /**
1448      * @hide draft / provisional / internal are hidden on OHOS
1449      */
1450     @Override
freeze()1451     public GlobalizationPreferences freeze() {
1452         frozen = true;
1453         return this;
1454     }
1455 
1456     /**
1457      * @hide draft / provisional / internal are hidden on OHOS
1458      */
1459     @Override
cloneAsThawed()1460     public GlobalizationPreferences cloneAsThawed() {
1461         try {
1462             GlobalizationPreferences result = (GlobalizationPreferences) clone();
1463             result.frozen = false;
1464             return result;
1465         } catch (CloneNotSupportedException e) {
1466             // will always work
1467             return null;
1468         }
1469     }
1470 }
1471 
1472