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