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