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