KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > util > ULocale


1 /*
2 ******************************************************************************
3 * Copyright (C) 2003-2006, 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 JavaDoc;
11 import java.lang.ref.SoftReference JavaDoc;
12 import java.util.Collections JavaDoc;
13 import java.util.Comparator JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.Locale JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.MissingResourceException JavaDoc;
19 import java.util.TreeMap JavaDoc;
20
21 import com.ibm.icu.impl.SimpleCache;
22 import com.ibm.icu.impl.ICUResourceBundle;
23 import com.ibm.icu.impl.LocaleUtility;
24 import com.ibm.icu.lang.UCharacter;
25 /**
26  * A class analogous to {@link java.util.Locale} that provides additional
27  * support for ICU protocol. In ICU 3.0 this class is enhanced to support
28  * RFC 3066 language identifiers.
29  *
30  * <p>Many classes and services in ICU follow a factory idiom, in
31  * which a factory method or object responds to a client request with
32  * an object. The request includes a locale (the <i>requested</i>
33  * locale), and the returned object is constructed using data for that
34  * locale. The system may lack data for the requested locale, in
35  * which case the locale fallback mechanism will be invoked until a
36  * populated locale is found (the <i>valid</i> locale). Furthermore,
37  * even when a populated locale is found (the <i>valid</i> locale),
38  * further fallback may be required to reach a locale containing the
39  * specific data required by the service (the <i>actual</i> locale).
40  *
41  * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
42  * Normalization 'cleans up' ICU locale ids as follows:
43  * <ul>
44  * <li>language, script, country, variant, and keywords are properly cased<br>
45  * (lower, title, upper, upper, and lower case respectively)</li>
46  * <li>hyphens used as separators are converted to underscores</li>
47  * <li>three-letter language and country ids are converted to two-letter
48  * equivalents where available</li>
49  * <li>surrounding spaces are removed from keywords and values</li>
50  * <li>if there are multiple keywords, they are put in sorted order</li>
51  * </ul>
52  * Canonicalization additionally performs the following:
53  * <ul>
54  * <li>POSIX ids are converted to ICU format IDs</li>
55  * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
56  * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form, with the currency
57  * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
58  * </ul>
59  * All ULocale constructors automatically normalize the locale id. To handle
60  * POSIX ids, <code>canonicalize</code> can be called to convert the id
61  * to canonical form, or the <code>canonicalInstance</code> factory method
62  * can be called.</p>
63  *
64  * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
65  * #ACTUAL_LOCALE} intended for use in methods named
66  * <tt>getLocale()</tt>. These methods exist in several ICU classes,
67  * including {@link com.ibm.icu.util.Calendar}, {@link
68  * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
69  * {@link com.ibm.icu.text.BreakIterator}, {@link
70  * com.ibm.icu.text.Collator}, {@link
71  * com.ibm.icu.text.DateFormatSymbols}, and {@link
72  * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
73  * any. Once an object of one of these classes has been created,
74  * <tt>getLocale()</tt> may be called on it to determine the valid and
75  * actual locale arrived at during the object's construction.
76  *
77  * <p>Note: The <tt>getLocale()</tt> method will be implemented in ICU
78  * 3.0; ICU 2.8 contains a partial preview implementation. The
79  * <i>actual</i> locale is returned correctly, but the <i>valid</i>
80  * locale is not, in most cases.
81  *
82  * @see java.util.Locale
83  * @author weiv
84  * @author Alan Liu
85  * @author Ram Viswanadha
86  * @stable ICU 2.8
87  */

88 public final class ULocale implements Serializable JavaDoc {
89     // using serialver from jdk1.4.2_05
90
private static final long serialVersionUID = 3715177670352309217L;
91
92     /**
93      * Useful constant for language.
94      * @stable ICU 3.0
95      */

96     public static final ULocale ENGLISH = new ULocale("en", Locale.ENGLISH);
97
98     /**
99      * Useful constant for language.
100      * @stable ICU 3.0
101      */

102     public static final ULocale FRENCH = new ULocale("fr", Locale.FRENCH);
103
104     /**
105      * Useful constant for language.
106      * @stable ICU 3.0
107      */

108     public static final ULocale GERMAN = new ULocale("de", Locale.GERMAN);
109
110     /**
111      * Useful constant for language.
112      * @stable ICU 3.0
113      */

114     public static final ULocale ITALIAN = new ULocale("it", Locale.ITALIAN);
115
116     /**
117      * Useful constant for language.
118      * @stable ICU 3.0
119      */

120     public static final ULocale JAPANESE = new ULocale("ja", Locale.JAPANESE);
121
122     /**
123      * Useful constant for language.
124      * @stable ICU 3.0
125      */

126     public static final ULocale KOREAN = new ULocale("ko", Locale.KOREAN);
127
128     /**
129      * Useful constant for language.
130      * @stable ICU 3.0
131      */

132     public static final ULocale CHINESE = new ULocale("zh", Locale.CHINESE);
133
134     /**
135      * Useful constant for language.
136      * @stable ICU 3.0
137      */

138     public static final ULocale SIMPLIFIED_CHINESE = new ULocale("zh_Hans", Locale.CHINESE);
139
140     /**
141      * Useful constant for language.
142      * @stable ICU 3.0
143      */

144     public static final ULocale TRADITIONAL_CHINESE = new ULocale("zh_Hant", Locale.CHINESE);
145
146     /**
147      * Useful constant for country/region.
148      * @stable ICU 3.0
149      */

150     public static final ULocale FRANCE = new ULocale("fr_FR", Locale.FRANCE);
151
152     /**
153      * Useful constant for country/region.
154      * @stable ICU 3.0
155      */

156     public static final ULocale GERMANY = new ULocale("de_DE", Locale.GERMANY);
157
158     /**
159      * Useful constant for country/region.
160      * @stable ICU 3.0
161      */

162     public static final ULocale ITALY = new ULocale("it_IT", Locale.ITALY);
163
164     /**
165      * Useful constant for country/region.
166      * @stable ICU 3.0
167      */

168     public static final ULocale JAPAN = new ULocale("ja_JP", Locale.JAPAN);
169
170     /**
171      * Useful constant for country/region.
172      * @stable ICU 3.0
173      */

174     public static final ULocale KOREA = new ULocale("ko_KR", Locale.KOREA);
175
176     /**
177      * Useful constant for country/region.
178      * @stable ICU 3.0
179      */

180     public static final ULocale CHINA = new ULocale("zh_Hans_CN", Locale.CHINA);
181
182     /**
183      * Useful constant for country/region.
184      * @stable ICU 3.0
185      */

186     public static final ULocale PRC = CHINA;
187
188     /**
189      * Useful constant for country/region.
190      * @stable ICU 3.0
191      */

192     public static final ULocale TAIWAN = new ULocale("zh_Hant_TW", Locale.TAIWAN);
193
194     /**
195      * Useful constant for country/region.
196      * @stable ICU 3.0
197      */

198     public static final ULocale UK = new ULocale("en_GB", Locale.UK);
199
200     /**
201      * Useful constant for country/region.
202      * @stable ICU 3.0
203      */

204     public static final ULocale US = new ULocale("en_US", Locale.US);
205
206     /**
207      * Useful constant for country/region.
208      * @stable ICU 3.0
209      */

210     public static final ULocale CANADA = new ULocale("en_CA", Locale.CANADA);
211
212     /**
213      * Useful constant for country/region.
214      * @stable ICU 3.0
215      */

216     public static final ULocale CANADA_FRENCH = new ULocale("fr_CA", Locale.CANADA_FRENCH);
217
218     /**
219      * Handy constant.
220      */

221     private static final String JavaDoc EMPTY_STRING = "";
222
223     // Used in both ULocale and IDParser, so moved up here.
224
private static final char UNDERSCORE = '_';
225
226     // default empty locale
227
private static final Locale JavaDoc EMPTY_LOCALE = new Locale JavaDoc("", "");
228
229     /**
230      * The root ULocale.
231      * @stable ICU 2.8
232      */

233     public static final ULocale ROOT = new ULocale("root", EMPTY_LOCALE);
234     
235     private static final SimpleCache CACHE = new SimpleCache();
236
237     /**
238      * Cache the locale.
239      */

240     private transient Locale JavaDoc locale;
241
242     /**
243      * The raw localeID that we were passed in.
244      */

245     private String JavaDoc localeID;
246
247     /**
248      * Tables used in normalizing portions of the id.
249      */

250     /* tables updated per http://lcweb.loc.gov/standards/iso639-2/
251        to include the revisions up to 2001/7/27 *CWB*/

252     /* The 3 character codes are the terminology codes like RFC 3066.
253        This is compatible with prior ICU codes */

254     /* "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in
255        the table but now at the end of the table because
256        3 character codes are duplicates. This avoids bad searches
257        going from 3 to 2 character codes.*/

258     /* The range qaa-qtz is reserved for local use. */
259
260     private static String JavaDoc[] _languages;
261     private static String JavaDoc[] _replacementLanguages;
262     private static String JavaDoc[] _obsoleteLanguages;
263     private static String JavaDoc[] _languages3;
264     private static String JavaDoc[] _obsoleteLanguages3;
265
266     // Avoid initializing languages tables unless we have to.
267
private static void initLanguageTables() {
268         if (_languages == null) {
269
270             /* This list MUST be in sorted order, and MUST contain the two-letter codes
271                if one exists otherwise use the three letter code */

272             String JavaDoc[] tempLanguages = {
273                 "aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", "afa",
274                 "afh", "ak", "akk", "ale", "alg", "am", "an", "ang", "apa",
275                 "ar", "arc", "arn", "arp", "art", "arw", "as", "ast",
276                 "ath", "aus", "av", "awa", "ay", "az", "ba", "bad",
277                 "bai", "bal", "ban", "bas", "bat", "be", "bej",
278                 "bem", "ber", "bg", "bh", "bho", "bi", "bik", "bin",
279                 "bla", "bm", "bn", "bnt", "bo", "br", "bra", "bs",
280                 "btk", "bua", "bug", "byn", "ca", "cad", "cai", "car", "cau",
281                 "ce", "ceb", "cel", "ch", "chb", "chg", "chk", "chm",
282                 "chn", "cho", "chp", "chr", "chy", "cmc", "co", "cop",
283                 "cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", "csb", "cu", "cus",
284                 "cv", "cy", "da", "dak", "dar", "day", "de", "del", "den",
285                 "dgr", "din", "doi", "dra", "dsb", "dua", "dum", "dv", "dyu",
286                 "dz", "ee", "efi", "egy", "eka", "el", "elx", "en",
287                 "enm", "eo", "es", "et", "eu", "ewo", "fa",
288                 "fan", "fat", "ff", "fi", "fiu", "fj", "fo", "fon",
289                 "fr", "frm", "fro", "fur", "fy", "ga", "gaa", "gay",
290                 "gba", "gd", "gem", "gez", "gil", "gl", "gmh", "gn",
291                 "goh", "gon", "gor", "got", "grb", "grc", "gu", "gv",
292                 "gwi", "ha", "hai", "haw", "he", "hi", "hil", "him",
293                 "hit", "hmn", "ho", "hr", "hsb", "ht", "hu", "hup", "hy", "hz",
294                 "ia", "iba", "id", "ie", "ig", "ii", "ijo", "ik",
295                 "ilo", "inc", "ine", "inh", "io", "ira", "iro", "is", "it",
296                 "iu", "ja", "jbo", "jpr", "jrb", "jv", "ka", "kaa", "kab",
297                 "kac", "kam", "kar", "kaw", "kbd", "kg", "kha", "khi",
298                 "kho", "ki", "kj", "kk", "kl", "km", "kmb", "kn",
299                 "ko", "kok", "kos", "kpe", "kr", "krc", "kro", "kru", "ks",
300                 "ku", "kum", "kut", "kv", "kw", "ky", "la", "lad",
301                 "lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol",
302                 "loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus",
303                 "lv", "mad", "mag", "mai", "mak", "man", "map", "mas",
304                 "mdf", "mdr", "men", "mg", "mga", "mh", "mi", "mic", "min",
305                 "mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno",
306                 "mo", "moh", "mos", "mr", "ms", "mt", "mul", "mun",
307                 "mus", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nap",
308                 "nb", "nd", "nds", "ne", "new", "ng", "nia", "nic",
309                 "niu", "nl", "nn", "no", "nog", "non", "nr", "nso", "nub",
310                 "nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oc", "oj",
311                 "om", "or", "os", "osa", "ota", "oto", "pa", "paa",
312                 "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",
313                 "pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu",
314                 "raj", "rap", "rar", "rm", "rn", "ro", "roa", "rom",
315                 "ru", "rup", "rw", "sa", "sad", "sah", "sai", "sal", "sam",
316                 "sas", "sat", "sc", "sco", "sd", "se", "sel", "sem",
317                 "sg", "sga", "sgn", "shn", "si", "sid", "sio", "sit",
318                 "sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn",
319                 "sms", "sn", "snk", "so", "sog", "son", "sq", "sr",
320                 "srr", "ss", "ssa", "st", "su", "suk", "sus", "sux",
321                 "sv", "sw", "syr", "ta", "tai", "te", "tem", "ter",
322                 "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl",
323                 "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr",
324                 "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw",
325                 "ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur",
326                 "uz", "vai", "ve", "vi", "vo", "vot", "wa", "wak",
327                 "wal", "war", "was", "wen", "wo", "xal", "xh", "yao", "yap",
328                 "yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd",
329                 "zu", "zun",
330             };
331
332             String JavaDoc[] tempReplacementLanguages = {
333                 "id", "he", "yi", "jv", "sr", "nb",/* replacement language codes */
334             };
335
336             String JavaDoc[] tempObsoleteLanguages = {
337                 "in", "iw", "ji", "jw", "sh", "no", /* obsolete language codes */
338             };
339
340             /* This list MUST contain a three-letter code for every two-letter code in the
341                list above, and they MUST ne in the same order (i.e., the same language must
342                be in the same place in both lists)! */

343             String JavaDoc[] tempLanguages3 = {
344                 /*"aa", "ab", "ace", "ach", "ada", "ady", "ae", "af", "afa", */
345                 "aar", "abk", "ace", "ach", "ada", "ady", "ave", "afr", "afa",
346                 /*"afh", "ak", "akk", "ale", "alg", "am", "an", "ang", "apa", */
347                 "afh", "aka", "akk", "ale", "alg", "amh", "arg", "ang", "apa",
348                 /*"ar", "arc", "arn", "arp", "art", "arw", "as", "ast", */
349                 "ara", "arc", "arn", "arp", "art", "arw", "asm", "ast",
350                 /*"ath", "aus", "av", "awa", "ay", "az", "ba", "bad", */
351                 "ath", "aus", "ava", "awa", "aym", "aze", "bak", "bad",
352                 /*"bai", "bal", "ban", "bas", "bat", "be", "bej", */
353                 "bai", "bal", "ban", "bas", "bat", "bel", "bej",
354                 /*"bem", "ber", "bg", "bh", "bho", "bi", "bik", "bin", */
355                 "bem", "ber", "bul", "bih", "bho", "bis", "bik", "bin",
356                 /*"bla", "bm", "bn", "bnt", "bo", "br", "bra", "bs", */
357                 "bla", "bam", "ben", "bnt", "bod", "bre", "bra", "bos",
358                 /*"btk", "bua", "bug", "byn", "ca", "cad", "cai", "car", "cau", */
359                 "btk", "bua", "bug", "byn", "cat", "cad", "cai", "car", "cau",
360                 /*"ce", "ceb", "cel", "ch", "chb", "chg", "chk", "chm", */
361                 "che", "ceb", "cel", "cha", "chb", "chg", "chk", "chm",
362                 /*"chn", "cho", "chp", "chr", "chy", "cmc", "co", "cop", */
363                 "chn", "cho", "chp", "chr", "chy", "cmc", "cos", "cop",
364                 /*"cpe", "cpf", "cpp", "cr", "crh", "crp", "cs", "csb", "cu", "cus", */
365                 "cpe", "cpf", "cpp", "cre", "crh", "crp", "ces", "csb", "chu", "cus",
366                 /*"cv", "cy", "da", "dak", "dar", "day", "de", "del", "den", */
367                 "chv", "cym", "dan", "dak", "dar", "day", "deu", "del", "den",
368                 /*"dgr", "din", "doi", "dra", "dsb", "dua", "dum", "dv", "dyu", */
369                 "dgr", "din", "doi", "dra", "dsb", "dua", "dum", "div", "dyu",
370                 /*"dz", "ee", "efi", "egy", "eka", "el", "elx", "en", */
371                 "dzo", "ewe", "efi", "egy", "eka", "ell", "elx", "eng",
372                 /*"enm", "eo", "es", "et", "eu", "ewo", "fa", */
373                 "enm", "epo", "spa", "est", "eus", "ewo", "fas",
374                 /*"fan", "fat", "ff", "fi", "fiu", "fj", "fo", "fon", */
375                 "fan", "fat", "ful", "fin", "fiu", "fij", "fao", "fon",
376                 /*"fr", "frm", "fro", "fur", "fy", "ga", "gaa", "gay", */
377                 "fra", "frm", "fro", "fur", "fry", "gle", "gaa", "gay",
378                 /*"gba", "gd", "gem", "gez", "gil", "gl", "gmh", "gn", */
379                 "gba", "gla", "gem", "gez", "gil", "glg", "gmh", "grn",
380                 /*"goh", "gon", "gor", "got", "grb", "grc", "gu", "gv", */
381                 "goh", "gon", "gor", "got", "grb", "grc", "guj", "glv",
382                 /*"gwi", "ha", "hai", "haw", "he", "hi", "hil", "him", */
383                 "gwi", "hau", "hai", "haw", "heb", "hin", "hil", "him",
384                 /*"hit", "hmn", "ho", "hr", "hsb", "ht", "hu", "hup", "hy", "hz", */
385                 "hit", "hmn", "hmo", "hrv", "hsb", "hat", "hun", "hup", "hye", "her",
386                 /*"ia", "iba", "id", "ie", "ig", "ii", "ijo", "ik", */
387                 "ina", "iba", "ind", "ile", "ibo", "iii", "ijo", "ipk",
388                 /*"ilo", "inc", "ine", "inh", "io", "ira", "iro", "is", "it", */
389                 "ilo", "inc", "ine", "inh", "ido", "ira", "iro", "isl", "ita",
390                 /*"iu", "ja", "jbo", "jpr", "jrb", "jv", "ka", "kaa", "kab", */
391                 "iku", "jpn", "jbo", "jpr", "jrb", "jaw", "kat", "kaa", "kab",
392                 /*"kac", "kam", "kar", "kaw", "kbd", "kg", "kha", "khi", */
393                 "kac", "kam", "kar", "kaw", "kbd", "kon", "kha", "khi",
394                 /*"kho", "ki", "kj", "kk", "kl", "km", "kmb", "kn", */
395                 "kho", "kik", "kua", "kaz", "kal", "khm", "kmb", "kan",
396                 /*"ko", "kok", "kos", "kpe", "kr", "krc", "kro", "kru", "ks", */
397                 "kor", "kok", "kos", "kpe", "kau", "krc", "kro", "kru", "kas",
398                 /*"ku", "kum", "kut", "kv", "kw", "ky", "la", "lad", */
399                 "kur", "kum", "kut", "kom", "cor", "kir", "lat", "lad",
400                 /*"lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol", */
401                 "lah", "lam", "ltz", "lez", "lug", "lim", "lin", "lao", "lol",
402                 /*"loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus", */
403                 "loz", "lit", "lub", "lua", "lui", "lun", "luo", "lus",
404                 /*"lv", "mad", "mag", "mai", "mak", "man", "map", "mas", */
405                 "lav", "mad", "mag", "mai", "mak", "man", "map", "mas",
406                 /*"mdf", "mdr", "men", "mg", "mga", "mh", "mi", "mic", "min", */
407                 "mdf", "mdr", "men", "mlg", "mga", "mah", "mri", "mic", "min",
408                 /*"mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno", */
409                 "mis", "mkd", "mkh", "mal", "mon", "mnc", "mni", "mno",
410                 /*"mo", "moh", "mos", "mr", "ms", "mt", "mul", "mun", */
411                 "mol", "moh", "mos", "mar", "msa", "mlt", "mul", "mun",
412                 /*"mus", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nap", */
413                 "mus", "mwr", "mya", "myn", "myv", "nau", "nah", "nai", "nap",
414                 /*"nb", "nd", "nds", "ne", "new", "ng", "nia", "nic", */
415                 "nob", "nde", "nds", "nep", "new", "ndo", "nia", "nic",
416                 /*"niu", "nl", "nn", "no", "nog", "non", "nr", "nso", "nub", */
417                 "niu", "nld", "nno", "nor", "nog", "non", "nbl", "nso", "nub",
418                 /*"nv", "nwc", "ny", "nym", "nyn", "nyo", "nzi", "oc", "oj", */
419                 "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi", "oci", "oji",
420                 /*"om", "or", "os", "osa", "ota", "oto", "pa", "paa", */
421                 "orm", "ori", "oss", "osa", "ota", "oto", "pan", "paa",
422                 /*"pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn", */
423                 "pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",
424                 /*"pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu", */
425                 "pli", "pol", "pon", "pra", "pro", "pus", "por", "que",
426                 /*"raj", "rap", "rar", "rm", "rn", "ro", "roa", "rom", */
427                 "raj", "rap", "rar", "roh", "run", "ron", "roa", "rom",
428                 /*"ru", "rup", "rw", "sa", "sad", "sah", "sai", "sal", "sam", */
429                 "rus", "rup", "kin", "san", "sad", "sah", "sai", "sal", "sam",
430                 /*"sas", "sat", "sc", "sco", "sd", "se", "sel", "sem", */
431                 "sas", "sat", "srd", "sco", "snd", "sme", "sel", "sem",
432                 /*"sg", "sga", "sgn", "shn", "si", "sid", "sio", "sit", */
433                 "sag", "sga", "sgn", "shn", "sin", "sid", "sio", "sit",
434                 /*"sk", "sl", "sla", "sm", "sma", "smi", "smj", "smn", */
435                 "slk", "slv", "sla", "smo", "sma", "smi", "smj", "smn",
436                 /*"sms", "sn", "snk", "so", "sog", "son", "sq", "sr", */
437                 "sms", "sna", "snk", "som", "sog", "son", "sqi", "srp",
438                 /*"srr", "ss", "ssa", "st", "su", "suk", "sus", "sux", */
439                 "srr", "ssw", "ssa", "sot", "sun", "suk", "sus", "sux",
440                 /*"sv", "sw", "syr", "ta", "tai", "te", "tem", "ter", */
441                 "swe", "swa", "syr", "tam", "tai", "tel", "tem", "ter",
442                 /*"tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl", */
443                 "tet", "tgk", "tha", "tir", "tig", "tiv", "tuk", "tkl",
444                 /*"tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", */
445                 "tgl", "tlh", "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur",
446                 /*"ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", */
447                 "tso", "tsi", "tat", "tum", "tup", "tut", "tvl", "twi",
448                 /*"ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur", */
449                 "tah", "tyv", "udm", "uig", "uga", "ukr", "umb", "und", "urd",
450                 /*"uz", "vai", "ve", "vi", "vo", "vot", "wa", "wak", */
451                 "uzb", "vai", "ven", "vie", "vol", "vot", "wln", "wak",
452                 /*"wal", "war", "was", "wen", "wo", "xal", "xh", "yao", "yap", */
453                 "wal", "war", "was", "wen", "wol", "xal", "xho", "yao", "yap",
454                 /*"yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd", */
455                 "yid", "yor", "ypk", "zha", "zap", "zen", "zho", "znd",
456                 /*"zu", "zun", */
457                 "zul", "zun",
458             };
459     
460             String JavaDoc[] tempObsoleteLanguages3 = {
461                 /* "in", "iw", "ji", "jw", "sh", */
462                 "ind", "heb", "yid", "jaw", "srp",
463             };
464
465             synchronized (ULocale.class) {
466                 if (_languages == null) {
467                     _languages = tempLanguages;
468                     _replacementLanguages = tempReplacementLanguages;
469                     _obsoleteLanguages = tempObsoleteLanguages;
470                     _languages3 = tempLanguages3;
471                     _obsoleteLanguages3 = tempObsoleteLanguages3;
472                 }
473             }
474         }
475     }
476
477     private static String JavaDoc[] _countries;
478     private static String JavaDoc[] _deprecatedCountries;
479     private static String JavaDoc[] _replacementCountries;
480     private static String JavaDoc[] _obsoleteCountries;
481     private static String JavaDoc[] _countries3;
482     private static String JavaDoc[] _obsoleteCountries3;
483
484     // Avoid initializing country tables unless we have to.
485
private static void initCountryTables() {
486         if (_countries == null) {
487             /* ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per
488                http://www.evertype.com/standards/iso3166/iso3166-1-en.html
489                added new codes keeping the old ones for compatibility
490                updated to include 1999/12/03 revisions *CWB*/

491     
492             /* RO(ROM) is now RO(ROU) according to
493                http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html
494             */

495     
496             /* This list MUST be in sorted order, and MUST contain only two-letter codes! */
497             String JavaDoc[] tempCountries = {
498                 "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN",
499                 "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AZ",
500                 "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI",
501                 "BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV",
502                 "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG",
503                 "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR",
504                 "CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK",
505                 "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER",
506                 "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR",
507                 "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GL",
508                 "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU",
509                 "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU",
510                 "ID", "IE", "IL", "IN", "IO", "IQ", "IR", "IS",
511                 "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI",
512                 "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA",
513                 "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU",
514                 "LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK",
515                 "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS",
516                 "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA",
517                 "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP",
518                 "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG",
519                 "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT",
520                 "PW", "PY", "QA", "RE", "RO", "RU", "RW", "SA",
521                 "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ",
522                 "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV",
523                 "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ",
524                 "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV",
525                 "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ",
526                 "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF",
527                 "WS", "YE", "YT", "YU", "ZA", "ZM", "ZW",
528             };
529
530             /* this table is used for 3 letter codes */
531             String JavaDoc[] tempObsoleteCountries = {
532                 "FX", "RO", "TP", "ZR", /* obsolete country codes */
533             };
534             
535             String JavaDoc[] tempDeprecatedCountries = {
536                "BU", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR" /* deprecated country list */
537             };
538             String JavaDoc[] tempReplacementCountries = {
539            /* "BU", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR" */
540                "MM", "BJ", "FR", "BF", "VU", "ZW", "TL", "CS", "CD", /* replacement country codes */
541             };
542     
543             /* This list MUST contain a three-letter code for every two-letter code in
544                the above list, and they MUST be listed in the same order! */

545             String JavaDoc[] tempCountries3 = {
546                 /*"AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", */
547                 "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", "ANT",
548                 /*"AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AZ", */
549                 "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "AZE",
550                 /*"BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", */
551                 "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI",
552                 /*"BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", */
553                 "BEN", "BMU", "BRN", "BOL", "BRA", "BHS", "BTN", "BVT",
554                 /*"BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", */
555                 "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG",
556                 /*"CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", */
557                 "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI",
558                 /*"CU", "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", */
559                 "CUB", "CPV", "CXR", "CYP", "CZE", "DEU", "DJI", "DNK",
560                 /*"DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", */
561                 "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH", "ERI",
562                 /*"ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", */
563                 "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA",
564                 /*"GA", "GB", "GD", "GE", "GF", "GH", "GI", "GL", */
565                 "GAB", "GBR", "GRD", "GEO", "GUF", "GHA", "GIB", "GRL",
566                 /*"GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", */
567                 "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM",
568                 /*"GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", */
569                 "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN",
570                 /*"ID", "IE", "IL", "IN", "IO", "IQ", "IR", "IS", */
571                 "IDN", "IRL", "ISR", "IND", "IOT", "IRQ", "IRN", "ISL",
572                 /*"IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI", */
573                 "ITA", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR",
574                 /*"KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", */
575                 "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO",
576                 /*"LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", */
577                 "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX",
578                 /*"LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", */
579                 "LVA", "LBY", "MAR", "MCO", "MDA", "MDG", "MHL", "MKD",
580                 /*"ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", */
581                 "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR",
582                 /*"MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", */
583                 "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM",
584                 /*"NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", */
585                 "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL",
586                 /*"NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", */
587                 "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG",
588                 /*"PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", */
589                 "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT",
590                 /*"PW", "PY", "QA", "RE", "RO", "RU", "RW", "SA", */
591                 "PLW", "PRY", "QAT", "REU", "ROU", "RUS", "RWA", "SAU",
592                 /*"SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", */
593                 "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM",
594                 /*"SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", */
595                 "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "STP", "SLV",
596                 /*"SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", */
597                 "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK",
598                 /*"TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", */
599                 "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV",
600                 /*"TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", */
601                 "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB",
602                 /*"VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", */
603                 "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF",
604                 /*"WS", "YE", "YT", "YU", "ZA", "ZM", "ZW", */
605                 "WSM", "YEM", "MYT", "YUG", "ZAF", "ZMB", "ZWE",
606             };
607     
608             String JavaDoc[] tempObsoleteCountries3 = {
609                 /*"FX", "RO", "TP", "ZR", */
610                 "FXX", "ROM", "TMP", "ZAR",
611             };
612
613             synchronized (ULocale.class) {
614                 if (_countries == null) {
615                     _countries = tempCountries;
616                     _deprecatedCountries = tempDeprecatedCountries;
617                     _replacementCountries = tempReplacementCountries;
618                     _obsoleteCountries = tempObsoleteCountries;
619                     _countries3 = tempCountries3;
620                     _obsoleteCountries3 = tempObsoleteCountries3;
621                 }
622             }
623         }
624     }
625
626     private static String JavaDoc[][] _variantsToKeywords;
627
628     private static void initVariantsTable() {
629         if (_variantsToKeywords == null) {
630             /**
631              * This table lists pairs of locale ids for canonicalization. The
632              * The first item is the normalized id, the second item is the
633              * canonicalized id.
634              */

635             String JavaDoc[][] tempVariantsToKeywords = {
636 // { EMPTY_STRING, "en_US_POSIX", null, null }, /* .NET name */
637
{ "C", "en_US_POSIX", null, null }, /* POSIX name */
638                 { "art_LOJBAN", "jbo", null, null }, /* registered name */
639                 { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */
640                 { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */
641                 { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
642                 { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */
643                 { "de_1901", "de__1901", null, null }, /* registered name */
644                 { "de_1906", "de__1906", null, null }, /* registered name */
645                 { "de__PHONEBOOK", "de", "collation", "phonebook" },
646                 { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
647                 { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
648                 { "de_LU_PREEURO", "de_LU", "currency", "EUR" },
649                 { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
650                 { "en_BOONT", "en__BOONT", null, null }, /* registered name */
651                 { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */
652                 { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
653                 { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
654                 { "es__TRADITIONAL", "es", "collation", "traditional" },
655                 { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
656                 { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
657                 { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
658                 { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
659                 { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
660                 { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
661                 { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
662                 { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
663                 { "hi__DIRECT", "hi", "collation", "direct" },
664                 { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
665                 { "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" },
666 // { "nb_NO_NY", "nn_NO", null, null },
667
{ "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
668                 { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
669                 { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
670                 { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */
671                 { "sr_SP_CYRL", "sr_Cyrl_CS", null, null }, /* .NET name */
672                 { "sr_SP_LATN", "sr_Latn_CS", null, null }, /* .NET name */
673                 { "sr_YU_CYRILLIC", "sr_Cyrl_CS", null, null }, /* Linux name */
674                 { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
675                 { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */
676                 { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */
677                 { "zh_CHS", "zh_Hans", null, null }, /* .NET name */
678                 { "zh_CHT", "zh_Hant", null, null }, /* .NET name */
679                 { "zh_GAN", "zh__GAN", null, null }, /* registered name */
680                 { "zh_GUOYU", "zh", null, null }, /* registered name */
681                 { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */
682                 { "zh_MIN", "zh__MIN", null, null }, /* registered name */
683                 { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */
684                 { "zh_WUU", "zh__WUU", null, null }, /* registered name */
685                 { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */
686                 { "zh_YUE", "zh__YUE", null, null }, /* registered name */
687                 { "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" },
688                 { "zh_TW_STROKE", "zh_TW", "collation", "stroke" },
689                 { "zh__PINYIN", "zh", "collation", "pinyin" }
690             };
691     
692             synchronized (ULocale.class) {
693                 if (_variantsToKeywords == null) {
694                     _variantsToKeywords = tempVariantsToKeywords;
695                 }
696             }
697         }
698     }
699
700     /**
701      * Private constructor used by static initializers.
702      */

703     private ULocale(String JavaDoc localeID, Locale JavaDoc locale) {
704         this.localeID = localeID;
705         this.locale = locale;
706     }
707
708     /**
709      * Construct a ULocale object from a {@link java.util.Locale}.
710      * @param loc a JDK locale
711      * @stable ICU 2.8
712      * @internal
713      */

714     private ULocale(Locale JavaDoc loc) {
715         this.localeID = getName(loc.toString());
716         this.locale = loc;
717     }
718
719     /**
720      * Return a ULocale object for a {@link java.util.Locale}.
721      * The ULocale is canonicalized.
722      * @param loc a JDK locale
723      * @stable ICU 3.2
724      */

725     public static ULocale forLocale(Locale JavaDoc loc) {
726         if (loc == null) {
727             return null;
728         }
729         ULocale result = (ULocale)CACHE.get(loc);
730         if (result == null) {
731             if (defaultULocale != null && loc == defaultULocale.locale) {
732             result = defaultULocale;
733         } else {
734                 String JavaDoc locStr = loc.toString();
735                 if (locStr.length() == 0) {
736                     result = ROOT;
737                 } else {
738                     result = new ULocale(locStr, loc);
739                 }
740             }
741             CACHE.put(loc, result);
742         }
743         return result;
744     }
745
746     /**
747      * Construct a ULocale from a RFC 3066 locale ID. The locale ID consists
748      * of optional language, script, country, and variant fields in that order,
749      * separated by underscores, followed by an optional keyword list. The
750      * script, if present, is four characters long-- this distinguishes it
751      * from a country code, which is two characters long. Other fields
752      * are distinguished by position as indicated by the underscores. The
753      * start of the keyword list is indicated by '@', and consists of one
754      * or more keyword/value pairs separated by commas.
755      * <p>
756      * This constructor does not canonicalize the localeID.
757      *
758      * @param localeID string representation of the locale, e.g:
759      * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR,collation=traditional"
760      * @stable ICU 2.8
761      */

762     public ULocale(String JavaDoc localeID) {
763         this.localeID = getName(localeID);
764     }
765
766     /**
767      * Convenience overload of ULocale(String, String, String) for
768      * compatibility with java.util.Locale.
769      * @see #ULocale(String, String, String)
770      * @stable ICU 3.4
771      */

772     public ULocale(String JavaDoc a, String JavaDoc b) {
773     this(a, b, null);
774     }
775
776     /**
777      * Construct a ULocale from a localeID constructed from the three 'fields' a, b, and c. These
778      * fields are concatenated using underscores to form a localeID of
779      * the form a_b_c, which is then handled like the localeID passed
780      * to <code>ULocale(String localeID)</code>.
781      *
782      * <p>Java locale strings consisting of language, country, and
783      * variant will be handled by this form, since the country code
784      * (being shorter than four letters long) will not be interpreted
785      * as a script code. If a script code is present, the final
786      * argument ('c') will be interpreted as the country code. It is
787      * recommended that this constructor only be used to ease porting,
788      * and that clients instead use the single-argument constructor
789      * when constructing a ULocale from a localeID.
790      * @param a first component of the locale id
791      * @param b second component of the locale id
792      * @param c third component of the locale id
793      * @see #ULocale(String)
794      * @stable ICU 3.0
795      */

796     public ULocale(String JavaDoc a, String JavaDoc b, String JavaDoc c) {
797         localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
798     }
799
800     /**
801      * Create a ULocale from the id by first canonicalizing the id.
802      * @param nonCanonicalID the locale id to canonicalize
803      * @return the locale created from the canonical version of the ID.
804      * @stable ICU 3.0
805      */

806     public static ULocale createCanonical(String JavaDoc nonCanonicalID) {
807         return new ULocale(canonicalize(nonCanonicalID), (Locale JavaDoc)null);
808     }
809
810     private static String JavaDoc lscvToID(String JavaDoc lang, String JavaDoc script, String JavaDoc country, String JavaDoc variant) {
811         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
812      
813         if (lang != null && lang.length() > 0) {
814             buf.append(lang);
815         }
816         if (script != null && script.length() > 0) {
817             buf.append(UNDERSCORE);
818             buf.append(script);
819         }
820         if (country != null && country.length() > 0) {
821             buf.append(UNDERSCORE);
822             buf.append(country);
823         }
824         if (variant != null && variant.length() > 0) {
825             if (country == null || country.length() == 0) {
826                 buf.append(UNDERSCORE);
827             }
828             buf.append(UNDERSCORE);
829             buf.append(variant);
830         }
831         return buf.toString();
832     }
833
834     /**
835      * Convert this ULocale object to a {@link java.util.Locale}.
836      * @return a JDK locale that either exactly represents this object
837      * or is the closest approximation.
838      * @stable ICU 2.8
839      */

840     public Locale JavaDoc toLocale() {
841         if (locale == null) {
842             String JavaDoc[] names = new IDParser(localeID).getLanguageScriptCountryVariant();
843             locale = new Locale JavaDoc(names[0], names[2], names[3]);
844         }
845         return locale;
846     }
847     
848     private static SoftReference JavaDoc nameCacheRef = new SoftReference JavaDoc(Collections.synchronizedMap(new HashMap JavaDoc()));
849     /**
850      * Keep our own default ULocale.
851      */

852     private static Locale JavaDoc defaultLocale = Locale.getDefault();
853     private static ULocale defaultULocale = new ULocale(defaultLocale);
854
855     /**
856      * Returns the current default ULocale.
857      * @stable ICU 2.8
858      */

859     public static ULocale getDefault() {
860         synchronized (ULocale.class) {
861             Locale JavaDoc currentDefault = Locale.getDefault();
862             if (defaultLocale != currentDefault) {
863                 defaultLocale = currentDefault;
864                 defaultULocale = new ULocale(defaultLocale);
865             }
866             return defaultULocale;
867         }
868     }
869
870     /**
871      * Sets the default ULocale. This also sets the default Locale.
872      * If the caller does not have write permission to the
873      * user.language property, a security exception will be thrown,
874      * and the default ULocale will remain unchanged.
875      * @param newLocale the new default locale
876      * @throws SecurityException
877      * if a security manager exists and its
878      * <code>checkPermission</code> method doesn't allow the operation.
879      * @throws NullPointerException if <code>newLocale</code> is null
880      * @see SecurityManager#checkPermission(java.security.Permission)
881      * @see java.util.PropertyPermission
882      * @stable ICU 3.0
883      */

884     public static synchronized void setDefault(ULocale newLocale){
885         Locale.setDefault(newLocale.toLocale());
886         defaultULocale = newLocale;
887     }
888     
889     /**
890      * This is for compatibility with Locale-- in actuality, since ULocale is
891      * immutable, there is no reason to clone it, so this API returns 'this'.
892      * @stable ICU 3.0
893      */

894     public Object JavaDoc clone() {
895         return this;
896     }
897
898     /**
899      * Returns the hashCode.
900      * @stable ICU 3.0
901      */

902     public int hashCode() {
903         return localeID.hashCode();
904     }
905     
906     /**
907      * Returns true if the other object is another ULocale with the
908      * same full name, or is a String localeID that matches the full name.
909      * Note that since names are not canonicalized, two ULocales that
910      * function identically might not compare equal.
911      *
912      * @return true if this Locale is equal to the specified object.
913      * @stable ICU 3.0
914      */

915     public boolean equals(Object JavaDoc obj) {
916         if (this == obj) {
917             return true;
918         }
919         if (obj instanceof String JavaDoc) {
920             return localeID.equals((String JavaDoc)obj);
921         }
922         if (obj instanceof ULocale) {
923             return localeID.equals(((ULocale)obj).localeID);
924         }
925         return false;
926     }
927     
928     /**
929      * Returns a list of all installed locales.
930      * @stable ICU 3.0
931      */

932     public static ULocale[] getAvailableLocales() {
933         return ICUResourceBundle.getAvailableULocales();
934     }
935
936     /**
937      * Returns a list of all 2-letter country codes defined in ISO 3166.
938      * Can be used to create Locales.
939      * @stable ICU 3.0
940      */

941     public static String JavaDoc[] getISOCountries() {
942         initCountryTables();
943         return (String JavaDoc[])_countries.clone();
944     }
945
946     /**
947      * Returns a list of all 2-letter language codes defined in ISO 639.
948      * Can be used to create Locales.
949      * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
950      * The list this function returns includes both the new and the old codes for the
951      * languages whose codes have changed.]
952      * @stable ICU 3.0
953      */

954     public static String JavaDoc[] getISOLanguages() {
955         initLanguageTables();
956         return (String JavaDoc[])_languages.clone();
957     }
958
959     /**
960      * Returns the language code for this locale, which will either be the empty string
961      * or a lowercase ISO 639 code.
962      * @see #getDisplayLanguage()
963      * @see #getDisplayLanguage(ULocale)
964      * @stable ICU 3.0
965      */

966     public String JavaDoc getLanguage() {
967         return getLanguage(localeID);
968     }
969     
970     /**
971      * Returns the language code for the locale ID,
972      * which will either be the empty string
973      * or a lowercase ISO 639 code.
974      * @see #getDisplayLanguage()
975      * @see #getDisplayLanguage(ULocale)
976      * @stable ICU 3.0
977      */

978     public static String JavaDoc getLanguage(String JavaDoc localeID) {
979         return new IDParser(localeID).getLanguage();
980     }
981      
982     /**
983      * Returns the script code for this locale, which might be the empty string.
984      * @see #getDisplayScript()
985      * @see #getDisplayScript(ULocale)
986      * @stable ICU 3.0
987      */

988     public String JavaDoc getScript() {
989         return getScript(localeID);
990     }
991
992     /**
993      * Returns the script code for the specified locale, which might be the empty string.
994      * @see #getDisplayScript()
995      * @see #getDisplayScript(ULocale)
996      * @stable ICU 3.0
997      */

998     public static String JavaDoc getScript(String JavaDoc localeID) {
999         return new IDParser(localeID).getScript();
1000    }
1001    
1002    /**
1003     * Returns the country/region code for this locale, which will either be the empty string
1004     * or an uppercase ISO 3166 2-letter code.
1005     * @see #getDisplayCountry()
1006     * @see #getDisplayCountry(ULocale)
1007     * @stable ICU 3.0
1008     */

1009    public String JavaDoc getCountry() {
1010        return getCountry(localeID);
1011    }
1012
1013    /**
1014     * Returns the country/region code for this locale, which will either be the empty string
1015     * or an uppercase ISO 3166 2-letter code.
1016     * @param localeID
1017     * @see #getDisplayCountry()
1018     * @see #getDisplayCountry(ULocale)
1019     * @stable ICU 3.0
1020     */

1021    public static String JavaDoc getCountry(String JavaDoc localeID) {
1022        return new IDParser(localeID).getCountry();
1023    }
1024    
1025    /**
1026     * Returns the variant code for this locale, which might be the empty string.
1027     * @see #getDisplayVariant()
1028     * @see #getDisplayVariant(ULocale)
1029     * @stable ICU 3.0
1030     */

1031    public String JavaDoc getVariant() {
1032        return getVariant(localeID);
1033    }
1034
1035    /**
1036     * Returns the variant code for the specified locale, which might be the empty string.
1037     * @see #getDisplayVariant()
1038     * @see #getDisplayVariant(ULocale)
1039     * @stable ICU 3.0
1040     */

1041    public static String JavaDoc getVariant(String JavaDoc localeID) {
1042        return new IDParser(localeID).getVariant();
1043    }
1044
1045    /**
1046     * Returns the fallback locale for the specified locale, which might be the empty string.
1047     * @stable ICU 3.2
1048     */

1049    public static String JavaDoc getFallback(String JavaDoc localeID) {
1050        return getFallbackString(getName(localeID));
1051    }
1052
1053    /**
1054     * Returns the fallback locale for this locale. If this locale is root, returns null.
1055     * @stable ICU 3.2
1056     */

1057    public ULocale getFallback() {
1058        if (localeID.length() == 0 || localeID.charAt(0) == '@') {
1059            return null;
1060        }
1061        return new ULocale(getFallbackString(localeID), (Locale JavaDoc)null);
1062    }
1063
1064    /**
1065     * Return the given (canonical) locale id minus the last part before the tags.
1066     */

1067    private static String JavaDoc getFallbackString(String JavaDoc fallback) {
1068        int limit = fallback.indexOf('@');
1069        if (limit == -1) {
1070            limit = fallback.length();
1071        }
1072        int start = fallback.lastIndexOf('_', limit);
1073        if (start == -1) {
1074            start = 0;
1075        }
1076        return fallback.substring(0, start) + fallback.substring(limit);
1077    }
1078
1079    /**
1080     * Returns the (normalized) base name for this locale.
1081     * @return the base name as a String.
1082     * @stable ICU 3.0
1083     */

1084    public String JavaDoc getBaseName() {
1085        return getBaseName(localeID);
1086    }
1087    
1088    /**
1089     * Returns the (normalized) base name for the specified locale.
1090     * @param localeID the locale ID as a string
1091     * @return the base name as a String.
1092     * @stable ICU 3.0
1093     */

1094    public static String JavaDoc getBaseName(String JavaDoc localeID){
1095        if (localeID.indexOf('@') == -1) {
1096            return localeID;
1097        }
1098        return new IDParser(localeID).getBaseName();
1099    }
1100
1101    /**
1102     * Returns the (normalized) full name for this locale.
1103     *
1104     * @return String the full name of the localeID
1105     * @stable ICU 3.0
1106     */

1107    public String JavaDoc getName() {
1108        return localeID; // always normalized
1109
}
1110
1111    /**
1112     * Returns the (normalized) full name for the specified locale.
1113     *
1114     * @param localeID the localeID as a string
1115     * @return String the full name of the localeID
1116     * @stable ICU 3.0
1117     */

1118    public static String JavaDoc getName(String JavaDoc localeID){
1119        Map JavaDoc cache = (Map JavaDoc)nameCacheRef.get();
1120        if (cache == null) {
1121            cache = Collections.synchronizedMap(new HashMap JavaDoc());
1122            nameCacheRef = new SoftReference JavaDoc(cache);
1123        }
1124        String JavaDoc name = (String JavaDoc)cache.get(localeID);
1125        if (name == null) {
1126            name = new IDParser(localeID).getName();
1127            cache.put(localeID, name);
1128        }
1129        return name;
1130    }
1131
1132    /**
1133     * Returns a string representation of this object.
1134     * @stable ICU 3.0
1135     */

1136    public String JavaDoc toString() {
1137        return localeID;
1138    }
1139
1140    /**
1141     * Returns an iterator over keywords for this locale. If there
1142     * are no keywords, returns null.
1143     * @return iterator over keywords, or null if there are no keywords.
1144     * @stable ICU 3.0
1145     */

1146    public Iterator JavaDoc getKeywords() {
1147        return getKeywords(localeID);
1148    }
1149
1150    /**
1151     * Returns an iterator over keywords for the specified locale. If there
1152     * are no keywords, returns null.
1153     * @return an iterator over the keywords in the specified locale, or null
1154     * if there are no keywords.
1155     * @stable ICU 3.0
1156     */

1157    public static Iterator JavaDoc getKeywords(String JavaDoc localeID){
1158        return new IDParser(localeID).getKeywords();
1159    }
1160
1161    /**
1162     * Returns the value for a keyword in this locale. If the keyword is not defined, returns null.
1163     * @param keywordName name of the keyword whose value is desired. Case insensitive.
1164     * @return the value of the keyword, or null.
1165     * @stable ICU 3.0
1166     */

1167    public String JavaDoc getKeywordValue(String JavaDoc keywordName){
1168        return getKeywordValue(localeID, keywordName);
1169    }
1170    
1171    /**
1172     * Returns the value for a keyword in the specified locale. If the keyword is not defined, returns null.
1173     * The locale name does not need to be normalized.
1174     * @param keywordName name of the keyword whose value is desired. Case insensitive.
1175     * @return String the value of the keyword as a string
1176     * @stable ICU 3.0
1177     */

1178    public static String JavaDoc getKeywordValue(String JavaDoc localeID, String JavaDoc keywordName) {
1179        return new IDParser(localeID).getKeywordValue(keywordName);
1180    }
1181
1182    /**
1183     * Utility class to parse and normalize locale ids (including POSIX style)
1184     */

1185    private static final class IDParser {
1186        private char[] id;
1187        private int index;
1188        private char[] buffer;
1189        private int blen;
1190        // um, don't handle POSIX ids unless we request it. why not? well... because.
1191
private boolean canonicalize;
1192        private boolean hadCountry;
1193
1194        // used when canonicalizing
1195
Map JavaDoc keywords;
1196        String JavaDoc baseName;
1197
1198        /**
1199         * Parsing constants.
1200         */

1201        private static final char KEYWORD_SEPARATOR = '@';
1202        private static final char HYPHEN = '-';
1203        private static final char KEYWORD_ASSIGN = '=';
1204        private static final char COMMA = ',';
1205        private static final char ITEM_SEPARATOR = ';';
1206        private static final char DOT = '.';
1207
1208        private IDParser(String JavaDoc localeID) {
1209            this(localeID, false);
1210        }
1211
1212        private IDParser(String JavaDoc localeID, boolean canonicalize) {
1213            id = localeID.toCharArray();
1214            index = 0;
1215            buffer = new char[id.length + 5];
1216            blen = 0;
1217            this.canonicalize = canonicalize;
1218        }
1219
1220        private void reset() {
1221            index = blen = 0;
1222        }
1223
1224        // utilities for working on text in the buffer
1225

1226        /**
1227         * Append c to the buffer.
1228         */

1229        private void append(char c) {
1230            try {
1231                buffer[blen] = c;
1232            }
1233            catch (IndexOutOfBoundsException JavaDoc e) {
1234                if (buffer.length > 512) {
1235                    // something is seriously wrong, let this go
1236
throw e;
1237                }
1238                char[] nbuffer = new char[buffer.length * 2];
1239                System.arraycopy(buffer, 0, nbuffer, 0, buffer.length);
1240                nbuffer[blen] = c;
1241                buffer = nbuffer;
1242            }
1243            ++blen;
1244        }
1245
1246        private void addSeparator() {
1247            append(UNDERSCORE);
1248        }
1249
1250        /**
1251         * Returns the text in the buffer from start to blen as a String.
1252         */

1253        private String JavaDoc getString(int start) {
1254            if (start == blen) {
1255                return EMPTY_STRING;
1256            }
1257            return new String JavaDoc(buffer, start, blen-start);
1258        }
1259
1260        /**
1261         * Set the length of the buffer to pos, then append the string.
1262         */

1263        private void set(int pos, String JavaDoc s) {
1264            this.blen = pos; // no safety
1265
append(s);
1266        }
1267
1268        /**
1269         * Append the string to the buffer.
1270         */

1271        private void append(String JavaDoc s) {
1272            for (int i = 0; i < s.length(); ++i) {
1273                append(s.charAt(i));
1274            }
1275        }
1276
1277        // utilities for parsing text out of the id
1278

1279        /**
1280         * Character to indicate no more text is available in the id.
1281         */

1282        private static final char DONE = '\uffff';
1283
1284        /**
1285         * Returns the character at index in the id, and advance index. The returned character
1286         * is DONE if index was at the limit of the buffer. The index is advanced regardless
1287         * so that decrementing the index will always 'unget' the last character returned.
1288         */

1289        private char next() {
1290            if (index == id.length) {
1291                index++;
1292                return DONE;
1293            }
1294
1295            return id[index++];
1296        }
1297
1298        /**
1299         * Advance index until the next terminator or id separator, and leave it there.
1300         */

1301        private void skipUntilTerminatorOrIDSeparator() {
1302            while (!isTerminatorOrIDSeparator(next())) {
1303            }
1304            --index;
1305        }
1306
1307        /**
1308         * Returns true if the character at index in the id is a terminator.
1309         */

1310        private boolean atTerminator() {
1311            return index >= id.length || isTerminator(id[index]);
1312        }
1313
1314        /*
1315         * Returns true if the character is an id separator (underscore or hyphen).
1316         */

1317/* private boolean isIDSeparator(char c) {
1318            return c == UNDERSCORE || c == HYPHEN;
1319        }*/

1320
1321        /**
1322         * Returns true if the character is a terminator (keyword separator, dot, or DONE).
1323         * Dot is a terminator because of the POSIX form, where dot precedes the codepage.
1324         */

1325        private boolean isTerminator(char c) {
1326            // always terminate at DOT, even if not handling POSIX. It's an error...
1327
return c == KEYWORD_SEPARATOR || c == DONE || c == DOT;
1328        }
1329
1330        /**
1331         * Returns true if the character is a terminator or id separator.
1332         */

1333        private boolean isTerminatorOrIDSeparator(char c) {
1334            return c == KEYWORD_SEPARATOR || c == UNDERSCORE || c == HYPHEN ||
1335                c == DONE || c == DOT;
1336        }
1337
1338        /**
1339         * Returns true if the start of the buffer has an experimental or private language
1340         * prefix, the pattern '[ixIX][-_].' shows the syntax checked.
1341         */

1342        private boolean haveExperimentalLanguagePrefix() {
1343            if (id.length > 2) {
1344                char c = id[1];
1345                if (c == HYPHEN || c == UNDERSCORE) {
1346                    c = id[0];
1347                    return c == 'x' || c == 'X' || c == 'i' || c == 'I';
1348                }
1349            }
1350            return false;
1351        }
1352
1353        /**
1354         * Returns true if a value separator occurs at or after index.
1355         */

1356        private boolean haveKeywordAssign() {
1357            // assume it is safe to start from index
1358
for (int i = index; i < id.length; ++i) {
1359                if (id[i] == KEYWORD_ASSIGN) {
1360                    return true;
1361                }
1362            }
1363            return false;
1364        }
1365
1366        /**
1367         * Advance index past language, and accumulate normalized language code in buffer.
1368         * Index must be at 0 when this is called. Index is left at a terminator or id
1369         * separator. Returns the start of the language code in the buffer.
1370         */

1371        private int parseLanguage() {
1372            if (haveExperimentalLanguagePrefix()) {
1373                append(Character.toLowerCase(id[0]));
1374                append(HYPHEN);
1375                index = 2;
1376            }
1377        
1378            char c;
1379            while(!isTerminatorOrIDSeparator(c = next())) {
1380                append(Character.toLowerCase(c));
1381            }
1382            --index; // unget
1383

1384            if (blen == 3) {
1385                initLanguageTables();
1386
1387                /* convert 3 character code to 2 character code if possible *CWB*/
1388                String JavaDoc lang = getString(0);
1389                int offset = findIndex(_languages3, lang);
1390                if (offset >= 0) {
1391                    set(0, _languages[offset]);
1392                } else {
1393                    offset = findIndex(_obsoleteLanguages3, lang);
1394                    if (offset >= 0) {
1395                        set(0, _obsoleteLanguages[offset]);
1396                    }
1397                }
1398            }
1399
1400            return 0;
1401        }
1402
1403        /**
1404         * Advance index past language. Index must be at 0 when this is called. Index
1405         * is left at a terminator or id separator.
1406         */

1407        private void skipLanguage() {
1408            if (haveExperimentalLanguagePrefix()) {
1409                index = 2;
1410            }
1411            skipUntilTerminatorOrIDSeparator();
1412        }
1413
1414        /**
1415         * Advance index past script, and accumulate normalized script in buffer.
1416         * Index must be immediately after the language.
1417         * If the item at this position is not a script (is not four characters
1418         * long) leave index and buffer unchanged. Otherwise index is left at
1419         * a terminator or id separator. Returns the start of the script code
1420         * in the buffer (this may be equal to the buffer length, if there is no
1421         * script).
1422         */

1423        private int parseScript() {
1424            if (!atTerminator()) {
1425                int oldIndex = index; // save original index
1426
++index;
1427
1428                int oldBlen = blen; // get before append hyphen, if we truncate everything is undone
1429
char c;
1430                while(!isTerminatorOrIDSeparator(c = next())) {
1431                    if (blen == oldBlen) { // first pass
1432
addSeparator();
1433                        append(Character.toUpperCase(c));
1434                    } else {
1435                        append(Character.toLowerCase(c));
1436                    }
1437                }
1438                --index; // unget
1439

1440                /* If it's not exactly 4 characters long, then it's not a script. */
1441                if (index - oldIndex != 5) { // +1 to account for separator
1442
index = oldIndex;
1443                    blen = oldBlen;
1444                } else {
1445                    oldBlen++; // index past hyphen, for clients who want to extract just the script
1446
}
1447
1448                return oldBlen;
1449            }
1450            return blen;
1451        }
1452
1453        /**
1454         * Advance index past script.
1455         * Index must be immediately after the language and IDSeparator.
1456         * If the item at this position is not a script (is not four characters
1457         * long) leave index. Otherwise index is left at a terminator or
1458         * id separator.
1459         */

1460        private void skipScript() {
1461            if (!atTerminator()) {
1462                int oldIndex = index;
1463                ++index;
1464
1465                skipUntilTerminatorOrIDSeparator();
1466                if (index - oldIndex != 5) { // +1 to account for separator
1467
index = oldIndex;
1468                }
1469            }
1470        }
1471
1472        /**
1473         * Advance index past country, and accumulate normalized country in buffer.
1474         * Index must be immediately after the script (if there is one, else language)
1475         * and IDSeparator. Return the start of the country code in the buffer.
1476         */

1477        private int parseCountry() {
1478            if (!atTerminator()) {
1479                ++index;
1480
1481                int oldBlen = blen;
1482                char c;
1483                while (!isTerminatorOrIDSeparator(c = next())) {
1484                    if (oldBlen == blen) { // first, add hyphen
1485
hadCountry = true; // we have a country, let variant parsing know
1486
addSeparator();
1487                        ++oldBlen; // increment past hyphen
1488
}
1489                    append(Character.toUpperCase(c));
1490                }
1491                --index; // unget
1492

1493                if (blen - oldBlen == 3) {
1494                    initCountryTables();
1495
1496                    /* convert 3 character code to 2 character code if possible *CWB*/
1497                    int offset = findIndex(_countries3, getString(oldBlen));
1498                    if (offset >= 0) {
1499                        set(oldBlen, _countries[offset]);
1500                    } else {
1501                        offset = findIndex(_obsoleteCountries3, getString(oldBlen));
1502                        if (offset >= 0) {
1503                            set(oldBlen, _obsoleteCountries[offset]);
1504                        }
1505                    }
1506                }
1507
1508                return oldBlen;
1509            }
1510
1511            return blen;
1512        }
1513
1514        /**
1515         * Advance index past country.
1516         * Index must be immediately after the script (if there is one, else language)
1517         * and IDSeparator.
1518         */

1519        private void skipCountry() {
1520            if (!atTerminator()) {
1521                ++index;
1522                skipUntilTerminatorOrIDSeparator();
1523            }
1524        }
1525
1526        /**
1527         * Advance index past variant, and accumulate normalized variant in buffer. This ignores
1528         * the codepage information from POSIX ids. Index must be immediately after the country
1529         * or script. Index is left at the keyword separator or at the end of the text. Return
1530         * the start of the variant code in the buffer.
1531         *
1532         * In standard form, we can have the following forms:
1533         * ll__VVVV
1534         * ll_CC_VVVV
1535         * ll_Ssss_VVVV
1536         * ll_Ssss_CC_VVVV
1537         *
1538         * This also handles POSIX ids, which can have the following forms (pppp is code page id):
1539         * ll_CC.pppp --> ll_CC
1540         * ll_CC.pppp@VVVV --> ll_CC_VVVV
1541         * ll_CC@VVVV --> ll_CC_VVVV
1542         *
1543         * We identify this use of '@' in POSIX ids by looking for an '=' following
1544         * the '@'. If there is one, we consider '@' to start a keyword list, instead of
1545         * being part of a POSIX id.
1546         *
1547         * Note: since it was decided that we want an option to not handle POSIX ids, this
1548         * becomes a bit more complex.
1549         */

1550        private int parseVariant() {
1551            int oldBlen = blen;
1552
1553            boolean start = true;
1554            boolean needSeparator = true;
1555            boolean skipping = false;
1556            char c;
1557            while ((c = next()) != DONE) {
1558                if (c == DOT) {
1559                    start = false;
1560                    skipping = true;
1561                } else if (c == KEYWORD_SEPARATOR) {
1562                    if (haveKeywordAssign()) {
1563                        break;
1564                    }
1565                    skipping = false;
1566                    start = false;
1567                    needSeparator = true; // add another underscore if we have more text
1568
} else if (start) {
1569                    start = false;
1570                } else if (!skipping) {
1571                    if (needSeparator) {
1572                        boolean incOldBlen = blen == oldBlen; // need to skip separators
1573
needSeparator = false;
1574                        if (incOldBlen && !hadCountry) { // no country, we'll need two
1575
addSeparator();
1576                            ++oldBlen; // for sure
1577
}
1578                        addSeparator();
1579                        if (incOldBlen) { // only for the first separator
1580
++oldBlen;
1581                        }
1582                    }
1583                    c = Character.toUpperCase(c);
1584                    if (c == HYPHEN || c == COMMA) {
1585                        c = UNDERSCORE;
1586                    }
1587                    append(c);
1588                }
1589            }
1590            --index; // unget
1591

1592            return oldBlen;
1593        }
1594
1595        // no need for skipvariant, to get the keywords we'll just scan directly for
1596
// the keyword separator
1597

1598        /**
1599         * Returns the normalized language id, or the empty string.
1600         */

1601        public String JavaDoc getLanguage() {
1602            reset();
1603            return getString(parseLanguage());
1604        }
1605   
1606        /**
1607         * Returns the normalized script id, or the empty string.
1608         */

1609        public String JavaDoc getScript() {
1610            reset();
1611            skipLanguage();
1612            return getString(parseScript());
1613        }
1614    
1615        /**
1616         * return the normalized country id, or the empty string.
1617         */

1618        public String JavaDoc getCountry() {
1619            reset();
1620            skipLanguage();
1621            skipScript();
1622            return getString(parseCountry());
1623        }
1624
1625        /**
1626         * Returns the normalized variant id, or the empty string.
1627         */

1628        public String JavaDoc getVariant() {
1629            reset();
1630            skipLanguage();
1631            skipScript();
1632            skipCountry();
1633            return getString(parseVariant());
1634        }
1635
1636        /**
1637         * Returns the language, script, country, and variant as separate strings.
1638         */

1639        public String JavaDoc[] getLanguageScriptCountryVariant() {
1640            reset();
1641            return new String JavaDoc[] {
1642                getString(parseLanguage()),
1643                getString(parseScript()),
1644                getString(parseCountry()),
1645                getString(parseVariant())
1646            };
1647        }
1648
1649        public void setBaseName(String JavaDoc baseName) {
1650            this.baseName = baseName;
1651        }
1652
1653        public void parseBaseName() {
1654            if (baseName != null) {
1655                set(0, baseName);
1656            } else {
1657                reset();
1658                parseLanguage();
1659                parseScript();
1660                parseCountry();
1661                parseVariant();
1662            
1663                // catch unwanted trailing underscore after country if there was no variant
1664
if (blen > 1 && buffer[blen-1] == UNDERSCORE) {
1665                    --blen;
1666                }
1667            }
1668        }
1669
1670        /**
1671         * Returns the normalized base form of the locale id. The base
1672         * form does not include keywords.
1673         */

1674        public String JavaDoc getBaseName() {
1675            if (baseName != null) {
1676                return baseName;
1677            }
1678            parseBaseName();
1679            return getString(0);
1680        }
1681
1682        /**
1683         * Returns the normalized full form of the locale id. The full
1684         * form includes keywords if they are present.
1685         */

1686        public String JavaDoc getName() {
1687            parseBaseName();
1688            parseKeywords();
1689            return getString(0);
1690        }
1691
1692        // keyword utilities
1693

1694        /**
1695         * If we have keywords, advance index to the start of the keywords and return true,
1696         * otherwise return false.
1697         */

1698        private boolean setToKeywordStart() {
1699            for (int i = index; i < id.length; ++i) {
1700                if (id[i] == KEYWORD_SEPARATOR) {
1701                    if (canonicalize) {
1702                        for (int j = ++i; j < id.length; ++j) { // increment i past separator for return
1703
if (id[j] == KEYWORD_ASSIGN) {
1704                                index = i;
1705                                return true;
1706                            }
1707                        }
1708                    } else {
1709                        if (++i < id.length) {
1710                            index = i;
1711                            return true;
1712                        }
1713                    }
1714                    break;
1715                }
1716            }
1717            return false;
1718        }
1719        
1720        private static boolean isDoneOrKeywordAssign(char c) {
1721            return c == DONE || c == KEYWORD_ASSIGN;
1722        }
1723
1724        private static boolean isDoneOrItemSeparator(char c) {
1725            return c == DONE || c == ITEM_SEPARATOR;
1726        }
1727
1728        private String JavaDoc getKeyword() {
1729            int start = index;
1730            while (!isDoneOrKeywordAssign(next())) {
1731            }
1732            --index;
1733            return new String JavaDoc(id, start, index-start).trim().toLowerCase();
1734        }
1735
1736        private String JavaDoc getValue() {
1737            int start = index;
1738            while (!isDoneOrItemSeparator(next())) {
1739            }
1740            --index;
1741            return new String JavaDoc(id, start, index-start).trim(); // leave case alone
1742
}
1743
1744        private Comparator JavaDoc getKeyComparator() {
1745            final Comparator JavaDoc comp = new Comparator JavaDoc() {
1746                    public int compare(Object JavaDoc lhs, Object JavaDoc rhs) {
1747                        return ((String JavaDoc)lhs).compareTo((String JavaDoc)rhs);
1748                    }
1749                };
1750            return comp;
1751        }
1752
1753        /**
1754         * Returns a map of the keywords and values, or null if there are none.
1755         */

1756        private Map JavaDoc getKeywordMap() {
1757            if (keywords == null) {
1758                TreeMap JavaDoc m = null;
1759                if (setToKeywordStart()) {
1760                    // trim spaces and convert to lower case, both keywords and values.
1761
do {
1762                        String JavaDoc key = getKeyword();
1763                        if (key.length() == 0) {
1764                            break;
1765                        }
1766                        char c = next();
1767                        if (c != KEYWORD_ASSIGN) {
1768                            // throw new IllegalArgumentException("key '" + key + "' missing a value.");
1769
if (c == DONE) {
1770                                break;
1771                            } else {
1772                                continue;
1773                            }
1774                        }
1775                        String JavaDoc value = getValue();
1776                        if (value.length() == 0) {
1777                            // throw new IllegalArgumentException("key '" + key + "' missing a value.");
1778
continue;
1779                        }
1780                        if (m == null) {
1781                            m = new TreeMap JavaDoc(getKeyComparator());
1782                        } else if (m.containsKey(key)) {
1783                            // throw new IllegalArgumentException("key '" + key + "' already has a value.");
1784
continue;
1785                        }
1786                        m.put(key, value);
1787                    } while (next() == ITEM_SEPARATOR);
1788                }
1789                keywords = m != null ? m : Collections.EMPTY_MAP;
1790            }
1791
1792            return keywords;
1793        }
1794
1795        /**
1796         * Parse the keywords and return start of the string in the buffer.
1797         */

1798        private int parseKeywords() {
1799            int oldBlen = blen;
1800            Map JavaDoc m = getKeywordMap();
1801            if (!m.isEmpty()) {
1802                Iterator JavaDoc iter = m.entrySet().iterator();
1803                boolean first = true;
1804                while (iter.hasNext()) {
1805                    append(first ? KEYWORD_SEPARATOR : ITEM_SEPARATOR);
1806                    first = false;
1807                    Map.Entry JavaDoc e = (Map.Entry JavaDoc)iter.next();
1808                    append((String JavaDoc)e.getKey());
1809                    append(KEYWORD_ASSIGN);
1810                    append((String JavaDoc)e.getValue());
1811                }
1812                if (blen != oldBlen) {
1813                    ++oldBlen;
1814                }
1815            }
1816            return oldBlen;
1817        }
1818
1819        /**
1820         * Returns an iterator over the keywords, or null if we have an empty map.
1821         */

1822        public Iterator JavaDoc getKeywords() {
1823            Map JavaDoc m = getKeywordMap();
1824            return m.isEmpty() ? null : m.keySet().iterator();
1825        }
1826
1827        /**
1828         * Returns the value for the named keyword, or null if the keyword is not
1829         * present.
1830         */

1831        public String JavaDoc getKeywordValue(String JavaDoc keywordName) {
1832            Map JavaDoc m = getKeywordMap();
1833            return m.isEmpty() ? null : (String JavaDoc)m.get(keywordName.trim().toLowerCase());
1834        }
1835
1836        /**
1837         * Set the keyword value only if it is not already set to something else.
1838         */

1839        public void defaultKeywordValue(String JavaDoc keywordName, String JavaDoc value) {
1840            setKeywordValue(keywordName, value, false);
1841        }
1842            
1843        /**
1844         * Set the value for the named keyword, or unset it if value is null. If
1845         * keywordName itself is null, unset all keywords. If keywordName is not null,
1846         * value must not be null.
1847         */

1848        public void setKeywordValue(String JavaDoc keywordName, String JavaDoc value) {
1849            setKeywordValue(keywordName, value, true);
1850        }
1851
1852        /**
1853         * Set the value for the named keyword, or unset it if value is null. If
1854         * keywordName itself is null, unset all keywords. If keywordName is not null,
1855         * value must not be null. If reset is true, ignore any previous value for
1856         * the keyword, otherwise do not change the keyword (including removal of
1857         * one or all keywords).
1858         */

1859        private void setKeywordValue(String JavaDoc keywordName, String JavaDoc value, boolean reset) {
1860            if (keywordName == null) {
1861                if (reset) {
1862                    // force new map, ignore value
1863
keywords = Collections.EMPTY_MAP;
1864                }
1865            } else {
1866                keywordName = keywordName.trim().toLowerCase();
1867                if (keywordName.length() == 0) {
1868                    throw new IllegalArgumentException JavaDoc("keyword must not be empty");
1869                }
1870                if (value != null) {
1871                    value = value.trim();
1872                    if (value.length() == 0) {
1873                        throw new IllegalArgumentException JavaDoc("value must not be empty");
1874                    }
1875                }
1876                Map JavaDoc m = getKeywordMap();
1877                if (m.isEmpty()) { // it is EMPTY_MAP
1878
if (value != null) {
1879                        // force new map
1880
keywords = new TreeMap JavaDoc(getKeyComparator());
1881                        keywords.put(keywordName, value.trim());
1882                    }
1883                } else {
1884                    if (reset || !m.containsKey(keywordName)) {
1885                        if (value != null) {
1886                            m.put(keywordName, value);
1887                        } else {
1888                            m.remove(keywordName);
1889                            if (m.isEmpty()) {
1890                                // force new map
1891
keywords = Collections.EMPTY_MAP;
1892                            }
1893                        }
1894                    }
1895                }
1896            }
1897        }
1898    }
1899
1900    /**
1901     * linear search of the string array. the arrays are unfortunately ordered by the
1902     * two-letter target code, not the three-letter search code, which seems backwards.
1903     */

1904    private static int findIndex(String JavaDoc[] array, String JavaDoc target){
1905        for (int i = 0; i < array.length; i++) {
1906            if (target.equals(array[i])) {
1907                return i;
1908            }
1909        }
1910        return -1;
1911    }
1912
1913    /**
1914     * Returns the canonical name for the specified locale ID. This is used to convert POSIX
1915     * and other grandfathered IDs to standard ICU form.
1916     * @param localeID the locale id
1917     * @return the canonicalized id
1918     * @stable ICU 3.0
1919     */

1920    public static String JavaDoc canonicalize(String JavaDoc localeID){
1921        IDParser parser = new IDParser(localeID, true);
1922        String JavaDoc baseName = parser.getBaseName();
1923        boolean foundVariant = false;
1924      
1925        // formerly, we always set to en_US_POSIX if the basename was empty, but
1926
// now we require that the entire id be empty, so that "@foo=bar"
1927
// will pass through unchanged.
1928
// {dlf} I'd rather keep "" unchanged.
1929
if (localeID.equals("")) {
1930            return "";
1931// return "en_US_POSIX";
1932
}
1933
1934        // we have an ID in the form xx_Yyyy_ZZ_KKKKK
1935

1936        initVariantsTable();
1937
1938        /* See if this is an already known locale */
1939        for (int i = 0; i < _variantsToKeywords.length; i++) {
1940            if (_variantsToKeywords[i][0].equals(baseName)) {
1941                foundVariant = true;
1942
1943                String JavaDoc[] vals = _variantsToKeywords[i];
1944                parser.setBaseName(vals[1]);
1945                if (vals[2] != null) {
1946                    parser.defaultKeywordValue(vals[2], vals[3]);
1947                }
1948                break;
1949            }
1950        }
1951
1952        /* convert the Euro variant to appropriate ID */
1953        if (!foundVariant) {
1954            int idx = baseName.indexOf("_EURO");
1955            if (idx > -1) {
1956                parser.setBaseName(baseName.substring(0, idx));
1957                parser.defaultKeywordValue("currency", "EUR");
1958            }
1959        }
1960
1961        /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
1962        if (!foundVariant) {
1963            if (parser.getLanguage().equals("nb") && parser.getVariant().equals("NY")) {
1964                parser.setBaseName(lscvToID("nn", parser.getScript(), parser.getCountry(), null));
1965            }
1966        }
1967
1968        return parser.getName();
1969    }
1970    
1971    /**
1972     * Given a keyword and a value, return a new locale with an updated
1973     * keyword and value. If keyword is null, this removes all keywords from the locale id.
1974     * Otherwise, if the value is null, this removes the value for this keyword from the
1975     * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
1976     * The keyword and value must not be empty.
1977     * @param keyword the keyword to add/remove, or null to remove all keywords.
1978     * @param value the value to add/set, or null to remove this particular keyword.
1979     * @return the updated locale
1980     * @stable ICU 3.2
1981     */

1982    public ULocale setKeywordValue(String JavaDoc keyword, String JavaDoc value) {
1983        return new ULocale(setKeywordValue(localeID, keyword, value), (Locale JavaDoc)null);
1984    }
1985
1986    /**
1987     * Given a locale id, a keyword, and a value, return a new locale id with an updated
1988     * keyword and value. If keyword is null, this removes all keywords from the locale id.
1989     * Otherwise, if the value is null, this removes the value for this keyword from the
1990     * locale id. Otherwise, this adds/replaces the value for this keyword in the locale id.
1991     * The keyword and value must not be empty.
1992     * @param localeID the locale id to modify
1993     * @param keyword the keyword to add/remove, or null to remove all keywords.
1994     * @param value the value to add/set, or null to remove this particular keyword.
1995     * @return the updated locale id
1996     * @stable ICU 3.2
1997     */

1998    public static String JavaDoc setKeywordValue(String JavaDoc localeID, String JavaDoc keyword, String JavaDoc value) {
1999        IDParser parser = new IDParser(localeID);
2000        parser.setKeywordValue(keyword, value);
2001        return parser.getName();
2002    }
2003
2004    /*
2005     * Given a locale id, a keyword, and a value, return a new locale id with an updated
2006     * keyword and value, if the keyword does not already have a value. The keyword and
2007     * value must not be null or empty.
2008     * @param localeID the locale id to modify
2009     * @param keyword the keyword to add, if not already present
2010     * @param value the value to add, if not already present
2011     * @return the updated locale id
2012     * @internal
2013     */

2014/* private static String defaultKeywordValue(String localeID, String keyword, String value) {
2015        IDParser parser = new IDParser(localeID);
2016        parser.defaultKeywordValue(keyword, value);
2017        return parser.getName();
2018    }*/

2019
2020    /**
2021     * Returns a three-letter abbreviation for this locale's language. If the locale
2022     * doesn't specify a language, returns the empty string. Otherwise, returns
2023     * a lowercase ISO 639-2/T language code.
2024     * The ISO 639-2 language codes can be found on-line at
2025     * <a HREF="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
2026     * @exception MissingResourceException Throws MissingResourceException if the
2027     * three-letter language abbreviation is not available for this locale.
2028     * @stable ICU 3.0
2029     */

2030    public String JavaDoc getISO3Language(){
2031        return getISO3Language(localeID);
2032    }
2033
2034    /**
2035     * Returns a three-letter abbreviation for this locale's language. If the locale
2036     * doesn't specify a language, returns the empty string. Otherwise, returns
2037     * a lowercase ISO 639-2/T language code.
2038     * The ISO 639-2 language codes can be found on-line at
2039     * <a HREF="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
2040     * @exception MissingResourceException Throws MissingResourceException if the
2041     * three-letter language abbreviation is not available for this locale.
2042     * @stable ICU 3.0
2043     */

2044    public static String JavaDoc getISO3Language(String JavaDoc localeID){
2045        initLanguageTables();
2046
2047        String JavaDoc language = getLanguage(localeID);
2048        int offset = findIndex(_languages, language);
2049        if(offset>=0){
2050            return _languages3[offset];
2051        } else {
2052            offset = findIndex(_obsoleteLanguages, language);
2053            if (offset >= 0) {
2054                return _obsoleteLanguages3[offset];
2055            }
2056        }
2057        return EMPTY_STRING;
2058    }
2059    
2060    /**
2061     * Returns a three-letter abbreviation for this locale's country/region. If the locale
2062     * doesn't specify a country, returns the empty string. Otherwise, returns
2063     * an uppercase ISO 3166 3-letter country code.
2064     * @exception MissingResourceException Throws MissingResourceException if the
2065     * three-letter country abbreviation is not available for this locale.
2066     * @stable ICU 3.0
2067     */

2068    public String JavaDoc getISO3Country(){
2069        return getISO3Country(localeID);
2070    }
2071    /**
2072     * Returns a three-letter abbreviation for this locale's country/region. If the locale
2073     * doesn't specify a country, returns the empty string. Otherwise, returns
2074     * an uppercase ISO 3166 3-letter country code.
2075     * @exception MissingResourceException Throws MissingResourceException if the
2076     * three-letter country abbreviation is not available for this locale.
2077     * @stable ICU 3.0
2078     */

2079    public static String JavaDoc getISO3Country(String JavaDoc localeID){
2080        initCountryTables();
2081
2082        String JavaDoc country = getCountry(localeID);
2083        int offset = findIndex(_countries, country);
2084        if(offset>=0){
2085            return _countries3[offset];
2086        }else{
2087            offset = findIndex(_obsoleteCountries, country);
2088            if(offset>=0){
2089                return _obsoleteCountries3[offset];
2090            }
2091        }
2092        return EMPTY_STRING;
2093    }
2094    
2095    // display names
2096

2097    /**
2098     * Utility to fetch locale display data from resource bundle tables.
2099     */

2100    private static String JavaDoc getTableString(String JavaDoc tableName, String JavaDoc subtableName, String JavaDoc item, String JavaDoc displayLocaleID) {
2101        if (item.length() > 0) {
2102            try {
2103                ICUResourceBundle bundle = (ICUResourceBundle)UResourceBundle.
2104                  getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, displayLocaleID);
2105                return getTableString(tableName, subtableName, item, bundle);
2106            } catch (Exception JavaDoc e) {
2107// System.out.println("gtsu: " + e.getMessage());
2108
}
2109        }
2110        return item;
2111    }
2112        
2113    /**
2114     * Utility to fetch locale display data from resource bundle tables.
2115     */

2116    private static String JavaDoc getTableString(String JavaDoc tableName, String JavaDoc subtableName, String JavaDoc item, ICUResourceBundle bundle) {
2117// System.out.println("gts table: " + tableName +
2118
// " subtable: " + subtableName +
2119
// " item: " + item +
2120
// " bundle: " + bundle.getULocale());
2121
try {
2122            for (;;) {
2123                // special case currency
2124
if ("currency".equals(subtableName)) {
2125                    ICUResourceBundle table = bundle.getWithFallback("Currencies");
2126                    table = table.getWithFallback(item);
2127                    return table.getString(1);
2128                } else {
2129                    ICUResourceBundle table = bundle.getWithFallback(tableName);
2130                    try {
2131                        if (subtableName != null) {
2132                            table = table.getWithFallback(subtableName);
2133                        }
2134                        return table.getStringWithFallback(item);
2135                    }
2136                    catch (MissingResourceException JavaDoc e) {
2137                        
2138                        if(subtableName==null){
2139                            try{
2140                                // may be a deprecated code
2141
String JavaDoc currentName = null;
2142                                if(tableName.equals("Countries")){
2143                                    currentName = getCurrentCountryID(item);
2144                                }else if(tableName.equals("Languages")){
2145                                    currentName = getCurrentLanguageID(item);
2146                                }
2147                                return table.getStringWithFallback(currentName);
2148                            }catch (MissingResourceException JavaDoc ex){/* fall through*/}
2149                        }
2150                        
2151                        // still can't figure out ?.. try the fallback mechanism
2152
String JavaDoc fallbackLocale = table.getWithFallback("Fallback").getString();
2153                        if (fallbackLocale.length() == 0) {
2154                            fallbackLocale = "root";
2155                        }
2156// System.out.println("bundle: " + bundle.getULocale() + " fallback: " + fallbackLocale);
2157
if(fallbackLocale.equals(table.getULocale().localeID)){
2158                            return item;
2159                        }
2160                        bundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,
2161                                                                                      fallbackLocale);
2162// System.out.println("fallback from " + table.getULocale() + " to " + fallbackLocale +
2163
// ", got bundle " + bundle.getULocale());
2164
}
2165                }
2166            }
2167        }
2168        catch (Exception JavaDoc e) {
2169// System.out.println("gtsi: " + e.getMessage());
2170
}
2171        return item;
2172    }
2173
2174    /**
2175     * Returns this locale's language localized for display in the default locale.
2176     * @return the localized language name.
2177     * @stable ICU 3.0
2178     */

2179    public String JavaDoc getDisplayLanguage() {
2180        return getDisplayLanguageInternal(localeID, getDefault().localeID);
2181    }
2182
2183    /**
2184     * Returns this locale's language localized for display in the provided locale.
2185     * @param displayLocale the locale in which to display the name.
2186     * @return the localized language name.
2187     * @stable ICU 3.0
2188     */

2189    public String JavaDoc getDisplayLanguage(ULocale displayLocale) {
2190        return getDisplayLanguageInternal(localeID, displayLocale.localeID);
2191    }
2192    
2193    /**
2194     * Returns a locale's language localized for display in the provided locale.
2195     * This is a cover for the ICU4C API.
2196     * @param localeID the id of the locale whose language will be displayed
2197     * @param displayLocaleID the id of the locale in which to display the name.
2198     * @return the localized language name.
2199     * @stable ICU 3.0
2200     */

2201    public static String JavaDoc getDisplayLanguage(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2202        return getDisplayLanguageInternal(localeID, getName(displayLocaleID));
2203    }
2204
2205    /**
2206     * Returns a locale's language localized for display in the provided locale.
2207     * This is a cover for the ICU4C API.
2208     * @param localeID the id of the locale whose language will be displayed.
2209     * @param displayLocale the locale in which to display the name.
2210     * @return the localized language name.
2211     * @stable ICU 3.0
2212     */

2213    public static String JavaDoc getDisplayLanguage(String JavaDoc localeID, ULocale displayLocale) {
2214        return getDisplayLanguageInternal(localeID, displayLocale.localeID);
2215    }
2216
2217    static String JavaDoc getCurrentCountryID(String JavaDoc oldID){
2218        initCountryTables();
2219        int offset = findIndex(_deprecatedCountries, oldID);
2220        if (offset >= 0) {
2221            return _replacementCountries[offset];
2222        }
2223        return oldID;
2224    }
2225    static String JavaDoc getCurrentLanguageID(String JavaDoc oldID){
2226        initLanguageTables();
2227        int offset = findIndex(_obsoleteLanguages, oldID);
2228        if (offset >= 0) {
2229            return _replacementLanguages[offset];
2230        }
2231        return oldID;
2232    }
2233    
2234
2235    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2236
private static String JavaDoc getDisplayLanguageInternal(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2237        return getTableString("Languages", null, new IDParser(localeID).getLanguage(), displayLocaleID);
2238    }
2239 
2240    /**
2241     * Returns this locale's script localized for display in the default locale.
2242     * @return the localized script name.
2243     * @stable ICU 3.0
2244     */

2245    public String JavaDoc getDisplayScript() {
2246        return getDisplayScriptInternal(localeID, getDefault().localeID);
2247    }
2248
2249    /**
2250     * Returns this locale's script localized for display in the provided locale.
2251     * @param displayLocale the locale in which to display the name.
2252     * @return the localized script name.
2253     * @stable ICU 3.0
2254     */

2255    public String JavaDoc getDisplayScript(ULocale displayLocale) {
2256        return getDisplayScriptInternal(localeID, displayLocale.localeID);
2257    }
2258    
2259    /**
2260     * Returns a locale's script localized for display in the provided locale.
2261     * This is a cover for the ICU4C API.
2262     * @param localeID the id of the locale whose script will be displayed
2263     * @param displayLocaleID the id of the locale in which to display the name.
2264     * @return the localized script name.
2265     * @stable ICU 3.0
2266     */

2267    public static String JavaDoc getDisplayScript(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2268        return getDisplayScriptInternal(localeID, getName(displayLocaleID));
2269    }
2270
2271    /**
2272     * Returns a locale's script localized for display in the provided locale.
2273     * @param localeID the id of the locale whose script will be displayed.
2274     * @param displayLocale the locale in which to display the name.
2275     * @return the localized script name.
2276     * @stable ICU 3.0
2277     */

2278    public static String JavaDoc getDisplayScript(String JavaDoc localeID, ULocale displayLocale) {
2279        return getDisplayScriptInternal(localeID, displayLocale.localeID);
2280    }
2281
2282    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2283
private static String JavaDoc getDisplayScriptInternal(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2284        return getTableString("Scripts", null, new IDParser(localeID).getScript(), displayLocaleID);
2285    }
2286
2287    /**
2288     * Returns this locale's country localized for display in the default locale.
2289     * @return the localized country name.
2290     * @stable ICU 3.0
2291     */

2292    public String JavaDoc getDisplayCountry() {
2293        return getDisplayCountryInternal(localeID, getDefault().localeID);
2294    }
2295    
2296    /**
2297     * Returns this locale's country localized for display in the provided locale.
2298     * @param displayLocale the locale in which to display the name.
2299     * @return the localized country name.
2300     * @stable ICU 3.0
2301     */

2302    public String JavaDoc getDisplayCountry(ULocale displayLocale){
2303        return getDisplayCountryInternal(localeID, displayLocale.localeID);
2304    }
2305    
2306    /**
2307     * Returns a locale's country localized for display in the provided locale.
2308     * This is a cover for the ICU4C API.
2309     * @param localeID the id of the locale whose country will be displayed
2310     * @param displayLocaleID the id of the locale in which to display the name.
2311     * @return the localized country name.
2312     * @stable ICU 3.0
2313     */

2314    public static String JavaDoc getDisplayCountry(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2315        return getDisplayCountryInternal(localeID, getName(displayLocaleID));
2316    }
2317
2318    /**
2319     * Returns a locale's country localized for display in the provided locale.
2320     * This is a cover for the ICU4C API.
2321     * @param localeID the id of the locale whose country will be displayed.
2322     * @param displayLocale the locale in which to display the name.
2323     * @return the localized country name.
2324     * @stable ICU 3.0
2325     */

2326    public static String JavaDoc getDisplayCountry(String JavaDoc localeID, ULocale displayLocale) {
2327        return getDisplayCountryInternal(localeID, displayLocale.localeID);
2328    }
2329
2330    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2331
private static String JavaDoc getDisplayCountryInternal(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2332        return getTableString("Countries", null, new IDParser(localeID).getCountry(), displayLocaleID);
2333    }
2334    
2335    /**
2336     * Returns this locale's variant localized for display in the default locale.
2337     * @return the localized variant name.
2338     * @stable ICU 3.0
2339     */

2340    public String JavaDoc getDisplayVariant() {
2341        return getDisplayVariantInternal(localeID, getDefault().localeID);
2342    }
2343
2344    /**
2345     * Returns this locale's variant localized for display in the provided locale.
2346     * @param displayLocale the locale in which to display the name.
2347     * @return the localized variant name.
2348     * @stable ICU 3.0
2349     */

2350    public String JavaDoc getDisplayVariant(ULocale displayLocale) {
2351        return getDisplayVariantInternal(localeID, displayLocale.localeID);
2352    }
2353    
2354    /**
2355     * Returns a locale's variant localized for display in the provided locale.
2356     * This is a cover for the ICU4C API.
2357     * @param localeID the id of the locale whose variant will be displayed
2358     * @param displayLocaleID the id of the locale in which to display the name.
2359     * @return the localized variant name.
2360     * @stable ICU 3.0
2361     */

2362    public static String JavaDoc getDisplayVariant(String JavaDoc localeID, String JavaDoc displayLocaleID){
2363        return getDisplayVariantInternal(localeID, getName(displayLocaleID));
2364    }
2365    
2366    /**
2367     * Returns a locale's variant localized for display in the provided locale.
2368     * This is a cover for the ICU4C API.
2369     * @param localeID the id of the locale whose variant will be displayed.
2370     * @param displayLocale the locale in which to display the name.
2371     * @return the localized variant name.
2372     * @stable ICU 3.0
2373     */

2374    public static String JavaDoc getDisplayVariant(String JavaDoc localeID, ULocale displayLocale) {
2375        return getDisplayVariantInternal(localeID, displayLocale.localeID);
2376    }
2377
2378    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2379
private static String JavaDoc getDisplayVariantInternal(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2380        return getTableString("Variants", null, new IDParser(localeID).getVariant(), displayLocaleID);
2381    }
2382
2383    /**
2384     * Returns a keyword localized for display in the default locale.
2385     * @param keyword the keyword to be displayed.
2386     * @return the localized keyword name.
2387     * @see #getKeywords()
2388     * @stable ICU 3.0
2389     */

2390    public static String JavaDoc getDisplayKeyword(String JavaDoc keyword) {
2391        return getDisplayKeywordInternal(keyword, getDefault().localeID);
2392    }
2393    
2394    /**
2395     * Returns a keyword localized for display in the specified locale.
2396     * @param keyword the keyword to be displayed.
2397     * @param displayLocaleID the id of the locale in which to display the keyword.
2398     * @return the localized keyword name.
2399     * @see #getKeywords(String)
2400     * @stable ICU 3.0
2401     */

2402    public static String JavaDoc getDisplayKeyword(String JavaDoc keyword, String JavaDoc displayLocaleID) {
2403        return getDisplayKeywordInternal(keyword, getName(displayLocaleID));
2404    }
2405
2406    /**
2407     * Returns a keyword localized for display in the specified locale.
2408     * @param keyword the keyword to be displayed.
2409     * @param displayLocale the locale in which to display the keyword.
2410     * @return the localized keyword name.
2411     * @see #getKeywords(String)
2412     * @stable ICU 3.0
2413     */

2414    public static String JavaDoc getDisplayKeyword(String JavaDoc keyword, ULocale displayLocale) {
2415        return getDisplayKeywordInternal(keyword, displayLocale.localeID);
2416    }
2417
2418    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2419
private static String JavaDoc getDisplayKeywordInternal(String JavaDoc keyword, String JavaDoc displayLocaleID) {
2420        return getTableString("Keys", null, keyword.trim().toLowerCase(), displayLocaleID);
2421    }
2422
2423    /**
2424     * Returns a keyword value localized for display in the default locale.
2425     * @param keyword the keyword whose value is to be displayed.
2426     * @return the localized value name.
2427     * @stable ICU 3.0
2428     */

2429    public String JavaDoc getDisplayKeywordValue(String JavaDoc keyword) {
2430        return getDisplayKeywordValueInternal(localeID, keyword, getDefault().localeID);
2431    }
2432    
2433    /**
2434     * Returns a keyword value localized for display in the specified locale.
2435     * @param keyword the keyword whose value is to be displayed.
2436     * @param displayLocale the locale in which to display the value.
2437     * @return the localized value name.
2438     * @stable ICU 3.0
2439     */

2440    public String JavaDoc getDisplayKeywordValue(String JavaDoc keyword, ULocale displayLocale) {
2441        return getDisplayKeywordValueInternal(localeID, keyword, displayLocale.localeID);
2442    }
2443
2444    /**
2445     * Returns a keyword value localized for display in the specified locale.
2446     * This is a cover for the ICU4C API.
2447     * @param localeID the id of the locale whose keyword value is to be displayed.
2448     * @param keyword the keyword whose value is to be displayed.
2449     * @param displayLocaleID the id of the locale in which to display the value.
2450     * @return the localized value name.
2451     * @stable ICU 3.0
2452     */

2453    public static String JavaDoc getDisplayKeywordValue(String JavaDoc localeID, String JavaDoc keyword, String JavaDoc displayLocaleID) {
2454        return getDisplayKeywordValueInternal(localeID, keyword, getName(displayLocaleID));
2455    }
2456
2457    /**
2458     * Returns a keyword value localized for display in the specified locale.
2459     * This is a cover for the ICU4C API.
2460     * @param localeID the id of the locale whose keyword value is to be displayed.
2461     * @param keyword the keyword whose value is to be displayed.
2462     * @param displayLocale the id of the locale in which to display the value.
2463     * @return the localized value name.
2464     * @stable ICU 3.0
2465     */

2466    public static String JavaDoc getDisplayKeywordValue(String JavaDoc localeID, String JavaDoc keyword, ULocale displayLocale) {
2467        return getDisplayKeywordValueInternal(localeID, keyword, displayLocale.localeID);
2468    }
2469
2470    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2471
private static String JavaDoc getDisplayKeywordValueInternal(String JavaDoc localeID, String JavaDoc keyword, String JavaDoc displayLocaleID) {
2472        keyword = keyword.trim().toLowerCase();
2473        String JavaDoc value = new IDParser(localeID).getKeywordValue(keyword);
2474        return getTableString("Types", keyword, value, displayLocaleID);
2475    }
2476    
2477    /**
2478     * Returns this locale name localized for display in the default locale.
2479     * @return the localized locale name.
2480     * @stable ICU 3.0
2481     */

2482    public String JavaDoc getDisplayName() {
2483        return getDisplayNameInternal(localeID, getDefault().localeID);
2484    }
2485    
2486    /**
2487     * Returns this locale name localized for display in the provided locale.
2488     * @param displayLocale the locale in which to display the locale name.
2489     * @return the localized locale name.
2490     * @stable ICU 3.0
2491     */

2492    public String JavaDoc getDisplayName(ULocale displayLocale) {
2493        return getDisplayNameInternal(localeID, displayLocale.localeID);
2494    }
2495    
2496    /**
2497     * Returns the locale ID localized for display in the provided locale.
2498     * This is a cover for the ICU4C API.
2499     * @param localeID the locale whose name is to be displayed.
2500     * @param displayLocaleID the id of the locale in which to display the locale name.
2501     * @return the localized locale name.
2502     * @stable ICU 3.0
2503     */

2504    public static String JavaDoc getDisplayName(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2505        return getDisplayNameInternal(localeID, getName(displayLocaleID));
2506    }
2507
2508    /**
2509     * Returns the locale ID localized for display in the provided locale.
2510     * This is a cover for the ICU4C API.
2511     * @param localeID the locale whose name is to be displayed.
2512     * @param displayLocale the locale in which to display the locale name.
2513     * @return the localized locale name.
2514     * @stable ICU 3.0
2515     */

2516    public static String JavaDoc getDisplayName(String JavaDoc localeID, ULocale displayLocale) {
2517        return getDisplayNameInternal(localeID, displayLocale.localeID);
2518    }
2519
2520    // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2521
private static String JavaDoc getDisplayNameInternal(String JavaDoc localeID, String JavaDoc displayLocaleID) {
2522        // lang
2523
// lang (script, country, variant, keyword=value, ...)
2524
// script, country, variant, keyword=value, ...
2525

2526        final String JavaDoc[] tableNames = { "Languages", "Scripts", "Countries", "Variants" };
2527
2528        ICUResourceBundle bundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, displayLocaleID);
2529
2530        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2531
2532        IDParser parser = new IDParser(localeID);
2533        String JavaDoc[] names = parser.getLanguageScriptCountryVariant();
2534
2535        boolean haveLanguage = names[0].length() > 0;
2536        boolean openParen = false;
2537        for (int i = 0; i < names.length; ++i) {
2538            String JavaDoc name = names[i];
2539            if (name.length() > 0) {
2540                name = getTableString(tableNames[i], null, name, bundle);
2541                if (buf.length() > 0) { // need a separator
2542
if (haveLanguage & !openParen) {
2543                        buf.append(" (");
2544                        openParen = true;
2545                    } else {
2546                        buf.append(", ");
2547                    }
2548                }
2549                buf.append(name);
2550            }
2551        }
2552
2553        Map JavaDoc m = parser.getKeywordMap();
2554        if (!m.isEmpty()) {
2555            Iterator JavaDoc keys = m.entrySet().iterator();
2556            while (keys.hasNext()) {
2557                if (buf.length() > 0) {
2558                    if (haveLanguage & !openParen) {
2559                        buf.append(" (");
2560                        openParen = true;
2561                    } else {
2562                        buf.append(", ");
2563                    }
2564                }
2565                Map.Entry JavaDoc e = (Map.Entry JavaDoc)keys.next();
2566                String JavaDoc key = (String JavaDoc)e.getKey();
2567                String JavaDoc val = (String JavaDoc)e.getValue();
2568                buf.append(getTableString("Keys", null, key, bundle));
2569                buf.append("=");
2570                buf.append(getTableString("Types", key, val, bundle));
2571            }
2572        }
2573
2574        if (openParen) {
2575            buf.append(")");
2576        }
2577            
2578        return buf.toString();
2579    }
2580
2581    /**
2582     * Selector for <tt>getLocale()</tt> indicating the locale of the
2583     * resource containing the data. This is always at or above the
2584     * valid locale. If the valid locale does not contain the
2585     * specific data being requested, then the actual locale will be
2586     * above the valid locale. If the object was not constructed from
2587     * locale data, then the valid locale is <i>null</i>.
2588     *
2589     * @draft ICU 2.8 (retain)
2590     * @provisional This API might change or be removed in a future release.
2591     */

2592    public static Type ACTUAL_LOCALE = new Type(0);
2593 
2594    /**
2595     * Selector for <tt>getLocale()</tt> indicating the most specific
2596     * locale for which any data exists. This is always at or above
2597     * the requested locale, and at or below the actual locale. If
2598     * the requested locale does not correspond to any resource data,
2599     * then the valid locale will be above the requested locale. If
2600     * the object was not constructed from locale data, then the
2601     * actual locale is <i>null</i>.
2602     *
2603     * <p>Note: The valid locale will be returned correctly in ICU
2604     * 3.0 or later. In ICU 2.8, it is not returned correctly.
2605     * @draft ICU 2.8 (retain)
2606     * @provisional This API might change or be removed in a future release.
2607     */

2608    public static Type VALID_LOCALE = new Type(1);
2609    
2610    /**
2611     * Opaque selector enum for <tt>getLocale()</tt>.
2612     * @see com.ibm.icu.util.ULocale
2613     * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
2614     * @see com.ibm.icu.util.ULocale#VALID_LOCALE
2615     * @draft ICU 2.8 (retainAll)
2616     * @provisional This API might change or be removed in a future release.
2617     */

2618    public static final class Type {
2619        private int localeType;
2620        private Type(int type) { localeType = type; }
2621    }
2622    
2623    
2624  /**
2625    * Based on a HTTP formatted list of acceptable locales, determine an available locale for the user.
2626    * NullPointerException is thrown if acceptLanguageList or availableLocales is
2627    * null. If fallback is non-null, it will contain true if a fallback locale (one
2628    * not in the acceptLanguageList) was returned. The value on entry is ignored.
2629    * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2630    * if a ROOT locale was used as a fallback (because nothing else in
2631    * availableLocales matched). No ULocale array element should be null; behavior
2632    * is undefined if this is the case.
2633    * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2634    * @param availableLocales list of available locales. One of these will be returned.
2635    * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2636    * @return one of the locales from the availableLocales list, or null if none match
2637    * @draft ICU 3.4
2638    * @provisional This API might change or be removed in a future release.
2639    */

2640
2641    public static ULocale acceptLanguage(String JavaDoc acceptLanguageList, ULocale[] availableLocales,
2642                                         boolean[] fallback) {
2643        /**
2644         * @internal ICU 3.4
2645         */

2646        class ULocaleAcceptLanguageQ implements Comparable JavaDoc {
2647            private double q;
2648            private double serial;
2649            public ULocaleAcceptLanguageQ(double theq, int theserial) {
2650                q = theq;
2651                serial = theserial;
2652            }
2653            public int compareTo(Object JavaDoc o) {
2654                ULocaleAcceptLanguageQ other = (ULocaleAcceptLanguageQ) o;
2655                if(q > other.q) { // reverse - to sort in descending order
2656
return -1;
2657                } else if(q < other.q) {
2658                    return 1;
2659                }
2660                if(serial < other.serial) {
2661                    return -1;
2662                } else if(serial > other.serial) {
2663                    return 1;
2664                } else {
2665                    return 0; // same object
2666
}
2667            }
2668        }
2669
2670        // 1st: parse out the acceptLanguageList into an array
2671

2672        TreeMap JavaDoc map = new TreeMap JavaDoc();
2673        
2674        final int l = acceptLanguageList.length();
2675        int n;
2676        for(n=0;n<l;n++) {
2677            int itemEnd = acceptLanguageList.indexOf(',',n);
2678            if(itemEnd == -1) {
2679                itemEnd = l;
2680            }
2681            int paramEnd = acceptLanguageList.indexOf(';',n);
2682            double q = 1.0;
2683 
2684            if((paramEnd != -1) && (paramEnd < itemEnd)) {
2685                /* semicolon (;) is closer than end (,) */
2686                int t = paramEnd + 1;
2687                while(UCharacter.isWhitespace(acceptLanguageList.charAt(t))) {
2688                    t++;
2689                }
2690                if(acceptLanguageList.charAt(t)=='q') {
2691                    t++;
2692                }
2693                while(UCharacter.isWhitespace(acceptLanguageList.charAt(t))) {
2694                    t++;
2695                }
2696                if(acceptLanguageList.charAt(t)=='=') {
2697                    t++;
2698                }
2699                while(UCharacter.isWhitespace(acceptLanguageList.charAt(t))) {
2700                    t++;
2701                }
2702                try {
2703                    String JavaDoc val = acceptLanguageList.substring(t,itemEnd).trim();
2704                    q = Double.parseDouble(val);
2705                } catch (NumberFormatException JavaDoc nfe) {
2706                    q = 1.0;
2707                }
2708            } else {
2709                q = 1.0; //default
2710
paramEnd = itemEnd;
2711            }
2712
2713            String JavaDoc loc = acceptLanguageList.substring(n,paramEnd).trim();
2714            int serial = map.size();
2715            ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(q,serial);
2716            map.put(entry, new ULocale(canonicalize(loc))); // sort in reverse order.. 1.0, 0.9, 0.8 .. etc
2717
n = itemEnd; // get next item. (n++ will skip over delimiter)
2718
}
2719        
2720        // 2. pull out the map
2721
ULocale acceptList[] = (ULocale[])map.values().toArray(new ULocale[map.size()]);
2722        
2723        // 3. call the real function
2724
return acceptLanguage(acceptList, availableLocales, fallback);
2725    }
2726    
2727   /**
2728    * Based on a list of acceptable locales, determine an available locale for the user.
2729    * NullPointerException is thrown if acceptLanguageList or availableLocales is
2730    * null. If fallback is non-null, it will contain true if a fallback locale (one
2731    * not in the acceptLanguageList) was returned. The value on entry is ignored.
2732    * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2733    * if a ROOT locale was used as a fallback (because nothing else in
2734    * availableLocales matched). No ULocale array element should be null; behavior
2735    * is undefined if this is the case.
2736    * @param acceptLanguageList list of acceptable locales
2737    * @param availableLocales list of available locales. One of these will be returned.
2738    * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2739    * @return one of the locales from the availableLocales list, or null if none match
2740    * @draft ICU 3.4
2741    * @provisional This API might change or be removed in a future release.
2742    */

2743
2744    public static ULocale acceptLanguage(ULocale[] acceptLanguageList, ULocale[]
2745    availableLocales, boolean[] fallback) {
2746        // fallbacklist
2747
int i,j;
2748        if(fallback != null) {
2749            fallback[0]=true;
2750        }
2751        for(i=0;i<acceptLanguageList.length;i++) {
2752            ULocale aLocale = acceptLanguageList[i];
2753            boolean[] setFallback = fallback;
2754            do {
2755                for(j=0;j<availableLocales.length;j++) {
2756                    if(availableLocales[j].equals(aLocale)) {
2757                        if(setFallback != null) {
2758                            setFallback[0]=false; // first time with this locale - not a fallback.
2759
}
2760                        return availableLocales[j];
2761                    }
2762                }
2763                Locale JavaDoc loc = aLocale.toLocale();
2764                Locale JavaDoc parent = LocaleUtility.fallback(loc);
2765                if(parent != null) {
2766                    aLocale = new ULocale(parent);
2767                } else {
2768                    aLocale = null;
2769                }
2770                setFallback = null; // Do not set fallback in later iterations
2771
} while (aLocale != null);
2772        }
2773        return null;
2774    }
2775
2776   /**
2777    * Based on a HTTP formatted list of acceptable locales, determine an available locale for the user.
2778    * NullPointerException is thrown if acceptLanguageList or availableLocales is
2779    * null. If fallback is non-null, it will contain true if a fallback locale (one
2780    * not in the acceptLanguageList) was returned. The value on entry is ignored.
2781    * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2782    * if a ROOT locale was used as a fallback (because nothing else in
2783    * availableLocales matched). No ULocale array element should be null; behavior
2784    * is undefined if this is the case.
2785    * This function will choose a locale from the ULocale.getAvailableLocales() list as available.
2786    * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2787    * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2788    * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
2789    * @draft ICU 3.4
2790    * @provisional This API might change or be removed in a future release.
2791    */

2792
2793    public static ULocale acceptLanguage(String JavaDoc acceptLanguageList, boolean[] fallback) {
2794        return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
2795                                fallback);
2796    }
2797
2798   /**
2799    * Based on an ordered array of acceptable locales, determine an available locale for the user.
2800    * NullPointerException is thrown if acceptLanguageList or availableLocales is
2801    * null. If fallback is non-null, it will contain true if a fallback locale (one
2802    * not in the acceptLanguageList) was returned. The value on entry is ignored.
2803    * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2804    * if a ROOT locale was used as a fallback (because nothing else in
2805    * availableLocales matched). No ULocale array element should be null; behavior
2806    * is undefined if this is the case.
2807    * This function will choose a locale from the ULocale.getAvailableLocales() list as available.
2808    * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
2809    * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2810    * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
2811    * @draft ICU 3.4
2812    * @provisional This API might change or be removed in a future release.
2813    */

2814
2815    public static ULocale acceptLanguage(ULocale[] acceptLanguageList, boolean[]
2816                                         fallback) {
2817        return acceptLanguage(acceptLanguageList, ULocale.getAvailableLocales(),
2818                fallback);
2819    }
2820}
2821
Popular Tags