1 /*
2  ******************************************************************************
3  * Copyright (C) 2003-2015, International Business Machines Corporation and
4  * others. All Rights Reserved.
5  ******************************************************************************
6  */
7 
8 package com.ibm.icu.util;
9 
10 import java.io.Serializable;
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.security.AccessControlException;
14 import java.security.AccessController;
15 import java.security.PrivilegedAction;
16 import java.text.ParseException;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.MissingResourceException;
23 import java.util.Set;
24 import java.util.TreeMap;
25 import java.util.TreeSet;
26 
27 import com.ibm.icu.impl.ICUCache;
28 import com.ibm.icu.impl.ICUResourceBundle;
29 import com.ibm.icu.impl.ICUResourceTableAccess;
30 import com.ibm.icu.impl.LocaleIDParser;
31 import com.ibm.icu.impl.LocaleIDs;
32 import com.ibm.icu.impl.LocaleUtility;
33 import com.ibm.icu.impl.SimpleCache;
34 import com.ibm.icu.impl.locale.AsciiUtil;
35 import com.ibm.icu.impl.locale.BaseLocale;
36 import com.ibm.icu.impl.locale.Extension;
37 import com.ibm.icu.impl.locale.InternalLocaleBuilder;
38 import com.ibm.icu.impl.locale.KeyTypeData;
39 import com.ibm.icu.impl.locale.LanguageTag;
40 import com.ibm.icu.impl.locale.LocaleExtensions;
41 import com.ibm.icu.impl.locale.LocaleSyntaxException;
42 import com.ibm.icu.impl.locale.ParseStatus;
43 import com.ibm.icu.impl.locale.UnicodeLocaleExtension;
44 import com.ibm.icu.lang.UScript;
45 import com.ibm.icu.text.LocaleDisplayNames;
46 import com.ibm.icu.text.LocaleDisplayNames.DialectHandling;
47 
48 /**
49  * {@icuenhanced java.util.Locale}.{@icu _usage_}
50  *
51  * A class analogous to {@link java.util.Locale} that provides additional
52  * support for ICU protocol.  In ICU 3.0 this class is enhanced to support
53  * RFC 3066 language identifiers.
54  *
55  * <p>Many classes and services in ICU follow a factory idiom, in
56  * which a factory method or object responds to a client request with
57  * an object.  The request includes a locale (the <i>requested</i>
58  * locale), and the returned object is constructed using data for that
59  * locale.  The system may lack data for the requested locale, in
60  * which case the locale fallback mechanism will be invoked until a
61  * populated locale is found (the <i>valid</i> locale).  Furthermore,
62  * even when a populated locale is found (the <i>valid</i> locale),
63  * further fallback may be required to reach a locale containing the
64  * specific data required by the service (the <i>actual</i> locale).
65  *
66  * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
67  * Normalization 'cleans up' ICU locale ids as follows:
68  * <ul>
69  * <li>language, script, country, variant, and keywords are properly cased<br>
70  * (lower, title, upper, upper, and lower case respectively)</li>
71  * <li>hyphens used as separators are converted to underscores</li>
72  * <li>three-letter language and country ids are converted to two-letter
73  * equivalents where available</li>
74  * <li>surrounding spaces are removed from keywords and values</li>
75  * <li>if there are multiple keywords, they are put in sorted order</li>
76  * </ul>
77  * Canonicalization additionally performs the following:
78  * <ul>
79  * <li>POSIX ids are converted to ICU format IDs</li>
80  * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
81  * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form,
82  * with the currency
83  * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
84  * </ul>
85  * All ULocale constructors automatically normalize the locale id.  To handle
86  * POSIX ids, <code>canonicalize</code> can be called to convert the id
87  * to canonical form, or the <code>canonicalInstance</code> factory method
88  * can be called.</p>
89  *
90  * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
91  * #ACTUAL_LOCALE} intended for use in methods named
92  * <tt>getLocale()</tt>.  These methods exist in several ICU classes,
93  * including {@link com.ibm.icu.util.Calendar}, {@link
94  * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
95  * {@link com.ibm.icu.text.BreakIterator},
96  * <a href="../text/Collator.html" title="class in com.ibm.icu.text"><code>Collator</code></a>,
97  * {@link com.ibm.icu.text.DateFormatSymbols}, and {@link
98  * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
99  * any.  Once an object of one of these classes has been created,
100  * <tt>getLocale()</tt> may be called on it to determine the valid and
101  * actual locale arrived at during the object's construction.
102  *
103  * <p>Note: The <i>actual</i> locale is returned correctly, but the <i>valid</i>
104  * locale is not, in most cases.
105  *
106  * @see java.util.Locale
107  * @author weiv
108  * @author Alan Liu
109  * @author Ram Viswanadha
110  * @stable ICU 2.8
111  */
112 public final class ULocale implements Serializable, Comparable<ULocale> {
113     // using serialver from jdk1.4.2_05
114     private static final long serialVersionUID = 3715177670352309217L;
115 
116     private static ICUCache<String, String> nameCache = new SimpleCache<String, String>();
117 
118     /**
119      * Useful constant for language.
120      * @stable ICU 3.0
121      */
122     public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
123 
124     /**
125      * Useful constant for language.
126      * @stable ICU 3.0
127      */
128     public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
129 
130     /**
131      * Useful constant for language.
132      * @stable ICU 3.0
133      */
134     public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
135 
136     /**
137      * Useful constant for language.
138      * @stable ICU 3.0
139      */
140     public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
141 
142     /**
143      * Useful constant for language.
144      * @stable ICU 3.0
145      */
146     public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
147 
148     /**
149      * Useful constant for language.
150      * @stable ICU 3.0
151      */
152     public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
153 
154     /**
155      * Useful constant for language.
156      * @stable ICU 3.0
157      */
158     public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
159 
160 
161     // Special note about static initializer for
162     //   - SIMPLIFIED_CHINESE
163     //   - TRADTIONAL_CHINESE
164     //   - CHINA
165     //   - TAIWAN
166     //
167     // Equivalent JDK Locale for ULocale.SIMPLIFIED_CHINESE is different
168     // by JRE version. JRE 7 or later supports a script tag "Hans", while
169     // JRE 6 or older does not. JDK's Locale.SIMPLIFIED_CHINESE is actually
170     // zh_CN, not zh_Hans. This is same in Java 7 or later versions.
171     //
172     // ULocale#toLocale() implementation uses Java reflection to create a Locale
173     // with a script tag. When a new ULocale is constructed with the single arg
174     // constructor, the volatile field 'Locale locale' is initialized by
175     // #toLocale() method.
176     //
177     // Because we cannot hardcode corresponding JDK Locale representation below,
178     // SIMPLIFIED_CHINESE is constructed without JDK Locale argument, and
179     // #toLocale() is used for resolving the best matching JDK Locale at runtime.
180     //
181     // The same thing applies to TRADITIONAL_CHINESE.
182 
183     /**
184      * Useful constant for language.
185      * @stable ICU 3.0
186      */
187     public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans");
188 
189 
190     /**
191      * Useful constant for language.
192      * @stable ICU 3.0
193      */
194     public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant");
195 
196     /**
197      * Useful constant for country/region.
198      * @stable ICU 3.0
199      */
200     public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
201 
202     /**
203      * Useful constant for country/region.
204      * @stable ICU 3.0
205      */
206     public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
207 
208     /**
209      * Useful constant for country/region.
210      * @stable ICU 3.0
211      */
212     public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
213 
214     /**
215      * Useful constant for country/region.
216      * @stable ICU 3.0
217      */
218     public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
219 
220     /**
221      * Useful constant for country/region.
222      * @stable ICU 3.0
223      */
224     public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
225 
226     /**
227      * Useful constant for country/region.
228      * @stable ICU 3.0
229      */
230     public static final ULocale CHINA = new ULocale("zh_Hans_CN");
231 
232     /**
233      * Useful constant for country/region.
234      * @stable ICU 3.0
235      */
236     public static final ULocale PRC = CHINA;
237 
238     /**
239      * Useful constant for country/region.
240      * @stable ICU 3.0
241      */
242     public static final ULocale TAIWAN = new ULocale("zh_Hant_TW");
243 
244     /**
245      * Useful constant for country/region.
246      * @stable ICU 3.0
247      */
248     public static final ULocale UK = new ULocale("en_GB", Locale.UK);
249 
250     /**
251      * Useful constant for country/region.
252      * @stable ICU 3.0
253      */
254     public static final ULocale US = new ULocale("en_US", Locale.US);
255 
256     /**
257      * Useful constant for country/region.
258      * @stable ICU 3.0
259      */
260     public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
261 
262     /**
263      * Useful constant for country/region.
264      * @stable ICU 3.0
265      */
266     public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
267 
268     /**
269      * Handy constant.
270      */
271     private static final String EMPTY_STRING = "";
272 
273     // Used in both ULocale and LocaleIDParser, so moved up here.
274     private static final char UNDERSCORE            = '_';
275 
276     // default empty locale
277     private static final Locale EMPTY_LOCALE = new Locale("", "");
278 
279     // special keyword key for Unicode locale attributes
280     private static final String LOCALE_ATTRIBUTE_KEY = "attribute";
281 
282     /**
283      * The root ULocale.
284      * @stable ICU 2.8
285      */
286     public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
287 
288     /**
289      * Enum for locale categories. These locale categories are used to get/set the default locale for
290      * the specific functionality represented by the category.
291      * @stable ICU 49
292      */
293     public enum Category {
294         /**
295          * Category used to represent the default locale for displaying user interfaces.
296          * @stable ICU 49
297          */
298         DISPLAY,
299         /**
300          * Category used to represent the default locale for formatting date, number and/or currency.
301          * @stable ICU 49
302          */
303         FORMAT
304     }
305 
306     private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>();
307 
308     /**
309      * Cache the locale.
310      */
311     private transient volatile Locale locale;
312 
313     /**
314      * The raw localeID that we were passed in.
315      */
316     private String localeID;
317 
318     /**
319      * Cache the locale data container fields.
320      * In future, we want to use them as the primary locale identifier storage.
321      */
322     private transient volatile BaseLocale baseLocale;
323     private transient volatile LocaleExtensions extensions;
324 
325 
326     private static String[][] CANONICALIZE_MAP;
327     private static String[][] variantsToKeywords;
328 
initCANONICALIZE_MAP()329     private static void initCANONICALIZE_MAP() {
330         if (CANONICALIZE_MAP == null) {
331             /**
332              * This table lists pairs of locale ids for canonicalization.  The
333              * The 1st item is the normalized id. The 2nd item is the
334              * canonicalized id. The 3rd is the keyword. The 4th is the keyword value.
335              */
336             String[][] tempCANONICALIZE_MAP = {
337                     //              { EMPTY_STRING,     "en_US_POSIX", null, null }, /* .NET name */
338                     { "C",              "en_US_POSIX", null, null }, /* POSIX name */
339                     { "art_LOJBAN",     "jbo", null, null }, /* registered name */
340                     { "az_AZ_CYRL",     "az_Cyrl_AZ", null, null }, /* .NET name */
341                     { "az_AZ_LATN",     "az_Latn_AZ", null, null }, /* .NET name */
342                     { "ca_ES_PREEURO",  "ca_ES", "currency", "ESP" },
343                     { "cel_GAULISH",    "cel__GAULISH", null, null }, /* registered name */
344                     { "de_1901",        "de__1901", null, null }, /* registered name */
345                     { "de_1906",        "de__1906", null, null }, /* registered name */
346                     { "de__PHONEBOOK",  "de", "collation", "phonebook" }, /* Old ICU name */
347                     { "de_AT_PREEURO",  "de_AT", "currency", "ATS" },
348                     { "de_DE_PREEURO",  "de_DE", "currency", "DEM" },
349                     { "de_LU_PREEURO",  "de_LU", "currency", "EUR" },
350                     { "el_GR_PREEURO",  "el_GR", "currency", "GRD" },
351                     { "en_BOONT",       "en__BOONT", null, null }, /* registered name */
352                     { "en_SCOUSE",      "en__SCOUSE", null, null }, /* registered name */
353                     { "en_BE_PREEURO",  "en_BE", "currency", "BEF" },
354                     { "en_IE_PREEURO",  "en_IE", "currency", "IEP" },
355                     { "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
356                     { "es_ES_PREEURO",  "es_ES", "currency", "ESP" },
357                     { "eu_ES_PREEURO",  "eu_ES", "currency", "ESP" },
358                     { "fi_FI_PREEURO",  "fi_FI", "currency", "FIM" },
359                     { "fr_BE_PREEURO",  "fr_BE", "currency", "BEF" },
360                     { "fr_FR_PREEURO",  "fr_FR", "currency", "FRF" },
361                     { "fr_LU_PREEURO",  "fr_LU", "currency", "LUF" },
362                     { "ga_IE_PREEURO",  "ga_IE", "currency", "IEP" },
363                     { "gl_ES_PREEURO",  "gl_ES", "currency", "ESP" },
364                     { "hi__DIRECT",     "hi", "collation", "direct" }, /* Old ICU name */
365                     { "it_IT_PREEURO",  "it_IT", "currency", "ITL" },
366                     { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
367                     //              { "nb_NO_NY",       "nn_NO", null, null },
368                     { "nl_BE_PREEURO",  "nl_BE", "currency", "BEF" },
369                     { "nl_NL_PREEURO",  "nl_NL", "currency", "NLG" },
370                     { "pt_PT_PREEURO",  "pt_PT", "currency", "PTE" },
371                     { "sl_ROZAJ",       "sl__ROZAJ", null, null }, /* registered name */
372                     { "sr_SP_CYRL",     "sr_Cyrl_RS", null, null }, /* .NET name */
373                     { "sr_SP_LATN",     "sr_Latn_RS", null, null }, /* .NET name */
374                     { "sr_YU_CYRILLIC", "sr_Cyrl_RS", null, null }, /* Linux name */
375                     { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
376                     { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
377                     { "uz_UZ_CYRL",     "uz_Cyrl_UZ", null, null }, /* .NET name */
378                     { "uz_UZ_LATN",     "uz_Latn_UZ", null, null }, /* .NET name */
379                     { "zh_CHS",         "zh_Hans", null, null }, /* .NET name */
380                     { "zh_CHT",         "zh_Hant", null, null }, /* .NET name */
381                     { "zh_GAN",         "zh__GAN", null, null }, /* registered name */
382                     { "zh_GUOYU",       "zh", null, null }, /* registered name */
383                     { "zh_HAKKA",       "zh__HAKKA", null, null }, /* registered name */
384                     { "zh_MIN",         "zh__MIN", null, null }, /* registered name */
385                     { "zh_MIN_NAN",     "zh__MINNAN", null, null }, /* registered name */
386                     { "zh_WUU",         "zh__WUU", null, null }, /* registered name */
387                     { "zh_XIANG",       "zh__XIANG", null, null }, /* registered name */
388                     { "zh_YUE",         "zh__YUE", null, null } /* registered name */
389             };
390 
391             synchronized (ULocale.class) {
392                 if (CANONICALIZE_MAP == null) {
393                     CANONICALIZE_MAP = tempCANONICALIZE_MAP;
394                 }
395             }
396         }
397         if (variantsToKeywords == null) {
398             /**
399              * This table lists pairs of locale ids for canonicalization.  The
400              * The first item is the normalized variant id.
401              */
402             String[][] tempVariantsToKeywords = {
403                     { "EURO",   "currency", "EUR" },
404                     { "PINYIN", "collation", "pinyin" }, /* Solaris variant */
405                     { "STROKE", "collation", "stroke" }  /* Solaris variant */
406             };
407 
408             synchronized (ULocale.class) {
409                 if (variantsToKeywords == null) {
410                     variantsToKeywords = tempVariantsToKeywords;
411                 }
412             }
413         }
414     }
415 
416     /**
417      * Private constructor used by static initializers.
418      */
ULocale(String localeID, Locale locale)419     private ULocale(String localeID, Locale locale) {
420         this.localeID = localeID;
421         this.locale = locale;
422     }
423 
424     /**
425      * Construct a ULocale object from a {@link java.util.Locale}.
426      * @param loc a JDK locale
427      */
ULocale(Locale loc)428     private ULocale(Locale loc) {
429         this.localeID = getName(forLocale(loc).toString());
430         this.locale = loc;
431     }
432 
433     /**
434      * {@icu} Returns a ULocale object for a {@link java.util.Locale}.
435      * The ULocale is canonicalized.
436      * @param loc a JDK locale
437      * @stable ICU 3.2
438      */
forLocale(Locale loc)439     public static ULocale forLocale(Locale loc) {
440         if (loc == null) {
441             return null;
442         }
443         ULocale result = CACHE.get(loc);
444         if (result == null) {
445             result = JDKLocaleHelper.toULocale(loc);
446             CACHE.put(loc, result);
447         }
448         return result;
449     }
450 
451     /**
452      * {@icu} Constructs a ULocale from a RFC 3066 locale ID. The locale ID consists
453      * of optional language, script, country, and variant fields in that order,
454      * separated by underscores, followed by an optional keyword list.  The
455      * script, if present, is four characters long-- this distinguishes it
456      * from a country code, which is two characters long.  Other fields
457      * are distinguished by position as indicated by the underscores.  The
458      * start of the keyword list is indicated by '@', and consists of two
459      * or more keyword/value pairs separated by semicolons(';').
460      *
461      * <p>This constructor does not canonicalize the localeID.  So, for
462      * example, "zh__pinyin" remains unchanged instead of converting
463      * to "zh@collation=pinyin".  By default ICU only recognizes the
464      * latter as specifying pinyin collation.  Use {@link #createCanonical}
465      * or {@link #canonicalize} if you need to canonicalize the localeID.
466      *
467      * @param localeID string representation of the locale, e.g:
468      * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR;collation=traditional"
469      * @stable ICU 2.8
470      */
ULocale(String localeID)471     public ULocale(String localeID) {
472         this.localeID = getName(localeID);
473     }
474 
475     /**
476      * Convenience overload of ULocale(String, String, String) for
477      * compatibility with java.util.Locale.
478      * @see #ULocale(String, String, String)
479      * @stable ICU 3.4
480      */
ULocale(String a, String b)481     public ULocale(String a, String b) {
482         this(a, b, null);
483     }
484 
485     /**
486      * Constructs a ULocale from a localeID constructed from the three 'fields' a, b, and
487      * c.  These fields are concatenated using underscores to form a localeID of the form
488      * a_b_c, which is then handled like the localeID passed to <code>ULocale(String
489      * localeID)</code>.
490      *
491      * <p>Java locale strings consisting of language, country, and
492      * variant will be handled by this form, since the country code
493      * (being shorter than four letters long) will not be interpreted
494      * as a script code.  If a script code is present, the final
495      * argument ('c') will be interpreted as the country code.  It is
496      * recommended that this constructor only be used to ease porting,
497      * and that clients instead use the single-argument constructor
498      * when constructing a ULocale from a localeID.
499      * @param a first component of the locale id
500      * @param b second component of the locale id
501      * @param c third component of the locale id
502      * @see #ULocale(String)
503      * @stable ICU 3.0
504      */
ULocale(String a, String b, String c)505     public ULocale(String a, String b, String c) {
506         localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
507     }
508 
509     /**
510      * {@icu} Creates a ULocale from the id by first canonicalizing the id.
511      * @param nonCanonicalID the locale id to canonicalize
512      * @return the locale created from the canonical version of the ID.
513      * @stable ICU 3.0
514      */
createCanonical(String nonCanonicalID)515     public static ULocale createCanonical(String nonCanonicalID) {
516         return new ULocale(canonicalize(nonCanonicalID), (Locale)null);
517     }
518 
lscvToID(String lang, String script, String country, String variant)519     private static String lscvToID(String lang, String script, String country, String variant) {
520         StringBuilder buf = new StringBuilder();
521 
522         if (lang != null && lang.length() > 0) {
523             buf.append(lang);
524         }
525         if (script != null && script.length() > 0) {
526             buf.append(UNDERSCORE);
527             buf.append(script);
528         }
529         if (country != null && country.length() > 0) {
530             buf.append(UNDERSCORE);
531             buf.append(country);
532         }
533         if (variant != null && variant.length() > 0) {
534             if (country == null || country.length() == 0) {
535                 buf.append(UNDERSCORE);
536             }
537             buf.append(UNDERSCORE);
538             buf.append(variant);
539         }
540         return buf.toString();
541     }
542 
543     /**
544      * {@icu} Converts this ULocale object to a {@link java.util.Locale}.
545      * @return a JDK locale that either exactly represents this object
546      * or is the closest approximation.
547      * @stable ICU 2.8
548      */
toLocale()549     public Locale toLocale() {
550         if (locale == null) {
551             locale = JDKLocaleHelper.toLocale(this);
552         }
553         return locale;
554     }
555 
556     /**
557      * Keep our own default ULocale.
558      */
559     private static Locale defaultLocale = Locale.getDefault();
560     private static ULocale defaultULocale;
561 
562     private static Locale[] defaultCategoryLocales = new Locale[Category.values().length];
563     private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length];
564 
565     static {
566         defaultULocale = forLocale(defaultLocale);
567 
568         // For Java 6 or older JRE, ICU initializes the default script from
569         // "user.script" system property. The system property was added
570         // in Java 7. On JRE 7, Locale.getDefault() should reflect the
571         // property value to the Locale's default. So ICU just relies on
572         // Locale.getDefault().
573 
574         // Note: The "user.script" property is only used by initialization.
575         //
576         if (JDKLocaleHelper.hasLocaleCategories()) {
577             for (Category cat: Category.values()) {
578                 int idx = cat.ordinal();
579                 defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
580                 defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
581             }
582         } else {
583             // Make sure the current default Locale is original.
584             // If not, it means that someone updated the default Locale.
585             // In this case, user.XXX properties are already out of date
586             // and we should not use user.script.
587             if (JDKLocaleHelper.isOriginalDefaultLocale(defaultLocale)) {
588                 // Use "user.script" if available
589                 String userScript = JDKLocaleHelper.getSystemProperty("user.script");
590                 if (userScript != null && LanguageTag.isScript(userScript)) {
591                     // Note: Builder or forLanguageTag cannot be used here
592                     // when one of Locale fields is not well-formed.
593                     BaseLocale base = defaultULocale.base();
594                     BaseLocale newBase = BaseLocale.getInstance(base.getLanguage(), userScript,
595                             base.getRegion(), base.getVariant());
596                     defaultULocale = getInstance(newBase, defaultULocale.extensions());
597                 }
598             }
599 
600             // Java 6 or older does not have separated category locales,
601             // use the non-category default for all
602             for (Category cat: Category.values()) {
603                 int idx = cat.ordinal();
604                 defaultCategoryLocales[idx] = defaultLocale;
605                 defaultCategoryULocales[idx] = defaultULocale;
606             }
607         }
608     }
609 
610     /**
611      * Returns the current default ULocale.
612      * <p>
613      * The default ULocale is synchronized to the default Java Locale. This method checks
614      * the current default Java Locale and returns an equivalent ULocale.
615      * <p>
616      * <b>Note:</b> Before Java 7, the JDK Locale was not able to represent a locale's script.
617      * Therefore, the script field in the default ULocale is always empty unless
618      * a ULocale with non-empty script is explicitly set by {@link #setDefault(ULocale)}
619      * on Java 6 or older systems.
620      * <p>
621      * <b>Note for ICU 49 or later:</b> Some JRE implementations allow users to override the default
622      * JDK Locale using system properties - <code>user.language</code>, <code>user.country</code>
623      * and <code>user.variant</code>. In addition to these system properties, some Java 7
624      * implementations support <code>user.script</code> for overriding the default Locale's script.
625      * ICU 49 and later versions use the <code>user.script</code> system property on Java 6
626      * or older systems supporting other <code>user.*</code> system properties to initialize
627      * the default ULocale. The <code>user.script</code> override for default ULocale is not
628      * used on Java 7, or if the current Java default Locale is changed after start up.
629      *
630      * @return the default ULocale.
631      * @stable ICU 2.8
632      */
getDefault()633     public static ULocale getDefault() {
634         synchronized (ULocale.class) {
635             if (defaultULocale == null) {
636                 // When Java's default locale has extensions (such as ja-JP-u-ca-japanese),
637                 // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently
638                 // stored in a resource bundle. However, UResourceBundle currently requires
639                 // non-null default ULocale. For now, this implementation returns ULocale.ROOT
640                 // to avoid the problem.
641 
642                 // TODO: Consider moving BCP47 mapping data out of resource bundle later.
643 
644                 return ULocale.ROOT;
645             }
646             Locale currentDefault = Locale.getDefault();
647             if (!defaultLocale.equals(currentDefault)) {
648                 defaultLocale = currentDefault;
649                 defaultULocale = forLocale(currentDefault);
650 
651                 if (!JDKLocaleHelper.hasLocaleCategories()) {
652                     // Detected Java default Locale change.
653                     // We need to update category defaults to match the
654                     // Java 7's behavior on Java 6 or older environment.
655                     for (Category cat : Category.values()) {
656                         int idx = cat.ordinal();
657                         defaultCategoryLocales[idx] = currentDefault;
658                         defaultCategoryULocales[idx] = forLocale(currentDefault);
659                     }
660                 }
661             }
662             return defaultULocale;
663         }
664     }
665 
666     /**
667      * Sets the default ULocale.  This also sets the default Locale.
668      * If the caller does not have write permission to the
669      * user.language property, a security exception will be thrown,
670      * and the default ULocale will remain unchanged.
671      * <p>
672      * By setting the default ULocale with this method, all of the default categoy locales
673      * are also set to the specified default ULocale.
674      * @param newLocale the new default locale
675      * @throws SecurityException if a security manager exists and its
676      *        <code>checkPermission</code> method doesn't allow the operation.
677      * @throws NullPointerException if <code>newLocale</code> is null
678      * @see SecurityManager#checkPermission(java.security.Permission)
679      * @see java.util.PropertyPermission
680      * @see ULocale#setDefault(Category, ULocale)
681      * @stable ICU 3.0
682      */
setDefault(ULocale newLocale)683     public static synchronized void setDefault(ULocale newLocale){
684         defaultLocale = newLocale.toLocale();
685         Locale.setDefault(defaultLocale);
686         defaultULocale = newLocale;
687         // This method also updates all category default locales
688         for (Category cat : Category.values()) {
689             setDefault(cat, newLocale);
690         }
691     }
692 
693     /**
694      * Returns the current default ULocale for the specified category.
695      *
696      * @param category the category
697      * @return the default ULocale for the specified category.
698      * @stable ICU 49
699      */
getDefault(Category category)700     public static ULocale getDefault(Category category) {
701         synchronized (ULocale.class) {
702             int idx = category.ordinal();
703             if (defaultCategoryULocales[idx] == null) {
704                 // Just in case this method is called during ULocale class
705                 // initialization. Unlike getDefault(), we do not have
706                 // cyclic dependency for category default.
707                 return ULocale.ROOT;
708             }
709             if (JDKLocaleHelper.hasLocaleCategories()) {
710                 Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
711                 if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
712                     defaultCategoryLocales[idx] = currentCategoryDefault;
713                     defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
714                 }
715             } else {
716                 // java.util.Locale.setDefault(Locale) in Java 7 updates
717                 // category locale defaults. On Java 6 or older environment,
718                 // ICU4J checks if the default locale has changed and update
719                 // category ULocales here if necessary.
720 
721                 // Note: When java.util.Locale.setDefault(Locale) is called
722                 // with a Locale same with the previous one, Java 7 still
723                 // updates category locale defaults. On Java 6 or older env,
724                 // there is no good way to detect the event, ICU4J simply
725                 // check if the default Java Locale has changed since last
726                 // time.
727 
728                 Locale currentDefault = Locale.getDefault();
729                 if (!defaultLocale.equals(currentDefault)) {
730                     defaultLocale = currentDefault;
731                     defaultULocale = forLocale(currentDefault);
732 
733                     for (Category cat : Category.values()) {
734                         int tmpIdx = cat.ordinal();
735                         defaultCategoryLocales[tmpIdx] = currentDefault;
736                         defaultCategoryULocales[tmpIdx] = forLocale(currentDefault);
737                     }
738                 }
739 
740                 // No synchronization with JDK Locale, because category default
741                 // is not supported in Java 6 or older versions
742             }
743             return defaultCategoryULocales[idx];
744         }
745     }
746 
747     /**
748      * Sets the default <code>ULocale</code> for the specified <code>Category</code>.
749      * This also sets the default <code>Locale</code> for the specified <code>Category</code>
750      * of the JVM. If the caller does not have write permission to the
751      * user.language property, a security exception will be thrown,
752      * and the default ULocale for the specified Category will remain unchanged.
753      *
754      * @param category the specified category to set the default locale
755      * @param newLocale the new default locale
756      * @see SecurityManager#checkPermission(java.security.Permission)
757      * @see java.util.PropertyPermission
758      * @stable ICU 49
759      */
setDefault(Category category, ULocale newLocale)760     public static synchronized void setDefault(Category category, ULocale newLocale) {
761         Locale newJavaDefault = newLocale.toLocale();
762         int idx = category.ordinal();
763         defaultCategoryULocales[idx] = newLocale;
764         defaultCategoryLocales[idx] = newJavaDefault;
765         JDKLocaleHelper.setDefault(category, newJavaDefault);
766     }
767 
768     /**
769      * This is for compatibility with Locale-- in actuality, since ULocale is
770      * immutable, there is no reason to clone it, so this API returns 'this'.
771      * @stable ICU 3.0
772      */
clone()773     public Object clone() {
774         return this;
775     }
776 
777     /**
778      * Returns the hashCode.
779      * @stable ICU 3.0
780      */
hashCode()781     public int hashCode() {
782         return localeID.hashCode();
783     }
784 
785     /**
786      * Returns true if the other object is another ULocale with the
787      * same full name.
788      * Note that since names are not canonicalized, two ULocales that
789      * function identically might not compare equal.
790      *
791      * @return true if this Locale is equal to the specified object.
792      * @stable ICU 3.0
793      */
equals(Object obj)794     public boolean equals(Object obj) {
795         if (this == obj) {
796             return true;
797         }
798         if (obj instanceof ULocale) {
799             return localeID.equals(((ULocale)obj).localeID);
800         }
801         return false;
802     }
803 
804     /**
805      * Compares two ULocale for ordering.
806      * <p><b>Note:</b> The order might change in future.</p>
807      *
808      * @param other the ULocale to be compared.
809      * @return a negative integer, zero, or a positive integer as this ULocale is less than, equal to, or greater
810      * than the specified ULocale.
811      * @throws NullPointerException if <code>other</code> is null.
812      *
813      * @stable ICU 53
814      */
compareTo(ULocale other)815     public int compareTo(ULocale other) {
816         if (this == other) {
817             return 0;
818         }
819 
820         int cmp = 0;
821 
822         // Language
823         cmp = getLanguage().compareTo(other.getLanguage());
824         if (cmp == 0) {
825             // Script
826             cmp = getScript().compareTo(other.getScript());
827             if (cmp == 0) {
828                 // Region
829                 cmp = getCountry().compareTo(other.getCountry());
830                 if (cmp == 0) {
831                     // Variant
832                     cmp = getVariant().compareTo(other.getVariant());
833                     if (cmp == 0) {
834                         // Keywords
835                         Iterator<String> thisKwdItr = getKeywords();
836                         Iterator<String> otherKwdItr = other.getKeywords();
837 
838                         if (thisKwdItr == null) {
839                             cmp = otherKwdItr == null ? 0 : -1;
840                         } else if (otherKwdItr == null) {
841                             cmp = 1;
842                         } else {
843                             // Both have keywords
844                             while (cmp == 0 && thisKwdItr.hasNext()) {
845                                 if (!otherKwdItr.hasNext()) {
846                                     cmp = 1;
847                                     break;
848                                 }
849                                 // Compare keyword keys
850                                 String thisKey = thisKwdItr.next();
851                                 String otherKey = otherKwdItr.next();
852                                 cmp = thisKey.compareTo(otherKey);
853                                 if (cmp == 0) {
854                                     // Compare keyword values
855                                     String thisVal = getKeywordValue(thisKey);
856                                     String otherVal = other.getKeywordValue(otherKey);
857                                     if (thisVal == null) {
858                                         cmp = otherVal == null ? 0 : -1;
859                                     } else if (otherVal == null) {
860                                         cmp = 1;
861                                     } else {
862                                         cmp = thisVal.compareTo(otherVal);
863                                     }
864                                 }
865                             }
866                             if (cmp == 0 && otherKwdItr.hasNext()) {
867                                 cmp = -1;
868                             }
869                         }
870                     }
871                 }
872             }
873         }
874 
875         // Normalize the result value:
876         // Note: String.compareTo() may return value other than -1, 0, 1.
877         // A value other than those are OK by the definition, but we don't want
878         // associate any semantics other than negative/zero/positive.
879         return (cmp < 0) ? -1 : ((cmp > 0) ? 1 : 0);
880     }
881 
882     /**
883      * {@icunote} Unlike the Locale API, this returns an array of <code>ULocale</code>,
884      * not <code>Locale</code>.  Returns a list of all installed locales.
885      * @stable ICU 3.0
886      */
getAvailableLocales()887     public static ULocale[] getAvailableLocales() {
888         return ICUResourceBundle.getAvailableULocales();
889     }
890 
891     /**
892      * Returns a list of all 2-letter country codes defined in ISO 3166.
893      * Can be used to create Locales.
894      * @stable ICU 3.0
895      */
getISOCountries()896     public static String[] getISOCountries() {
897         return LocaleIDs.getISOCountries();
898     }
899 
900     /**
901      * Returns a list of all 2-letter language codes defined in ISO 639.
902      * Can be used to create Locales.
903      * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
904      * The list this function returns includes both the new and the old codes for the
905      * languages whose codes have changed.]
906      * @stable ICU 3.0
907      */
getISOLanguages()908     public static String[] getISOLanguages() {
909         return LocaleIDs.getISOLanguages();
910     }
911 
912     /**
913      * Returns the language code for this locale, which will either be the empty string
914      * or a lowercase ISO 639 code.
915      * @see #getDisplayLanguage()
916      * @see #getDisplayLanguage(ULocale)
917      * @stable ICU 3.0
918      */
getLanguage()919     public String getLanguage() {
920         return base().getLanguage();
921     }
922 
923     /**
924      * Returns the language code for the locale ID,
925      * which will either be the empty string
926      * or a lowercase ISO 639 code.
927      * @see #getDisplayLanguage()
928      * @see #getDisplayLanguage(ULocale)
929      * @stable ICU 3.0
930      */
getLanguage(String localeID)931     public static String getLanguage(String localeID) {
932         return new LocaleIDParser(localeID).getLanguage();
933     }
934 
935     /**
936      * Returns the script code for this locale, which might be the empty string.
937      * @see #getDisplayScript()
938      * @see #getDisplayScript(ULocale)
939      * @stable ICU 3.0
940      */
getScript()941     public String getScript() {
942         return base().getScript();
943     }
944 
945     /**
946      * {@icu} Returns the script code for the specified locale, which might be the empty
947      * string.
948      * @see #getDisplayScript()
949      * @see #getDisplayScript(ULocale)
950      * @stable ICU 3.0
951      */
getScript(String localeID)952     public static String getScript(String localeID) {
953         return new LocaleIDParser(localeID).getScript();
954     }
955 
956     /**
957      * Returns the country/region code for this locale, which will either be the empty string
958      * or an uppercase ISO 3166 2-letter code.
959      * @see #getDisplayCountry()
960      * @see #getDisplayCountry(ULocale)
961      * @stable ICU 3.0
962      */
getCountry()963     public String getCountry() {
964         return base().getRegion();
965     }
966 
967     /**
968      * {@icu} Returns the country/region code for this locale, which will either be the empty string
969      * or an uppercase ISO 3166 2-letter code.
970      * @param localeID The locale identification string.
971      * @see #getDisplayCountry()
972      * @see #getDisplayCountry(ULocale)
973      * @stable ICU 3.0
974      */
getCountry(String localeID)975     public static String getCountry(String localeID) {
976         return new LocaleIDParser(localeID).getCountry();
977     }
978 
979     /**
980      * Returns the variant code for this locale, which might be the empty string.
981      * @see #getDisplayVariant()
982      * @see #getDisplayVariant(ULocale)
983      * @stable ICU 3.0
984      */
getVariant()985     public String getVariant() {
986         return base().getVariant();
987     }
988 
989     /**
990      * {@icu} Returns the variant code for the specified locale, which might be the empty string.
991      * @see #getDisplayVariant()
992      * @see #getDisplayVariant(ULocale)
993      * @stable ICU 3.0
994      */
getVariant(String localeID)995     public static String getVariant(String localeID) {
996         return new LocaleIDParser(localeID).getVariant();
997     }
998 
999     /**
1000      * {@icu} Returns the fallback locale for the specified locale, which might be the
1001      * empty string.
1002      * @stable ICU 3.2
1003      */
getFallback(String localeID)1004     public static String getFallback(String localeID) {
1005         return getFallbackString(getName(localeID));
1006     }
1007 
1008     /**
1009      * {@icu} Returns the fallback locale for this locale.  If this locale is root,
1010      * returns null.
1011      * @stable ICU 3.2
1012      */
getFallback()1013     public ULocale getFallback() {
1014         if (localeID.length() == 0 || localeID.charAt(0) == '@') {
1015             return null;
1016         }
1017         return new ULocale(getFallbackString(localeID), (Locale)null);
1018     }
1019 
1020     /**
1021      * Returns the given (canonical) locale id minus the last part before the tags.
1022      */
getFallbackString(String fallback)1023     private static String getFallbackString(String fallback) {
1024         int extStart = fallback.indexOf('@');
1025         if (extStart == -1) {
1026             extStart = fallback.length();
1027         }
1028         int last = fallback.lastIndexOf('_', extStart);
1029         if (last == -1) {
1030             last = 0;
1031         } else {
1032             // truncate empty segment
1033             while (last > 0) {
1034                 if (fallback.charAt(last - 1) != '_') {
1035                     break;
1036                 }
1037                 last--;
1038             }
1039         }
1040         return fallback.substring(0, last) + fallback.substring(extStart);
1041     }
1042 
1043     /**
1044      * {@icu} Returns the (normalized) base name for this locale,
1045      * like {@link #getName()}, but without keywords.
1046      *
1047      * @return the base name as a String.
1048      * @stable ICU 3.0
1049      */
getBaseName()1050     public String getBaseName() {
1051         return getBaseName(localeID);
1052     }
1053 
1054     /**
1055      * {@icu} Returns the (normalized) base name for the specified locale,
1056      * like {@link #getName(String)}, but without keywords.
1057      *
1058      * @param localeID the locale ID as a string
1059      * @return the base name as a String.
1060      * @stable ICU 3.0
1061      */
getBaseName(String localeID)1062     public static String getBaseName(String localeID){
1063         if (localeID.indexOf('@') == -1) {
1064             return localeID;
1065         }
1066         return new LocaleIDParser(localeID).getBaseName();
1067     }
1068 
1069     /**
1070      * {@icu} Returns the (normalized) full name for this locale.
1071      *
1072      * @return String the full name of the localeID
1073      * @stable ICU 3.0
1074      */
getName()1075     public String getName() {
1076         return localeID; // always normalized
1077     }
1078 
1079     /**
1080      * Gets the shortest length subtag's size.
1081      *
1082      * @param localeID
1083      * @return The size of the shortest length subtag
1084      **/
getShortestSubtagLength(String localeID)1085     private static int getShortestSubtagLength(String localeID) {
1086         int localeIDLength = localeID.length();
1087         int length = localeIDLength;
1088         boolean reset = true;
1089         int tmpLength = 0;
1090 
1091         for (int i = 0; i < localeIDLength; i++) {
1092             if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') {
1093                 if (reset) {
1094                     reset = false;
1095                     tmpLength = 0;
1096                 }
1097                 tmpLength++;
1098             } else {
1099                 if (tmpLength != 0 && tmpLength < length) {
1100                     length = tmpLength;
1101                 }
1102                 reset = true;
1103             }
1104         }
1105 
1106         return length;
1107     }
1108 
1109     /**
1110      * {@icu} Returns the (normalized) full name for the specified locale.
1111      *
1112      * @param localeID the localeID as a string
1113      * @return String the full name of the localeID
1114      * @stable ICU 3.0
1115      */
getName(String localeID)1116     public static String getName(String localeID){
1117         String tmpLocaleID;
1118         // Convert BCP47 id if necessary
1119         if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) {
1120             tmpLocaleID = forLanguageTag(localeID).getName();
1121             if (tmpLocaleID.length() == 0) {
1122                 tmpLocaleID = localeID;
1123             }
1124         } else {
1125             tmpLocaleID = localeID;
1126         }
1127         String name = nameCache.get(tmpLocaleID);
1128         if (name == null) {
1129             name = new LocaleIDParser(tmpLocaleID).getName();
1130             nameCache.put(tmpLocaleID, name);
1131         }
1132         return name;
1133     }
1134 
1135     /**
1136      * Returns a string representation of this object.
1137      * @stable ICU 3.0
1138      */
toString()1139     public String toString() {
1140         return localeID;
1141     }
1142 
1143     /**
1144      * {@icu} Returns an iterator over keywords for this locale.  If there
1145      * are no keywords, returns null.
1146      * @return iterator over keywords, or null if there are no keywords.
1147      * @stable ICU 3.0
1148      */
getKeywords()1149     public Iterator<String> getKeywords() {
1150         return getKeywords(localeID);
1151     }
1152 
1153     /**
1154      * {@icu} Returns an iterator over keywords for the specified locale.  If there
1155      * are no keywords, returns null.
1156      * @return an iterator over the keywords in the specified locale, or null
1157      * if there are no keywords.
1158      * @stable ICU 3.0
1159      */
getKeywords(String localeID)1160     public static Iterator<String> getKeywords(String localeID){
1161         return new LocaleIDParser(localeID).getKeywords();
1162     }
1163 
1164     /**
1165      * {@icu} Returns the value for a keyword in this locale. If the keyword is not
1166      * defined, returns null.
1167      * @param keywordName name of the keyword whose value is desired. Case insensitive.
1168      * @return the value of the keyword, or null.
1169      * @stable ICU 3.0
1170      */
getKeywordValue(String keywordName)1171     public String getKeywordValue(String keywordName){
1172         return getKeywordValue(localeID, keywordName);
1173     }
1174 
1175     /**
1176      * {@icu} Returns the value for a keyword in the specified locale. If the keyword is
1177      * not defined, returns null.  The locale name does not need to be normalized.
1178      * @param keywordName name of the keyword whose value is desired. Case insensitive.
1179      * @return String the value of the keyword as a string
1180      * @stable ICU 3.0
1181      */
getKeywordValue(String localeID, String keywordName)1182     public static String getKeywordValue(String localeID, String keywordName) {
1183         return new LocaleIDParser(localeID).getKeywordValue(keywordName);
1184     }
1185 
1186     /**
1187      * {@icu} Returns the canonical name for the specified locale ID.  This is used to
1188      * convert POSIX and other grandfathered IDs to standard ICU form.
1189      * @param localeID the locale id
1190      * @return the canonicalized id
1191      * @stable ICU 3.0
1192      */
canonicalize(String localeID)1193     public static String canonicalize(String localeID){
1194         LocaleIDParser parser = new LocaleIDParser(localeID, true);
1195         String baseName = parser.getBaseName();
1196         boolean foundVariant = false;
1197 
1198         // formerly, we always set to en_US_POSIX if the basename was empty, but
1199         // now we require that the entire id be empty, so that "@foo=bar"
1200         // will pass through unchanged.
1201         // {dlf} I'd rather keep "" unchanged.
1202         if (localeID.equals("")) {
1203             return "";
1204             //              return "en_US_POSIX";
1205         }
1206 
1207         // we have an ID in the form xx_Yyyy_ZZ_KKKKK
1208 
1209         initCANONICALIZE_MAP();
1210 
1211         /* convert the variants to appropriate ID */
1212         for (int i = 0; i < variantsToKeywords.length; i++) {
1213             String[] vals = variantsToKeywords[i];
1214             int idx = baseName.lastIndexOf("_" + vals[0]);
1215             if (idx > -1) {
1216                 foundVariant = true;
1217 
1218                 baseName = baseName.substring(0, idx);
1219                 if (baseName.endsWith("_")) {
1220                     baseName = baseName.substring(0, --idx);
1221                 }
1222                 parser.setBaseName(baseName);
1223                 parser.defaultKeywordValue(vals[1], vals[2]);
1224                 break;
1225             }
1226         }
1227 
1228         /* See if this is an already known locale */
1229         for (int i = 0; i < CANONICALIZE_MAP.length; i++) {
1230             if (CANONICALIZE_MAP[i][0].equals(baseName)) {
1231                 foundVariant = true;
1232 
1233                 String[] vals = CANONICALIZE_MAP[i];
1234                 parser.setBaseName(vals[1]);
1235                 if (vals[2] != null) {
1236                     parser.defaultKeywordValue(vals[2], vals[3]);
1237                 }
1238                 break;
1239             }
1240         }
1241 
1242         /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
1243         if (!foundVariant) {
1244             if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
1245                 parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
1246             }
1247         }
1248 
1249         return parser.getName();
1250     }
1251 
1252     /**
1253      * {@icu} Given a keyword and a value, return a new locale with an updated
1254      * keyword and value.  If the keyword is null, this removes all keywords from the locale id.
1255      * Otherwise, if the value is null, this removes the value for this keyword from the
1256      * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
1257      * The keyword and value must not be empty.
1258      *
1259      * <p>Related: {@link #getBaseName()} returns the locale ID string with all keywords removed.
1260      *
1261      * @param keyword the keyword to add/remove, or null to remove all keywords.
1262      * @param value the value to add/set, or null to remove this particular keyword.
1263      * @return the updated locale
1264      * @stable ICU 3.2
1265      */
setKeywordValue(String keyword, String value)1266     public ULocale setKeywordValue(String keyword, String value) {
1267         return new ULocale(setKeywordValue(localeID, keyword, value), (Locale)null);
1268     }
1269 
1270     /**
1271      * Given a locale id, a keyword, and a value, return a new locale id with an updated
1272      * keyword and value.  If the keyword is null, this removes all keywords from the locale id.
1273      * Otherwise, if the value is null, this removes the value for this keyword from the
1274      * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
1275      * The keyword and value must not be empty.
1276      *
1277      * <p>Related: {@link #getBaseName(String)} returns the locale ID string with all keywords removed.
1278      *
1279      * @param localeID the locale id to modify
1280      * @param keyword the keyword to add/remove, or null to remove all keywords.
1281      * @param value the value to add/set, or null to remove this particular keyword.
1282      * @return the updated locale id
1283      * @stable ICU 3.2
1284      */
setKeywordValue(String localeID, String keyword, String value)1285     public static String setKeywordValue(String localeID, String keyword, String value) {
1286         LocaleIDParser parser = new LocaleIDParser(localeID);
1287         parser.setKeywordValue(keyword, value);
1288         return parser.getName();
1289     }
1290 
1291     /*
1292      * Given a locale id, a keyword, and a value, return a new locale id with an updated
1293      * keyword and value, if the keyword does not already have a value.  The keyword and
1294      * value must not be null or empty.
1295      * @param localeID the locale id to modify
1296      * @param keyword the keyword to add, if not already present
1297      * @param value the value to add, if not already present
1298      * @return the updated locale id
1299      */
1300     /*    private static String defaultKeywordValue(String localeID, String keyword, String value) {
1301         LocaleIDParser parser = new LocaleIDParser(localeID);
1302         parser.defaultKeywordValue(keyword, value);
1303         return parser.getName();
1304     }*/
1305 
1306     /**
1307      * Returns a three-letter abbreviation for this locale's language.  If the locale
1308      * doesn't specify a language, returns the empty string.  Otherwise, returns
1309      * a lowercase ISO 639-2/T language code.
1310      * The ISO 639-2 language codes can be found on-line at
1311      *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
1312      * @exception MissingResourceException Throws MissingResourceException if the
1313      * three-letter language abbreviation is not available for this locale.
1314      * @stable ICU 3.0
1315      */
getISO3Language()1316     public String getISO3Language(){
1317         return getISO3Language(localeID);
1318     }
1319 
1320     /**
1321      * {@icu} Returns a three-letter abbreviation for this locale's language.  If the locale
1322      * doesn't specify a language, returns the empty string.  Otherwise, returns
1323      * a lowercase ISO 639-2/T language code.
1324      * The ISO 639-2 language codes can be found on-line at
1325      *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
1326      * @exception MissingResourceException Throws MissingResourceException if the
1327      * three-letter language abbreviation is not available for this locale.
1328      * @stable ICU 3.0
1329      */
getISO3Language(String localeID)1330     public static String getISO3Language(String localeID) {
1331         return LocaleIDs.getISO3Language(getLanguage(localeID));
1332     }
1333 
1334     /**
1335      * Returns a three-letter abbreviation for this locale's country/region.  If the locale
1336      * doesn't specify a country, returns the empty string.  Otherwise, returns
1337      * an uppercase ISO 3166 3-letter country code.
1338      * @exception MissingResourceException Throws MissingResourceException if the
1339      * three-letter country abbreviation is not available for this locale.
1340      * @stable ICU 3.0
1341      */
getISO3Country()1342     public String getISO3Country() {
1343         return getISO3Country(localeID);
1344     }
1345 
1346     /**
1347      * {@icu} Returns a three-letter abbreviation for this locale's country/region.  If the locale
1348      * doesn't specify a country, returns the empty string.  Otherwise, returns
1349      * an uppercase ISO 3166 3-letter country code.
1350      * @exception MissingResourceException Throws MissingResourceException if the
1351      * three-letter country abbreviation is not available for this locale.
1352      * @stable ICU 3.0
1353      */
getISO3Country(String localeID)1354     public static String getISO3Country(String localeID) {
1355         return LocaleIDs.getISO3Country(getCountry(localeID));
1356     }
1357 
1358     /**
1359      * Pairs of (language subtag, + or -) for finding out fast if common languages
1360      * are LTR (minus) or RTL (plus).
1361      */
1362     private static final String LANG_DIR_STRING =
1363             "root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-";
1364 
1365     /**
1366      * {@icu} Returns whether this locale's script is written right-to-left.
1367      * If there is no script subtag, then the likely script is used,
1368      * see {@link #addLikelySubtags(ULocale)}.
1369      * If no likely script is known, then false is returned.
1370      *
1371      * <p>A script is right-to-left according to the CLDR script metadata
1372      * which corresponds to whether the script's letters have Bidi_Class=R or AL.
1373      *
1374      * <p>Returns true for "ar" and "en-Hebr", false for "zh" and "fa-Cyrl".
1375      *
1376      * @return true if the locale's script is written right-to-left
1377      * @draft ICU 54
1378      * @provisional This API might change or be removed in a future release.
1379      */
isRightToLeft()1380     public boolean isRightToLeft() {
1381         String script = getScript();
1382         if (script.length() == 0) {
1383             // Fastpath: We know the likely scripts and their writing direction
1384             // for some common languages.
1385             String lang = getLanguage();
1386             if (lang.length() == 0) {
1387                 return false;
1388             }
1389             int langIndex = LANG_DIR_STRING.indexOf(lang);
1390             if (langIndex >= 0) {
1391                 switch (LANG_DIR_STRING.charAt(langIndex + lang.length())) {
1392                 case '-': return false;
1393                 case '+': return true;
1394                 default: break;  // partial match of a longer code
1395                 }
1396             }
1397             // Otherwise, find the likely script.
1398             ULocale likely = addLikelySubtags(this);
1399             script = likely.getScript();
1400             if (script.length() == 0) {
1401                 return false;
1402             }
1403         }
1404         int scriptCode = UScript.getCodeFromName(script);
1405         return UScript.isRightToLeft(scriptCode);
1406     }
1407 
1408     // display names
1409 
1410     /**
1411      * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
1412      * @return the localized language name.
1413      * @see Category#DISPLAY
1414      * @stable ICU 3.0
1415      */
getDisplayLanguage()1416     public String getDisplayLanguage() {
1417         return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false);
1418     }
1419 
1420     /**
1421      * Returns this locale's language localized for display in the provided locale.
1422      * @param displayLocale the locale in which to display the name.
1423      * @return the localized language name.
1424      * @stable ICU 3.0
1425      */
getDisplayLanguage(ULocale displayLocale)1426     public String getDisplayLanguage(ULocale displayLocale) {
1427         return getDisplayLanguageInternal(this, displayLocale, false);
1428     }
1429 
1430     /**
1431      * {@icu} Returns a locale's language localized for display in the provided locale.
1432      * This is a cover for the ICU4C API.
1433      * @param localeID the id of the locale whose language will be displayed
1434      * @param displayLocaleID the id of the locale in which to display the name.
1435      * @return the localized language name.
1436      * @stable ICU 3.0
1437      */
getDisplayLanguage(String localeID, String displayLocaleID)1438     public static String getDisplayLanguage(String localeID, String displayLocaleID) {
1439         return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
1440                 false);
1441     }
1442 
1443     /**
1444      * {@icu} Returns a locale's language localized for display in the provided locale.
1445      * This is a cover for the ICU4C API.
1446      * @param localeID the id of the locale whose language will be displayed.
1447      * @param displayLocale the locale in which to display the name.
1448      * @return the localized language name.
1449      * @stable ICU 3.0
1450      */
getDisplayLanguage(String localeID, ULocale displayLocale)1451     public static String getDisplayLanguage(String localeID, ULocale displayLocale) {
1452         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
1453     }
1454     /**
1455      * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
1456      * If a dialect name is present in the data, then it is returned.
1457      * @return the localized language name.
1458      * @see Category#DISPLAY
1459      * @stable ICU 4.4
1460      */
getDisplayLanguageWithDialect()1461     public String getDisplayLanguageWithDialect() {
1462         return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true);
1463     }
1464 
1465     /**
1466      * {@icu} Returns this locale's language localized for display in the provided locale.
1467      * If a dialect name is present in the data, then it is returned.
1468      * @param displayLocale the locale in which to display the name.
1469      * @return the localized language name.
1470      * @stable ICU 4.4
1471      */
getDisplayLanguageWithDialect(ULocale displayLocale)1472     public String getDisplayLanguageWithDialect(ULocale displayLocale) {
1473         return getDisplayLanguageInternal(this, displayLocale, true);
1474     }
1475 
1476     /**
1477      * {@icu} Returns a locale's language localized for display in the provided locale.
1478      * If a dialect name is present in the data, then it is returned.
1479      * This is a cover for the ICU4C API.
1480      * @param localeID the id of the locale whose language will be displayed
1481      * @param displayLocaleID the id of the locale in which to display the name.
1482      * @return the localized language name.
1483      * @stable ICU 4.4
1484      */
getDisplayLanguageWithDialect(String localeID, String displayLocaleID)1485     public static String getDisplayLanguageWithDialect(String localeID, String displayLocaleID) {
1486         return getDisplayLanguageInternal(new ULocale(localeID), new ULocale(displayLocaleID),
1487                 true);
1488     }
1489 
1490     /**
1491      * {@icu} Returns a locale's language localized for display in the provided locale.
1492      * If a dialect name is present in the data, then it is returned.
1493      * This is a cover for the ICU4C API.
1494      * @param localeID the id of the locale whose language will be displayed.
1495      * @param displayLocale the locale in which to display the name.
1496      * @return the localized language name.
1497      * @stable ICU 4.4
1498      */
getDisplayLanguageWithDialect(String localeID, ULocale displayLocale)1499     public static String getDisplayLanguageWithDialect(String localeID, ULocale displayLocale) {
1500         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, true);
1501     }
1502 
getDisplayLanguageInternal(ULocale locale, ULocale displayLocale, boolean useDialect)1503     private static String getDisplayLanguageInternal(ULocale locale, ULocale displayLocale,
1504             boolean useDialect) {
1505         String lang = useDialect ? locale.getBaseName() : locale.getLanguage();
1506         return LocaleDisplayNames.getInstance(displayLocale).languageDisplayName(lang);
1507     }
1508 
1509     /**
1510      * Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
1511      * @return the localized script name.
1512      * @see Category#DISPLAY
1513      * @stable ICU 3.0
1514      */
getDisplayScript()1515     public String getDisplayScript() {
1516         return getDisplayScriptInternal(this, getDefault(Category.DISPLAY));
1517     }
1518 
1519     /**
1520      * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
1521      * @return the localized script name.
1522      * @see Category#DISPLAY
1523      * @internal
1524      * @deprecated This API is ICU internal only.
1525      */
1526     @Deprecated
getDisplayScriptInContext()1527     public String getDisplayScriptInContext() {
1528         return getDisplayScriptInContextInternal(this, getDefault(Category.DISPLAY));
1529     }
1530 
1531     /**
1532      * Returns this locale's script localized for display in the provided locale.
1533      * @param displayLocale the locale in which to display the name.
1534      * @return the localized script name.
1535      * @stable ICU 3.0
1536      */
getDisplayScript(ULocale displayLocale)1537     public String getDisplayScript(ULocale displayLocale) {
1538         return getDisplayScriptInternal(this, displayLocale);
1539     }
1540 
1541     /**
1542      * {@icu} Returns this locale's script localized for display in the provided locale.
1543      * @param displayLocale the locale in which to display the name.
1544      * @return the localized script name.
1545      * @internal
1546      * @deprecated This API is ICU internal only.
1547      */
1548     @Deprecated
getDisplayScriptInContext(ULocale displayLocale)1549     public String getDisplayScriptInContext(ULocale displayLocale) {
1550         return getDisplayScriptInContextInternal(this, displayLocale);
1551     }
1552 
1553     /**
1554      * {@icu} Returns a locale's script localized for display in the provided locale.
1555      * This is a cover for the ICU4C API.
1556      * @param localeID the id of the locale whose script will be displayed
1557      * @param displayLocaleID the id of the locale in which to display the name.
1558      * @return the localized script name.
1559      * @stable ICU 3.0
1560      */
getDisplayScript(String localeID, String displayLocaleID)1561     public static String getDisplayScript(String localeID, String displayLocaleID) {
1562         return getDisplayScriptInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1563     }
1564     /**
1565      * {@icu} Returns a locale's script localized for display in the provided locale.
1566      * This is a cover for the ICU4C API.
1567      * @param localeID the id of the locale whose script will be displayed
1568      * @param displayLocaleID the id of the locale in which to display the name.
1569      * @return the localized script name.
1570      * @internal
1571      * @deprecated This API is ICU internal only.
1572      */
1573     @Deprecated
getDisplayScriptInContext(String localeID, String displayLocaleID)1574     public static String getDisplayScriptInContext(String localeID, String displayLocaleID) {
1575         return getDisplayScriptInContextInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1576     }
1577 
1578     /**
1579      * {@icu} Returns a locale's script localized for display in the provided locale.
1580      * @param localeID the id of the locale whose script will be displayed.
1581      * @param displayLocale the locale in which to display the name.
1582      * @return the localized script name.
1583      * @stable ICU 3.0
1584      */
getDisplayScript(String localeID, ULocale displayLocale)1585     public static String getDisplayScript(String localeID, ULocale displayLocale) {
1586         return getDisplayScriptInternal(new ULocale(localeID), displayLocale);
1587     }
1588     /**
1589      * {@icu} Returns a locale's script localized for display in the provided locale.
1590      * @param localeID the id of the locale whose script will be displayed.
1591      * @param displayLocale the locale in which to display the name.
1592      * @return the localized script name.
1593      * @internal
1594      * @deprecated This API is ICU internal only.
1595      */
1596     @Deprecated
getDisplayScriptInContext(String localeID, ULocale displayLocale)1597     public static String getDisplayScriptInContext(String localeID, ULocale displayLocale) {
1598         return getDisplayScriptInContextInternal(new ULocale(localeID), displayLocale);
1599     }
1600 
1601     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
getDisplayScriptInternal(ULocale locale, ULocale displayLocale)1602     private static String getDisplayScriptInternal(ULocale locale, ULocale displayLocale) {
1603         return LocaleDisplayNames.getInstance(displayLocale)
1604                 .scriptDisplayName(locale.getScript());
1605     }
1606 
getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale)1607     private static String getDisplayScriptInContextInternal(ULocale locale, ULocale displayLocale) {
1608         return LocaleDisplayNames.getInstance(displayLocale)
1609                 .scriptDisplayNameInContext(locale.getScript());
1610     }
1611 
1612     /**
1613      * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale.
1614      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1615      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
1616      * @return the localized country name.
1617      * @see Category#DISPLAY
1618      * @stable ICU 3.0
1619      */
getDisplayCountry()1620     public String getDisplayCountry() {
1621         return getDisplayCountryInternal(this, getDefault(Category.DISPLAY));
1622     }
1623 
1624     /**
1625      * Returns this locale's country localized for display in the provided locale.
1626      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1627      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
1628      * @param displayLocale the locale in which to display the name.
1629      * @return the localized country name.
1630      * @stable ICU 3.0
1631      */
getDisplayCountry(ULocale displayLocale)1632     public String getDisplayCountry(ULocale displayLocale){
1633         return getDisplayCountryInternal(this, displayLocale);
1634     }
1635 
1636     /**
1637      * {@icu} Returns a locale's country localized for display in the provided locale.
1638      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1639      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
1640      * This is a cover for the ICU4C API.
1641      * @param localeID the id of the locale whose country will be displayed
1642      * @param displayLocaleID the id of the locale in which to display the name.
1643      * @return the localized country name.
1644      * @stable ICU 3.0
1645      */
getDisplayCountry(String localeID, String displayLocaleID)1646     public static String getDisplayCountry(String localeID, String displayLocaleID) {
1647         return getDisplayCountryInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1648     }
1649 
1650     /**
1651      * {@icu} Returns a locale's country localized for display in the provided locale.
1652      * <b>Warning: </b>this is for the region part of a valid locale ID; it cannot just be the region code (like "FR").
1653      * To get the display name for a region alone, or for other options, use {@link LocaleDisplayNames} instead.
1654      * This is a cover for the ICU4C API.
1655      * @param localeID the id of the locale whose country will be displayed.
1656      * @param displayLocale the locale in which to display the name.
1657      * @return the localized country name.
1658      * @stable ICU 3.0
1659      */
getDisplayCountry(String localeID, ULocale displayLocale)1660     public static String getDisplayCountry(String localeID, ULocale displayLocale) {
1661         return getDisplayCountryInternal(new ULocale(localeID), displayLocale);
1662     }
1663 
1664     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
getDisplayCountryInternal(ULocale locale, ULocale displayLocale)1665     private static String getDisplayCountryInternal(ULocale locale, ULocale displayLocale) {
1666         return LocaleDisplayNames.getInstance(displayLocale)
1667                 .regionDisplayName(locale.getCountry());
1668     }
1669 
1670     /**
1671      * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale.
1672      * @return the localized variant name.
1673      * @see Category#DISPLAY
1674      * @stable ICU 3.0
1675      */
getDisplayVariant()1676     public String getDisplayVariant() {
1677         return getDisplayVariantInternal(this, getDefault(Category.DISPLAY));
1678     }
1679 
1680     /**
1681      * Returns this locale's variant localized for display in the provided locale.
1682      * @param displayLocale the locale in which to display the name.
1683      * @return the localized variant name.
1684      * @stable ICU 3.0
1685      */
getDisplayVariant(ULocale displayLocale)1686     public String getDisplayVariant(ULocale displayLocale) {
1687         return getDisplayVariantInternal(this, displayLocale);
1688     }
1689 
1690     /**
1691      * {@icu} Returns a locale's variant localized for display in the provided locale.
1692      * This is a cover for the ICU4C API.
1693      * @param localeID the id of the locale whose variant will be displayed
1694      * @param displayLocaleID the id of the locale in which to display the name.
1695      * @return the localized variant name.
1696      * @stable ICU 3.0
1697      */
getDisplayVariant(String localeID, String displayLocaleID)1698     public static String getDisplayVariant(String localeID, String displayLocaleID){
1699         return getDisplayVariantInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1700     }
1701 
1702     /**
1703      * {@icu} Returns a locale's variant localized for display in the provided locale.
1704      * This is a cover for the ICU4C API.
1705      * @param localeID the id of the locale whose variant will be displayed.
1706      * @param displayLocale the locale in which to display the name.
1707      * @return the localized variant name.
1708      * @stable ICU 3.0
1709      */
getDisplayVariant(String localeID, ULocale displayLocale)1710     public static String getDisplayVariant(String localeID, ULocale displayLocale) {
1711         return getDisplayVariantInternal(new ULocale(localeID), displayLocale);
1712     }
1713 
getDisplayVariantInternal(ULocale locale, ULocale displayLocale)1714     private static String getDisplayVariantInternal(ULocale locale, ULocale displayLocale) {
1715         return LocaleDisplayNames.getInstance(displayLocale)
1716                 .variantDisplayName(locale.getVariant());
1717     }
1718 
1719     /**
1720      * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale.
1721      * @param keyword the keyword to be displayed.
1722      * @return the localized keyword name.
1723      * @see #getKeywords()
1724      * @see Category#DISPLAY
1725      * @stable ICU 3.0
1726      */
getDisplayKeyword(String keyword)1727     public static String getDisplayKeyword(String keyword) {
1728         return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY));
1729     }
1730 
1731     /**
1732      * {@icu} Returns a keyword localized for display in the specified locale.
1733      * @param keyword the keyword to be displayed.
1734      * @param displayLocaleID the id of the locale in which to display the keyword.
1735      * @return the localized keyword name.
1736      * @see #getKeywords(String)
1737      * @stable ICU 3.0
1738      */
getDisplayKeyword(String keyword, String displayLocaleID)1739     public static String getDisplayKeyword(String keyword, String displayLocaleID) {
1740         return getDisplayKeywordInternal(keyword, new ULocale(displayLocaleID));
1741     }
1742 
1743     /**
1744      * {@icu} Returns a keyword localized for display in the specified locale.
1745      * @param keyword the keyword to be displayed.
1746      * @param displayLocale the locale in which to display the keyword.
1747      * @return the localized keyword name.
1748      * @see #getKeywords(String)
1749      * @stable ICU 3.0
1750      */
getDisplayKeyword(String keyword, ULocale displayLocale)1751     public static String getDisplayKeyword(String keyword, ULocale displayLocale) {
1752         return getDisplayKeywordInternal(keyword, displayLocale);
1753     }
1754 
getDisplayKeywordInternal(String keyword, ULocale displayLocale)1755     private static String getDisplayKeywordInternal(String keyword, ULocale displayLocale) {
1756         return LocaleDisplayNames.getInstance(displayLocale).keyDisplayName(keyword);
1757     }
1758 
1759     /**
1760      * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale.
1761      * @param keyword the keyword whose value is to be displayed.
1762      * @return the localized value name.
1763      * @see Category#DISPLAY
1764      * @stable ICU 3.0
1765      */
getDisplayKeywordValue(String keyword)1766     public String getDisplayKeywordValue(String keyword) {
1767         return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY));
1768     }
1769 
1770     /**
1771      * {@icu} Returns a keyword value localized for display in the specified locale.
1772      * @param keyword the keyword whose value is to be displayed.
1773      * @param displayLocale the locale in which to display the value.
1774      * @return the localized value name.
1775      * @stable ICU 3.0
1776      */
getDisplayKeywordValue(String keyword, ULocale displayLocale)1777     public String getDisplayKeywordValue(String keyword, ULocale displayLocale) {
1778         return getDisplayKeywordValueInternal(this, keyword, displayLocale);
1779     }
1780 
1781     /**
1782      * {@icu} Returns a keyword value localized for display in the specified locale.
1783      * This is a cover for the ICU4C API.
1784      * @param localeID the id of the locale whose keyword value is to be displayed.
1785      * @param keyword the keyword whose value is to be displayed.
1786      * @param displayLocaleID the id of the locale in which to display the value.
1787      * @return the localized value name.
1788      * @stable ICU 3.0
1789      */
getDisplayKeywordValue(String localeID, String keyword, String displayLocaleID)1790     public static String getDisplayKeywordValue(String localeID, String keyword,
1791             String displayLocaleID) {
1792         return getDisplayKeywordValueInternal(new ULocale(localeID), keyword,
1793                 new ULocale(displayLocaleID));
1794     }
1795 
1796     /**
1797      * {@icu} Returns a keyword value localized for display in the specified locale.
1798      * This is a cover for the ICU4C API.
1799      * @param localeID the id of the locale whose keyword value is to be displayed.
1800      * @param keyword the keyword whose value is to be displayed.
1801      * @param displayLocale the id of the locale in which to display the value.
1802      * @return the localized value name.
1803      * @stable ICU 3.0
1804      */
getDisplayKeywordValue(String localeID, String keyword, ULocale displayLocale)1805     public static String getDisplayKeywordValue(String localeID, String keyword,
1806             ULocale displayLocale) {
1807         return getDisplayKeywordValueInternal(new ULocale(localeID), keyword, displayLocale);
1808     }
1809 
1810     // displayLocaleID is canonical, localeID need not be since parsing will fix this.
getDisplayKeywordValueInternal(ULocale locale, String keyword, ULocale displayLocale)1811     private static String getDisplayKeywordValueInternal(ULocale locale, String keyword,
1812             ULocale displayLocale) {
1813         keyword = AsciiUtil.toLowerString(keyword.trim());
1814         String value = locale.getKeywordValue(keyword);
1815         return LocaleDisplayNames.getInstance(displayLocale).keyValueDisplayName(keyword, value);
1816     }
1817 
1818     /**
1819      * Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
1820      * @return the localized locale name.
1821      * @see Category#DISPLAY
1822      * @stable ICU 3.0
1823      */
getDisplayName()1824     public String getDisplayName() {
1825         return getDisplayNameInternal(this, getDefault(Category.DISPLAY));
1826     }
1827 
1828     /**
1829      * Returns this locale name localized for display in the provided locale.
1830      * @param displayLocale the locale in which to display the locale name.
1831      * @return the localized locale name.
1832      * @stable ICU 3.0
1833      */
getDisplayName(ULocale displayLocale)1834     public String getDisplayName(ULocale displayLocale) {
1835         return getDisplayNameInternal(this, displayLocale);
1836     }
1837 
1838     /**
1839      * {@icu} Returns the locale ID localized for display in the provided locale.
1840      * This is a cover for the ICU4C API.
1841      * @param localeID the locale whose name is to be displayed.
1842      * @param displayLocaleID the id of the locale in which to display the locale name.
1843      * @return the localized locale name.
1844      * @stable ICU 3.0
1845      */
getDisplayName(String localeID, String displayLocaleID)1846     public static String getDisplayName(String localeID, String displayLocaleID) {
1847         return getDisplayNameInternal(new ULocale(localeID), new ULocale(displayLocaleID));
1848     }
1849 
1850     /**
1851      * {@icu} Returns the locale ID localized for display in the provided locale.
1852      * This is a cover for the ICU4C API.
1853      * @param localeID the locale whose name is to be displayed.
1854      * @param displayLocale the locale in which to display the locale name.
1855      * @return the localized locale name.
1856      * @stable ICU 3.0
1857      */
getDisplayName(String localeID, ULocale displayLocale)1858     public static String getDisplayName(String localeID, ULocale displayLocale) {
1859         return getDisplayNameInternal(new ULocale(localeID), displayLocale);
1860     }
1861 
getDisplayNameInternal(ULocale locale, ULocale displayLocale)1862     private static String getDisplayNameInternal(ULocale locale, ULocale displayLocale) {
1863         return LocaleDisplayNames.getInstance(displayLocale).localeDisplayName(locale);
1864     }
1865 
1866     /**
1867      * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
1868      * If a dialect name is present in the locale data, then it is returned.
1869      * @return the localized locale name.
1870      * @see Category#DISPLAY
1871      * @stable ICU 4.4
1872      */
getDisplayNameWithDialect()1873     public String getDisplayNameWithDialect() {
1874         return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY));
1875     }
1876 
1877     /**
1878      * {@icu} Returns this locale name localized for display in the provided locale.
1879      * If a dialect name is present in the locale data, then it is returned.
1880      * @param displayLocale the locale in which to display the locale name.
1881      * @return the localized locale name.
1882      * @stable ICU 4.4
1883      */
getDisplayNameWithDialect(ULocale displayLocale)1884     public String getDisplayNameWithDialect(ULocale displayLocale) {
1885         return getDisplayNameWithDialectInternal(this, displayLocale);
1886     }
1887 
1888     /**
1889      * {@icu} Returns the locale ID localized for display in the provided locale.
1890      * If a dialect name is present in the locale data, then it is returned.
1891      * This is a cover for the ICU4C API.
1892      * @param localeID the locale whose name is to be displayed.
1893      * @param displayLocaleID the id of the locale in which to display the locale name.
1894      * @return the localized locale name.
1895      * @stable ICU 4.4
1896      */
getDisplayNameWithDialect(String localeID, String displayLocaleID)1897     public static String getDisplayNameWithDialect(String localeID, String displayLocaleID) {
1898         return getDisplayNameWithDialectInternal(new ULocale(localeID),
1899                 new ULocale(displayLocaleID));
1900     }
1901 
1902     /**
1903      * {@icu} Returns the locale ID localized for display in the provided locale.
1904      * If a dialect name is present in the locale data, then it is returned.
1905      * This is a cover for the ICU4C API.
1906      * @param localeID the locale whose name is to be displayed.
1907      * @param displayLocale the locale in which to display the locale name.
1908      * @return the localized locale name.
1909      * @stable ICU 4.4
1910      */
getDisplayNameWithDialect(String localeID, ULocale displayLocale)1911     public static String getDisplayNameWithDialect(String localeID, ULocale displayLocale) {
1912         return getDisplayNameWithDialectInternal(new ULocale(localeID), displayLocale);
1913     }
1914 
getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale)1915     private static String getDisplayNameWithDialectInternal(ULocale locale, ULocale displayLocale) {
1916         return LocaleDisplayNames.getInstance(displayLocale, DialectHandling.DIALECT_NAMES)
1917                 .localeDisplayName(locale);
1918     }
1919 
1920     /**
1921      * {@icu} Returns this locale's layout orientation for characters.  The possible
1922      * values are "left-to-right", "right-to-left", "top-to-bottom" or
1923      * "bottom-to-top".
1924      * @return The locale's layout orientation for characters.
1925      * @stable ICU 4.0
1926      */
getCharacterOrientation()1927     public String getCharacterOrientation() {
1928         return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this,
1929                 "layout", "characters");
1930     }
1931 
1932     /**
1933      * {@icu} Returns this locale's layout orientation for lines.  The possible
1934      * values are "left-to-right", "right-to-left", "top-to-bottom" or
1935      * "bottom-to-top".
1936      * @return The locale's layout orientation for lines.
1937      * @stable ICU 4.0
1938      */
getLineOrientation()1939     public String getLineOrientation() {
1940         return ICUResourceTableAccess.getTableString(ICUResourceBundle.ICU_BASE_NAME, this,
1941                 "layout", "lines");
1942     }
1943 
1944     /**
1945      * {@icu} Selector for <tt>getLocale()</tt> indicating the locale of the
1946      * resource containing the data.  This is always at or above the
1947      * valid locale.  If the valid locale does not contain the
1948      * specific data being requested, then the actual locale will be
1949      * above the valid locale.  If the object was not constructed from
1950      * locale data, then the valid locale is <i>null</i>.
1951      *
1952      * @draft ICU 2.8 (retain)
1953      * @provisional This API might change or be removed in a future release.
1954      */
1955     public static Type ACTUAL_LOCALE = new Type();
1956 
1957     /**
1958      * {@icu} Selector for <tt>getLocale()</tt> indicating the most specific
1959      * locale for which any data exists.  This is always at or above
1960      * the requested locale, and at or below the actual locale.  If
1961      * the requested locale does not correspond to any resource data,
1962      * then the valid locale will be above the requested locale.  If
1963      * the object was not constructed from locale data, then the
1964      * actual locale is <i>null</i>.
1965      *
1966      * <p>Note: The valid locale will be returned correctly in ICU
1967      * 3.0 or later.  In ICU 2.8, it is not returned correctly.
1968      * @draft ICU 2.8 (retain)
1969      * @provisional This API might change or be removed in a future release.
1970      */
1971     public static Type VALID_LOCALE = new Type();
1972 
1973     /**
1974      * Opaque selector enum for <tt>getLocale()</tt>.
1975      * @see com.ibm.icu.util.ULocale
1976      * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
1977      * @see com.ibm.icu.util.ULocale#VALID_LOCALE
1978      * @draft ICU 2.8 (retainAll)
1979      * @provisional This API might change or be removed in a future release.
1980      */
1981     public static final class Type {
Type()1982         private Type() {}
1983     }
1984 
1985     /**
1986      * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
1987      * locale for the user.  NullPointerException is thrown if acceptLanguageList or
1988      * availableLocales is null.  If fallback is non-null, it will contain true if a
1989      * fallback locale (one not in the acceptLanguageList) was returned.  The value on
1990      * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
1991      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
1992      * availableLocales matched).  No ULocale array element should be null; behavior is
1993      * undefined if this is the case.
1994      * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
1995      * @param availableLocales list of available locales. One of these will be returned.
1996      * @param fallback if non-null, a 1-element array containing a boolean to be set with
1997      * the fallback status
1998      * @return one of the locales from the availableLocales list, or null if none match
1999      * @stable ICU 3.4
2000      */
acceptLanguage(String acceptLanguageList, ULocale[] availableLocales, boolean[] fallback)2001     public static ULocale acceptLanguage(String acceptLanguageList, ULocale[] availableLocales,
2002             boolean[] fallback) {
2003         if (acceptLanguageList == null) {
2004             throw new NullPointerException();
2005         }
2006         ULocale acceptList[] = null;
2007         try {
2008             acceptList = parseAcceptLanguage(acceptLanguageList, true);
2009         } catch (ParseException pe) {
2010             acceptList = null;
2011         }
2012         if (acceptList == null) {
2013             return null;
2014         }
2015         return acceptLanguage(acceptList, availableLocales, fallback);
2016     }
2017 
2018     /**
2019      * {@icu} Based on a list of acceptable locales, determine an available locale for the
2020      * user.  NullPointerException is thrown if acceptLanguageList or availableLocales is
2021      * null.  If fallback is non-null, it will contain true if a fallback locale (one not
2022      * in the acceptLanguageList) was returned.  The value on entry is ignored.  ULocale
2023      * will be one of the locales in availableLocales, or the ROOT ULocale if if a ROOT
2024      * locale was used as a fallback (because nothing else in availableLocales matched).
2025      * No ULocale array element should be null; behavior is undefined if this is the case.
2026      * @param acceptLanguageList list of acceptable locales
2027      * @param availableLocales list of available locales. One of these will be returned.
2028      * @param fallback if non-null, a 1-element array containing a boolean to be set with
2029      * the fallback status
2030      * @return one of the locales from the availableLocales list, or null if none match
2031      * @stable ICU 3.4
2032      */
2033 
acceptLanguage(ULocale[] acceptLanguageList, ULocale[] availableLocales, boolean[] fallback)2034     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
2035             availableLocales, boolean[] fallback) {
2036         // fallbacklist
2037         int i,j;
2038         if(fallback != null) {
2039             fallback[0]=true;
2040         }
2041         for(i=0;i<acceptLanguageList.length;i++) {
2042             ULocale aLocale = acceptLanguageList[i];
2043             boolean[] setFallback = fallback;
2044             do {
2045                 for(j=0;j<availableLocales.length;j++) {
2046                     if(availableLocales[j].equals(aLocale)) {
2047                         if(setFallback != null) {
2048                             setFallback[0]=false; // first time with this locale - not a fallback.
2049                         }
2050                         return availableLocales[j];
2051                     }
2052                     // compare to scriptless alias, so locales such as
2053                     // zh_TW, zh_CN are considered as available locales - see #7190
2054                     if (aLocale.getScript().length() == 0
2055                             && availableLocales[j].getScript().length() > 0
2056                             && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
2057                             && availableLocales[j].getCountry().equals(aLocale.getCountry())
2058                             && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
2059                         ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
2060                         if (minAvail.getScript().length() == 0) {
2061                             if(setFallback != null) {
2062                                 setFallback[0] = false; // not a fallback.
2063                             }
2064                             return aLocale;
2065                         }
2066                     }
2067                 }
2068                 Locale loc = aLocale.toLocale();
2069                 Locale parent = LocaleUtility.fallback(loc);
2070                 if(parent != null) {
2071                     aLocale = new ULocale(parent);
2072                 } else {
2073                     aLocale = null;
2074                 }
2075                 setFallback = null; // Do not set fallback in later iterations
2076             } while (aLocale != null);
2077         }
2078         return null;
2079     }
2080 
2081     /**
2082      * {@icu} Based on a HTTP formatted list of acceptable locales, determine an available
2083      * locale for the user.  NullPointerException is thrown if acceptLanguageList or
2084      * availableLocales is null.  If fallback is non-null, it will contain true if a
2085      * fallback locale (one not in the acceptLanguageList) was returned.  The value on
2086      * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
2087      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
2088      * availableLocales matched).  No ULocale array element should be null; behavior is
2089      * undefined if this is the case.  This function will choose a locale from the
2090      * ULocale.getAvailableLocales() list as available.
2091      * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2092      * @param fallback if non-null, a 1-element array containing a boolean to be set with
2093      * the fallback status
2094      * @return one of the locales from the ULocale.getAvailableLocales() list, or null if
2095      * none match
2096      * @stable ICU 3.4
2097      */
acceptLanguage(String acceptLanguageList, boolean[] fallback)2098     public static ULocale acceptLanguage(String acceptLanguageList, boolean[] fallback) {
2099         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
2100                 fallback);
2101     }
2102 
2103     /**
2104      * {@icu} Based on an ordered array of acceptable locales, determine an available
2105      * locale for the user.  NullPointerException is thrown if acceptLanguageList or
2106      * availableLocales is null.  If fallback is non-null, it will contain true if a
2107      * fallback locale (one not in the acceptLanguageList) was returned.  The value on
2108      * entry is ignored.  ULocale will be one of the locales in availableLocales, or the
2109      * ROOT ULocale if if a ROOT locale was used as a fallback (because nothing else in
2110      * availableLocales matched).  No ULocale array element should be null; behavior is
2111      * undefined if this is the case.  This function will choose a locale from the
2112      * ULocale.getAvailableLocales() list as available.
2113      * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
2114      * @param fallback if non-null, a 1-element array containing a boolean to be set with
2115      * the fallback status
2116      * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
2117      * @stable ICU 3.4
2118      */
acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback)2119     public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[] fallback) {
2120         return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
2121                 fallback);
2122     }
2123 
2124     /**
2125      * Package local method used for parsing Accept-Language string
2126      */
parseAcceptLanguage(String acceptLanguage, boolean isLenient)2127     static ULocale[] parseAcceptLanguage(String acceptLanguage, boolean isLenient)
2128             throws ParseException {
2129         class ULocaleAcceptLanguageQ implements Comparable<ULocaleAcceptLanguageQ> {
2130             private double q;
2131             private double serial;
2132             public ULocaleAcceptLanguageQ(double theq, int theserial) {
2133                 q = theq;
2134                 serial = theserial;
2135             }
2136             public int compareTo(ULocaleAcceptLanguageQ other) {
2137                 if (q > other.q) { // reverse - to sort in descending order
2138                     return -1;
2139                 } else if (q < other.q) {
2140                     return 1;
2141                 }
2142                 if (serial < other.serial) {
2143                     return -1;
2144                 } else if (serial > other.serial) {
2145                     return 1;
2146                 } else {
2147                     return 0; // same object
2148                 }
2149             }
2150         }
2151 
2152         // parse out the acceptLanguage into an array
2153         TreeMap<ULocaleAcceptLanguageQ, ULocale> map =
2154                 new TreeMap<ULocaleAcceptLanguageQ, ULocale>();
2155         StringBuilder languageRangeBuf = new StringBuilder();
2156         StringBuilder qvalBuf = new StringBuilder();
2157         int state = 0;
2158         acceptLanguage += ","; // append comma to simplify the parsing code
2159         int n;
2160         boolean subTag = false;
2161         boolean q1 = false;
2162         for (n = 0; n < acceptLanguage.length(); n++) {
2163             boolean gotLanguageQ = false;
2164             char c = acceptLanguage.charAt(n);
2165             switch (state) {
2166             case 0: // before language-range start
2167                 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
2168                     // in language-range
2169                     languageRangeBuf.append(c);
2170                     state = 1;
2171                     subTag = false;
2172                 } else if (c == '*') {
2173                     languageRangeBuf.append(c);
2174                     state = 2;
2175                 } else if (c != ' ' && c != '\t') {
2176                     // invalid character
2177                     state = -1;
2178                 }
2179                 break;
2180             case 1: // in language-range
2181                 if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) {
2182                     languageRangeBuf.append(c);
2183                 } else if (c == '-') {
2184                     subTag = true;
2185                     languageRangeBuf.append(c);
2186                 } else if (c == '_') {
2187                     if (isLenient) {
2188                         subTag = true;
2189                         languageRangeBuf.append(c);
2190                     } else {
2191                         state = -1;
2192                     }
2193                 } else if ('0' <= c && c <= '9') {
2194                     if (subTag) {
2195                         languageRangeBuf.append(c);
2196                     } else {
2197                         // DIGIT is allowed only in language sub tag
2198                         state = -1;
2199                     }
2200                 } else if (c == ',') {
2201                     // language-q end
2202                     gotLanguageQ = true;
2203                 } else if (c == ' ' || c == '\t') {
2204                     // language-range end
2205                     state = 3;
2206                 } else if (c == ';') {
2207                     // before q
2208                     state = 4;
2209                 } else {
2210                     // invalid character for language-range
2211                     state = -1;
2212                 }
2213                 break;
2214             case 2: // saw wild card range
2215                 if (c == ',') {
2216                     // language-q end
2217                     gotLanguageQ = true;
2218                 } else if (c == ' ' || c == '\t') {
2219                     // language-range end
2220                     state = 3;
2221                 } else if (c == ';') {
2222                     // before q
2223                     state = 4;
2224                 } else {
2225                     // invalid
2226                     state = -1;
2227                 }
2228                 break;
2229             case 3: // language-range end
2230                 if (c == ',') {
2231                     // language-q end
2232                     gotLanguageQ = true;
2233                 } else if (c == ';') {
2234                     // before q
2235                     state =4;
2236                 } else if (c != ' ' && c != '\t') {
2237                     // invalid
2238                     state = -1;
2239                 }
2240                 break;
2241             case 4: // before q
2242                 if (c == 'q') {
2243                     // before equal
2244                     state = 5;
2245                 } else if (c != ' ' && c != '\t') {
2246                     // invalid
2247                     state = -1;
2248                 }
2249                 break;
2250             case 5: // before equal
2251                 if (c == '=') {
2252                     // before q value
2253                     state = 6;
2254                 } else if (c != ' ' && c != '\t') {
2255                     // invalid
2256                     state = -1;
2257                 }
2258                 break;
2259             case 6: // before q value
2260                 if (c == '0') {
2261                     // q value start with 0
2262                     q1 = false;
2263                     qvalBuf.append(c);
2264                     state = 7;
2265                 } else if (c == '1') {
2266                     // q value start with 1
2267                     qvalBuf.append(c);
2268                     state = 7;
2269                 } else if (c == '.') {
2270                     if (isLenient) {
2271                         qvalBuf.append(c);
2272                         state = 8;
2273                     } else {
2274                         state = -1;
2275                     }
2276                 } else if (c != ' ' && c != '\t') {
2277                     // invalid
2278                     state = -1;
2279                 }
2280                 break;
2281             case 7: // q value start
2282                 if (c == '.') {
2283                     // before q value fraction part
2284                     qvalBuf.append(c);
2285                     state = 8;
2286                 } else if (c == ',') {
2287                     // language-q end
2288                     gotLanguageQ = true;
2289                 } else if (c == ' ' || c == '\t') {
2290                     // after q value
2291                     state = 10;
2292                 } else {
2293                     // invalid
2294                     state = -1;
2295                 }
2296                 break;
2297             case 8: // before q value fraction part
2298                 if ('0' <= c || c <= '9') {
2299                     if (q1 && c != '0' && !isLenient) {
2300                         // if q value starts with 1, the fraction part must be 0
2301                         state = -1;
2302                     } else {
2303                         // in q value fraction part
2304                         qvalBuf.append(c);
2305                         state = 9;
2306                     }
2307                 } else {
2308                     // invalid
2309                     state = -1;
2310                 }
2311                 break;
2312             case 9: // in q value fraction part
2313                 if ('0' <= c && c <= '9') {
2314                     if (q1 && c != '0') {
2315                         // if q value starts with 1, the fraction part must be 0
2316                         state = -1;
2317                     } else {
2318                         qvalBuf.append(c);
2319                     }
2320                 } else if (c == ',') {
2321                     // language-q end
2322                     gotLanguageQ = true;
2323                 } else if (c == ' ' || c == '\t') {
2324                     // after q value
2325                     state = 10;
2326                 } else {
2327                     // invalid
2328                     state = -1;
2329                 }
2330                 break;
2331             case 10: // after q value
2332                 if (c == ',') {
2333                     // language-q end
2334                     gotLanguageQ = true;
2335                 } else if (c != ' ' && c != '\t') {
2336                     // invalid
2337                     state = -1;
2338                 }
2339                 break;
2340             }
2341             if (state == -1) {
2342                 // error state
2343                 throw new ParseException("Invalid Accept-Language", n);
2344             }
2345             if (gotLanguageQ) {
2346                 double q = 1.0;
2347                 if (qvalBuf.length() != 0) {
2348                     try {
2349                         q = Double.parseDouble(qvalBuf.toString());
2350                     } catch (NumberFormatException nfe) {
2351                         // Already validated, so it should never happen
2352                         q = 1.0;
2353                     }
2354                     if (q > 1.0) {
2355                         q = 1.0;
2356                     }
2357                 }
2358                 if (languageRangeBuf.charAt(0) != '*') {
2359                     int serial = map.size();
2360                     ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q, serial);
2361                     // sort in reverse order..   1.0, 0.9, 0.8 .. etc
2362                     map.put(entry, new ULocale(canonicalize(languageRangeBuf.toString())));
2363                 }
2364 
2365                 // reset buffer and parse state
2366                 languageRangeBuf.setLength(0);
2367                 qvalBuf.setLength(0);
2368                 state = 0;
2369             }
2370         }
2371         if (state != 0) {
2372             // Well, the parser should handle all cases.  So just in case.
2373             throw new ParseException("Invalid AcceptlLanguage", n);
2374         }
2375 
2376         // pull out the map
2377         ULocale acceptList[] = map.values().toArray(new ULocale[map.size()]);
2378         return acceptList;
2379     }
2380 
2381     private static final String UNDEFINED_LANGUAGE = "und";
2382     private static final String UNDEFINED_SCRIPT = "Zzzz";
2383     private static final String UNDEFINED_REGION = "ZZ";
2384 
2385     /**
2386      * {@icu} Adds the likely subtags for a provided locale ID, per the algorithm
2387      * described in the following CLDR technical report:
2388      *
2389      *   http://www.unicode.org/reports/tr35/#Likely_Subtags
2390      *
2391      * If the provided ULocale instance is already in the maximal form, or there is no
2392      * data available available for maximization, it will be returned.  For example,
2393      * "und-Zzzz" cannot be maximized, since there is no reasonable maximization.
2394      * Otherwise, a new ULocale instance with the maximal form is returned.
2395      *
2396      * Examples:
2397      *
2398      * "en" maximizes to "en_Latn_US"
2399      *
2400      * "de" maximizes to "de_Latn_US"
2401      *
2402      * "sr" maximizes to "sr_Cyrl_RS"
2403      *
2404      * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
2405      *
2406      * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
2407      *
2408      * @param loc The ULocale to maximize
2409      * @return The maximized ULocale instance.
2410      * @stable ICU 4.0
2411      */
addLikelySubtags(ULocale loc)2412     public static ULocale addLikelySubtags(ULocale loc) {
2413         String[] tags = new String[3];
2414         String trailing = null;
2415 
2416         int trailingIndex = parseTagString(
2417                 loc.localeID,
2418                 tags);
2419 
2420         if (trailingIndex < loc.localeID.length()) {
2421             trailing = loc.localeID.substring(trailingIndex);
2422         }
2423 
2424         String newLocaleID =
2425                 createLikelySubtagsString(
2426                         tags[0],
2427                         tags[1],
2428                         tags[2],
2429                         trailing);
2430 
2431         return newLocaleID == null ? loc : new ULocale(newLocaleID);
2432     }
2433 
2434     /**
2435      * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
2436      * in the following CLDR technical report:<blockquote>
2437      *
2438      *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
2439      *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
2440      *
2441      * If the provided ULocale instance is already in the minimal form, or there
2442      * is no data available for minimization, it will be returned.  Since the
2443      * minimization algorithm relies on proper maximization, see the comments
2444      * for addLikelySubtags for reasons why there might not be any data.
2445      *
2446      * Examples:<pre>
2447      *
2448      * "en_Latn_US" minimizes to "en"
2449      *
2450      * "de_Latn_US" minimizes to "de"
2451      *
2452      * "sr_Cyrl_RS" minimizes to "sr"
2453      *
2454      * "zh_Hant_TW" minimizes to "zh_TW" (The region is preferred to the
2455      * script, and minimizing to "zh" would imply "zh_Hans_CN".) </pre>
2456      *
2457      * @param loc The ULocale to minimize
2458      * @return The minimized ULocale instance.
2459      * @stable ICU 4.0
2460      */
minimizeSubtags(ULocale loc)2461     public static ULocale minimizeSubtags(ULocale loc) {
2462         return minimizeSubtags(loc, Minimize.FAVOR_REGION);
2463     }
2464 
2465     /**
2466      * Options for minimizeSubtags.
2467      * @internal
2468      * @deprecated This API is ICU internal only.
2469      */
2470     @Deprecated
2471     public enum Minimize {
2472         /**
2473          * Favor including the script, when either the region <b>or</b> the script could be suppressed, but not both.
2474          * @internal
2475          * @deprecated This API is ICU internal only.
2476          */
2477         @Deprecated
2478         FAVOR_SCRIPT,
2479         /**
2480          * Favor including the region, when either the region <b>or</b> the script could be suppressed, but not both.
2481          * @internal
2482          * @deprecated This API is ICU internal only.
2483          */
2484         @Deprecated
2485         FAVOR_REGION
2486     }
2487 
2488     /**
2489      * {@icu} Minimizes the subtags for a provided locale ID, per the algorithm described
2490      * in the following CLDR technical report:<blockquote>
2491      *
2492      *   <a href="http://www.unicode.org/reports/tr35/#Likely_Subtags"
2493      *>http://www.unicode.org/reports/tr35/#Likely_Subtags</a></blockquote>
2494      *
2495      * If the provided ULocale instance is already in the minimal form, or there
2496      * is no data available for minimization, it will be returned.  Since the
2497      * minimization algorithm relies on proper maximization, see the comments
2498      * for addLikelySubtags for reasons why there might not be any data.
2499      *
2500      * Examples:<pre>
2501      *
2502      * "en_Latn_US" minimizes to "en"
2503      *
2504      * "de_Latn_US" minimizes to "de"
2505      *
2506      * "sr_Cyrl_RS" minimizes to "sr"
2507      *
2508      * "zh_Hant_TW" minimizes to "zh_TW" if fieldToFavor == {@link Minimize#FAVOR_REGION}
2509      * "zh_Hant_TW" minimizes to "zh_Hant" if fieldToFavor == {@link Minimize#FAVOR_SCRIPT}
2510      * </pre>
2511      * The fieldToFavor only has an effect if either the region or the script could be suppressed, but not both.
2512      * @param loc The ULocale to minimize
2513      * @param fieldToFavor Indicate which should be preferred, when either the region <b>or</b> the script could be suppressed, but not both.
2514      * @return The minimized ULocale instance.
2515      * @internal
2516      * @deprecated This API is ICU internal only.
2517      */
2518     @Deprecated
minimizeSubtags(ULocale loc, Minimize fieldToFavor)2519     public static ULocale minimizeSubtags(ULocale loc, Minimize fieldToFavor) {
2520         String[] tags = new String[3];
2521 
2522         int trailingIndex = parseTagString(
2523                 loc.localeID,
2524                 tags);
2525 
2526         String originalLang = tags[0];
2527         String originalScript = tags[1];
2528         String originalRegion = tags[2];
2529         String originalTrailing = null;
2530 
2531         if (trailingIndex < loc.localeID.length()) {
2532             /*
2533              * Create a String that contains everything
2534              * after the language, script, and region.
2535              */
2536             originalTrailing = loc.localeID.substring(trailingIndex);
2537         }
2538 
2539         /**
2540          * First, we need to first get the maximization
2541          * by adding any likely subtags.
2542          **/
2543         String maximizedLocaleID =
2544                 createLikelySubtagsString(
2545                         originalLang,
2546                         originalScript,
2547                         originalRegion,
2548                         null);
2549 
2550         /**
2551          * If maximization fails, there's nothing
2552          * we can do.
2553          **/
2554         if (isEmptyString(maximizedLocaleID)) {
2555             return loc;
2556         }
2557         else {
2558             /**
2559              * Start first with just the language.
2560              **/
2561             String tag =
2562                     createLikelySubtagsString(
2563                             originalLang,
2564                             null,
2565                             null,
2566                             null);
2567 
2568             if (tag.equals(maximizedLocaleID)) {
2569                 String newLocaleID =
2570                         createTagString(
2571                                 originalLang,
2572                                 null,
2573                                 null,
2574                                 originalTrailing);
2575 
2576                 return new ULocale(newLocaleID);
2577             }
2578         }
2579 
2580         /**
2581          * Next, try the language and region.
2582          **/
2583         if (fieldToFavor == Minimize.FAVOR_REGION) {
2584             if (originalRegion.length() != 0) {
2585                 String tag =
2586                         createLikelySubtagsString(
2587                                 originalLang,
2588                                 null,
2589                                 originalRegion,
2590                                 null);
2591 
2592                 if (tag.equals(maximizedLocaleID)) {
2593                     String newLocaleID =
2594                             createTagString(
2595                                     originalLang,
2596                                     null,
2597                                     originalRegion,
2598                                     originalTrailing);
2599 
2600                     return new ULocale(newLocaleID);
2601                 }
2602             }
2603             if (originalScript.length() != 0){
2604                 String tag =
2605                         createLikelySubtagsString(
2606                                 originalLang,
2607                                 originalScript,
2608                                 null,
2609                                 null);
2610 
2611                 if (tag.equals(maximizedLocaleID)) {
2612                     String newLocaleID =
2613                             createTagString(
2614                                     originalLang,
2615                                     originalScript,
2616                                     null,
2617                                     originalTrailing);
2618 
2619                     return new ULocale(newLocaleID);
2620                 }
2621             }
2622         } else { // FAVOR_SCRIPT, so
2623             if (originalScript.length() != 0){
2624                 String tag =
2625                         createLikelySubtagsString(
2626                                 originalLang,
2627                                 originalScript,
2628                                 null,
2629                                 null);
2630 
2631                 if (tag.equals(maximizedLocaleID)) {
2632                     String newLocaleID =
2633                             createTagString(
2634                                     originalLang,
2635                                     originalScript,
2636                                     null,
2637                                     originalTrailing);
2638 
2639                     return new ULocale(newLocaleID);
2640                 }
2641             }
2642             if (originalRegion.length() != 0) {
2643                 String tag =
2644                         createLikelySubtagsString(
2645                                 originalLang,
2646                                 null,
2647                                 originalRegion,
2648                                 null);
2649 
2650                 if (tag.equals(maximizedLocaleID)) {
2651                     String newLocaleID =
2652                             createTagString(
2653                                     originalLang,
2654                                     null,
2655                                     originalRegion,
2656                                     originalTrailing);
2657 
2658                     return new ULocale(newLocaleID);
2659                 }
2660             }
2661         }
2662         return loc;
2663     }
2664 
2665     /**
2666      * A trivial utility function that checks for a null
2667      * reference or checks the length of the supplied String.
2668      *
2669      *   @param string The string to check
2670      *
2671      *   @return true if the String is empty, or if the reference is null.
2672      */
isEmptyString(String string)2673     private static boolean isEmptyString(String string) {
2674         return string == null || string.length() == 0;
2675     }
2676 
2677     /**
2678      * Append a tag to a StringBuilder, adding the separator if necessary.The tag must
2679      * not be a zero-length string.
2680      *
2681      * @param tag The tag to add.
2682      * @param buffer The output buffer.
2683      **/
appendTag(String tag, StringBuilder buffer)2684     private static void appendTag(String tag, StringBuilder buffer) {
2685         if (buffer.length() != 0) {
2686             buffer.append(UNDERSCORE);
2687         }
2688 
2689         buffer.append(tag);
2690     }
2691 
2692     /**
2693      * Create a tag string from the supplied parameters.  The lang, script and region
2694      * parameters may be null references.
2695      *
2696      * If any of the language, script or region parameters are empty, and the alternateTags
2697      * parameter is not null, it will be parsed for potential language, script and region tags
2698      * to be used when constructing the new tag.  If the alternateTags parameter is null, or
2699      * it contains no language tag, the default tag for the unknown language is used.
2700      *
2701      * @param lang The language tag to use.
2702      * @param script The script tag to use.
2703      * @param region The region tag to use.
2704      * @param trailing Any trailing data to append to the new tag.
2705      * @param alternateTags A string containing any alternate tags.
2706      * @return The new tag string.
2707      **/
createTagString(String lang, String script, String region, String trailing, String alternateTags)2708     private static String createTagString(String lang, String script, String region,
2709             String trailing, String alternateTags) {
2710 
2711         LocaleIDParser parser = null;
2712         boolean regionAppended = false;
2713 
2714         StringBuilder tag = new StringBuilder();
2715 
2716         if (!isEmptyString(lang)) {
2717             appendTag(
2718                     lang,
2719                     tag);
2720         }
2721         else if (isEmptyString(alternateTags)) {
2722             /*
2723              * Append the value for an unknown language, if
2724              * we found no language.
2725              */
2726             appendTag(
2727                     UNDEFINED_LANGUAGE,
2728                     tag);
2729         }
2730         else {
2731             parser = new LocaleIDParser(alternateTags);
2732 
2733             String alternateLang = parser.getLanguage();
2734 
2735             /*
2736              * Append the value for an unknown language, if
2737              * we found no language.
2738              */
2739             appendTag(
2740                     !isEmptyString(alternateLang) ? alternateLang : UNDEFINED_LANGUAGE,
2741                             tag);
2742         }
2743 
2744         if (!isEmptyString(script)) {
2745             appendTag(
2746                     script,
2747                     tag);
2748         }
2749         else if (!isEmptyString(alternateTags)) {
2750             /*
2751              * Parse the alternateTags string for the script.
2752              */
2753             if (parser == null) {
2754                 parser = new LocaleIDParser(alternateTags);
2755             }
2756 
2757             String alternateScript = parser.getScript();
2758 
2759             if (!isEmptyString(alternateScript)) {
2760                 appendTag(
2761                         alternateScript,
2762                         tag);
2763             }
2764         }
2765 
2766         if (!isEmptyString(region)) {
2767             appendTag(
2768                     region,
2769                     tag);
2770 
2771             regionAppended = true;
2772         }
2773         else if (!isEmptyString(alternateTags)) {
2774             /*
2775              * Parse the alternateTags string for the region.
2776              */
2777             if (parser == null) {
2778                 parser = new LocaleIDParser(alternateTags);
2779             }
2780 
2781             String alternateRegion = parser.getCountry();
2782 
2783             if (!isEmptyString(alternateRegion)) {
2784                 appendTag(
2785                         alternateRegion,
2786                         tag);
2787 
2788                 regionAppended = true;
2789             }
2790         }
2791 
2792         if (trailing != null && trailing.length() > 1) {
2793             /*
2794              * The current ICU format expects two underscores
2795              * will separate the variant from the preceeding
2796              * parts of the tag, if there is no region.
2797              */
2798             int separators = 0;
2799 
2800             if (trailing.charAt(0) == UNDERSCORE) {
2801                 if (trailing.charAt(1) == UNDERSCORE) {
2802                     separators = 2;
2803                 }
2804             }
2805             else {
2806                 separators = 1;
2807             }
2808 
2809             if (regionAppended) {
2810                 /*
2811                  * If we appended a region, we may need to strip
2812                  * the extra separator from the variant portion.
2813                  */
2814                 if (separators == 2) {
2815                     tag.append(trailing.substring(1));
2816                 }
2817                 else {
2818                     tag.append(trailing);
2819                 }
2820             }
2821             else {
2822                 /*
2823                  * If we did not append a region, we may need to add
2824                  * an extra separator to the variant portion.
2825                  */
2826                 if (separators == 1) {
2827                     tag.append(UNDERSCORE);
2828                 }
2829                 tag.append(trailing);
2830             }
2831         }
2832 
2833         return tag.toString();
2834     }
2835 
2836     /**
2837      * Create a tag string from the supplied parameters.  The lang, script and region
2838      * parameters may be null references.If the lang parameter is an empty string, the
2839      * default value for an unknown language is written to the output buffer.
2840      *
2841      * @param lang The language tag to use.
2842      * @param script The script tag to use.
2843      * @param region The region tag to use.
2844      * @param trailing Any trailing data to append to the new tag.
2845      * @return The new String.
2846      **/
createTagString(String lang, String script, String region, String trailing)2847     static String createTagString(String lang, String script, String region, String trailing) {
2848         return createTagString(lang, script, region, trailing, null);
2849     }
2850 
2851     /**
2852      * Parse the language, script, and region subtags from a tag string, and return the results.
2853      *
2854      * This function does not return the canonical strings for the unknown script and region.
2855      *
2856      * @param localeID The locale ID to parse.
2857      * @param tags An array of three String references to return the subtag strings.
2858      * @return The number of chars of the localeID parameter consumed.
2859      **/
parseTagString(String localeID, String tags[])2860     private static int parseTagString(String localeID, String tags[]) {
2861         LocaleIDParser parser = new LocaleIDParser(localeID);
2862 
2863         String lang = parser.getLanguage();
2864         String script = parser.getScript();
2865         String region = parser.getCountry();
2866 
2867         if (isEmptyString(lang)) {
2868             tags[0] = UNDEFINED_LANGUAGE;
2869         }
2870         else {
2871             tags[0] = lang;
2872         }
2873 
2874         if (script.equals(UNDEFINED_SCRIPT)) {
2875             tags[1] = "";
2876         }
2877         else {
2878             tags[1] = script;
2879         }
2880 
2881         if (region.equals(UNDEFINED_REGION)) {
2882             tags[2] = "";
2883         }
2884         else {
2885             tags[2] = region;
2886         }
2887 
2888         /*
2889          * Search for the variant.  If there is one, then return the index of
2890          * the preceeding separator.
2891          * If there's no variant, search for the keyword delimiter,
2892          * and return its index.  Otherwise, return the length of the
2893          * string.
2894          *
2895          * $TOTO(dbertoni) we need to take into account that we might
2896          * find a part of the language as the variant, since it can
2897          * can have a variant portion that is long enough to contain
2898          * the same characters as the variant.
2899          */
2900         String variant = parser.getVariant();
2901 
2902         if (!isEmptyString(variant)){
2903             int index = localeID.indexOf(variant);
2904 
2905 
2906             return  index > 0 ? index - 1 : index;
2907         }
2908         else
2909         {
2910             int index = localeID.indexOf('@');
2911 
2912             return index == -1 ? localeID.length() : index;
2913         }
2914     }
2915 
lookupLikelySubtags(String localeId)2916     private static String lookupLikelySubtags(String localeId) {
2917         UResourceBundle bundle =
2918                 UResourceBundle.getBundleInstance(
2919                         ICUResourceBundle.ICU_BASE_NAME, "likelySubtags");
2920         try {
2921             return bundle.getString(localeId);
2922         }
2923         catch(MissingResourceException e) {
2924             return null;
2925         }
2926     }
2927 
createLikelySubtagsString(String lang, String script, String region, String variants)2928     private static String createLikelySubtagsString(String lang, String script, String region,
2929             String variants) {
2930 
2931         /**
2932          * Try the language with the script and region first.
2933          */
2934         if (!isEmptyString(script) && !isEmptyString(region)) {
2935 
2936             String searchTag =
2937                     createTagString(
2938                             lang,
2939                             script,
2940                             region,
2941                             null);
2942 
2943             String likelySubtags = lookupLikelySubtags(searchTag);
2944 
2945             /*
2946             if (likelySubtags == null) {
2947                 if (likelySubtags2 != null) {
2948                     System.err.println("Tag mismatch: \"(null)\" \"" + likelySubtags2 + "\"");
2949                 }
2950             }
2951             else if (likelySubtags2 == null) {
2952                 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"(null)\"");
2953             }
2954             else if (!likelySubtags.equals(likelySubtags2)) {
2955                 System.err.println("Tag mismatch: \"" + likelySubtags + "\" \"" + likelySubtags2
2956                     + "\"");
2957             }
2958              */
2959             if (likelySubtags != null) {
2960                 // Always use the language tag from the
2961                 // maximal string, since it may be more
2962                 // specific than the one provided.
2963                 return createTagString(
2964                         null,
2965                         null,
2966                         null,
2967                         variants,
2968                         likelySubtags);
2969             }
2970         }
2971 
2972         /**
2973          * Try the language with just the script.
2974          **/
2975         if (!isEmptyString(script)) {
2976 
2977             String searchTag =
2978                     createTagString(
2979                             lang,
2980                             script,
2981                             null,
2982                             null);
2983 
2984             String likelySubtags = lookupLikelySubtags(searchTag);
2985             if (likelySubtags != null) {
2986                 // Always use the language tag from the
2987                 // maximal string, since it may be more
2988                 // specific than the one provided.
2989                 return createTagString(
2990                         null,
2991                         null,
2992                         region,
2993                         variants,
2994                         likelySubtags);
2995             }
2996         }
2997 
2998         /**
2999          * Try the language with just the region.
3000          **/
3001         if (!isEmptyString(region)) {
3002 
3003             String searchTag =
3004                     createTagString(
3005                             lang,
3006                             null,
3007                             region,
3008                             null);
3009 
3010             String likelySubtags = lookupLikelySubtags(searchTag);
3011 
3012             if (likelySubtags != null) {
3013                 // Always use the language tag from the
3014                 // maximal string, since it may be more
3015                 // specific than the one provided.
3016                 return createTagString(
3017                         null,
3018                         script,
3019                         null,
3020                         variants,
3021                         likelySubtags);
3022             }
3023         }
3024 
3025         /**
3026          * Finally, try just the language.
3027          **/
3028         {
3029             String searchTag =
3030                     createTagString(
3031                             lang,
3032                             null,
3033                             null,
3034                             null);
3035 
3036             String likelySubtags = lookupLikelySubtags(searchTag);
3037 
3038             if (likelySubtags != null) {
3039                 // Always use the language tag from the
3040                 // maximal string, since it may be more
3041                 // specific than the one provided.
3042                 return createTagString(
3043                         null,
3044                         script,
3045                         region,
3046                         variants,
3047                         likelySubtags);
3048             }
3049         }
3050 
3051         return null;
3052     }
3053 
3054     // --------------------------------
3055     //      BCP47/OpenJDK APIs
3056     // --------------------------------
3057 
3058     /**
3059      * The key for the private use locale extension ('x').
3060      *
3061      * @see #getExtension(char)
3062      * @see Builder#setExtension(char, String)
3063      *
3064      * @stable ICU 4.2
3065      */
3066     public static final char PRIVATE_USE_EXTENSION = 'x';
3067 
3068     /**
3069      * The key for Unicode locale extension ('u').
3070      *
3071      * @see #getExtension(char)
3072      * @see Builder#setExtension(char, String)
3073      *
3074      * @stable ICU 4.2
3075      */
3076     public static final char UNICODE_LOCALE_EXTENSION = 'u';
3077 
3078     /**
3079      * Returns the extension (or private use) value associated with
3080      * the specified key, or null if there is no extension
3081      * associated with the key. To be well-formed, the key must be one
3082      * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
3083      * for example 'z' and 'Z' represent the same extension.
3084      *
3085      * @param key the extension key
3086      * @return The extension, or null if this locale defines no
3087      * extension for the specified key.
3088      * @throws IllegalArgumentException if key is not well-formed
3089      * @see #PRIVATE_USE_EXTENSION
3090      * @see #UNICODE_LOCALE_EXTENSION
3091      *
3092      * @stable ICU 4.2
3093      */
getExtension(char key)3094     public String getExtension(char key) {
3095         if (!LocaleExtensions.isValidKey(key)) {
3096             throw new IllegalArgumentException("Invalid extension key: " + key);
3097         }
3098         return extensions().getExtensionValue(key);
3099     }
3100 
3101     /**
3102      * Returns the set of extension keys associated with this locale, or the
3103      * empty set if it has no extensions. The returned set is unmodifiable.
3104      * The keys will all be lower-case.
3105      *
3106      * @return the set of extension keys, or the empty set if this locale has
3107      * no extensions
3108      * @stable ICU 4.2
3109      */
getExtensionKeys()3110     public Set<Character> getExtensionKeys() {
3111         return extensions().getKeys();
3112     }
3113 
3114     /**
3115      * Returns the set of unicode locale attributes associated with
3116      * this locale, or the empty set if it has no attributes. The
3117      * returned set is unmodifiable.
3118      *
3119      * @return The set of attributes.
3120      * @stable ICU 4.6
3121      */
getUnicodeLocaleAttributes()3122     public Set<String> getUnicodeLocaleAttributes() {
3123         return extensions().getUnicodeLocaleAttributes();
3124     }
3125 
3126     /**
3127      * Returns the Unicode locale type associated with the specified Unicode locale key
3128      * for this locale. Returns the empty string for keys that are defined with no type.
3129      * Returns null if the key is not defined. Keys are case-insensitive. The key must
3130      * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
3131      * thrown.
3132      *
3133      * @param key the Unicode locale key
3134      * @return The Unicode locale type associated with the key, or null if the
3135      * locale does not define the key.
3136      * @throws IllegalArgumentException if the key is not well-formed
3137      * @throws NullPointerException if <code>key</code> is null
3138      *
3139      * @stable ICU 4.4
3140      */
getUnicodeLocaleType(String key)3141     public String getUnicodeLocaleType(String key) {
3142         if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) {
3143             throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
3144         }
3145         return extensions().getUnicodeLocaleType(key);
3146     }
3147 
3148     /**
3149      * Returns the set of Unicode locale keys defined by this locale, or the empty set if
3150      * this locale has none.  The returned set is immutable.  Keys are all lower case.
3151      *
3152      * @return The set of Unicode locale keys, or the empty set if this locale has
3153      * no Unicode locale keywords.
3154      *
3155      * @stable ICU 4.4
3156      */
getUnicodeLocaleKeys()3157     public Set<String> getUnicodeLocaleKeys() {
3158         return extensions().getUnicodeLocaleKeys();
3159     }
3160 
3161     /**
3162      * Returns a well-formed IETF BCP 47 language tag representing
3163      * this locale.
3164      *
3165      * <p>If this <code>ULocale</code> has a language, script, country, or
3166      * variant that does not satisfy the IETF BCP 47 language tag
3167      * syntax requirements, this method handles these fields as
3168      * described below:
3169      *
3170      * <p><b>Language:</b> If language is empty, or not well-formed
3171      * (for example "a" or "e2"), it will be emitted as "und" (Undetermined).
3172      *
3173      * <p><b>Script:</b> If script is not well-formed (for example "12"
3174      * or "Latin"), it will be omitted.
3175      *
3176      * <p><b>Country:</b> If country is not well-formed (for example "12"
3177      * or "USA"), it will be omitted.
3178      *
3179      * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment
3180      * (delimited by '-' or '_') is emitted as a subtag.  Otherwise:
3181      * <ul>
3182      *
3183      * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
3184      * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
3185      * ill-formed sub-segment and all following will be appended to
3186      * the private use subtag.  The first appended subtag will be
3187      * "lvariant", followed by the sub-segments in order, separated by
3188      * hyphen. For example, "x-lvariant-WIN",
3189      * "Oracle-x-lvariant-JDK-Standard-Edition".
3190      *
3191      * <li>if any sub-segment does not match
3192      * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
3193      * and the problematic sub-segment and all following sub-segments
3194      * will be omitted.  If the remainder is non-empty, it will be
3195      * emitted as a private use subtag as above (even if the remainder
3196      * turns out to be well-formed).  For example,
3197      * "Solaris_isjustthecoolestthing" is emitted as
3198      * "x-lvariant-Solaris", not as "solaris".</li></ul>
3199      *
3200      * <p><b>Note:</b> Although the language tag created by this
3201      * method is well-formed (satisfies the syntax requirements
3202      * defined by the IETF BCP 47 specification), it is not
3203      * necessarily a valid BCP 47 language tag.  For example,
3204      * <pre>
3205      *   new Locale("xx", "YY").toLanguageTag();</pre>
3206      *
3207      * will return "xx-YY", but the language subtag "xx" and the
3208      * region subtag "YY" are invalid because they are not registered
3209      * in the IANA Language Subtag Registry.
3210      *
3211      * @return a BCP47 language tag representing the locale
3212      * @see #forLanguageTag(String)
3213      *
3214      * @stable ICU 4.2
3215      */
toLanguageTag()3216     public String toLanguageTag() {
3217         BaseLocale base = base();
3218         LocaleExtensions exts = extensions();
3219 
3220         if (base.getVariant().equalsIgnoreCase("POSIX")) {
3221             // special handling for variant POSIX
3222             base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), "");
3223             if (exts.getUnicodeLocaleType("va") == null) {
3224                 // add va-posix
3225                 InternalLocaleBuilder ilocbld = new InternalLocaleBuilder();
3226                 try {
3227                     ilocbld.setLocale(BaseLocale.ROOT, exts);
3228                     ilocbld.setUnicodeLocaleKeyword("va", "posix");
3229                     exts = ilocbld.getLocaleExtensions();
3230                 } catch (LocaleSyntaxException e) {
3231                     // this should not happen
3232                     throw new RuntimeException(e);
3233                 }
3234             }
3235         }
3236 
3237         LanguageTag tag = LanguageTag.parseLocale(base, exts);
3238 
3239         StringBuilder buf = new StringBuilder();
3240         String subtag = tag.getLanguage();
3241         if (subtag.length() > 0) {
3242             buf.append(LanguageTag.canonicalizeLanguage(subtag));
3243         }
3244 
3245         subtag = tag.getScript();
3246         if (subtag.length() > 0) {
3247             buf.append(LanguageTag.SEP);
3248             buf.append(LanguageTag.canonicalizeScript(subtag));
3249         }
3250 
3251         subtag = tag.getRegion();
3252         if (subtag.length() > 0) {
3253             buf.append(LanguageTag.SEP);
3254             buf.append(LanguageTag.canonicalizeRegion(subtag));
3255         }
3256 
3257         List<String>subtags = tag.getVariants();
3258         for (String s : subtags) {
3259             buf.append(LanguageTag.SEP);
3260             buf.append(LanguageTag.canonicalizeVariant(s));
3261         }
3262 
3263         subtags = tag.getExtensions();
3264         for (String s : subtags) {
3265             buf.append(LanguageTag.SEP);
3266             buf.append(LanguageTag.canonicalizeExtension(s));
3267         }
3268 
3269         subtag = tag.getPrivateuse();
3270         if (subtag.length() > 0) {
3271             if (buf.length() > 0) {
3272                 buf.append(LanguageTag.SEP);
3273             }
3274             buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
3275             buf.append(LanguageTag.canonicalizePrivateuse(subtag));
3276         }
3277 
3278         return buf.toString();
3279     }
3280 
3281     /**
3282      * Returns a locale for the specified IETF BCP 47 language tag string.
3283      *
3284      * <p>If the specified language tag contains any ill-formed subtags,
3285      * the first such subtag and all following subtags are ignored.  Compare
3286      * to {@link ULocale.Builder#setLanguageTag} which throws an exception
3287      * in this case.
3288      *
3289      * <p>The following <b>conversions</b> are performed:<ul>
3290      *
3291      * <li>The language code "und" is mapped to language "".
3292      *
3293      * <li>The portion of a private use subtag prefixed by "lvariant",
3294      * if any, is removed and appended to the variant field in the
3295      * result locale (without case normalization).  If it is then
3296      * empty, the private use subtag is discarded:
3297      *
3298      * <pre>
3299      *     ULocale loc;
3300      *     loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j);
3301      *     loc.getVariant(); // returns "ICU4J"
3302      *     loc.getExtension('x'); // returns null
3303      *
3304      *     loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def");
3305      *     loc.getVariant(); // returns "ICU4J_ABC_DEF"
3306      *     loc.getExtension('x'); // returns "urp"
3307      * </pre>
3308      *
3309      * <li>When the languageTag argument contains an extlang subtag,
3310      * the first such subtag is used as the language, and the primary
3311      * language subtag and other extlang subtags are ignored:
3312      *
3313      * <pre>
3314      *     ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
3315      *     ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
3316      * </pre>
3317      *
3318      * <li>Case is normalized. Language is normalized to lower case,
3319      * script to title case, country to upper case, variant to upper case,
3320      * and extensions to lower case.
3321      *
3322      * <p>This implements the 'Language-Tag' production of BCP47, and
3323      * so supports grandfathered (regular and irregular) as well as
3324      * private use language tags.  Stand alone private use tags are
3325      * represented as empty language and extension 'x-whatever',
3326      * and grandfathered tags are converted to their canonical replacements
3327      * where they exist.
3328      *
3329      * <p>Grandfathered tags with canonical replacements are as follows:
3330      *
3331      * <table>
3332      * <tbody align="center">
3333      * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>modern replacement</th></tr>
3334      * <tr><td>art-lojban</td><td>&nbsp;</td><td>jbo</td></tr>
3335      * <tr><td>i-ami</td><td>&nbsp;</td><td>ami</td></tr>
3336      * <tr><td>i-bnn</td><td>&nbsp;</td><td>bnn</td></tr>
3337      * <tr><td>i-hak</td><td>&nbsp;</td><td>hak</td></tr>
3338      * <tr><td>i-klingon</td><td>&nbsp;</td><td>tlh</td></tr>
3339      * <tr><td>i-lux</td><td>&nbsp;</td><td>lb</td></tr>
3340      * <tr><td>i-navajo</td><td>&nbsp;</td><td>nv</td></tr>
3341      * <tr><td>i-pwn</td><td>&nbsp;</td><td>pwn</td></tr>
3342      * <tr><td>i-tao</td><td>&nbsp;</td><td>tao</td></tr>
3343      * <tr><td>i-tay</td><td>&nbsp;</td><td>tay</td></tr>
3344      * <tr><td>i-tsu</td><td>&nbsp;</td><td>tsu</td></tr>
3345      * <tr><td>no-bok</td><td>&nbsp;</td><td>nb</td></tr>
3346      * <tr><td>no-nyn</td><td>&nbsp;</td><td>nn</td></tr>
3347      * <tr><td>sgn-BE-FR</td><td>&nbsp;</td><td>sfb</td></tr>
3348      * <tr><td>sgn-BE-NL</td><td>&nbsp;</td><td>vgt</td></tr>
3349      * <tr><td>sgn-CH-DE</td><td>&nbsp;</td><td>sgg</td></tr>
3350      * <tr><td>zh-guoyu</td><td>&nbsp;</td><td>cmn</td></tr>
3351      * <tr><td>zh-hakka</td><td>&nbsp;</td><td>hak</td></tr>
3352      * <tr><td>zh-min-nan</td><td>&nbsp;</td><td>nan</td></tr>
3353      * <tr><td>zh-xiang</td><td>&nbsp;</td><td>hsn</td></tr>
3354      * </tbody>
3355      * </table>
3356      *
3357      * <p>Grandfathered tags with no modern replacement will be
3358      * converted as follows:
3359      *
3360      * <table>
3361      * <tbody align="center">
3362      * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>converts to</th></tr>
3363      * <tr><td>cel-gaulish</td><td>&nbsp;</td><td>xtg-x-cel-gaulish</td></tr>
3364      * <tr><td>en-GB-oed</td><td>&nbsp;</td><td>en-GB-x-oed</td></tr>
3365      * <tr><td>i-default</td><td>&nbsp;</td><td>en-x-i-default</td></tr>
3366      * <tr><td>i-enochian</td><td>&nbsp;</td><td>und-x-i-enochian</td></tr>
3367      * <tr><td>i-mingo</td><td>&nbsp;</td><td>see-x-i-mingo</td></tr>
3368      * <tr><td>zh-min</td><td>&nbsp;</td><td>nan-x-zh-min</td></tr>
3369      * </tbody>
3370      * </table>
3371      *
3372      * <p>For a list of all grandfathered tags, see the
3373      * IANA Language Subtag Registry (search for "Type: grandfathered").
3374      *
3375      * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
3376      * and <code>forLanguageTag</code> will round-trip.
3377      *
3378      * @param languageTag the language tag
3379      * @return The locale that best represents the language tag.
3380      * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
3381      * @see #toLanguageTag()
3382      * @see ULocale.Builder#setLanguageTag(String)
3383      *
3384      * @stable ICU 4.2
3385      */
forLanguageTag(String languageTag)3386     public static ULocale forLanguageTag(String languageTag) {
3387         LanguageTag tag = LanguageTag.parse(languageTag, null);
3388         InternalLocaleBuilder bldr = new InternalLocaleBuilder();
3389         bldr.setLanguageTag(tag);
3390         return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions());
3391     }
3392 
3393     /**
3394      * {@icu} Converts the specified keyword (legacy key, or BCP 47 Unicode locale
3395      * extension key) to the equivalent BCP 47 Unicode locale extension key.
3396      * For example, BCP 47 Unicode locale extension key "co" is returned for
3397      * the input keyword "collation".
3398      * <p>
3399      * When the specified keyword is unknown, but satisfies the BCP syntax,
3400      * then the lower-case version of the input keyword will be returned.
3401      * For example,
3402      * <code>toUnicodeLocaleKey("ZZ")</code> returns "zz".
3403      *
3404      * @param keyword       the input locale keyword (either legacy key
3405      *                      such as "collation" or BCP 47 Unicode locale extension
3406      *                      key such as "co").
3407      * @return              the well-formed BCP 47 Unicode locale extension key,
3408      *                      or null if the specified locale keyword cannot be mapped
3409      *                      to a well-formed BCP 47 Unicode locale extension key.
3410      * @see #toLegacyKey(String)
3411      * @draft ICU 54
3412      * @provisional This API might change or be removed in a future release.
3413      */
toUnicodeLocaleKey(String keyword)3414     public static String toUnicodeLocaleKey(String keyword) {
3415         String bcpKey = KeyTypeData.toBcpKey(keyword);
3416         if (bcpKey == null && UnicodeLocaleExtension.isKey(keyword)) {
3417             // unknown keyword, but syntax is fine..
3418             bcpKey = AsciiUtil.toLowerString(keyword);
3419         }
3420         return bcpKey;
3421     }
3422 
3423     /**
3424      * {@icu} Converts the specified keyword value (legacy type, or BCP 47
3425      * Unicode locale extension type) to the well-formed BCP 47 Unicode locale
3426      * extension type for the specified keyword (category). For example, BCP 47
3427      * Unicode locale extension type "phonebk" is returned for the input
3428      * keyword value "phonebook", with the keyword "collation" (or "co").
3429      * <p>
3430      * When the specified keyword is not recognized, but the specified value
3431      * satisfies the syntax of the BCP 47 Unicode locale extension type,
3432      * or when the specified keyword allows 'variable' type and the specified
3433      * value satisfies the syntax, the lower-case version of the input value
3434      * will be returned. For example,
3435      * <code>toUnicodeLocaleType("Foo", "Bar")</code> returns "bar",
3436      * <code>toUnicodeLocaleType("variableTop", "00A4")</code> returns "00a4".
3437      *
3438      * @param keyword       the locale keyword (either legacy key such as
3439      *                      "collation" or BCP 47 Unicode locale extension
3440      *                      key such as "co").
3441      * @param value         the locale keyword value (either legacy type
3442      *                      such as "phonebook" or BCP 47 Unicode locale extension
3443      *                      type such as "phonebk").
3444      * @return              the well-formed BCP47 Unicode locale extension type,
3445      *                      or null if the locale keyword value cannot be mapped to
3446      *                      a well-formed BCP 47 Unicode locale extension type.
3447      * @see #toLegacyType(String, String)
3448      * @draft ICU 54
3449      * @provisional This API might change or be removed in a future release.
3450      */
toUnicodeLocaleType(String keyword, String value)3451     public static String toUnicodeLocaleType(String keyword, String value) {
3452         String bcpType = KeyTypeData.toBcpType(keyword, value, null, null);
3453         if (bcpType == null && UnicodeLocaleExtension.isType(value)) {
3454             // unknown keyword, but syntax is fine..
3455             bcpType = AsciiUtil.toLowerString(value);
3456         }
3457         return bcpType;
3458     }
3459 
3460     /**
3461      * {@icu} Converts the specified keyword (BCP 47 Unicode locale extension key, or
3462      * legacy key) to the legacy key. For example, legacy key "collation" is
3463      * returned for the input BCP 47 Unicode locale extension key "co".
3464      *
3465      * @param keyword       the input locale keyword (either BCP 47 Unicode locale
3466      *                      extension key or legacy key).
3467      * @return              the well-formed legacy key, or null if the specified
3468      *                      keyword cannot be mapped to a well-formed legacy key.
3469      * @see #toUnicodeLocaleKey(String)
3470      * @draft ICU 54
3471      * @provisional This API might change or be removed in a future release.
3472      */
toLegacyKey(String keyword)3473     public static String toLegacyKey(String keyword) {
3474         String legacyKey = KeyTypeData.toLegacyKey(keyword);
3475         if (legacyKey == null) {
3476             // Checks if the specified locale key is well-formed with the legacy locale syntax.
3477             //
3478             // Note:
3479             //  Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
3480             //  However, a key should not contain '=' obviously. For now, all existing
3481             //  keys are using ASCII alphabetic letters only. We won't add any new key
3482             //  that is not compatible with the BCP 47 syntax. Therefore, we assume
3483             //  a valid key consist from [0-9a-zA-Z], no symbols.
3484             if (keyword.matches("[0-9a-zA-Z]+")) {
3485                 legacyKey = AsciiUtil.toLowerString(keyword);
3486             }
3487         }
3488         return legacyKey;
3489     }
3490 
3491     /**
3492      * {@icu} Converts the specified keyword value (BCP 47 Unicode locale extension type,
3493      * or legacy type or type alias) to the canonical legacy type. For example,
3494      * the legacy type "phonebook" is returned for the input BCP 47 Unicode
3495      * locale extension type "phonebk" with the keyword "collation" (or "co").
3496      * <p>
3497      * When the specified keyword is not recognized, but the specified value
3498      * satisfies the syntax of legacy key, or when the specified keyword
3499      * allows 'variable' type and the specified value satisfies the syntax,
3500      * the lower-case version of the input value will be returned.
3501      * For example,
3502      * <code>toLegacyType("Foo", "Bar")</code> returns "bar",
3503      * <code>toLegacyType("vt", "00A4")</code> returns "00a4".
3504      *
3505      * @param keyword       the locale keyword (either legacy keyword such as
3506      *                      "collation" or BCP 47 Unicode locale extension
3507      *                      key such as "co").
3508      * @param value         the locale keyword value (either BCP 47 Unicode locale
3509      *                      extension type such as "phonebk" or legacy keyword value
3510      *                      such as "phonebook").
3511      * @return              the well-formed legacy type, or null if the specified
3512      *                      keyword value cannot be mapped to a well-formed legacy
3513      *                      type.
3514      * @see #toUnicodeLocaleType(String, String)
3515      * @draft ICU 54
3516      * @provisional This API might change or be removed in a future release.
3517      */
toLegacyType(String keyword, String value)3518     public static String toLegacyType(String keyword, String value) {
3519         String legacyType = KeyTypeData.toLegacyType(keyword, value, null, null);
3520         if (legacyType == null) {
3521             // Checks if the specified locale type is well-formed with the legacy locale syntax.
3522             //
3523             // Note:
3524             //  Neither ICU nor LDML/CLDR provides the definition of keyword syntax.
3525             //  However, a type should not contain '=' obviously. For now, all existing
3526             //  types are using ASCII alphabetic letters with a few symbol letters. We won't
3527             //  add any new type that is not compatible with the BCP 47 syntax except timezone
3528             //  IDs. For now, we assume a valid type start with [0-9a-zA-Z], but may contain
3529             //  '-' '_' '/' in the middle.
3530             if (value.matches("[0-9a-zA-Z]+([_/\\-][0-9a-zA-Z]+)*")) {
3531                 legacyType = AsciiUtil.toLowerString(value);
3532             }
3533         }
3534         return legacyType;
3535     }
3536 
3537     /**
3538      * <code>Builder</code> is used to build instances of <code>ULocale</code>
3539      * from values configured by the setters.  Unlike the <code>ULocale</code>
3540      * constructors, the <code>Builder</code> checks if a value configured by a
3541      * setter satisfies the syntax requirements defined by the <code>ULocale</code>
3542      * class.  A <code>ULocale</code> object created by a <code>Builder</code> is
3543      * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
3544      * without losing information.
3545      *
3546      * <p><b>Note:</b> The <code>ULocale</code> class does not provide any
3547      * syntactic restrictions on variant, while BCP 47 requires each variant
3548      * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
3549      * alphanumerics.  The method <code>setVariant</code> throws
3550      * <code>IllformedLocaleException</code> for a variant that does not satisfy
3551      * this restriction. If it is necessary to support such a variant, use a
3552      * ULocale constructor.  However, keep in mind that a <code>ULocale</code>
3553      * object created this way might lose the variant information when
3554      * transformed to a BCP 47 language tag.
3555      *
3556      * <p>The following example shows how to create a <code>Locale</code> object
3557      * with the <code>Builder</code>.
3558      * <blockquote>
3559      * <pre>
3560      *     ULocale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
3561      * </pre>
3562      * </blockquote>
3563      *
3564      * <p>Builders can be reused; <code>clear()</code> resets all
3565      * fields to their default values.
3566      *
3567      * @see ULocale#toLanguageTag()
3568      *
3569      * @stable ICU 4.2
3570      */
3571     public static final class Builder {
3572 
3573         private final InternalLocaleBuilder _locbld;
3574 
3575         /**
3576          * Constructs an empty Builder. The default value of all
3577          * fields, extensions, and private use information is the
3578          * empty string.
3579          *
3580          * @stable ICU 4.2
3581          */
Builder()3582         public Builder() {
3583             _locbld = new InternalLocaleBuilder();
3584         }
3585 
3586         /**
3587          * Resets the <code>Builder</code> to match the provided
3588          * <code>locale</code>.  Existing state is discarded.
3589          *
3590          * <p>All fields of the locale must be well-formed, see {@link Locale}.
3591          *
3592          * <p>Locales with any ill-formed fields cause
3593          * <code>IllformedLocaleException</code> to be thrown.
3594          *
3595          * @param locale the locale
3596          * @return This builder.
3597          * @throws IllformedLocaleException if <code>locale</code> has
3598          * any ill-formed fields.
3599          * @throws NullPointerException if <code>locale</code> is null.
3600          *
3601          * @stable ICU 4.2
3602          */
setLocale(ULocale locale)3603         public Builder setLocale(ULocale locale) {
3604             try {
3605                 _locbld.setLocale(locale.base(), locale.extensions());
3606             } catch (LocaleSyntaxException e) {
3607                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3608             }
3609             return this;
3610         }
3611 
3612         /**
3613          * Resets the Builder to match the provided IETF BCP 47
3614          * language tag.  Discards the existing state.  Null and the
3615          * empty string cause the builder to be reset, like {@link
3616          * #clear}.  Grandfathered tags (see {@link
3617          * ULocale#forLanguageTag}) are converted to their canonical
3618          * form before being processed.  Otherwise, the language tag
3619          * must be well-formed (see {@link ULocale}) or an exception is
3620          * thrown (unlike <code>ULocale.forLanguageTag</code>, which
3621          * just discards ill-formed and following portions of the
3622          * tag).
3623          *
3624          * @param languageTag the language tag
3625          * @return This builder.
3626          * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
3627          * @see ULocale#forLanguageTag(String)
3628          *
3629          * @stable ICU 4.2
3630          */
setLanguageTag(String languageTag)3631         public Builder setLanguageTag(String languageTag) {
3632             ParseStatus sts = new ParseStatus();
3633             LanguageTag tag = LanguageTag.parse(languageTag, sts);
3634             if (sts.isError()) {
3635                 throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
3636             }
3637             _locbld.setLanguageTag(tag);
3638 
3639             return this;
3640         }
3641 
3642         /**
3643          * Sets the language.  If <code>language</code> is the empty string or
3644          * null, the language in this <code>Builder</code> is removed.  Otherwise,
3645          * the language must be <a href="./Locale.html#def_language">well-formed</a>
3646          * or an exception is thrown.
3647          *
3648          * <p>The typical language value is a two or three-letter language
3649          * code as defined in ISO639.
3650          *
3651          * @param language the language
3652          * @return This builder.
3653          * @throws IllformedLocaleException if <code>language</code> is ill-formed
3654          *
3655          * @stable ICU 4.2
3656          */
setLanguage(String language)3657         public Builder setLanguage(String language) {
3658             try {
3659                 _locbld.setLanguage(language);
3660             } catch (LocaleSyntaxException e) {
3661                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3662             }
3663             return this;
3664         }
3665 
3666         /**
3667          * Sets the script. If <code>script</code> is null or the empty string,
3668          * the script in this <code>Builder</code> is removed.
3669          * Otherwise, the script must be well-formed or an exception is thrown.
3670          *
3671          * <p>The typical script value is a four-letter script code as defined by ISO 15924.
3672          *
3673          * @param script the script
3674          * @return This builder.
3675          * @throws IllformedLocaleException if <code>script</code> is ill-formed
3676          *
3677          * @stable ICU 4.2
3678          */
setScript(String script)3679         public Builder setScript(String script) {
3680             try {
3681                 _locbld.setScript(script);
3682             } catch (LocaleSyntaxException e) {
3683                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3684             }
3685             return this;
3686         }
3687 
3688         /**
3689          * Sets the region.  If region is null or the empty string, the region
3690          * in this <code>Builder</code> is removed.  Otherwise,
3691          * the region must be well-formed or an exception is thrown.
3692          *
3693          * <p>The typical region value is a two-letter ISO 3166 code or a
3694          * three-digit UN M.49 area code.
3695          *
3696          * <p>The country value in the <code>Locale</code> created by the
3697          * <code>Builder</code> is always normalized to upper case.
3698          *
3699          * @param region the region
3700          * @return This builder.
3701          * @throws IllformedLocaleException if <code>region</code> is ill-formed
3702          *
3703          * @stable ICU 4.2
3704          */
setRegion(String region)3705         public Builder setRegion(String region) {
3706             try {
3707                 _locbld.setRegion(region);
3708             } catch (LocaleSyntaxException e) {
3709                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3710             }
3711             return this;
3712         }
3713 
3714         /**
3715          * Sets the variant.  If variant is null or the empty string, the
3716          * variant in this <code>Builder</code> is removed.  Otherwise, it
3717          * must consist of one or more well-formed subtags, or an exception is thrown.
3718          *
3719          * <p><b>Note:</b> This method checks if <code>variant</code>
3720          * satisfies the IETF BCP 47 variant subtag's syntax requirements,
3721          * and normalizes the value to lowercase letters.  However,
3722          * the <code>ULocale</code> class does not impose any syntactic
3723          * restriction on variant.  To set such a variant,
3724          * use a ULocale constructor.
3725          *
3726          * @param variant the variant
3727          * @return This builder.
3728          * @throws IllformedLocaleException if <code>variant</code> is ill-formed
3729          *
3730          * @stable ICU 4.2
3731          */
setVariant(String variant)3732         public Builder setVariant(String variant) {
3733             try {
3734                 _locbld.setVariant(variant);
3735             } catch (LocaleSyntaxException e) {
3736                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3737             }
3738             return this;
3739         }
3740 
3741         /**
3742          * Sets the extension for the given key. If the value is null or the
3743          * empty string, the extension is removed.  Otherwise, the extension
3744          * must be well-formed or an exception is thrown.
3745          *
3746          * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
3747          * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
3748          * Setting a value for this key replaces any existing Unicode locale key/type
3749          * pairs with those defined in the extension.
3750          *
3751          * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
3752          * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
3753          * well-formed, the value for this key needs only to have subtags of one to
3754          * eight alphanumeric characters, not two to eight as in the general case.
3755          *
3756          * @param key the extension key
3757          * @param value the extension value
3758          * @return This builder.
3759          * @throws IllformedLocaleException if <code>key</code> is illegal
3760          * or <code>value</code> is ill-formed
3761          * @see #setUnicodeLocaleKeyword(String, String)
3762          *
3763          * @stable ICU 4.2
3764          */
setExtension(char key, String value)3765         public Builder setExtension(char key, String value) {
3766             try {
3767                 _locbld.setExtension(key, value);
3768             } catch (LocaleSyntaxException e) {
3769                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3770             }
3771             return this;
3772         }
3773 
3774         /**
3775          * Sets the Unicode locale keyword type for the given key.  If the type
3776          * is null, the Unicode keyword is removed.  Otherwise, the key must be
3777          * non-null and both key and type must be well-formed or an exception
3778          * is thrown.
3779          *
3780          * <p>Keys and types are converted to lower case.
3781          *
3782          * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
3783          * replaces all Unicode locale keywords with those defined in the
3784          * extension.
3785          *
3786          * @param key the Unicode locale key
3787          * @param type the Unicode locale type
3788          * @return This builder.
3789          * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
3790          * is ill-formed
3791          * @throws NullPointerException if <code>key</code> is null
3792          * @see #setExtension(char, String)
3793          *
3794          * @stable ICU 4.4
3795          */
setUnicodeLocaleKeyword(String key, String type)3796         public Builder setUnicodeLocaleKeyword(String key, String type) {
3797             try {
3798                 _locbld.setUnicodeLocaleKeyword(key, type);
3799             } catch (LocaleSyntaxException e) {
3800                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3801             }
3802             return this;
3803         }
3804 
3805         /**
3806          * Adds a unicode locale attribute, if not already present, otherwise
3807          * has no effect.  The attribute must not be null and must be well-formed
3808          * or an exception is thrown.
3809          *
3810          * @param attribute the attribute
3811          * @return This builder.
3812          * @throws NullPointerException if <code>attribute</code> is null
3813          * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
3814          * @see #setExtension(char, String)
3815          *
3816          * @stable ICU 4.6
3817          */
addUnicodeLocaleAttribute(String attribute)3818         public Builder addUnicodeLocaleAttribute(String attribute) {
3819             try {
3820                 _locbld.addUnicodeLocaleAttribute(attribute);
3821             } catch (LocaleSyntaxException e) {
3822                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3823             }
3824             return this;
3825         }
3826 
3827         /**
3828          * Removes a unicode locale attribute, if present, otherwise has no
3829          * effect.  The attribute must not be null and must be well-formed
3830          * or an exception is thrown.
3831          *
3832          * <p>Attribute comparision for removal is case-insensitive.
3833          *
3834          * @param attribute the attribute
3835          * @return This builder.
3836          * @throws NullPointerException if <code>attribute</code> is null
3837          * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
3838          * @see #setExtension(char, String)
3839          *
3840          * @stable ICU 4.6
3841          */
removeUnicodeLocaleAttribute(String attribute)3842         public Builder removeUnicodeLocaleAttribute(String attribute) {
3843             try {
3844                 _locbld.removeUnicodeLocaleAttribute(attribute);
3845             } catch (LocaleSyntaxException e) {
3846                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
3847             }
3848             return this;
3849         }
3850 
3851         /**
3852          * Resets the builder to its initial, empty state.
3853          *
3854          * @return this builder
3855          *
3856          * @stable ICU 4.2
3857          */
clear()3858         public Builder clear() {
3859             _locbld.clear();
3860             return this;
3861         }
3862 
3863         /**
3864          * Resets the extensions to their initial, empty state.
3865          * Language, script, region and variant are unchanged.
3866          *
3867          * @return this builder
3868          * @see #setExtension(char, String)
3869          *
3870          * @stable ICU 4.2
3871          */
clearExtensions()3872         public Builder clearExtensions() {
3873             _locbld.clearExtensions();
3874             return this;
3875         }
3876 
3877         /**
3878          * Returns an instance of <code>ULocale</code> created from the fields set
3879          * on this builder.
3880          *
3881          * @return a new Locale
3882          *
3883          * @stable ICU 4.4
3884          */
build()3885         public ULocale build() {
3886             return getInstance(_locbld.getBaseLocale(), _locbld.getLocaleExtensions());
3887         }
3888     }
3889 
getInstance(BaseLocale base, LocaleExtensions exts)3890     private static ULocale getInstance(BaseLocale base, LocaleExtensions exts) {
3891         String id = lscvToID(base.getLanguage(), base.getScript(), base.getRegion(),
3892                 base.getVariant());
3893 
3894         Set<Character> extKeys = exts.getKeys();
3895         if (!extKeys.isEmpty()) {
3896             // legacy locale ID assume Unicode locale keywords and
3897             // other extensions are at the same level.
3898             // e.g. @a=ext-for-aa;calendar=japanese;m=ext-for-mm;x=priv-use
3899 
3900             TreeMap<String, String> kwds = new TreeMap<String, String>();
3901             for (Character key : extKeys) {
3902                 Extension ext = exts.getExtension(key);
3903                 if (ext instanceof UnicodeLocaleExtension) {
3904                     UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
3905                     Set<String> ukeys = uext.getUnicodeLocaleKeys();
3906                     for (String bcpKey : ukeys) {
3907                         String bcpType = uext.getUnicodeLocaleType(bcpKey);
3908                         // convert to legacy key/type
3909                         String lkey = toLegacyKey(bcpKey);
3910                         String ltype = toLegacyType(bcpKey, ((bcpType.length() == 0) ? "yes" : bcpType)); // use "yes" as the value of typeless keywords
3911                         // special handling for u-va-posix, since this is a variant, not a keyword
3912                         if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) {
3913                             id = id + "_POSIX";
3914                         } else {
3915                             kwds.put(lkey, ltype);
3916                         }
3917                     }
3918                     // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy
3919                     Set<String> uattributes = uext.getUnicodeLocaleAttributes();
3920                     if (uattributes.size() > 0) {
3921                         StringBuilder attrbuf = new StringBuilder();
3922                         for (String attr : uattributes) {
3923                             if (attrbuf.length() > 0) {
3924                                 attrbuf.append('-');
3925                             }
3926                             attrbuf.append(attr);
3927                         }
3928                         kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString());
3929                     }
3930                 } else {
3931                     kwds.put(String.valueOf(key), ext.getValue());
3932                 }
3933             }
3934 
3935             if (!kwds.isEmpty()) {
3936                 StringBuilder buf = new StringBuilder(id);
3937                 buf.append("@");
3938                 Set<Map.Entry<String, String>> kset = kwds.entrySet();
3939                 boolean insertSep = false;
3940                 for (Map.Entry<String, String> kwd : kset) {
3941                     if (insertSep) {
3942                         buf.append(";");
3943                     } else {
3944                         insertSep = true;
3945                     }
3946                     buf.append(kwd.getKey());
3947                     buf.append("=");
3948                     buf.append(kwd.getValue());
3949                 }
3950 
3951                 id = buf.toString();
3952             }
3953         }
3954         return new ULocale(id);
3955     }
3956 
base()3957     private BaseLocale base() {
3958         if (baseLocale == null) {
3959             String language, script, region, variant;
3960             language = script = region = variant = "";
3961             if (!equals(ULocale.ROOT)) {
3962                 LocaleIDParser lp = new LocaleIDParser(localeID);
3963                 language = lp.getLanguage();
3964                 script = lp.getScript();
3965                 region = lp.getCountry();
3966                 variant = lp.getVariant();
3967             }
3968             baseLocale = BaseLocale.getInstance(language, script, region, variant);
3969         }
3970         return baseLocale;
3971     }
3972 
extensions()3973     private LocaleExtensions extensions() {
3974         if (extensions == null) {
3975             Iterator<String> kwitr = getKeywords();
3976             if (kwitr == null) {
3977                 extensions = LocaleExtensions.EMPTY_EXTENSIONS;
3978             } else {
3979                 InternalLocaleBuilder intbld = new InternalLocaleBuilder();
3980                 while (kwitr.hasNext()) {
3981                     String key = kwitr.next();
3982                     if (key.equals(LOCALE_ATTRIBUTE_KEY)) {
3983                         // special keyword used for representing Unicode locale attributes
3984                         String[] uattributes = getKeywordValue(key).split("[-_]");
3985                         for (String uattr : uattributes) {
3986                             try {
3987                                 intbld.addUnicodeLocaleAttribute(uattr);
3988                             } catch (LocaleSyntaxException e) {
3989                                 // ignore and fall through
3990                             }
3991                         }
3992                     } else if (key.length() >= 2) {
3993                         String bcpKey = toUnicodeLocaleKey(key);
3994                         String bcpType = toUnicodeLocaleType(key, getKeywordValue(key));
3995                         if (bcpKey != null && bcpType != null) {
3996                             try {
3997                                 intbld.setUnicodeLocaleKeyword(bcpKey, bcpType);
3998                             } catch (LocaleSyntaxException e) {
3999                                 // ignore and fall through
4000                             }
4001                         }
4002                     } else if (key.length() == 1 && (key.charAt(0) != UNICODE_LOCALE_EXTENSION)) {
4003                         try  {
4004                             intbld.setExtension(key.charAt(0), getKeywordValue(key).replace("_",
4005                                     LanguageTag.SEP));
4006                         } catch (LocaleSyntaxException e) {
4007                             // ignore and fall through
4008                         }
4009                     }
4010                 }
4011                 extensions = intbld.getLocaleExtensions();
4012             }
4013         }
4014         return extensions;
4015     }
4016 
4017     /*
4018      * JDK Locale Helper
4019      */
4020     private static final class JDKLocaleHelper {
4021         private static boolean hasScriptsAndUnicodeExtensions = false;
4022         private static boolean hasLocaleCategories = false;
4023 
4024         /*
4025          * New methods in Java 7 Locale class
4026          */
4027         private static Method mGetScript;
4028         private static Method mGetExtensionKeys;
4029         private static Method mGetExtension;
4030         private static Method mGetUnicodeLocaleKeys;
4031         private static Method mGetUnicodeLocaleAttributes;
4032         private static Method mGetUnicodeLocaleType;
4033         private static Method mForLanguageTag;
4034 
4035         private static Method mGetDefault;
4036         private static Method mSetDefault;
4037         private static Object eDISPLAY;
4038         private static Object eFORMAT;
4039 
4040         /*
4041          * This table is used for mapping between ICU and special Java
4042          * 6 locales.  When an ICU locale matches <minumum base> with
4043          * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
4044          * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
4045          * are mapped to Java locale "ja_JP_JP".  ICU locale "nn" is mapped
4046          * to Java locale "no_NO_NY".
4047          */
4048         private static final String[][] JAVA6_MAPDATA = {
4049             //  { <Java>,       <ICU base>, <keyword>,  <value>,    <minimum base>
4050             { "ja_JP_JP",   "ja_JP",    "calendar", "japanese", "ja"},
4051             { "no_NO_NY",   "nn_NO",    null,       null,       "nn"},
4052             { "th_TH_TH",   "th_TH",    "numbers",  "thai",     "th"},
4053         };
4054 
4055         static {
4056             do {
4057                 try {
4058                     mGetScript = Locale.class.getMethod("getScript", (Class[]) null);
4059                     mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null);
4060                     mGetExtension = Locale.class.getMethod("getExtension", char.class);
4061                     mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null);
4062                     mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null);
4063                     mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class);
4064                     mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
4065 
4066                     hasScriptsAndUnicodeExtensions = true;
4067                 } catch (NoSuchMethodException e) {
4068                 } catch (IllegalArgumentException e) {
4069                 } catch (SecurityException e) {
4070                     // TODO : report?
4071                 }
4072 
4073                 try {
4074                     Class<?> cCategory = null;
4075                     Class<?>[] classes = Locale.class.getDeclaredClasses();
4076                     for (Class<?> c : classes) {
4077                         if (c.getName().equals("java.util.Locale$Category")) {
4078                             cCategory = c;
4079                             break;
4080                         }
4081                     }
4082                     if (cCategory == null) {
4083                         break;
4084                     }
4085                     mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory);
4086                     mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class);
4087 
4088                     Method mName = cCategory.getMethod("name", (Class[]) null);
4089                     Object[] enumConstants = cCategory.getEnumConstants();
4090                     for (Object e : enumConstants) {
4091                         String catVal = (String)mName.invoke(e, (Object[])null);
4092                         if (catVal.equals("DISPLAY")) {
4093                             eDISPLAY = e;
4094                         } else if (catVal.equals("FORMAT")) {
4095                             eFORMAT = e;
4096                         }
4097                     }
4098                     if (eDISPLAY == null || eFORMAT == null) {
4099                         break;
4100                     }
4101 
4102                     hasLocaleCategories = true;
4103                 } catch (NoSuchMethodException e) {
4104                 } catch (IllegalArgumentException e) {
4105                 } catch (IllegalAccessException e) {
4106                 } catch (InvocationTargetException e) {
4107                 } catch (SecurityException e) {
4108                     // TODO : report?
4109                 }
4110             } while (false);
4111         }
4112 
JDKLocaleHelper()4113         private JDKLocaleHelper() {
4114         }
4115 
hasLocaleCategories()4116         public static boolean hasLocaleCategories() {
4117             return hasLocaleCategories;
4118         }
4119 
toULocale(Locale loc)4120         public static ULocale toULocale(Locale loc) {
4121             return hasScriptsAndUnicodeExtensions ? toULocale7(loc) : toULocale6(loc);
4122         }
4123 
toLocale(ULocale uloc)4124         public static Locale toLocale(ULocale uloc) {
4125             return hasScriptsAndUnicodeExtensions ? toLocale7(uloc) : toLocale6(uloc);
4126         }
4127 
toULocale7(Locale loc)4128         private static ULocale toULocale7(Locale loc) {
4129             String language = loc.getLanguage();
4130             String script = "";
4131             String country = loc.getCountry();
4132             String variant = loc.getVariant();
4133 
4134             Set<String> attributes = null;
4135             Map<String, String> keywords = null;
4136 
4137             try {
4138                 script = (String) mGetScript.invoke(loc, (Object[]) null);
4139                 @SuppressWarnings("unchecked")
4140                 Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null);
4141                 if (!extKeys.isEmpty()) {
4142                     for (Character extKey : extKeys) {
4143                         if (extKey.charValue() == 'u') {
4144                             // Found Unicode locale extension
4145 
4146                             // attributes
4147                             @SuppressWarnings("unchecked")
4148                             Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null);
4149                             if (!uAttributes.isEmpty()) {
4150                                 attributes = new TreeSet<String>();
4151                                 for (String attr : uAttributes) {
4152                                     attributes.add(attr);
4153                                 }
4154                             }
4155 
4156                             // keywords
4157                             @SuppressWarnings("unchecked")
4158                             Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null);
4159                             for (String kwKey : uKeys) {
4160                                 String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey);
4161                                 if (kwVal != null) {
4162                                     if (kwKey.equals("va")) {
4163                                         // va-* is interpreted as a variant
4164                                         variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant;
4165                                     } else {
4166                                         if (keywords == null) {
4167                                             keywords = new TreeMap<String, String>();
4168                                         }
4169                                         keywords.put(kwKey, kwVal);
4170                                     }
4171                                 }
4172                             }
4173                         } else {
4174                             String extVal = (String) mGetExtension.invoke(loc, extKey);
4175                             if (extVal != null) {
4176                                 if (keywords == null) {
4177                                     keywords = new TreeMap<String, String>();
4178                                 }
4179                                 keywords.put(String.valueOf(extKey), extVal);
4180                             }
4181                         }
4182                     }
4183                 }
4184             } catch (IllegalAccessException e) {
4185                 throw new RuntimeException(e);
4186             } catch (InvocationTargetException e) {
4187                 throw new RuntimeException(e);
4188             }
4189 
4190             // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU,
4191             // and it should be transformed to nn_NO.
4192 
4193             // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert
4194             // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY.
4195 
4196             if (language.equals("no") && country.equals("NO") && variant.equals("NY")) {
4197                 language = "nn";
4198                 variant = "";
4199             }
4200 
4201             // Constructing ID
4202             StringBuilder buf = new StringBuilder(language);
4203 
4204             if (script.length() > 0) {
4205                 buf.append('_');
4206                 buf.append(script);
4207             }
4208 
4209             if (country.length() > 0) {
4210                 buf.append('_');
4211                 buf.append(country);
4212             }
4213 
4214             if (variant.length() > 0) {
4215                 if (country.length() == 0) {
4216                     buf.append('_');
4217                 }
4218                 buf.append('_');
4219                 buf.append(variant);
4220             }
4221 
4222             if (attributes != null) {
4223                 // transform Unicode attributes into a keyword
4224                 StringBuilder attrBuf = new StringBuilder();
4225                 for (String attr : attributes) {
4226                     if (attrBuf.length() != 0) {
4227                         attrBuf.append('-');
4228                     }
4229                     attrBuf.append(attr);
4230                 }
4231                 if (keywords == null) {
4232                     keywords = new TreeMap<String, String>();
4233                 }
4234                 keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString());
4235             }
4236 
4237             if (keywords != null) {
4238                 buf.append('@');
4239                 boolean addSep = false;
4240                 for (Entry<String, String> kwEntry : keywords.entrySet()) {
4241                     String kwKey = kwEntry.getKey();
4242                     String kwVal = kwEntry.getValue();
4243 
4244                     if (kwKey.length() != 1) {
4245                         // Unicode locale key
4246                         kwKey = toLegacyKey(kwKey);
4247                         // use "yes" as the value of typeless keywords
4248                         kwVal = toLegacyType(kwKey, ((kwVal.length() == 0) ? "yes" : kwVal));
4249                     }
4250 
4251                     if (addSep) {
4252                         buf.append(';');
4253                     } else {
4254                         addSep = true;
4255                     }
4256                     buf.append(kwKey);
4257                     buf.append('=');
4258                     buf.append(kwVal);
4259                 }
4260             }
4261 
4262             return new ULocale(getName(buf.toString()), loc);
4263         }
4264 
toULocale6(Locale loc)4265         private static ULocale toULocale6(Locale loc) {
4266             ULocale uloc = null;
4267             String locStr = loc.toString();
4268             if (locStr.length() == 0) {
4269                 uloc = ULocale.ROOT;
4270             } else {
4271                 for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
4272                     if (JAVA6_MAPDATA[i][0].equals(locStr)) {
4273                         LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]);
4274                         p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]);
4275                         locStr = p.getName();
4276                         break;
4277                     }
4278                 }
4279                 uloc = new ULocale(getName(locStr), loc);
4280             }
4281             return uloc;
4282         }
4283 
toLocale7(ULocale uloc)4284         private static Locale toLocale7(ULocale uloc) {
4285             Locale loc = null;
4286             String ulocStr = uloc.getName();
4287             if (uloc.getScript().length() > 0 || ulocStr.contains("@")) {
4288                 // With script or keywords available, the best way
4289                 // to get a mapped Locale is to go through a language tag.
4290                 // A Locale with script or keywords can only have variants
4291                 // that is 1 to 8 alphanum. If this ULocale has a variant
4292                 // subtag not satisfying the criteria, the variant subtag
4293                 // will be lost.
4294                 String tag = uloc.toLanguageTag();
4295 
4296                 // Workaround for variant casing problem:
4297                 //
4298                 // The variant field in ICU is case insensitive and normalized
4299                 // to upper case letters by getVariant(), while
4300                 // the variant field in JDK Locale is case sensitive.
4301                 // ULocale#toLanguageTag use lower case characters for
4302                 // BCP 47 variant and private use x-lvariant.
4303                 //
4304                 // Locale#forLanguageTag in JDK preserves character casing
4305                 // for variant. Because ICU always normalizes variant to
4306                 // upper case, we convert language tag to upper case here.
4307                 tag = AsciiUtil.toUpperString(tag);
4308 
4309                 try {
4310                     loc = (Locale)mForLanguageTag.invoke(null, tag);
4311                 } catch (IllegalAccessException e) {
4312                     throw new RuntimeException(e);
4313                 } catch (InvocationTargetException e) {
4314                     throw new RuntimeException(e);
4315                 }
4316             }
4317             if (loc == null) {
4318                 // Without script or keywords, use a Locale constructor,
4319                 // so we can preserve any ill-formed variants.
4320                 loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
4321             }
4322             return loc;
4323         }
4324 
toLocale6(ULocale uloc)4325         private static Locale toLocale6(ULocale uloc) {
4326             String locstr = uloc.getBaseName();
4327             for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
4328                 if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) {
4329                     if (JAVA6_MAPDATA[i][2] != null) {
4330                         String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]);
4331                         if (val != null && val.equals(JAVA6_MAPDATA[i][3])) {
4332                             locstr = JAVA6_MAPDATA[i][0];
4333                             break;
4334                         }
4335                     } else {
4336                         locstr = JAVA6_MAPDATA[i][0];
4337                         break;
4338                     }
4339                 }
4340             }
4341             LocaleIDParser p = new LocaleIDParser(locstr);
4342             String[] names = p.getLanguageScriptCountryVariant();
4343             return new Locale(names[0], names[2], names[3]);
4344         }
4345 
getDefault(Category category)4346         public static Locale getDefault(Category category) {
4347             Locale loc = Locale.getDefault();
4348             if (hasLocaleCategories) {
4349                 Object cat = null;
4350                 switch (category) {
4351                 case DISPLAY:
4352                     cat = eDISPLAY;
4353                     break;
4354                 case FORMAT:
4355                     cat = eFORMAT;
4356                     break;
4357                 }
4358                 if (cat != null) {
4359                     try {
4360                         loc = (Locale)mGetDefault.invoke(null, cat);
4361                     } catch (InvocationTargetException e) {
4362                         // fall through - use the base default
4363                     } catch (IllegalArgumentException e) {
4364                         // fall through - use the base default
4365                     } catch (IllegalAccessException e) {
4366                         // fall through - use the base default
4367                     }
4368                 }
4369             }
4370             return loc;
4371         }
4372 
setDefault(Category category, Locale newLocale)4373         public static void setDefault(Category category, Locale newLocale) {
4374             if (hasLocaleCategories) {
4375                 Object cat = null;
4376                 switch (category) {
4377                 case DISPLAY:
4378                     cat = eDISPLAY;
4379                     break;
4380                 case FORMAT:
4381                     cat = eFORMAT;
4382                     break;
4383                 }
4384                 if (cat != null) {
4385                     try {
4386                         mSetDefault.invoke(null, cat, newLocale);
4387                     } catch (InvocationTargetException e) {
4388                         // fall through - no effects
4389                     } catch (IllegalArgumentException e) {
4390                         // fall through - no effects
4391                     } catch (IllegalAccessException e) {
4392                         // fall through - no effects
4393                     }
4394                 }
4395             }
4396         }
4397 
4398         // Returns true if the given Locale matches the original
4399         // default locale initialized by JVM by checking user.XXX
4400         // system properties. When the system properties are not accessible,
4401         // this method returns false.
isOriginalDefaultLocale(Locale loc)4402         public static boolean isOriginalDefaultLocale(Locale loc) {
4403             if (hasScriptsAndUnicodeExtensions) {
4404                 String script = "";
4405                 try {
4406                     script = (String) mGetScript.invoke(loc, (Object[]) null);
4407                 } catch (Exception e) {
4408                     return false;
4409                 }
4410 
4411                 return loc.getLanguage().equals(getSystemProperty("user.language"))
4412                         && loc.getCountry().equals(getSystemProperty("user.country"))
4413                         && loc.getVariant().equals(getSystemProperty("user.variant"))
4414                         && script.equals(getSystemProperty("user.script"));
4415             }
4416             return loc.getLanguage().equals(getSystemProperty("user.language"))
4417                     && loc.getCountry().equals(getSystemProperty("user.country"))
4418                     && loc.getVariant().equals(getSystemProperty("user.variant"));
4419         }
4420 
getSystemProperty(String key)4421         public static String getSystemProperty(String key) {
4422             String val = null;
4423             final String fkey = key;
4424             if (System.getSecurityManager() != null) {
4425                 try {
4426                     val = AccessController.doPrivileged(new PrivilegedAction<String>() {
4427                         public String run() {
4428                             return System.getProperty(fkey);
4429                         }
4430                     });
4431                 } catch (AccessControlException e) {
4432                     // ignore
4433                 }
4434             } else {
4435                 val = System.getProperty(fkey);
4436             }
4437             return val;
4438         }
4439     }
4440 }
4441