Source Code Cross Referenced for ULocale.java in  » Internationalization-Localization » icu4j » com » ibm » icu » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Internationalization Localization » icu4j » com.ibm.icu.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         ******************************************************************************
0003:         * Copyright (C) 2003-2006, International Business Machines Corporation and   *
0004:         * others. All Rights Reserved.                                               *
0005:         ******************************************************************************
0006:         */
0007:
0008:        package com.ibm.icu.util;
0009:
0010:        import java.io.Serializable;
0011:        import java.util.Collections;
0012:        import java.util.Comparator;
0013:        import java.util.HashMap;
0014:        import java.util.Iterator;
0015:        import java.util.Locale;
0016:        import java.util.Map;
0017:        import java.util.MissingResourceException;
0018:        import java.util.TreeMap;
0019:        import java.lang.ref.SoftReference;
0020:
0021:        import com.ibm.icu.impl.LocaleUtility;
0022:        import com.ibm.icu.impl.ICUResourceBundle;
0023:        import com.ibm.icu.lang.UCharacter;
0024:
0025:        /**
0026:         * A class analogous to {@link java.util.Locale} that provides additional
0027:         * support for ICU protocol.  In ICU 3.0 this class is enhanced to support
0028:         * RFC 3066 language identifiers.
0029:         *
0030:         * <p>Many classes and services in ICU follow a factory idiom, in
0031:         * which a factory method or object responds to a client request with
0032:         * an object.  The request includes a locale (the <i>requested</i>
0033:         * locale), and the returned object is constructed using data for that
0034:         * locale.  The system may lack data for the requested locale, in
0035:         * which case the locale fallback mechanism will be invoked until a
0036:         * populated locale is found (the <i>valid</i> locale).  Furthermore,
0037:         * even when a populated locale is found (the <i>valid</i> locale),
0038:         * further fallback may be required to reach a locale containing the
0039:         * specific data required by the service (the <i>actual</i> locale).
0040:         *
0041:         * <p>ULocale performs <b>'normalization'</b> and <b>'canonicalization'</b> of locale ids.
0042:         * Normalization 'cleans up' ICU locale ids as follows:
0043:         * <ul>
0044:         * <li>language, script, country, variant, and keywords are properly cased<br>
0045:         * (lower, title, upper, upper, and lower case respectively)</li>
0046:         * <li>hyphens used as separators are converted to underscores</li>
0047:         * <li>three-letter language and country ids are converted to two-letter
0048:         * equivalents where available</li>
0049:         * <li>surrounding spaces are removed from keywords and values</li>
0050:         * <li>if there are multiple keywords, they are put in sorted order</li>
0051:         * </ul>
0052:         * Canonicalization additionally performs the following:
0053:         * <ul>
0054:         * <li>POSIX ids are converted to ICU format IDs</li>
0055:         * <li>'grandfathered' 3066 ids are converted to ICU standard form</li>
0056:         * <li>'PREEURO' and 'EURO' variants are converted to currency keyword form, with the currency
0057:         * id appropriate to the country of the locale (for PREEURO) or EUR (for EURO).
0058:         * </ul>
0059:         * All ULocale constructors automatically normalize the locale id.  To handle
0060:         * POSIX ids, <code>canonicalize</code> can be called to convert the id
0061:         * to canonical form, or the <code>canonicalInstance</code> factory method
0062:         * can be called.</p>
0063:         *
0064:         * <p>This class provides selectors {@link #VALID_LOCALE} and {@link
0065:         * #ACTUAL_LOCALE} intended for use in methods named
0066:         * <tt>getLocale()</tt>.  These methods exist in several ICU classes,
0067:         * including {@link com.ibm.icu.util.Calendar}, {@link
0068:         * com.ibm.icu.util.Currency}, {@link com.ibm.icu.text.UFormat},
0069:         * {@link com.ibm.icu.text.BreakIterator}, {@link
0070:         * com.ibm.icu.text.Collator}, {@link
0071:         * com.ibm.icu.text.DateFormatSymbols}, and {@link
0072:         * com.ibm.icu.text.DecimalFormatSymbols} and their subclasses, if
0073:         * any.  Once an object of one of these classes has been created,
0074:         * <tt>getLocale()</tt> may be called on it to determine the valid and
0075:         * actual locale arrived at during the object's construction.
0076:         *
0077:         * <p>Note: The <tt>getLocale()</tt> method will be implemented in ICU
0078:         * 3.0; ICU 2.8 contains a partial preview implementation.  The
0079:         * <i>actual</i> locale is returned correctly, but the <i>valid</i>
0080:         * locale is not, in most cases.
0081:         *
0082:         * @see java.util.Locale
0083:         * @author weiv
0084:         * @author Alan Liu
0085:         * @author Ram Viswanadha
0086:         * @stable ICU 2.8 
0087:         */
0088:        public final class ULocale implements  Serializable {
0089:            // using serialver from jdk1.4.2_05
0090:            private static final long serialVersionUID = 3715177670352309217L;
0091:
0092:            /** 
0093:             * Useful constant for language.
0094:             * @stable ICU 3.0
0095:             */
0096:            public static final ULocale ENGLISH = new ULocale("en",
0097:                    Locale.ENGLISH);
0098:
0099:            /** 
0100:             * Useful constant for language.
0101:             * @stable ICU 3.0
0102:             */
0103:            public static final ULocale FRENCH = new ULocale("fr",
0104:                    Locale.FRENCH);
0105:
0106:            /** 
0107:             * Useful constant for language.
0108:             * @stable ICU 3.0
0109:             */
0110:            public static final ULocale GERMAN = new ULocale("de",
0111:                    Locale.GERMAN);
0112:
0113:            /** 
0114:             * Useful constant for language.
0115:             * @stable ICU 3.0
0116:             */
0117:            public static final ULocale ITALIAN = new ULocale("it",
0118:                    Locale.ITALIAN);
0119:
0120:            /** 
0121:             * Useful constant for language.
0122:             * @stable ICU 3.0
0123:             */
0124:            public static final ULocale JAPANESE = new ULocale("ja",
0125:                    Locale.JAPANESE);
0126:
0127:            /** 
0128:             * Useful constant for language.
0129:             * @stable ICU 3.0
0130:             */
0131:            public static final ULocale KOREAN = new ULocale("ko",
0132:                    Locale.KOREAN);
0133:
0134:            /** 
0135:             * Useful constant for language.
0136:             * @stable ICU 3.0
0137:             */
0138:            public static final ULocale CHINESE = new ULocale("zh",
0139:                    Locale.CHINESE);
0140:
0141:            /** 
0142:             * Useful constant for language.
0143:             * @stable ICU 3.0
0144:             */
0145:            public static final ULocale SIMPLIFIED_CHINESE = new ULocale(
0146:                    "zh_Hans", Locale.CHINESE);
0147:
0148:            /** 
0149:             * Useful constant for language.
0150:             * @stable ICU 3.0
0151:             */
0152:            public static final ULocale TRADITIONAL_CHINESE = new ULocale(
0153:                    "zh_Hant", Locale.CHINESE);
0154:
0155:            /** 
0156:             * Useful constant for country/region.
0157:             * @stable ICU 3.0
0158:             */
0159:            public static final ULocale FRANCE = new ULocale("fr_FR",
0160:                    Locale.FRANCE);
0161:
0162:            /** 
0163:             * Useful constant for country/region.
0164:             * @stable ICU 3.0
0165:             */
0166:            public static final ULocale GERMANY = new ULocale("de_DE",
0167:                    Locale.GERMANY);
0168:
0169:            /** 
0170:             * Useful constant for country/region.
0171:             * @stable ICU 3.0
0172:             */
0173:            public static final ULocale ITALY = new ULocale("it_IT",
0174:                    Locale.ITALY);
0175:
0176:            /** 
0177:             * Useful constant for country/region.
0178:             * @stable ICU 3.0
0179:             */
0180:            public static final ULocale JAPAN = new ULocale("ja_JP",
0181:                    Locale.JAPAN);
0182:
0183:            /** 
0184:             * Useful constant for country/region.
0185:             * @stable ICU 3.0
0186:             */
0187:            public static final ULocale KOREA = new ULocale("ko_KR",
0188:                    Locale.KOREA);
0189:
0190:            /** 
0191:             * Useful constant for country/region.
0192:             * @stable ICU 3.0
0193:             */
0194:            public static final ULocale CHINA = new ULocale("zh_Hans_CN",
0195:                    Locale.CHINA);
0196:
0197:            /** 
0198:             * Useful constant for country/region.
0199:             * @stable ICU 3.0
0200:             */
0201:            public static final ULocale PRC = CHINA;
0202:
0203:            /** 
0204:             * Useful constant for country/region.
0205:             * @stable ICU 3.0
0206:             */
0207:            public static final ULocale TAIWAN = new ULocale("zh_Hant_TW",
0208:                    Locale.TAIWAN);
0209:
0210:            /** 
0211:             * Useful constant for country/region.
0212:             * @stable ICU 3.0
0213:             */
0214:            public static final ULocale UK = new ULocale("en_GB", Locale.UK);
0215:
0216:            /** 
0217:             * Useful constant for country/region.
0218:             * @stable ICU 3.0
0219:             */
0220:            public static final ULocale US = new ULocale("en_US", Locale.US);
0221:
0222:            /** 
0223:             * Useful constant for country/region.
0224:             * @stable ICU 3.0
0225:             */
0226:            public static final ULocale CANADA = new ULocale("en_CA",
0227:                    Locale.CANADA);
0228:
0229:            /** 
0230:             * Useful constant for country/region.
0231:             * @stable ICU 3.0
0232:             */
0233:            public static final ULocale CANADA_FRENCH = new ULocale("fr_CA",
0234:                    Locale.CANADA_FRENCH);
0235:
0236:            /**
0237:             * Handy constant.
0238:             */
0239:            private static final String EMPTY_STRING = "";
0240:
0241:            // Used in both ULocale and IDParser, so moved up here.
0242:            private static final char UNDERSCORE = '_';
0243:
0244:            // default empty locale
0245:            private static final Locale EMPTY_LOCALE = new Locale("", "");
0246:
0247:            /**
0248:             * The root ULocale.
0249:             * @stable ICU 2.8
0250:             */
0251:            public static final ULocale ROOT = new ULocale("root", EMPTY_LOCALE);
0252:
0253:            private static final HashMap CACHE = new HashMap(20);
0254:            static {
0255:                CACHE.put(EMPTY_LOCALE, ROOT);
0256:                CACHE.put(Locale.ENGLISH, ENGLISH);
0257:                CACHE.put(Locale.FRENCH, FRENCH);
0258:                CACHE.put(Locale.GERMAN, GERMAN);
0259:                CACHE.put(Locale.ITALIAN, ITALIAN);
0260:                CACHE.put(Locale.JAPANESE, JAPANESE);
0261:                CACHE.put(Locale.KOREAN, KOREAN);
0262:                CACHE.put(Locale.CHINESE, CHINESE);
0263:                CACHE.put(Locale.SIMPLIFIED_CHINESE, SIMPLIFIED_CHINESE);
0264:                CACHE.put(Locale.TRADITIONAL_CHINESE, TRADITIONAL_CHINESE);
0265:                CACHE.put(Locale.FRANCE, FRANCE);
0266:                CACHE.put(Locale.GERMANY, GERMANY);
0267:                CACHE.put(Locale.ITALY, ITALY);
0268:                CACHE.put(Locale.JAPAN, JAPAN);
0269:                CACHE.put(Locale.KOREA, KOREA);
0270:                CACHE.put(Locale.CHINA, CHINA);
0271:                CACHE.put(Locale.TAIWAN, TAIWAN);
0272:                CACHE.put(Locale.UK, UK);
0273:                CACHE.put(Locale.US, US);
0274:                CACHE.put(Locale.CANADA, CANADA);
0275:                CACHE.put(Locale.CANADA_FRENCH, CANADA_FRENCH);
0276:            }
0277:
0278:            /**
0279:             * Cache the locale.
0280:             */
0281:            private transient Locale locale;
0282:
0283:            /**
0284:             * The raw localeID that we were passed in.
0285:             */
0286:            private String localeID;
0287:
0288:            /**
0289:             * Tables used in normalizing portions of the id.
0290:             */
0291:            /* tables updated per http://lcweb.loc.gov/standards/iso639-2/ 
0292:               to include the revisions up to 2001/7/27 *CWB*/
0293:            /* The 3 character codes are the terminology codes like RFC 3066.  
0294:               This is compatible with prior ICU codes */
0295:            /* "in" "iw" "ji" "jw" & "sh" have been withdrawn but are still in 
0296:               the table but now at the end of the table because 
0297:               3 character codes are duplicates.  This avoids bad searches
0298:               going from 3 to 2 character codes.*/
0299:            /* The range qaa-qtz is reserved for local use. */
0300:
0301:            private static String[] _languages;
0302:            private static String[] _replacementLanguages;
0303:            private static String[] _obsoleteLanguages;
0304:            private static String[] _languages3;
0305:            private static String[] _obsoleteLanguages3;
0306:
0307:            // Avoid initializing languages tables unless we have to.
0308:            private static void initLanguageTables() {
0309:                if (_languages == null) {
0310:
0311:                    /* This list MUST be in sorted order, and MUST contain the two-letter codes
0312:                       if one exists otherwise use the three letter code */
0313:                    String[] tempLanguages = { "aa", "ab", "ace", "ach", "ada",
0314:                            "ady", "ae", "af", "afa", "afh", "ak", "akk",
0315:                            "ale", "alg", "am", "an", "ang", "apa", "ar",
0316:                            "arc", "arn", "arp", "art", "arw", "as", "ast",
0317:                            "ath", "aus", "av", "awa", "ay", "az", "ba", "bad",
0318:                            "bai", "bal", "ban", "bas", "bat", "be", "bej",
0319:                            "bem", "ber", "bg", "bh", "bho", "bi", "bik",
0320:                            "bin", "bla", "bm", "bn", "bnt", "bo", "br", "bra",
0321:                            "bs", "btk", "bua", "bug", "byn", "ca", "cad",
0322:                            "cai", "car", "cau", "ce", "ceb", "cel", "ch",
0323:                            "chb", "chg", "chk", "chm", "chn", "cho", "chp",
0324:                            "chr", "chy", "cmc", "co", "cop", "cpe", "cpf",
0325:                            "cpp", "cr", "crh", "crp", "cs", "csb", "cu",
0326:                            "cus", "cv", "cy", "da", "dak", "dar", "day", "de",
0327:                            "del", "den", "dgr", "din", "doi", "dra", "dsb",
0328:                            "dua", "dum", "dv", "dyu", "dz", "ee", "efi",
0329:                            "egy", "eka", "el", "elx", "en", "enm", "eo", "es",
0330:                            "et", "eu", "ewo", "fa", "fan", "fat", "ff", "fi",
0331:                            "fiu", "fj", "fo", "fon", "fr", "frm", "fro",
0332:                            "fur", "fy", "ga", "gaa", "gay", "gba", "gd",
0333:                            "gem", "gez", "gil", "gl", "gmh", "gn", "goh",
0334:                            "gon", "gor", "got", "grb", "grc", "gu", "gv",
0335:                            "gwi", "ha", "hai", "haw", "he", "hi", "hil",
0336:                            "him", "hit", "hmn", "ho", "hr", "hsb", "ht", "hu",
0337:                            "hup", "hy", "hz", "ia", "iba", "id", "ie", "ig",
0338:                            "ii", "ijo", "ik", "ilo", "inc", "ine", "inh",
0339:                            "io", "ira", "iro", "is", "it", "iu", "ja", "jbo",
0340:                            "jpr", "jrb", "jv", "ka", "kaa", "kab", "kac",
0341:                            "kam", "kar", "kaw", "kbd", "kg", "kha", "khi",
0342:                            "kho", "ki", "kj", "kk", "kl", "km", "kmb", "kn",
0343:                            "ko", "kok", "kos", "kpe", "kr", "krc", "kro",
0344:                            "kru", "ks", "ku", "kum", "kut", "kv", "kw", "ky",
0345:                            "la", "lad", "lah", "lam", "lb", "lez", "lg", "li",
0346:                            "ln", "lo", "lol", "loz", "lt", "lu", "lua", "lui",
0347:                            "lun", "luo", "lus", "lv", "mad", "mag", "mai",
0348:                            "mak", "man", "map", "mas", "mdf", "mdr", "men",
0349:                            "mg", "mga", "mh", "mi", "mic", "min", "mis", "mk",
0350:                            "mkh", "ml", "mn", "mnc", "mni", "mno", "mo",
0351:                            "moh", "mos", "mr", "ms", "mt", "mul", "mun",
0352:                            "mus", "mwr", "my", "myn", "myv", "na", "nah",
0353:                            "nai", "nap", "nb", "nd", "nds", "ne", "new", "ng",
0354:                            "nia", "nic", "niu", "nl", "nn", "no", "nog",
0355:                            "non", "nr", "nso", "nub", "nv", "nwc", "ny",
0356:                            "nym", "nyn", "nyo", "nzi", "oc", "oj", "om", "or",
0357:                            "os", "osa", "ota", "oto", "pa", "paa", "pag",
0358:                            "pal", "pam", "pap", "pau", "peo", "phi", "phn",
0359:                            "pi", "pl", "pon", "pra", "pro", "ps", "pt", "qu",
0360:                            "raj", "rap", "rar", "rm", "rn", "ro", "roa",
0361:                            "rom", "ru", "rup", "rw", "sa", "sad", "sah",
0362:                            "sai", "sal", "sam", "sas", "sat", "sc", "sco",
0363:                            "sd", "se", "sel", "sem", "sg", "sga", "sgn",
0364:                            "shn", "si", "sid", "sio", "sit", "sk", "sl",
0365:                            "sla", "sm", "sma", "smi", "smj", "smn", "sms",
0366:                            "sn", "snk", "so", "sog", "son", "sq", "sr", "srr",
0367:                            "ss", "ssa", "st", "su", "suk", "sus", "sux", "sv",
0368:                            "sw", "syr", "ta", "tai", "te", "tem", "ter",
0369:                            "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl",
0370:                            "tl", "tlh", "tli", "tmh", "tn", "to", "tog",
0371:                            "tpi", "tr", "ts", "tsi", "tt", "tum", "tup",
0372:                            "tut", "tvl", "tw", "ty", "tyv", "udm", "ug",
0373:                            "uga", "uk", "umb", "und", "ur", "uz", "vai", "ve",
0374:                            "vi", "vo", "vot", "wa", "wak", "wal", "war",
0375:                            "was", "wen", "wo", "xal", "xh", "yao", "yap",
0376:                            "yi", "yo", "ypk", "za", "zap", "zen", "zh", "znd",
0377:                            "zu", "zun", };
0378:
0379:                    String[] tempReplacementLanguages = { "id", "he", "yi",
0380:                            "jv", "sr", "nb",/* replacement language codes */
0381:                    };
0382:
0383:                    String[] tempObsoleteLanguages = { "in", "iw", "ji", "jw",
0384:                            "sh", "no", /* obsolete language codes */
0385:                    };
0386:
0387:                    /* This list MUST contain a three-letter code for every two-letter code in the
0388:                       list above, and they MUST ne in the same order (i.e., the same language must
0389:                       be in the same place in both lists)! */
0390:                    String[] tempLanguages3 = {
0391:                    /*"aa",  "ab",  "ace", "ach", "ada", "ady", "ae",  "af",  "afa",    */
0392:                    "aar", "abk", "ace", "ach", "ada", "ady", "ave", "afr",
0393:                            "afa",
0394:                            /*"afh", "ak",  "akk", "ale", "alg", "am",  "an",  "ang", "apa",    */
0395:                            "afh", "aka", "akk", "ale", "alg", "amh", "arg",
0396:                            "ang", "apa",
0397:                            /*"ar",  "arc", "arn", "arp", "art", "arw", "as",  "ast",    */
0398:                            "ara", "arc", "arn", "arp", "art", "arw", "asm",
0399:                            "ast",
0400:                            /*"ath", "aus", "av",  "awa", "ay",  "az",  "ba",  "bad",    */
0401:                            "ath", "aus", "ava", "awa", "aym", "aze", "bak",
0402:                            "bad",
0403:                            /*"bai", "bal", "ban", "bas", "bat", "be",  "bej",    */
0404:                            "bai", "bal", "ban", "bas", "bat", "bel", "bej",
0405:                            /*"bem", "ber", "bg",  "bh",  "bho", "bi",  "bik", "bin",    */
0406:                            "bem", "ber", "bul", "bih", "bho", "bis", "bik",
0407:                            "bin",
0408:                            /*"bla", "bm",  "bn",  "bnt", "bo",  "br",  "bra", "bs",     */
0409:                            "bla", "bam", "ben", "bnt", "bod", "bre", "bra",
0410:                            "bos",
0411:                            /*"btk", "bua", "bug", "byn", "ca",  "cad", "cai", "car", "cau",    */
0412:                            "btk", "bua", "bug", "byn", "cat", "cad", "cai",
0413:                            "car", "cau",
0414:                            /*"ce",  "ceb", "cel", "ch",  "chb", "chg", "chk", "chm",    */
0415:                            "che", "ceb", "cel", "cha", "chb", "chg", "chk",
0416:                            "chm",
0417:                            /*"chn", "cho", "chp", "chr", "chy", "cmc", "co",  "cop",    */
0418:                            "chn", "cho", "chp", "chr", "chy", "cmc", "cos",
0419:                            "cop",
0420:                            /*"cpe", "cpf", "cpp", "cr",  "crh", "crp", "cs",  "csb", "cu",  "cus",    */
0421:                            "cpe", "cpf", "cpp", "cre", "crh", "crp", "ces",
0422:                            "csb", "chu", "cus",
0423:                            /*"cv",  "cy",  "da",  "dak", "dar", "day", "de",  "del", "den",    */
0424:                            "chv", "cym", "dan", "dak", "dar", "day", "deu",
0425:                            "del", "den",
0426:                            /*"dgr", "din", "doi", "dra", "dsb", "dua", "dum", "dv",  "dyu",    */
0427:                            "dgr", "din", "doi", "dra", "dsb", "dua", "dum",
0428:                            "div", "dyu",
0429:                            /*"dz",  "ee",  "efi", "egy", "eka", "el",  "elx", "en",     */
0430:                            "dzo", "ewe", "efi", "egy", "eka", "ell", "elx",
0431:                            "eng",
0432:                            /*"enm", "eo",  "es",  "et",  "eu",  "ewo", "fa",     */
0433:                            "enm", "epo", "spa", "est", "eus", "ewo", "fas",
0434:                            /*"fan", "fat", "ff",  "fi",  "fiu", "fj",  "fo",  "fon",    */
0435:                            "fan", "fat", "ful", "fin", "fiu", "fij", "fao",
0436:                            "fon",
0437:                            /*"fr",  "frm", "fro", "fur", "fy",  "ga",  "gaa", "gay",    */
0438:                            "fra", "frm", "fro", "fur", "fry", "gle", "gaa",
0439:                            "gay",
0440:                            /*"gba", "gd",  "gem", "gez", "gil", "gl",  "gmh", "gn",     */
0441:                            "gba", "gla", "gem", "gez", "gil", "glg", "gmh",
0442:                            "grn",
0443:                            /*"goh", "gon", "gor", "got", "grb", "grc", "gu",  "gv",     */
0444:                            "goh", "gon", "gor", "got", "grb", "grc", "guj",
0445:                            "glv",
0446:                            /*"gwi", "ha",  "hai", "haw", "he",  "hi",  "hil", "him",    */
0447:                            "gwi", "hau", "hai", "haw", "heb", "hin", "hil",
0448:                            "him",
0449:                            /*"hit", "hmn", "ho",  "hr",  "hsb", "ht",  "hu",  "hup", "hy",  "hz",     */
0450:                            "hit", "hmn", "hmo", "hrv", "hsb", "hat", "hun",
0451:                            "hup", "hye", "her",
0452:                            /*"ia",  "iba", "id",  "ie",  "ig",  "ii",  "ijo", "ik",     */
0453:                            "ina", "iba", "ind", "ile", "ibo", "iii", "ijo",
0454:                            "ipk",
0455:                            /*"ilo", "inc", "ine", "inh", "io",  "ira", "iro", "is",  "it",      */
0456:                            "ilo", "inc", "ine", "inh", "ido", "ira", "iro",
0457:                            "isl", "ita",
0458:                            /*"iu",  "ja",  "jbo", "jpr", "jrb", "jv",  "ka",  "kaa", "kab",   */
0459:                            "iku", "jpn", "jbo", "jpr", "jrb", "jaw", "kat",
0460:                            "kaa", "kab",
0461:                            /*"kac", "kam", "kar", "kaw", "kbd", "kg",  "kha", "khi",    */
0462:                            "kac", "kam", "kar", "kaw", "kbd", "kon", "kha",
0463:                            "khi",
0464:                            /*"kho", "ki",  "kj",  "kk",  "kl",  "km",  "kmb", "kn",     */
0465:                            "kho", "kik", "kua", "kaz", "kal", "khm", "kmb",
0466:                            "kan",
0467:                            /*"ko",  "kok", "kos", "kpe", "kr",  "krc", "kro", "kru", "ks",     */
0468:                            "kor", "kok", "kos", "kpe", "kau", "krc", "kro",
0469:                            "kru", "kas",
0470:                            /*"ku",  "kum", "kut", "kv",  "kw",  "ky",  "la",  "lad",    */
0471:                            "kur", "kum", "kut", "kom", "cor", "kir", "lat",
0472:                            "lad",
0473:                            /*"lah", "lam", "lb",  "lez", "lg",  "li",  "ln",  "lo",  "lol",    */
0474:                            "lah", "lam", "ltz", "lez", "lug", "lim", "lin",
0475:                            "lao", "lol",
0476:                            /*"loz", "lt",  "lu",  "lua", "lui", "lun", "luo", "lus",    */
0477:                            "loz", "lit", "lub", "lua", "lui", "lun", "luo",
0478:                            "lus",
0479:                            /*"lv",  "mad", "mag", "mai", "mak", "man", "map", "mas",    */
0480:                            "lav", "mad", "mag", "mai", "mak", "man", "map",
0481:                            "mas",
0482:                            /*"mdf", "mdr", "men", "mg",  "mga", "mh",  "mi",  "mic", "min",    */
0483:                            "mdf", "mdr", "men", "mlg", "mga", "mah", "mri",
0484:                            "mic", "min",
0485:                            /*"mis", "mk",  "mkh", "ml",  "mn",  "mnc", "mni", "mno",    */
0486:                            "mis", "mkd", "mkh", "mal", "mon", "mnc", "mni",
0487:                            "mno",
0488:                            /*"mo",  "moh", "mos", "mr",  "ms",  "mt",  "mul", "mun",    */
0489:                            "mol", "moh", "mos", "mar", "msa", "mlt", "mul",
0490:                            "mun",
0491:                            /*"mus", "mwr", "my",  "myn", "myv", "na",  "nah", "nai", "nap",    */
0492:                            "mus", "mwr", "mya", "myn", "myv", "nau", "nah",
0493:                            "nai", "nap",
0494:                            /*"nb",  "nd",  "nds", "ne",  "new", "ng",  "nia", "nic",    */
0495:                            "nob", "nde", "nds", "nep", "new", "ndo", "nia",
0496:                            "nic",
0497:                            /*"niu", "nl",  "nn",  "no",  "nog", "non", "nr",  "nso", "nub",    */
0498:                            "niu", "nld", "nno", "nor", "nog", "non", "nbl",
0499:                            "nso", "nub",
0500:                            /*"nv",  "nwc", "ny",  "nym", "nyn", "nyo", "nzi", "oc",  "oj",     */
0501:                            "nav", "nwc", "nya", "nym", "nyn", "nyo", "nzi",
0502:                            "oci", "oji",
0503:                            /*"om",  "or",  "os",  "osa", "ota", "oto", "pa",  "paa",    */
0504:                            "orm", "ori", "oss", "osa", "ota", "oto", "pan",
0505:                            "paa",
0506:                            /*"pag", "pal", "pam", "pap", "pau", "peo", "phi", "phn",    */
0507:                            "pag", "pal", "pam", "pap", "pau", "peo", "phi",
0508:                            "phn",
0509:                            /*"pi",  "pl",  "pon", "pra", "pro", "ps",  "pt",  "qu",     */
0510:                            "pli", "pol", "pon", "pra", "pro", "pus", "por",
0511:                            "que",
0512:                            /*"raj", "rap", "rar", "rm",  "rn",  "ro",  "roa", "rom",    */
0513:                            "raj", "rap", "rar", "roh", "run", "ron", "roa",
0514:                            "rom",
0515:                            /*"ru",  "rup", "rw",  "sa",  "sad", "sah", "sai", "sal", "sam",    */
0516:                            "rus", "rup", "kin", "san", "sad", "sah", "sai",
0517:                            "sal", "sam",
0518:                            /*"sas", "sat", "sc",  "sco", "sd",  "se",  "sel", "sem",    */
0519:                            "sas", "sat", "srd", "sco", "snd", "sme", "sel",
0520:                            "sem",
0521:                            /*"sg",  "sga", "sgn", "shn", "si",  "sid", "sio", "sit",    */
0522:                            "sag", "sga", "sgn", "shn", "sin", "sid", "sio",
0523:                            "sit",
0524:                            /*"sk",  "sl",  "sla", "sm",  "sma", "smi", "smj", "smn",    */
0525:                            "slk", "slv", "sla", "smo", "sma", "smi", "smj",
0526:                            "smn",
0527:                            /*"sms", "sn",  "snk", "so",  "sog", "son", "sq",  "sr",     */
0528:                            "sms", "sna", "snk", "som", "sog", "son", "sqi",
0529:                            "srp",
0530:                            /*"srr", "ss",  "ssa", "st",  "su",  "suk", "sus", "sux",    */
0531:                            "srr", "ssw", "ssa", "sot", "sun", "suk", "sus",
0532:                            "sux",
0533:                            /*"sv",  "sw",  "syr", "ta",  "tai", "te",  "tem", "ter",    */
0534:                            "swe", "swa", "syr", "tam", "tai", "tel", "tem",
0535:                            "ter",
0536:                            /*"tet", "tg",  "th",  "ti",  "tig", "tiv", "tk",  "tkl",    */
0537:                            "tet", "tgk", "tha", "tir", "tig", "tiv", "tuk",
0538:                            "tkl",
0539:                            /*"tl",  "tlh", "tli", "tmh", "tn",  "to",  "tog", "tpi", "tr",     */
0540:                            "tgl", "tlh", "tli", "tmh", "tsn", "ton", "tog",
0541:                            "tpi", "tur",
0542:                            /*"ts",  "tsi", "tt",  "tum", "tup", "tut", "tvl", "tw",     */
0543:                            "tso", "tsi", "tat", "tum", "tup", "tut", "tvl",
0544:                            "twi",
0545:                            /*"ty",  "tyv", "udm", "ug",  "uga", "uk",  "umb", "und", "ur",     */
0546:                            "tah", "tyv", "udm", "uig", "uga", "ukr", "umb",
0547:                            "und", "urd",
0548:                            /*"uz",  "vai", "ve",  "vi",  "vo",  "vot", "wa",  "wak",    */
0549:                            "uzb", "vai", "ven", "vie", "vol", "vot", "wln",
0550:                            "wak",
0551:                            /*"wal", "war", "was", "wen", "wo",  "xal", "xh",  "yao", "yap",    */
0552:                            "wal", "war", "was", "wen", "wol", "xal", "xho",
0553:                            "yao", "yap",
0554:                            /*"yi",  "yo",  "ypk", "za",  "zap", "zen", "zh",  "znd",    */
0555:                            "yid", "yor", "ypk", "zha", "zap", "zen", "zho",
0556:                            "znd",
0557:                            /*"zu",  "zun",                                              */
0558:                            "zul", "zun", };
0559:
0560:                    String[] tempObsoleteLanguages3 = {
0561:                    /* "in",  "iw",  "ji",  "jw",  "sh", */
0562:                    "ind", "heb", "yid", "jaw", "srp", };
0563:
0564:                    synchronized (ULocale.class) {
0565:                        if (_languages == null) {
0566:                            _languages = tempLanguages;
0567:                            _replacementLanguages = tempReplacementLanguages;
0568:                            _obsoleteLanguages = tempObsoleteLanguages;
0569:                            _languages3 = tempLanguages3;
0570:                            _obsoleteLanguages3 = tempObsoleteLanguages3;
0571:                        }
0572:                    }
0573:                }
0574:            }
0575:
0576:            private static String[] _countries;
0577:            private static String[] _deprecatedCountries;
0578:            private static String[] _replacementCountries;
0579:            private static String[] _obsoleteCountries;
0580:            private static String[] _countries3;
0581:            private static String[] _obsoleteCountries3;
0582:
0583:            // Avoid initializing country tables unless we have to.
0584:            private static void initCountryTables() {
0585:                if (_countries == null) {
0586:                    /* ZR(ZAR) is now CD(COD) and FX(FXX) is PS(PSE) as per
0587:                       http://www.evertype.com/standards/iso3166/iso3166-1-en.html 
0588:                       added new codes keeping the old ones for compatibility
0589:                       updated to include 1999/12/03 revisions *CWB*/
0590:
0591:                    /* RO(ROM) is now RO(ROU) according to 
0592:                       http://www.iso.org/iso/en/prods-services/iso3166ma/03updates-on-iso-3166/nlv3e-rou.html
0593:                     */
0594:
0595:                    /* This list MUST be in sorted order, and MUST contain only two-letter codes! */
0596:                    String[] tempCountries = { "AD", "AE", "AF", "AG", "AI",
0597:                            "AL", "AM", "AN", "AO", "AQ", "AR", "AS", "AT",
0598:                            "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF",
0599:                            "BG", "BH", "BI", "BJ", "BM", "BN", "BO", "BR",
0600:                            "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC",
0601:                            "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM",
0602:                            "CN", "CO", "CR", "CU", "CV", "CX", "CY", "CZ",
0603:                            "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE",
0604:                            "EG", "EH", "ER", "ES", "ET", "FI", "FJ", "FK",
0605:                            "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF",
0606:                            "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR",
0607:                            "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN",
0608:                            "HR", "HT", "HU", "ID", "IE", "IL", "IN", "IO",
0609:                            "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE",
0610:                            "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW",
0611:                            "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR",
0612:                            "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD",
0613:                            "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP",
0614:                            "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX",
0615:                            "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI",
0616:                            "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA",
0617:                            "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN",
0618:                            "PR", "PS", "PT", "PW", "PY", "QA", "RE", "RO",
0619:                            "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG",
0620:                            "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO",
0621:                            "SR", "ST", "SV", "SY", "SZ", "TC", "TD", "TF",
0622:                            "TG", "TH", "TJ", "TK", "TL", "TM", "TN", "TO",
0623:                            "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM",
0624:                            "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI",
0625:                            "VN", "VU", "WF", "WS", "YE", "YT", "YU", "ZA",
0626:                            "ZM", "ZW", };
0627:
0628:                    /* this table is used for 3 letter codes */
0629:                    String[] tempObsoleteCountries = { "FX", "RO", "TP", "ZR", /* obsolete country codes */
0630:                    };
0631:
0632:                    String[] tempDeprecatedCountries = { "BU", "DY", "FX",
0633:                            "HV", "NH", "RH", "TP", "YU", "ZR" /* deprecated country list */
0634:                    };
0635:                    String[] tempReplacementCountries = {
0636:                    /*  "BU", "DY", "FX", "HV", "NH", "RH", "TP", "YU", "ZR" */
0637:                    "MM", "BJ", "FR", "BF", "VU", "ZW", "TL", "CS", "CD", /* replacement country codes */
0638:                    };
0639:
0640:                    /* This list MUST contain a three-letter code for every two-letter code in
0641:                       the above list, and they MUST be listed in the same order! */
0642:                    String[] tempCountries3 = {
0643:                    /*"AD",  "AE",  "AF",  "AG",  "AI",  "AL",  "AM",  "AN",     */
0644:                    "AND", "ARE", "AFG", "ATG", "AIA", "ALB", "ARM", "ANT",
0645:                    /*"AO",  "AQ",  "AR",  "AS",  "AT",  "AU",  "AW",  "AZ",     */
0646:                    "AGO", "ATA", "ARG", "ASM", "AUT", "AUS", "ABW", "AZE",
0647:                    /*"BA",  "BB",  "BD",  "BE",  "BF",  "BG",  "BH",  "BI",     */
0648:                    "BIH", "BRB", "BGD", "BEL", "BFA", "BGR", "BHR", "BDI",
0649:                    /*"BJ",  "BM",  "BN",  "BO",  "BR",  "BS",  "BT",  "BV",     */
0650:                    "BEN", "BMU", "BRN", "BOL", "BRA", "BHS", "BTN", "BVT",
0651:                    /*"BW",  "BY",  "BZ",  "CA",  "CC",  "CD",  "CF",  "CG",     */
0652:                    "BWA", "BLR", "BLZ", "CAN", "CCK", "COD", "CAF", "COG",
0653:                    /*"CH",  "CI",  "CK",  "CL",  "CM",  "CN",  "CO",  "CR",     */
0654:                    "CHE", "CIV", "COK", "CHL", "CMR", "CHN", "COL", "CRI",
0655:                    /*"CU",  "CV",  "CX",  "CY",  "CZ",  "DE",  "DJ",  "DK",     */
0656:                    "CUB", "CPV", "CXR", "CYP", "CZE", "DEU", "DJI", "DNK",
0657:                    /*"DM",  "DO",  "DZ",  "EC",  "EE",  "EG",  "EH",  "ER",     */
0658:                    "DMA", "DOM", "DZA", "ECU", "EST", "EGY", "ESH", "ERI",
0659:                    /*"ES",  "ET",  "FI",  "FJ",  "FK",  "FM",  "FO",  "FR",     */
0660:                    "ESP", "ETH", "FIN", "FJI", "FLK", "FSM", "FRO", "FRA",
0661:                    /*"GA",  "GB",  "GD",  "GE",  "GF",  "GH",  "GI",  "GL",     */
0662:                    "GAB", "GBR", "GRD", "GEO", "GUF", "GHA", "GIB", "GRL",
0663:                    /*"GM",  "GN",  "GP",  "GQ",  "GR",  "GS",  "GT",  "GU",     */
0664:                    "GMB", "GIN", "GLP", "GNQ", "GRC", "SGS", "GTM", "GUM",
0665:                    /*"GW",  "GY",  "HK",  "HM",  "HN",  "HR",  "HT",  "HU",     */
0666:                    "GNB", "GUY", "HKG", "HMD", "HND", "HRV", "HTI", "HUN",
0667:                    /*"ID",  "IE",  "IL",  "IN",  "IO",  "IQ",  "IR",  "IS",     */
0668:                    "IDN", "IRL", "ISR", "IND", "IOT", "IRQ", "IRN", "ISL",
0669:                    /*"IT",  "JM",  "JO",  "JP",  "KE",  "KG",  "KH",  "KI",     */
0670:                    "ITA", "JAM", "JOR", "JPN", "KEN", "KGZ", "KHM", "KIR",
0671:                    /*"KM",  "KN",  "KP",  "KR",  "KW",  "KY",  "KZ",  "LA",     */
0672:                    "COM", "KNA", "PRK", "KOR", "KWT", "CYM", "KAZ", "LAO",
0673:                    /*"LB",  "LC",  "LI",  "LK",  "LR",  "LS",  "LT",  "LU",     */
0674:                    "LBN", "LCA", "LIE", "LKA", "LBR", "LSO", "LTU", "LUX",
0675:                    /*"LV",  "LY",  "MA",  "MC",  "MD",  "MG",  "MH",  "MK",     */
0676:                    "LVA", "LBY", "MAR", "MCO", "MDA", "MDG", "MHL", "MKD",
0677:                    /*"ML",  "MM",  "MN",  "MO",  "MP",  "MQ",  "MR",  "MS",     */
0678:                    "MLI", "MMR", "MNG", "MAC", "MNP", "MTQ", "MRT", "MSR",
0679:                    /*"MT",  "MU",  "MV",  "MW",  "MX",  "MY",  "MZ",  "NA",     */
0680:                    "MLT", "MUS", "MDV", "MWI", "MEX", "MYS", "MOZ", "NAM",
0681:                    /*"NC",  "NE",  "NF",  "NG",  "NI",  "NL",  "NO",  "NP",     */
0682:                    "NCL", "NER", "NFK", "NGA", "NIC", "NLD", "NOR", "NPL",
0683:                    /*"NR",  "NU",  "NZ",  "OM",  "PA",  "PE",  "PF",  "PG",     */
0684:                    "NRU", "NIU", "NZL", "OMN", "PAN", "PER", "PYF", "PNG",
0685:                    /*"PH",  "PK",  "PL",  "PM",  "PN",  "PR",  "PS",  "PT",     */
0686:                    "PHL", "PAK", "POL", "SPM", "PCN", "PRI", "PSE", "PRT",
0687:                    /*"PW",  "PY",  "QA",  "RE",  "RO",  "RU",  "RW",  "SA",     */
0688:                    "PLW", "PRY", "QAT", "REU", "ROU", "RUS", "RWA", "SAU",
0689:                    /*"SB",  "SC",  "SD",  "SE",  "SG",  "SH",  "SI",  "SJ",     */
0690:                    "SLB", "SYC", "SDN", "SWE", "SGP", "SHN", "SVN", "SJM",
0691:                    /*"SK",  "SL",  "SM",  "SN",  "SO",  "SR",  "ST",  "SV",     */
0692:                    "SVK", "SLE", "SMR", "SEN", "SOM", "SUR", "STP", "SLV",
0693:                    /*"SY",  "SZ",  "TC",  "TD",  "TF",  "TG",  "TH",  "TJ",     */
0694:                    "SYR", "SWZ", "TCA", "TCD", "ATF", "TGO", "THA", "TJK",
0695:                    /*"TK",  "TL",  "TM",  "TN",  "TO",  "TR",  "TT",  "TV",     */
0696:                    "TKL", "TLS", "TKM", "TUN", "TON", "TUR", "TTO", "TUV",
0697:                    /*"TW",  "TZ",  "UA",  "UG",  "UM",  "US",  "UY",  "UZ",     */
0698:                    "TWN", "TZA", "UKR", "UGA", "UMI", "USA", "URY", "UZB",
0699:                    /*"VA",  "VC",  "VE",  "VG",  "VI",  "VN",  "VU",  "WF",     */
0700:                    "VAT", "VCT", "VEN", "VGB", "VIR", "VNM", "VUT", "WLF",
0701:                    /*"WS",  "YE",  "YT",  "YU",  "ZA",  "ZM",  "ZW",            */
0702:                    "WSM", "YEM", "MYT", "YUG", "ZAF", "ZMB", "ZWE", };
0703:
0704:                    String[] tempObsoleteCountries3 = {
0705:                    /*"FX",  "RO",  "TP",  "ZR",   */
0706:                    "FXX", "ROM", "TMP", "ZAR", };
0707:
0708:                    synchronized (ULocale.class) {
0709:                        if (_countries == null) {
0710:                            _countries = tempCountries;
0711:                            _deprecatedCountries = tempDeprecatedCountries;
0712:                            _replacementCountries = tempReplacementCountries;
0713:                            _obsoleteCountries = tempObsoleteCountries;
0714:                            _countries3 = tempCountries3;
0715:                            _obsoleteCountries3 = tempObsoleteCountries3;
0716:                        }
0717:                    }
0718:                }
0719:            }
0720:
0721:            private static String[][] _variantsToKeywords;
0722:
0723:            private static void initVariantsTable() {
0724:                if (_variantsToKeywords == null) {
0725:                    /**
0726:                     * This table lists pairs of locale ids for canonicalization.  The
0727:                     * The first item is the normalized id, the second item is the
0728:                     * canonicalized id.
0729:                     */
0730:                    String[][] tempVariantsToKeywords = {
0731:                            //              { EMPTY_STRING,     "en_US_POSIX", null, null }, /* .NET name */
0732:                            { "C", "en_US_POSIX", null, null }, /* POSIX name */
0733:                            { "art_LOJBAN", "jbo", null, null }, /* registered name */
0734:                            { "az_AZ_CYRL", "az_Cyrl_AZ", null, null }, /* .NET name */
0735:                            { "az_AZ_LATN", "az_Latn_AZ", null, null }, /* .NET name */
0736:                            { "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
0737:                            { "cel_GAULISH", "cel__GAULISH", null, null }, /* registered name */
0738:                            { "de_1901", "de__1901", null, null }, /* registered name */
0739:                            { "de_1906", "de__1906", null, null }, /* registered name */
0740:                            { "de__PHONEBOOK", "de", "collation", "phonebook" },
0741:                            { "de_AT_PREEURO", "de_AT", "currency", "ATS" },
0742:                            { "de_DE_PREEURO", "de_DE", "currency", "DEM" },
0743:                            { "de_LU_PREEURO", "de_LU", "currency", "EUR" },
0744:                            { "el_GR_PREEURO", "el_GR", "currency", "GRD" },
0745:                            { "en_BOONT", "en__BOONT", null, null }, /* registered name */
0746:                            { "en_SCOUSE", "en__SCOUSE", null, null }, /* registered name */
0747:                            { "en_BE_PREEURO", "en_BE", "currency", "BEF" },
0748:                            { "en_IE_PREEURO", "en_IE", "currency", "IEP" },
0749:                            { "es__TRADITIONAL", "es", "collation",
0750:                                    "traditional" },
0751:                            { "es_ES_PREEURO", "es_ES", "currency", "ESP" },
0752:                            { "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
0753:                            { "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
0754:                            { "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
0755:                            { "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
0756:                            { "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
0757:                            { "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
0758:                            { "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
0759:                            { "hi__DIRECT", "hi", "collation", "direct" },
0760:                            { "it_IT_PREEURO", "it_IT", "currency", "ITL" },
0761:                            { "ja_JP_TRADITIONAL", "ja_JP", "calendar",
0762:                                    "japanese" },
0763:                            //              { "nb_NO_NY",       "nn_NO", null, null },
0764:                            { "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
0765:                            { "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
0766:                            { "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
0767:                            { "sl_ROZAJ", "sl__ROZAJ", null, null }, /* registered name */
0768:                            { "sr_SP_CYRL", "sr_Cyrl_CS", null, null }, /* .NET name */
0769:                            { "sr_SP_LATN", "sr_Latn_CS", null, null }, /* .NET name */
0770:                            { "sr_YU_CYRILLIC", "sr_Cyrl_CS", null, null }, /* Linux name */
0771:                            { "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", null, null }, /* Linux name */
0772:                            { "uz_UZ_CYRL", "uz_Cyrl_UZ", null, null }, /* .NET name */
0773:                            { "uz_UZ_LATN", "uz_Latn_UZ", null, null }, /* .NET name */
0774:                            { "zh_CHS", "zh_Hans", null, null }, /* .NET name */
0775:                            { "zh_CHT", "zh_Hant", null, null }, /* .NET name */
0776:                            { "zh_GAN", "zh__GAN", null, null }, /* registered name */
0777:                            { "zh_GUOYU", "zh", null, null }, /* registered name */
0778:                            { "zh_HAKKA", "zh__HAKKA", null, null }, /* registered name */
0779:                            { "zh_MIN", "zh__MIN", null, null }, /* registered name */
0780:                            { "zh_MIN_NAN", "zh__MINNAN", null, null }, /* registered name */
0781:                            { "zh_WUU", "zh__WUU", null, null }, /* registered name */
0782:                            { "zh_XIANG", "zh__XIANG", null, null }, /* registered name */
0783:                            { "zh_YUE", "zh__YUE", null, null }, /* registered name */
0784:                            { "th_TH_TRADITIONAL", "th_TH", "calendar",
0785:                                    "buddhist" },
0786:                            { "zh_TW_STROKE", "zh_TW", "collation", "stroke" },
0787:                            { "zh__PINYIN", "zh", "collation", "pinyin" } };
0788:
0789:                    synchronized (ULocale.class) {
0790:                        if (_variantsToKeywords == null) {
0791:                            _variantsToKeywords = tempVariantsToKeywords;
0792:                        }
0793:                    }
0794:                }
0795:            }
0796:
0797:            /**
0798:             * Private constructor used by static initializers.
0799:             */
0800:            private ULocale(String localeID, Locale locale) {
0801:                this .localeID = localeID;
0802:                this .locale = locale;
0803:            }
0804:
0805:            /**
0806:             * Construct a ULocale object from a {@link java.util.Locale}.
0807:             * @param loc a JDK locale
0808:             * @stable ICU 2.8
0809:             * @internal
0810:             */
0811:            private ULocale(Locale loc) {
0812:                this .localeID = getName(loc.toString());
0813:                this .locale = loc;
0814:            }
0815:
0816:            /**
0817:             * Return a ULocale object for a {@link java.util.Locale}.
0818:             * The ULocale is canonicalized.
0819:             * @param loc a JDK locale
0820:             * @stable ICU 3.2
0821:             */
0822:            public static ULocale forLocale(Locale loc) {
0823:                if (loc == null) {
0824:                    return null;
0825:                }
0826:                if (loc.toString().length() == 0) {
0827:                    return ROOT;
0828:                }
0829:                ULocale result = (ULocale) CACHE.get(loc);
0830:                if (result == null && defaultULocale != null
0831:                        && loc == defaultULocale.locale) {
0832:                    result = defaultULocale;
0833:                } else {
0834:                    result = new ULocale(loc.toString(), loc);
0835:                }
0836:                return result;
0837:            }
0838:
0839:            /**
0840:             * Construct a ULocale from a RFC 3066 locale ID. The locale ID consists
0841:             * of optional language, script, country, and variant fields in that order, 
0842:             * separated by underscores, followed by an optional keyword list.  The
0843:             * script, if present, is four characters long-- this distinguishes it
0844:             * from a country code, which is two characters long.  Other fields
0845:             * are distinguished by position as indicated by the underscores.  The
0846:             * start of the keyword list is indicated by '@', and consists of one
0847:             * or more keyword/value pairs separated by commas.
0848:             * <p>
0849:             * This constructor does not canonicalize the localeID.
0850:             * 
0851:             * @param localeID string representation of the locale, e.g:
0852:             * "en_US", "sy_Cyrl_YU", "zh__pinyin", "es_ES@currency=EUR,collation=traditional"
0853:             * @stable ICU 2.8
0854:             */
0855:            public ULocale(String localeID) {
0856:                this .localeID = getName(localeID);
0857:            }
0858:
0859:            /**
0860:             * Convenience overload of ULocale(String, String, String) for 
0861:             * compatibility with java.util.Locale.
0862:             * @see #ULocale(String, String, String)
0863:             * @stable ICU 3.4
0864:             */
0865:            public ULocale(String a, String b) {
0866:                this (a, b, null);
0867:            }
0868:
0869:            /**
0870:             * Construct a ULocale from a localeID constructed from the three 'fields' a, b, and c.  These
0871:             * fields are concatenated using underscores to form a localeID of
0872:             * the form a_b_c, which is then handled like the localeID passed
0873:             * to <code>ULocale(String localeID)</code>.  
0874:             *
0875:             * <p>Java locale strings consisting of language, country, and
0876:             * variant will be handled by this form, since the country code
0877:             * (being shorter than four letters long) will not be interpreted
0878:             * as a script code.  If a script code is present, the final
0879:             * argument ('c') will be interpreted as the country code.  It is
0880:             * recommended that this constructor only be used to ease porting,
0881:             * and that clients instead use the single-argument constructor
0882:             * when constructing a ULocale from a localeID.
0883:             * @param a first component of the locale id
0884:             * @param b second component of the locale id
0885:             * @param c third component of the locale id
0886:             * @see #ULocale(String)
0887:             * @stable ICU 3.0 
0888:             */
0889:            public ULocale(String a, String b, String c) {
0890:                localeID = getName(lscvToID(a, b, c, EMPTY_STRING));
0891:            }
0892:
0893:            /**
0894:             * Create a ULocale from the id by first canonicalizing the id.
0895:             * @param nonCanonicalID the locale id to canonicalize
0896:             * @return the locale created from the canonical version of the ID.
0897:             * @stable ICU 3.0
0898:             */
0899:            public static ULocale createCanonical(String nonCanonicalID) {
0900:                return new ULocale(canonicalize(nonCanonicalID), (Locale) null);
0901:            }
0902:
0903:            private static String lscvToID(String lang, String script,
0904:                    String country, String variant) {
0905:                StringBuffer buf = new StringBuffer();
0906:
0907:                if (lang != null && lang.length() > 0) {
0908:                    buf.append(lang);
0909:                }
0910:                if (script != null && script.length() > 0) {
0911:                    buf.append(UNDERSCORE);
0912:                    buf.append(script);
0913:                }
0914:                if (country != null && country.length() > 0) {
0915:                    buf.append(UNDERSCORE);
0916:                    buf.append(country);
0917:                }
0918:                if (variant != null && variant.length() > 0) {
0919:                    if (country == null || country.length() == 0) {
0920:                        buf.append(UNDERSCORE);
0921:                    }
0922:                    buf.append(UNDERSCORE);
0923:                    buf.append(variant);
0924:                }
0925:                return buf.toString();
0926:            }
0927:
0928:            /**
0929:             * Convert this ULocale object to a {@link java.util.Locale}.
0930:             * @return a JDK locale that either exactly represents this object
0931:             * or is the closest approximation.
0932:             * @stable ICU 2.8
0933:             */
0934:            public Locale toLocale() {
0935:                if (locale == null) {
0936:                    String[] names = new IDParser(localeID)
0937:                            .getLanguageScriptCountryVariant();
0938:                    locale = new Locale(names[0], names[2], names[3]);
0939:                }
0940:                return locale;
0941:            }
0942:
0943:            /**
0944:             * Keep our own default ULocale.
0945:             */
0946:            private static ULocale defaultULocale;
0947:
0948:            /**
0949:             * Returns the current default ULocale.
0950:             * @stable ICU 2.8
0951:             */
0952:            public static ULocale getDefault() {
0953:                synchronized (ULocale.class) {
0954:                    Locale defaultLocale = Locale.getDefault();
0955:                    if (defaultULocale == null
0956:                            || defaultULocale.toLocale() != defaultLocale) {
0957:                        defaultULocale = new ULocale(defaultLocale);
0958:                    }
0959:                    return defaultULocale;
0960:                }
0961:            }
0962:
0963:            /**
0964:             * Sets the default ULocale.  This also sets the default Locale.
0965:             * If the caller does not have write permission to the
0966:             * user.language property, a security exception will be thrown,
0967:             * and the default ULocale will remain unchanged.
0968:             * @param newLocale the new default locale
0969:             * @throws SecurityException
0970:             *        if a security manager exists and its
0971:             *        <code>checkPermission</code> method doesn't allow the operation.
0972:             * @throws NullPointerException if <code>newLocale</code> is null
0973:             * @see SecurityManager#checkPermission
0974:             * @see java.util.PropertyPermission
0975:             * @stable ICU 3.0 
0976:             */
0977:            public static synchronized void setDefault(ULocale newLocale) {
0978:                Locale.setDefault(newLocale.toLocale());
0979:                defaultULocale = newLocale;
0980:            }
0981:
0982:            /**
0983:             * This is for compatibility with Locale-- in actuality, since ULocale is
0984:             * immutable, there is no reason to clone it, so this API returns 'this'.
0985:             * @stable ICU 3.0
0986:             */
0987:            public Object clone() {
0988:                return this ;
0989:            }
0990:
0991:            /**
0992:             * Returns the hashCode.
0993:             * @stable ICU 3.0
0994:             */
0995:            public int hashCode() {
0996:                return localeID.hashCode();
0997:            }
0998:
0999:            /**
1000:             * Returns true if the other object is another ULocale with the
1001:             * same full name, or is a String localeID that matches the full name.
1002:             * Note that since names are not canonicalized, two ULocales that
1003:             * function identically might not compare equal.
1004:             *
1005:             * @return true if this Locale is equal to the specified object.
1006:             * @stable ICU 3.0 
1007:             */
1008:            public boolean equals(Object obj) {
1009:                if (this  == obj) {
1010:                    return true;
1011:                }
1012:                if (obj instanceof  String) {
1013:                    return localeID.equals((String) obj);
1014:                }
1015:                if (obj instanceof  ULocale) {
1016:                    return localeID.equals(((ULocale) obj).localeID);
1017:                }
1018:                return false;
1019:            }
1020:
1021:            /**
1022:             * Returns a list of all installed locales.
1023:             * @stable ICU 3.0
1024:             */
1025:            public static ULocale[] getAvailableLocales() {
1026:                return ICUResourceBundle.getAvailableULocales();
1027:            }
1028:
1029:            /**
1030:             * Returns a list of all 2-letter country codes defined in ISO 3166.
1031:             * Can be used to create Locales.
1032:             * @stable ICU 3.0
1033:             */
1034:            public static String[] getISOCountries() {
1035:                initCountryTables();
1036:                return (String[]) _countries.clone();
1037:            }
1038:
1039:            /**
1040:             * Returns a list of all 2-letter language codes defined in ISO 639.
1041:             * Can be used to create Locales.
1042:             * [NOTE:  ISO 639 is not a stable standard-- some languages' codes have changed.
1043:             * The list this function returns includes both the new and the old codes for the
1044:             * languages whose codes have changed.]
1045:             * @stable ICU 3.0
1046:             */
1047:            public static String[] getISOLanguages() {
1048:                initLanguageTables();
1049:                return (String[]) _languages.clone();
1050:            }
1051:
1052:            /**
1053:             * Returns the language code for this locale, which will either be the empty string
1054:             * or a lowercase ISO 639 code.
1055:             * @see #getDisplayLanguage
1056:             * @stable ICU 3.0
1057:             */
1058:            public String getLanguage() {
1059:                return getLanguage(localeID);
1060:            }
1061:
1062:            /**
1063:             * Returns the language code for the locale ID,
1064:             * which will either be the empty string
1065:             * or a lowercase ISO 639 code.
1066:             * @see #getDisplayLanguage
1067:             * @stable ICU 3.0
1068:             */
1069:            public static String getLanguage(String localeID) {
1070:                return new IDParser(localeID).getLanguage();
1071:            }
1072:
1073:            /**
1074:             * Returns the script code for this locale, which might be the empty string.
1075:             * @see #getDisplayScript
1076:             * @stable ICU 3.0
1077:             */
1078:            public String getScript() {
1079:                return getScript(localeID);
1080:            }
1081:
1082:            /**
1083:             * Returns the script code for the specified locale, which might be the empty string.
1084:             * @see #getDisplayScript
1085:             * @stable ICU 3.0
1086:             */
1087:            public static String getScript(String localeID) {
1088:                return new IDParser(localeID).getScript();
1089:            }
1090:
1091:            /**
1092:             * Returns the country/region code for this locale, which will either be the empty string
1093:             * or an uppercase ISO 3166 2-letter code.
1094:             * @see #getDisplayCountry
1095:             * @stable ICU 3.0
1096:             */
1097:            public String getCountry() {
1098:                return getCountry(localeID);
1099:            }
1100:
1101:            /**
1102:             * Returns the country/region code for this locale, which will either be the empty string
1103:             * or an uppercase ISO 3166 2-letter code.
1104:             * @param localeID
1105:             * @see #getDisplayCountry
1106:             * @stable ICU 3.0
1107:             */
1108:            public static String getCountry(String localeID) {
1109:                return new IDParser(localeID).getCountry();
1110:            }
1111:
1112:            /**
1113:             * Returns the variant code for this locale, which might be the empty string.
1114:             * @see #getDisplayVariant
1115:             * @stable ICU 3.0
1116:             */
1117:            public String getVariant() {
1118:                return getVariant(localeID);
1119:            }
1120:
1121:            /**
1122:             * Returns the variant code for the specified locale, which might be the empty string.
1123:             * @see #getDisplayVariant
1124:             * @stable ICU 3.0
1125:             */
1126:            public static String getVariant(String localeID) {
1127:                return new IDParser(localeID).getVariant();
1128:            }
1129:
1130:            /**
1131:             * Returns the fallback locale for the specified locale, which might be the empty string.
1132:             * @stable ICU 3.2
1133:             */
1134:            public static String getFallback(String localeID) {
1135:                return getFallbackString(getName(localeID));
1136:            }
1137:
1138:            /**
1139:             * Returns the fallback locale for this locale.  If this locale is root, returns null.
1140:             * @stable ICU 3.2
1141:             */
1142:            public ULocale getFallback() {
1143:                if (localeID.length() == 0 || localeID.charAt(0) == '@') {
1144:                    return null;
1145:                }
1146:                return new ULocale(getFallbackString(localeID), (Locale) null);
1147:            }
1148:
1149:            /**
1150:             * Return the given (canonical) locale id minus the last part before the tags.
1151:             */
1152:            private static String getFallbackString(String fallback) {
1153:                int limit = fallback.indexOf('@');
1154:                if (limit == -1) {
1155:                    limit = fallback.length();
1156:                }
1157:                int start = fallback.lastIndexOf('_', limit);
1158:                if (start == -1) {
1159:                    start = 0;
1160:                }
1161:                return fallback.substring(0, start) + fallback.substring(limit);
1162:            }
1163:
1164:            /**
1165:             * Returns the (normalized) base name for this locale.
1166:             * @return the base name as a String.
1167:             * @stable ICU 3.0
1168:             */
1169:            public String getBaseName() {
1170:                return getBaseName(localeID);
1171:            }
1172:
1173:            /**
1174:             * Returns the (normalized) base name for the specified locale.
1175:             * @param localeID the locale ID as a string
1176:             * @return the base name as a String.
1177:             * @stable ICU 3.0
1178:             */
1179:            public static String getBaseName(String localeID) {
1180:                if (localeID.indexOf('@') == -1) {
1181:                    return localeID;
1182:                }
1183:                return new IDParser(localeID).getBaseName();
1184:            }
1185:
1186:            /**
1187:             * Returns the (normalized) full name for this locale.
1188:             *
1189:             * @return String the full name of the localeID
1190:             * @stable ICU 3.0
1191:             */
1192:            public String getName() {
1193:                return localeID; // always normalized
1194:            }
1195:
1196:            /**
1197:             * Returns the (normalized) full name for the specified locale.
1198:             *
1199:             * @param localeID the localeID as a string
1200:             * @return String the full name of the localeID
1201:             * @stable ICU 3.0
1202:             */
1203:            public static String getName(String localeID) {
1204:                Map cache = (Map) nameCacheRef.get();
1205:                if (cache == null) {
1206:                    cache = Collections.synchronizedMap(new HashMap());
1207:                    nameCacheRef = new SoftReference(cache);
1208:                }
1209:                String name = (String) cache.get(localeID);
1210:                if (name == null) {
1211:                    name = new IDParser(localeID).getName();
1212:                    cache.put(localeID, name);
1213:                }
1214:                return name;
1215:            }
1216:
1217:            private static SoftReference nameCacheRef = new SoftReference(
1218:                    Collections.synchronizedMap(new HashMap()));
1219:
1220:            /**
1221:             * Returns a string representation of this object.
1222:             * @stable ICU 3.0
1223:             */
1224:            public String toString() {
1225:                return localeID;
1226:            }
1227:
1228:            /**
1229:             * Returns an iterator over keywords for this locale.  If there 
1230:             * are no keywords, returns null.
1231:             * @return iterator over keywords, or null if there are no keywords.
1232:             * @stable ICU 3.0
1233:             */
1234:            public Iterator getKeywords() {
1235:                return getKeywords(localeID);
1236:            }
1237:
1238:            /**
1239:             * Returns an iterator over keywords for the specified locale.  If there 
1240:             * are no keywords, returns null.
1241:             * @return an iterator over the keywords in the specified locale, or null
1242:             * if there are no keywords.
1243:             * @stable ICU 3.0
1244:             */
1245:            public static Iterator getKeywords(String localeID) {
1246:                return new IDParser(localeID).getKeywords();
1247:            }
1248:
1249:            /**
1250:             * Returns the value for a keyword in this locale. If the keyword is not defined, returns null.
1251:             * @param keywordName name of the keyword whose value is desired. Case insensitive.
1252:             * @return the value of the keyword, or null.
1253:             * @stable ICU 3.0
1254:             */
1255:            public String getKeywordValue(String keywordName) {
1256:                return getKeywordValue(localeID, keywordName);
1257:            }
1258:
1259:            /**
1260:             * Returns the value for a keyword in the specified locale. If the keyword is not defined, returns null. 
1261:             * The locale name does not need to be normalized.
1262:             * @param keywordName name of the keyword whose value is desired. Case insensitive.
1263:             * @return String the value of the keyword as a string
1264:             * @stable ICU 3.0
1265:             */
1266:            public static String getKeywordValue(String localeID,
1267:                    String keywordName) {
1268:                return new IDParser(localeID).getKeywordValue(keywordName);
1269:            }
1270:
1271:            /**
1272:             * Utility class to parse and normalize locale ids (including POSIX style)
1273:             */
1274:            private static final class IDParser {
1275:                private char[] id;
1276:                private int index;
1277:                private char[] buffer;
1278:                private int blen;
1279:                // um, don't handle POSIX ids unless we request it.  why not?  well... because.
1280:                private boolean canonicalize;
1281:                private boolean hadCountry;
1282:
1283:                // used when canonicalizing
1284:                Map keywords;
1285:                String baseName;
1286:
1287:                /**
1288:                 * Parsing constants.
1289:                 */
1290:                private static final char KEYWORD_SEPARATOR = '@';
1291:                private static final char HYPHEN = '-';
1292:                private static final char KEYWORD_ASSIGN = '=';
1293:                private static final char COMMA = ',';
1294:                private static final char ITEM_SEPARATOR = ';';
1295:                private static final char DOT = '.';
1296:
1297:                private IDParser(String localeID) {
1298:                    this (localeID, false);
1299:                }
1300:
1301:                private IDParser(String localeID, boolean canonicalize) {
1302:                    id = localeID.toCharArray();
1303:                    index = 0;
1304:                    buffer = new char[id.length + 5];
1305:                    blen = 0;
1306:                    this .canonicalize = canonicalize;
1307:                }
1308:
1309:                private void reset() {
1310:                    index = blen = 0;
1311:                }
1312:
1313:                // utilities for working on text in the buffer
1314:
1315:                /**
1316:                 * Append c to the buffer.
1317:                 */
1318:                private void append(char c) {
1319:                    try {
1320:                        buffer[blen] = c;
1321:                    } catch (IndexOutOfBoundsException e) {
1322:                        if (buffer.length > 512) {
1323:                            // something is seriously wrong, let this go
1324:                            throw e;
1325:                        }
1326:                        char[] nbuffer = new char[buffer.length * 2];
1327:                        System.arraycopy(buffer, 0, nbuffer, 0, buffer.length);
1328:                        nbuffer[blen] = c;
1329:                        buffer = nbuffer;
1330:                    }
1331:                    ++blen;
1332:                }
1333:
1334:                private void addSeparator() {
1335:                    append(UNDERSCORE);
1336:                }
1337:
1338:                /**
1339:                 * Returns the text in the buffer from start to blen as a String.
1340:                 */
1341:                private String getString(int start) {
1342:                    if (start == blen) {
1343:                        return EMPTY_STRING;
1344:                    }
1345:                    return new String(buffer, start, blen - start);
1346:                }
1347:
1348:                /**
1349:                 * Set the length of the buffer to pos, then append the string.
1350:                 */
1351:                private void set(int pos, String s) {
1352:                    this .blen = pos; // no safety
1353:                    append(s);
1354:                }
1355:
1356:                /**
1357:                 * Append the string to the buffer.
1358:                 */
1359:                private void append(String s) {
1360:                    for (int i = 0; i < s.length(); ++i) {
1361:                        append(s.charAt(i));
1362:                    }
1363:                }
1364:
1365:                // utilities for parsing text out of the id
1366:
1367:                /**
1368:                 * Character to indicate no more text is available in the id.
1369:                 */
1370:                private static final char DONE = '\uffff';
1371:
1372:                /**
1373:                 * Returns the character at index in the id, and advance index.  The returned character
1374:                 * is DONE if index was at the limit of the buffer.  The index is advanced regardless
1375:                 * so that decrementing the index will always 'unget' the last character returned.
1376:                 */
1377:                private char next() {
1378:                    if (index == id.length) {
1379:                        index++;
1380:                        return DONE;
1381:                    }
1382:
1383:                    return id[index++];
1384:                }
1385:
1386:                /**
1387:                 * Advance index until the next terminator or id separator, and leave it there.
1388:                 */
1389:                private void skipUntilTerminatorOrIDSeparator() {
1390:                    while (!isTerminatorOrIDSeparator(next())) {
1391:                    }
1392:                    --index;
1393:                }
1394:
1395:                /**
1396:                 * Returns true if the character at index in the id is a terminator.
1397:                 */
1398:                private boolean atTerminator() {
1399:                    return index >= id.length || isTerminator(id[index]);
1400:                }
1401:
1402:                /**
1403:                 * Returns true if the character is an id separator (underscore or hyphen).
1404:                 */
1405:                private boolean isIDSeparator(char c) {
1406:                    return c == UNDERSCORE || c == HYPHEN;
1407:                }
1408:
1409:                /**
1410:                 * Returns true if the character is a terminator (keyword separator, dot, or DONE).
1411:                 * Dot is a terminator because of the POSIX form, where dot precedes the codepage.
1412:                 */
1413:                private boolean isTerminator(char c) {
1414:                    // always terminate at DOT, even if not handling POSIX.  It's an error...
1415:                    return c == KEYWORD_SEPARATOR || c == DONE || c == DOT;
1416:                }
1417:
1418:                /**
1419:                 * Returns true if the character is a terminator or id separator.
1420:                 */
1421:                private boolean isTerminatorOrIDSeparator(char c) {
1422:                    return c == KEYWORD_SEPARATOR || c == UNDERSCORE
1423:                            || c == HYPHEN || c == DONE || c == DOT;
1424:                }
1425:
1426:                /**
1427:                 * Returns true if the start of the buffer has an experimental or private language 
1428:                 * prefix, the pattern '[ixIX][-_].' shows the syntax checked.
1429:                 */
1430:                private boolean haveExperimentalLanguagePrefix() {
1431:                    if (id.length > 2) {
1432:                        char c = id[1];
1433:                        if (c == HYPHEN || c == UNDERSCORE) {
1434:                            c = id[0];
1435:                            return c == 'x' || c == 'X' || c == 'i' || c == 'I';
1436:                        }
1437:                    }
1438:                    return false;
1439:                }
1440:
1441:                /**
1442:                 * Returns true if a value separator occurs at or after index.
1443:                 */
1444:                private boolean haveKeywordAssign() {
1445:                    // assume it is safe to start from index
1446:                    for (int i = index; i < id.length; ++i) {
1447:                        if (id[i] == KEYWORD_ASSIGN) {
1448:                            return true;
1449:                        }
1450:                    }
1451:                    return false;
1452:                }
1453:
1454:                /**
1455:                 * Advance index past language, and accumulate normalized language code in buffer.
1456:                 * Index must be at 0 when this is called.  Index is left at a terminator or id 
1457:                 * separator.  Returns the start of the language code in the buffer.
1458:                 */
1459:                private int parseLanguage() {
1460:                    if (haveExperimentalLanguagePrefix()) {
1461:                        append(Character.toLowerCase(id[0]));
1462:                        append(HYPHEN);
1463:                        index = 2;
1464:                    }
1465:
1466:                    char c;
1467:                    while (!isTerminatorOrIDSeparator(c = next())) {
1468:                        append(Character.toLowerCase(c));
1469:                    }
1470:                    --index; // unget
1471:
1472:                    if (blen == 3) {
1473:                        initLanguageTables();
1474:
1475:                        /* convert 3 character code to 2 character code if possible *CWB*/
1476:                        String lang = getString(0);
1477:                        int offset = findIndex(_languages3, lang);
1478:                        if (offset >= 0) {
1479:                            set(0, _languages[offset]);
1480:                        } else {
1481:                            offset = findIndex(_obsoleteLanguages3, lang);
1482:                            if (offset >= 0) {
1483:                                set(0, _obsoleteLanguages[offset]);
1484:                            }
1485:                        }
1486:                    }
1487:
1488:                    return 0;
1489:                }
1490:
1491:                /**
1492:                 * Advance index past language.  Index must be at 0 when this is called.  Index
1493:                 * is left at a terminator or id separator.
1494:                 */
1495:                private void skipLanguage() {
1496:                    if (haveExperimentalLanguagePrefix()) {
1497:                        index = 2;
1498:                    }
1499:                    skipUntilTerminatorOrIDSeparator();
1500:                }
1501:
1502:                /**
1503:                 * Advance index past script, and accumulate normalized script in buffer.
1504:                 * Index must be immediately after the language.
1505:                 * If the item at this position is not a script (is not four characters
1506:                 * long) leave index and buffer unchanged.  Otherwise index is left at
1507:                 * a terminator or id separator.  Returns the start of the script code
1508:                 * in the buffer (this may be equal to the buffer length, if there is no
1509:                 * script).
1510:                 */
1511:                private int parseScript() {
1512:                    if (!atTerminator()) {
1513:                        int oldIndex = index; // save original index
1514:                        ++index;
1515:
1516:                        int oldBlen = blen; // get before append hyphen, if we truncate everything is undone
1517:                        char c;
1518:                        while (!isTerminatorOrIDSeparator(c = next())) {
1519:                            if (blen == oldBlen) { // first pass
1520:                                addSeparator();
1521:                                append(Character.toUpperCase(c));
1522:                            } else {
1523:                                append(Character.toLowerCase(c));
1524:                            }
1525:                        }
1526:                        --index; // unget
1527:
1528:                        /* If it's not exactly 4 characters long, then it's not a script. */
1529:                        if (index - oldIndex != 5) { // +1 to account for separator
1530:                            index = oldIndex;
1531:                            blen = oldBlen;
1532:                        } else {
1533:                            oldBlen++; // index past hyphen, for clients who want to extract just the script
1534:                        }
1535:
1536:                        return oldBlen;
1537:                    }
1538:                    return blen;
1539:                }
1540:
1541:                /**
1542:                 * Advance index past script.
1543:                 * Index must be immediately after the language and IDSeparator.
1544:                 * If the item at this position is not a script (is not four characters
1545:                 * long) leave index.  Otherwise index is left at a terminator or
1546:                 * id separator.
1547:                 */
1548:                private void skipScript() {
1549:                    if (!atTerminator()) {
1550:                        int oldIndex = index;
1551:                        ++index;
1552:
1553:                        skipUntilTerminatorOrIDSeparator();
1554:                        if (index - oldIndex != 5) { // +1 to account for separator
1555:                            index = oldIndex;
1556:                        }
1557:                    }
1558:                }
1559:
1560:                /**
1561:                 * Advance index past country, and accumulate normalized country in buffer.
1562:                 * Index must be immediately after the script (if there is one, else language)
1563:                 * and IDSeparator.  Return the start of the country code in the buffer.
1564:                 */
1565:                private int parseCountry() {
1566:                    if (!atTerminator()) {
1567:                        ++index;
1568:
1569:                        int oldBlen = blen;
1570:                        char c;
1571:                        while (!isTerminatorOrIDSeparator(c = next())) {
1572:                            if (oldBlen == blen) { // first, add hyphen
1573:                                hadCountry = true; // we have a country, let variant parsing know
1574:                                addSeparator();
1575:                                ++oldBlen; // increment past hyphen
1576:                            }
1577:                            append(Character.toUpperCase(c));
1578:                        }
1579:                        --index; // unget
1580:
1581:                        if (blen - oldBlen == 3) {
1582:                            initCountryTables();
1583:
1584:                            /* convert 3 character code to 2 character code if possible *CWB*/
1585:                            int offset = findIndex(_countries3,
1586:                                    getString(oldBlen));
1587:                            if (offset >= 0) {
1588:                                set(oldBlen, _countries[offset]);
1589:                            } else {
1590:                                offset = findIndex(_obsoleteCountries3,
1591:                                        getString(oldBlen));
1592:                                if (offset >= 0) {
1593:                                    set(oldBlen, _obsoleteCountries[offset]);
1594:                                }
1595:                            }
1596:                        }
1597:
1598:                        return oldBlen;
1599:                    }
1600:
1601:                    return blen;
1602:                }
1603:
1604:                /**
1605:                 * Advance index past country.
1606:                 * Index must be immediately after the script (if there is one, else language)
1607:                 * and IDSeparator.
1608:                 */
1609:                private void skipCountry() {
1610:                    if (!atTerminator()) {
1611:                        ++index;
1612:                        skipUntilTerminatorOrIDSeparator();
1613:                    }
1614:                }
1615:
1616:                /**
1617:                 * Advance index past variant, and accumulate normalized variant in buffer.  This ignores
1618:                 * the codepage information from POSIX ids.  Index must be immediately after the country
1619:                 * or script.  Index is left at the keyword separator or at the end of the text.  Return
1620:                 * the start of the variant code in the buffer.
1621:                 *
1622:                 * In standard form, we can have the following forms:
1623:                 * ll__VVVV
1624:                 * ll_CC_VVVV
1625:                 * ll_Ssss_VVVV
1626:                 * ll_Ssss_CC_VVVV
1627:                 *
1628:                 * This also handles POSIX ids, which can have the following forms (pppp is code page id):
1629:                 * ll_CC.pppp          --> ll_CC
1630:                 * ll_CC.pppp@VVVV     --> ll_CC_VVVV
1631:                 * ll_CC@VVVV          --> ll_CC_VVVV
1632:                 *
1633:                 * We identify this use of '@' in POSIX ids by looking for an '=' following
1634:                 * the '@'.  If there is one, we consider '@' to start a keyword list, instead of
1635:                 * being part of a POSIX id.
1636:                 *
1637:                 * Note:  since it was decided that we want an option to not handle POSIX ids, this
1638:                 * becomes a bit more complex.
1639:                 */
1640:                private int parseVariant() {
1641:                    int oldBlen = blen;
1642:
1643:                    boolean start = true;
1644:                    boolean needSeparator = true;
1645:                    boolean skipping = false;
1646:                    char c;
1647:                    while ((c = next()) != DONE) {
1648:                        if (c == DOT) {
1649:                            start = false;
1650:                            skipping = true;
1651:                        } else if (c == KEYWORD_SEPARATOR) {
1652:                            if (haveKeywordAssign()) {
1653:                                break;
1654:                            }
1655:                            skipping = false;
1656:                            start = false;
1657:                            needSeparator = true; // add another underscore if we have more text
1658:                        } else if (start) {
1659:                            start = false;
1660:                        } else if (!skipping) {
1661:                            if (needSeparator) {
1662:                                boolean incOldBlen = blen == oldBlen; // need to skip separators
1663:                                needSeparator = false;
1664:                                if (incOldBlen && !hadCountry) { // no country, we'll need two
1665:                                    addSeparator();
1666:                                    ++oldBlen; // for sure
1667:                                }
1668:                                addSeparator();
1669:                                if (incOldBlen) { // only for the first separator
1670:                                    ++oldBlen;
1671:                                }
1672:                            }
1673:                            c = Character.toUpperCase(c);
1674:                            if (c == HYPHEN || c == COMMA) {
1675:                                c = UNDERSCORE;
1676:                            }
1677:                            append(c);
1678:                        }
1679:                    }
1680:                    --index; // unget
1681:
1682:                    return oldBlen;
1683:                }
1684:
1685:                // no need for skipvariant, to get the keywords we'll just scan directly for 
1686:                // the keyword separator
1687:
1688:                /**
1689:                 * Returns the normalized language id, or the empty string.
1690:                 */
1691:                public String getLanguage() {
1692:                    reset();
1693:                    return getString(parseLanguage());
1694:                }
1695:
1696:                /**
1697:                 * Returns the normalized script id, or the empty string.
1698:                 */
1699:                public String getScript() {
1700:                    reset();
1701:                    skipLanguage();
1702:                    return getString(parseScript());
1703:                }
1704:
1705:                /**
1706:                 * return the normalized country id, or the empty string.
1707:                 */
1708:                public String getCountry() {
1709:                    reset();
1710:                    skipLanguage();
1711:                    skipScript();
1712:                    return getString(parseCountry());
1713:                }
1714:
1715:                /**
1716:                 * Returns the normalized variant id, or the empty string.
1717:                 */
1718:                public String getVariant() {
1719:                    reset();
1720:                    skipLanguage();
1721:                    skipScript();
1722:                    skipCountry();
1723:                    return getString(parseVariant());
1724:                }
1725:
1726:                /**
1727:                 * Returns the language, script, country, and variant as separate strings.
1728:                 */
1729:                public String[] getLanguageScriptCountryVariant() {
1730:                    reset();
1731:                    return new String[] { getString(parseLanguage()),
1732:                            getString(parseScript()),
1733:                            getString(parseCountry()),
1734:                            getString(parseVariant()) };
1735:                }
1736:
1737:                public void setBaseName(String baseName) {
1738:                    this .baseName = baseName;
1739:                }
1740:
1741:                public void parseBaseName() {
1742:                    if (baseName != null) {
1743:                        set(0, baseName);
1744:                    } else {
1745:                        reset();
1746:                        parseLanguage();
1747:                        parseScript();
1748:                        parseCountry();
1749:                        parseVariant();
1750:
1751:                        // catch unwanted trailing underscore after country if there was no variant
1752:                        if (blen > 1 && buffer[blen - 1] == UNDERSCORE) {
1753:                            --blen;
1754:                        }
1755:                    }
1756:                }
1757:
1758:                /**
1759:                 * Returns the normalized base form of the locale id.  The base
1760:                 * form does not include keywords.
1761:                 */
1762:                public String getBaseName() {
1763:                    if (baseName != null) {
1764:                        return baseName;
1765:                    }
1766:                    parseBaseName();
1767:                    return getString(0);
1768:                }
1769:
1770:                /**
1771:                 * Returns the normalized full form of the locale id.  The full
1772:                 * form includes keywords if they are present.
1773:                 */
1774:                public String getName() {
1775:                    parseBaseName();
1776:                    parseKeywords();
1777:                    return getString(0);
1778:                }
1779:
1780:                // keyword utilities
1781:
1782:                /**
1783:                 * If we have keywords, advance index to the start of the keywords and return true, 
1784:                 * otherwise return false.
1785:                 */
1786:                private boolean setToKeywordStart() {
1787:                    for (int i = index; i < id.length; ++i) {
1788:                        if (id[i] == KEYWORD_SEPARATOR) {
1789:                            if (canonicalize) {
1790:                                for (int j = ++i; j < id.length; ++j) { // increment i past separator for return
1791:                                    if (id[j] == KEYWORD_ASSIGN) {
1792:                                        index = i;
1793:                                        return true;
1794:                                    }
1795:                                }
1796:                            } else {
1797:                                if (++i < id.length) {
1798:                                    index = i;
1799:                                    return true;
1800:                                }
1801:                            }
1802:                            break;
1803:                        }
1804:                    }
1805:                    return false;
1806:                }
1807:
1808:                private static boolean isDoneOrKeywordAssign(char c) {
1809:                    return c == DONE || c == KEYWORD_ASSIGN;
1810:                }
1811:
1812:                private static boolean isDoneOrItemSeparator(char c) {
1813:                    return c == DONE || c == ITEM_SEPARATOR;
1814:                }
1815:
1816:                private String getKeyword() {
1817:                    int start = index;
1818:                    while (!isDoneOrKeywordAssign(next())) {
1819:                    }
1820:                    --index;
1821:                    return new String(id, start, index - start).trim()
1822:                            .toLowerCase();
1823:                }
1824:
1825:                private String getValue() {
1826:                    int start = index;
1827:                    while (!isDoneOrItemSeparator(next())) {
1828:                    }
1829:                    --index;
1830:                    return new String(id, start, index - start).trim(); // leave case alone
1831:                }
1832:
1833:                private Comparator getKeyComparator() {
1834:                    final Comparator comp = new Comparator() {
1835:                        public int compare(Object lhs, Object rhs) {
1836:                            return ((String) lhs).compareTo((String) rhs);
1837:                        }
1838:                    };
1839:                    return comp;
1840:                }
1841:
1842:                /**
1843:                 * Returns a map of the keywords and values, or null if there are none.
1844:                 */
1845:                private Map getKeywordMap() {
1846:                    if (keywords == null) {
1847:                        TreeMap m = null;
1848:                        if (setToKeywordStart()) {
1849:                            // trim spaces and convert to lower case, both keywords and values.
1850:                            do {
1851:                                String key = getKeyword();
1852:                                if (key.length() == 0) {
1853:                                    break;
1854:                                }
1855:                                char c = next();
1856:                                if (c != KEYWORD_ASSIGN) {
1857:                                    // throw new IllegalArgumentException("key '" + key + "' missing a value.");
1858:                                    if (c == DONE) {
1859:                                        break;
1860:                                    } else {
1861:                                        continue;
1862:                                    }
1863:                                }
1864:                                String value = getValue();
1865:                                if (value.length() == 0) {
1866:                                    // throw new IllegalArgumentException("key '" + key + "' missing a value.");
1867:                                    continue;
1868:                                }
1869:                                if (m == null) {
1870:                                    m = new TreeMap(getKeyComparator());
1871:                                } else if (m.containsKey(key)) {
1872:                                    // throw new IllegalArgumentException("key '" + key + "' already has a value.");
1873:                                    continue;
1874:                                }
1875:                                m.put(key, value);
1876:                            } while (next() == ITEM_SEPARATOR);
1877:                        }
1878:                        keywords = m != null ? m : Collections.EMPTY_MAP;
1879:                    }
1880:
1881:                    return keywords;
1882:                }
1883:
1884:                /**
1885:                 * Parse the keywords and return start of the string in the buffer.
1886:                 */
1887:                private int parseKeywords() {
1888:                    int oldBlen = blen;
1889:                    Map m = getKeywordMap();
1890:                    if (!m.isEmpty()) {
1891:                        Iterator iter = m.entrySet().iterator();
1892:                        boolean first = true;
1893:                        while (iter.hasNext()) {
1894:                            append(first ? KEYWORD_SEPARATOR : ITEM_SEPARATOR);
1895:                            first = false;
1896:                            Map.Entry e = (Map.Entry) iter.next();
1897:                            append((String) e.getKey());
1898:                            append(KEYWORD_ASSIGN);
1899:                            append((String) e.getValue());
1900:                        }
1901:                        if (blen != oldBlen) {
1902:                            ++oldBlen;
1903:                        }
1904:                    }
1905:                    return oldBlen;
1906:                }
1907:
1908:                /**
1909:                 * Returns an iterator over the keywords, or null if we have an empty map.
1910:                 */
1911:                public Iterator getKeywords() {
1912:                    Map m = getKeywordMap();
1913:                    return m.isEmpty() ? null : m.keySet().iterator();
1914:                }
1915:
1916:                /**
1917:                 * Returns the value for the named keyword, or null if the keyword is not
1918:                 * present.
1919:                 */
1920:                public String getKeywordValue(String keywordName) {
1921:                    Map m = getKeywordMap();
1922:                    return m.isEmpty() ? null : (String) m.get(keywordName
1923:                            .trim().toLowerCase());
1924:                }
1925:
1926:                /**
1927:                 * Set the keyword value only if it is not already set to something else.
1928:                 */
1929:                public void defaultKeywordValue(String keywordName, String value) {
1930:                    setKeywordValue(keywordName, value, false);
1931:                }
1932:
1933:                /**
1934:                 * Set the value for the named keyword, or unset it if value is null.  If
1935:                 * keywordName itself is null, unset all keywords.  If keywordName is not null,
1936:                 * value must not be null.
1937:                 */
1938:                public void setKeywordValue(String keywordName, String value) {
1939:                    setKeywordValue(keywordName, value, true);
1940:                }
1941:
1942:                /**
1943:                 * Set the value for the named keyword, or unset it if value is null.  If
1944:                 * keywordName itself is null, unset all keywords.  If keywordName is not null,
1945:                 * value must not be null.  If reset is true, ignore any previous value for 
1946:                 * the keyword, otherwise do not change the keyword (including removal of
1947:                 * one or all keywords).
1948:                 */
1949:                private void setKeywordValue(String keywordName, String value,
1950:                        boolean reset) {
1951:                    if (keywordName == null) {
1952:                        if (reset) {
1953:                            // force new map, ignore value
1954:                            keywords = Collections.EMPTY_MAP;
1955:                        }
1956:                    } else {
1957:                        keywordName = keywordName.trim().toLowerCase();
1958:                        if (keywordName.length() == 0) {
1959:                            throw new IllegalArgumentException(
1960:                                    "keyword must not be empty");
1961:                        }
1962:                        if (value != null) {
1963:                            value = value.trim();
1964:                            if (value.length() == 0) {
1965:                                throw new IllegalArgumentException(
1966:                                        "value must not be empty");
1967:                            }
1968:                        }
1969:                        Map m = getKeywordMap();
1970:                        if (m.isEmpty()) { // it is EMPTY_MAP
1971:                            if (value != null) {
1972:                                // force new map
1973:                                keywords = new TreeMap(getKeyComparator());
1974:                                keywords.put(keywordName, value.trim());
1975:                            }
1976:                        } else {
1977:                            if (reset || !m.containsKey(keywordName)) {
1978:                                if (value != null) {
1979:                                    m.put(keywordName, value);
1980:                                } else {
1981:                                    m.remove(keywordName);
1982:                                    if (m.isEmpty()) {
1983:                                        // force new map
1984:                                        keywords = Collections.EMPTY_MAP;
1985:                                    }
1986:                                }
1987:                            }
1988:                        }
1989:                    }
1990:                }
1991:            }
1992:
1993:            /**
1994:             * linear search of the string array. the arrays are unfortunately ordered by the
1995:             * two-letter target code, not the three-letter search code, which seems backwards.
1996:             */
1997:            private static int findIndex(String[] array, String target) {
1998:                for (int i = 0; i < array.length; i++) {
1999:                    if (target.equals(array[i])) {
2000:                        return i;
2001:                    }
2002:                }
2003:                return -1;
2004:            }
2005:
2006:            /**
2007:             * Returns the canonical name for the specified locale ID.  This is used to convert POSIX
2008:             * and other grandfathered IDs to standard ICU form.
2009:             * @param localeID the locale id
2010:             * @return the canonicalized id
2011:             * @stable ICU 3.0
2012:             */
2013:            public static String canonicalize(String localeID) {
2014:                IDParser parser = new IDParser(localeID, true);
2015:                String baseName = parser.getBaseName();
2016:                boolean foundVariant = false;
2017:
2018:                // formerly, we always set to en_US_POSIX if the basename was empty, but
2019:                // now we require that the entire id be empty, so that "@foo=bar"
2020:                // will pass through unchanged.
2021:                // {dlf} I'd rather keep "" unchanged.
2022:                if (localeID.equals("")) {
2023:                    return "";
2024:                    //              return "en_US_POSIX";
2025:                }
2026:
2027:                // we have an ID in the form xx_Yyyy_ZZ_KKKKK
2028:
2029:                initVariantsTable();
2030:
2031:                /* See if this is an already known locale */
2032:                for (int i = 0; i < _variantsToKeywords.length; i++) {
2033:                    if (_variantsToKeywords[i][0].equals(baseName)) {
2034:                        foundVariant = true;
2035:
2036:                        String[] vals = _variantsToKeywords[i];
2037:                        parser.setBaseName(vals[1]);
2038:                        if (vals[2] != null) {
2039:                            parser.defaultKeywordValue(vals[2], vals[3]);
2040:                        }
2041:                        break;
2042:                    }
2043:                }
2044:
2045:                /* convert the Euro variant to appropriate ID */
2046:                if (!foundVariant) {
2047:                    int idx = baseName.indexOf("_EURO");
2048:                    if (idx > -1) {
2049:                        parser.setBaseName(baseName.substring(0, idx));
2050:                        parser.defaultKeywordValue("currency", "EUR");
2051:                    }
2052:                }
2053:
2054:                /* total mondo hack for Norwegian, fortunately the main NY case is handled earlier */
2055:                if (!foundVariant) {
2056:                    if (parser.getLanguage().equals("nb")
2057:                            && parser.getVariant().equals("NY")) {
2058:                        parser.setBaseName(lscvToID("nn", parser.getScript(),
2059:                                parser.getCountry(), null));
2060:                    }
2061:                }
2062:
2063:                return parser.getName();
2064:            }
2065:
2066:            /**
2067:             * Given a keyword and a value, return a new locale with an updated
2068:             * keyword and value.  If keyword is null, this removes all keywords from the locale id.
2069:             * Otherwise, if the value is null, this removes the value for this keyword from the
2070:             * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
2071:             * The keyword and value must not be empty.
2072:             * @param keyword the keyword to add/remove, or null to remove all keywords.
2073:             * @param value the value to add/set, or null to remove this particular keyword.
2074:             * @return the updated locale
2075:             * @stable ICU 3.2
2076:             */
2077:            public ULocale setKeywordValue(String keyword, String value) {
2078:                return new ULocale(setKeywordValue(localeID, keyword, value),
2079:                        (Locale) null);
2080:            }
2081:
2082:            /**
2083:             * Given a locale id, a keyword, and a value, return a new locale id with an updated
2084:             * keyword and value.  If keyword is null, this removes all keywords from the locale id.
2085:             * Otherwise, if the value is null, this removes the value for this keyword from the
2086:             * locale id.  Otherwise, this adds/replaces the value for this keyword in the locale id.
2087:             * The keyword and value must not be empty.
2088:             * @param localeID the locale id to modify
2089:             * @param keyword the keyword to add/remove, or null to remove all keywords.
2090:             * @param value the value to add/set, or null to remove this particular keyword.
2091:             * @return the updated locale id
2092:             * @stable ICU 3.2
2093:             */
2094:            public static String setKeywordValue(String localeID,
2095:                    String keyword, String value) {
2096:                IDParser parser = new IDParser(localeID);
2097:                parser.setKeywordValue(keyword, value);
2098:                return parser.getName();
2099:            }
2100:
2101:            /**
2102:             * Given a locale id, a keyword, and a value, return a new locale id with an updated
2103:             * keyword and value, if the keyword does not already have a value.  The keyword and
2104:             * value must not be null or empty.
2105:             * @param localeID the locale id to modify
2106:             * @param keyword the keyword to add, if not already present
2107:             * @param value the value to add, if not already present
2108:             * @return the updated locale id
2109:             * @internal
2110:             */
2111:            private static String defaultKeywordValue(String localeID,
2112:                    String keyword, String value) {
2113:                IDParser parser = new IDParser(localeID);
2114:                parser.defaultKeywordValue(keyword, value);
2115:                return parser.getName();
2116:            }
2117:
2118:            /**
2119:             * Returns a three-letter abbreviation for this locale's language.  If the locale
2120:             * doesn't specify a language, returns the empty string.  Otherwise, returns
2121:             * a lowercase ISO 639-2/T language code.
2122:             * The ISO 639-2 language codes can be found on-line at
2123:             *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
2124:             * @exception MissingResourceException Throws MissingResourceException if the
2125:             * three-letter language abbreviation is not available for this locale.
2126:             * @stable ICU 3.0
2127:             */
2128:            public String getISO3Language() {
2129:                return getISO3Language(localeID);
2130:            }
2131:
2132:            /**
2133:             * Returns a three-letter abbreviation for this locale's language.  If the locale
2134:             * doesn't specify a language, returns the empty string.  Otherwise, returns
2135:             * a lowercase ISO 639-2/T language code.
2136:             * The ISO 639-2 language codes can be found on-line at
2137:             *   <a href="ftp://dkuug.dk/i18n/iso-639-2.txt"><code>ftp://dkuug.dk/i18n/iso-639-2.txt</code></a>
2138:             * @exception MissingResourceException Throws MissingResourceException if the
2139:             * three-letter language abbreviation is not available for this locale.
2140:             * @stable ICU 3.0
2141:             */
2142:            public static String getISO3Language(String localeID) {
2143:                initLanguageTables();
2144:
2145:                String language = getLanguage(localeID);
2146:                int offset = findIndex(_languages, language);
2147:                if (offset >= 0) {
2148:                    return _languages3[offset];
2149:                } else {
2150:                    offset = findIndex(_obsoleteLanguages, language);
2151:                    if (offset >= 0) {
2152:                        return _obsoleteLanguages3[offset];
2153:                    }
2154:                }
2155:                return EMPTY_STRING;
2156:            }
2157:
2158:            /**
2159:             * Returns a three-letter abbreviation for this locale's country/region.  If the locale
2160:             * doesn't specify a country, returns the empty string.  Otherwise, returns
2161:             * an uppercase ISO 3166 3-letter country code.
2162:             * @exception MissingResourceException Throws MissingResourceException if the
2163:             * three-letter country abbreviation is not available for this locale.
2164:             * @stable ICU 3.0
2165:             */
2166:            public String getISO3Country() {
2167:                return getISO3Country(localeID);
2168:            }
2169:
2170:            /**
2171:             * Returns a three-letter abbreviation for this locale's country/region.  If the locale
2172:             * doesn't specify a country, returns the empty string.  Otherwise, returns
2173:             * an uppercase ISO 3166 3-letter country code.
2174:             * @exception MissingResourceException Throws MissingResourceException if the
2175:             * three-letter country abbreviation is not available for this locale.
2176:             * @stable ICU 3.0
2177:             */
2178:            public static String getISO3Country(String localeID) {
2179:                initCountryTables();
2180:
2181:                String country = getCountry(localeID);
2182:                int offset = findIndex(_countries, country);
2183:                if (offset >= 0) {
2184:                    return _countries3[offset];
2185:                } else {
2186:                    offset = findIndex(_obsoleteCountries, country);
2187:                    if (offset >= 0) {
2188:                        return _obsoleteCountries3[offset];
2189:                    }
2190:                }
2191:                return EMPTY_STRING;
2192:            }
2193:
2194:            // display names
2195:
2196:            /**
2197:             * Utility to fetch locale display data from resource bundle tables.
2198:             */
2199:            private static String getTableString(String tableName,
2200:                    String subtableName, String item, String displayLocaleID) {
2201:                if (item.length() > 0) {
2202:                    try {
2203:                        ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle
2204:                                .getBundleInstance(
2205:                                        ICUResourceBundle.ICU_BASE_NAME,
2206:                                        displayLocaleID);
2207:                        return getTableString(tableName, subtableName, item,
2208:                                bundle);
2209:                    } catch (Exception e) {
2210:                        //              System.out.println("gtsu: " + e.getMessage());
2211:                    }
2212:                }
2213:                return item;
2214:            }
2215:
2216:            /**
2217:             * Utility to fetch locale display data from resource bundle tables.
2218:             */
2219:            private static String getTableString(String tableName,
2220:                    String subtableName, String item, ICUResourceBundle bundle) {
2221:                //      System.out.println("gts table: " + tableName + 
2222:                //                         " subtable: " + subtableName +
2223:                //                         " item: " + item +
2224:                //                         " bundle: " + bundle.getULocale());
2225:                try {
2226:                    for (;;) {
2227:                        // special case currency
2228:                        if ("currency".equals(subtableName)) {
2229:                            ICUResourceBundle table = bundle
2230:                                    .getWithFallback("Currencies");
2231:                            table = table.getWithFallback(item);
2232:                            return table.getString(1);
2233:                        } else {
2234:                            ICUResourceBundle table = bundle
2235:                                    .getWithFallback(tableName);
2236:                            try {
2237:                                if (subtableName != null) {
2238:                                    table = table.getWithFallback(subtableName);
2239:                                }
2240:                                return table.getStringWithFallback(item);
2241:                            } catch (MissingResourceException e) {
2242:
2243:                                if (subtableName == null) {
2244:                                    try {
2245:                                        // may be a deprecated code
2246:                                        String currentName = null;
2247:                                        if (tableName.equals("Countries")) {
2248:                                            currentName = getCurrentCountryID(item);
2249:                                        } else if (tableName
2250:                                                .equals("Languages")) {
2251:                                            currentName = getCurrentLanguageID(item);
2252:                                        }
2253:                                        return table
2254:                                                .getStringWithFallback(currentName);
2255:                                    } catch (MissingResourceException ex) {/* fall through*/
2256:                                    }
2257:                                }
2258:
2259:                                // still can't figure out ?.. try the fallback mechanism
2260:                                String fallbackLocale = table.getWithFallback(
2261:                                        "Fallback").getString();
2262:                                if (fallbackLocale.length() == 0) {
2263:                                    fallbackLocale = "root";
2264:                                }
2265:                                //                      System.out.println("bundle: " + bundle.getULocale() + " fallback: " + fallbackLocale);
2266:                                if (fallbackLocale
2267:                                        .equals(table.getULocale().localeID)) {
2268:                                    return item;
2269:                                }
2270:                                bundle = (ICUResourceBundle) UResourceBundle
2271:                                        .getBundleInstance(
2272:                                                ICUResourceBundle.ICU_BASE_NAME,
2273:                                                fallbackLocale);
2274:                                //                          System.out.println("fallback from " + table.getULocale() + " to " + fallbackLocale + 
2275:                                //                                             ", got bundle " + bundle.getULocale());                      
2276:                            }
2277:                        }
2278:                    }
2279:                } catch (Exception e) {
2280:                    //          System.out.println("gtsi: " + e.getMessage());
2281:                }
2282:                return item;
2283:            }
2284:
2285:            /**
2286:             * Returns this locale's language localized for display in the default locale.
2287:             * @return the localized language name.
2288:             * @stable ICU 3.0
2289:             */
2290:            public String getDisplayLanguage() {
2291:                return getDisplayLanguageInternal(localeID,
2292:                        getDefault().localeID);
2293:            }
2294:
2295:            /**
2296:             * Returns this locale's language localized for display in the provided locale.
2297:             * @param displayLocale the locale in which to display the name.
2298:             * @return the localized language name.
2299:             * @stable ICU 3.0
2300:             */
2301:            public String getDisplayLanguage(ULocale displayLocale) {
2302:                return getDisplayLanguageInternal(localeID,
2303:                        displayLocale.localeID);
2304:            }
2305:
2306:            /**
2307:             * Returns a locale's language 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 language will be displayed
2310:             * @param displayLocaleID the id of the locale in which to display the name.
2311:             * @return the localized language name.
2312:             * @stable ICU 3.0
2313:             */
2314:            public static String getDisplayLanguage(String localeID,
2315:                    String displayLocaleID) {
2316:                return getDisplayLanguageInternal(localeID,
2317:                        getName(displayLocaleID));
2318:            }
2319:
2320:            /**
2321:             * Returns a locale's language localized for display in the provided locale.
2322:             * This is a cover for the ICU4C API.
2323:             * @param localeID the id of the locale whose language will be displayed.
2324:             * @param displayLocale the locale in which to display the name.
2325:             * @return the localized language name.
2326:             * @stable ICU 3.0
2327:             */
2328:            public static String getDisplayLanguage(String localeID,
2329:                    ULocale displayLocale) {
2330:                return getDisplayLanguageInternal(localeID,
2331:                        displayLocale.localeID);
2332:            }
2333:
2334:            static String getCurrentCountryID(String oldID) {
2335:                initCountryTables();
2336:                int offset = findIndex(_deprecatedCountries, oldID);
2337:                if (offset >= 0) {
2338:                    return _replacementCountries[offset];
2339:                }
2340:                return oldID;
2341:            }
2342:
2343:            static String getCurrentLanguageID(String oldID) {
2344:                initLanguageTables();
2345:                int offset = findIndex(_obsoleteLanguages, oldID);
2346:                if (offset >= 0) {
2347:                    return _replacementLanguages[offset];
2348:                }
2349:                return oldID;
2350:            }
2351:
2352:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2353:            private static String getDisplayLanguageInternal(String localeID,
2354:                    String displayLocaleID) {
2355:                return getTableString("Languages", null, new IDParser(localeID)
2356:                        .getLanguage(), displayLocaleID);
2357:            }
2358:
2359:            /**
2360:             * Returns this locale's script localized for display in the default locale.
2361:             * @return the localized script name.
2362:             * @stable ICU 3.0
2363:             */
2364:            public String getDisplayScript() {
2365:                return getDisplayScriptInternal(localeID, getDefault().localeID);
2366:            }
2367:
2368:            /**
2369:             * Returns this locale's script localized for display in the provided locale.
2370:             * @param displayLocale the locale in which to display the name.
2371:             * @return the localized script name.
2372:             * @stable ICU 3.0
2373:             */
2374:            public String getDisplayScript(ULocale displayLocale) {
2375:                return getDisplayScriptInternal(localeID,
2376:                        displayLocale.localeID);
2377:            }
2378:
2379:            /**
2380:             * Returns a locale's script localized for display in the provided locale.
2381:             * This is a cover for the ICU4C API.
2382:             * @param localeID the id of the locale whose script will be displayed
2383:             * @param displayLocaleID the id of the locale in which to display the name.
2384:             * @return the localized script name.
2385:             * @stable ICU 3.0
2386:             */
2387:            public static String getDisplayScript(String localeID,
2388:                    String displayLocaleID) {
2389:                return getDisplayScriptInternal(localeID,
2390:                        getName(displayLocaleID));
2391:            }
2392:
2393:            /**
2394:             * Returns a locale's script localized for display in the provided locale.
2395:             * @param localeID the id of the locale whose script will be displayed.
2396:             * @param displayLocale the locale in which to display the name.
2397:             * @return the localized script name.
2398:             * @stable ICU 3.0
2399:             */
2400:            public static String getDisplayScript(String localeID,
2401:                    ULocale displayLocale) {
2402:                return getDisplayScriptInternal(localeID,
2403:                        displayLocale.localeID);
2404:            }
2405:
2406:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2407:            private static String getDisplayScriptInternal(String localeID,
2408:                    String displayLocaleID) {
2409:                return getTableString("Scripts", null, new IDParser(localeID)
2410:                        .getScript(), displayLocaleID);
2411:            }
2412:
2413:            /**
2414:             * Returns this locale's country localized for display in the default locale.
2415:             * @return the localized country name.
2416:             * @stable ICU 3.0
2417:             */
2418:            public String getDisplayCountry() {
2419:                return getDisplayCountryInternal(localeID,
2420:                        getDefault().localeID);
2421:            }
2422:
2423:            /**
2424:             * Returns this locale's country localized for display in the provided locale.
2425:             * @param displayLocale the locale in which to display the name.
2426:             * @return the localized country name.
2427:             * @stable ICU 3.0
2428:             */
2429:            public String getDisplayCountry(ULocale displayLocale) {
2430:                return getDisplayCountryInternal(localeID,
2431:                        displayLocale.localeID);
2432:            }
2433:
2434:            /**
2435:             * Returns a locale's country localized for display in the provided locale.
2436:             * This is a cover for the ICU4C API.
2437:             * @param localeID the id of the locale whose country will be displayed
2438:             * @param displayLocaleID the id of the locale in which to display the name.
2439:             * @return the localized country name.
2440:             * @stable ICU 3.0
2441:             */
2442:            public static String getDisplayCountry(String localeID,
2443:                    String displayLocaleID) {
2444:                return getDisplayCountryInternal(localeID,
2445:                        getName(displayLocaleID));
2446:            }
2447:
2448:            /**
2449:             * Returns a locale's country localized for display in the provided locale.
2450:             * This is a cover for the ICU4C API.
2451:             * @param localeID the id of the locale whose country will be displayed.
2452:             * @param displayLocale the locale in which to display the name.
2453:             * @return the localized country name.
2454:             * @stable ICU 3.0
2455:             */
2456:            public static String getDisplayCountry(String localeID,
2457:                    ULocale displayLocale) {
2458:                return getDisplayCountryInternal(localeID,
2459:                        displayLocale.localeID);
2460:            }
2461:
2462:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2463:            private static String getDisplayCountryInternal(String localeID,
2464:                    String displayLocaleID) {
2465:                return getTableString("Countries", null, new IDParser(localeID)
2466:                        .getCountry(), displayLocaleID);
2467:            }
2468:
2469:            /**
2470:             * Returns this locale's variant localized for display in the default locale.
2471:             * @return the localized variant name.
2472:             * @stable ICU 3.0
2473:             */
2474:            public String getDisplayVariant() {
2475:                return getDisplayVariantInternal(localeID,
2476:                        getDefault().localeID);
2477:            }
2478:
2479:            /**
2480:             * Returns this locale's variant localized for display in the provided locale.
2481:             * @param displayLocale the locale in which to display the name.
2482:             * @return the localized variant name.
2483:             * @stable ICU 3.0
2484:             */
2485:            public String getDisplayVariant(ULocale displayLocale) {
2486:                return getDisplayVariantInternal(localeID,
2487:                        displayLocale.localeID);
2488:            }
2489:
2490:            /**
2491:             * Returns a locale's variant localized for display in the provided locale.
2492:             * This is a cover for the ICU4C API.
2493:             * @param localeID the id of the locale whose variant will be displayed
2494:             * @param displayLocaleID the id of the locale in which to display the name.
2495:             * @return the localized variant name.
2496:             * @stable ICU 3.0
2497:             */
2498:            public static String getDisplayVariant(String localeID,
2499:                    String displayLocaleID) {
2500:                return getDisplayVariantInternal(localeID,
2501:                        getName(displayLocaleID));
2502:            }
2503:
2504:            /**
2505:             * Returns a locale's variant localized for display in the provided locale.
2506:             * This is a cover for the ICU4C API.
2507:             * @param localeID the id of the locale whose variant will be displayed.
2508:             * @param displayLocale the locale in which to display the name.
2509:             * @return the localized variant name.
2510:             * @stable ICU 3.0
2511:             */
2512:            public static String getDisplayVariant(String localeID,
2513:                    ULocale displayLocale) {
2514:                return getDisplayVariantInternal(localeID,
2515:                        displayLocale.localeID);
2516:            }
2517:
2518:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2519:            private static String getDisplayVariantInternal(String localeID,
2520:                    String displayLocaleID) {
2521:                return getTableString("Variants", null, new IDParser(localeID)
2522:                        .getVariant(), displayLocaleID);
2523:            }
2524:
2525:            /**
2526:             * Returns a keyword localized for display in the default locale.
2527:             * @param keyword the keyword to be displayed.
2528:             * @return the localized keyword name.
2529:             * @see #getKeywords
2530:             * @stable ICU 3.0
2531:             */
2532:            public static String getDisplayKeyword(String keyword) {
2533:                return getDisplayKeywordInternal(keyword, getDefault().localeID);
2534:            }
2535:
2536:            /**
2537:             * Returns a keyword localized for display in the specified locale.
2538:             * @param keyword the keyword to be displayed.
2539:             * @param displayLocaleID the id of the locale in which to display the keyword.
2540:             * @return the localized keyword name.
2541:             * @see #getKeywords
2542:             * @stable ICU 3.0
2543:             */
2544:            public static String getDisplayKeyword(String keyword,
2545:                    String displayLocaleID) {
2546:                return getDisplayKeywordInternal(keyword,
2547:                        getName(displayLocaleID));
2548:            }
2549:
2550:            /**
2551:             * Returns a keyword localized for display in the specified locale.
2552:             * @param keyword the keyword to be displayed.
2553:             * @param displayLocale the locale in which to display the keyword.
2554:             * @return the localized keyword name.
2555:             * @see #getKeywords
2556:             * @stable ICU 3.0
2557:             */
2558:            public static String getDisplayKeyword(String keyword,
2559:                    ULocale displayLocale) {
2560:                return getDisplayKeywordInternal(keyword,
2561:                        displayLocale.localeID);
2562:            }
2563:
2564:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2565:            private static String getDisplayKeywordInternal(String keyword,
2566:                    String displayLocaleID) {
2567:                return getTableString("Keys", null, keyword.trim()
2568:                        .toLowerCase(), displayLocaleID);
2569:            }
2570:
2571:            /**
2572:             * Returns a keyword value localized for display in the default locale.
2573:             * @param keyword the keyword whose value is to be displayed.
2574:             * @return the localized value name.
2575:             * @stable ICU 3.0
2576:             */
2577:            public String getDisplayKeywordValue(String keyword) {
2578:                return getDisplayKeywordValueInternal(localeID, keyword,
2579:                        getDefault().localeID);
2580:            }
2581:
2582:            /**
2583:             * Returns a keyword value localized for display in the specified locale.
2584:             * @param keyword the keyword whose value is to be displayed.
2585:             * @param displayLocale the locale in which to display the value.
2586:             * @return the localized value name.
2587:             * @stable ICU 3.0
2588:             */
2589:            public String getDisplayKeywordValue(String keyword,
2590:                    ULocale displayLocale) {
2591:                return getDisplayKeywordValueInternal(localeID, keyword,
2592:                        displayLocale.localeID);
2593:            }
2594:
2595:            /**
2596:             * Returns a keyword value localized for display in the specified locale.
2597:             * This is a cover for the ICU4C API.
2598:             * @param localeID the id of the locale whose keyword value is to be displayed.
2599:             * @param keyword the keyword whose value is to be displayed.
2600:             * @param displayLocaleID the id of the locale in which to display the value.
2601:             * @return the localized value name.
2602:             * @stable ICU 3.0
2603:             */
2604:            public static String getDisplayKeywordValue(String localeID,
2605:                    String keyword, String displayLocaleID) {
2606:                return getDisplayKeywordValueInternal(localeID, keyword,
2607:                        getName(displayLocaleID));
2608:            }
2609:
2610:            /**
2611:             * Returns a keyword value localized for display in the specified locale.
2612:             * This is a cover for the ICU4C API.
2613:             * @param localeID the id of the locale whose keyword value is to be displayed.
2614:             * @param keyword the keyword whose value is to be displayed.
2615:             * @param displayLocale the id of the locale in which to display the value.
2616:             * @return the localized value name.
2617:             * @stable ICU 3.0
2618:             */
2619:            public static String getDisplayKeywordValue(String localeID,
2620:                    String keyword, ULocale displayLocale) {
2621:                return getDisplayKeywordValueInternal(localeID, keyword,
2622:                        displayLocale.localeID);
2623:            }
2624:
2625:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2626:            private static String getDisplayKeywordValueInternal(
2627:                    String localeID, String keyword, String displayLocaleID) {
2628:                keyword = keyword.trim().toLowerCase();
2629:                String value = new IDParser(localeID).getKeywordValue(keyword);
2630:                return getTableString("Types", keyword, value, displayLocaleID);
2631:            }
2632:
2633:            /**
2634:             * Returns this locale name localized for display in the default locale.
2635:             * @return the localized locale name.
2636:             * @stable ICU 3.0
2637:             */
2638:            public String getDisplayName() {
2639:                return getDisplayNameInternal(localeID, getDefault().localeID);
2640:            }
2641:
2642:            /**
2643:             * Returns this locale name localized for display in the provided locale.
2644:             * @param displayLocale the locale in which to display the locale name.
2645:             * @return the localized locale name.
2646:             * @stable ICU 3.0
2647:             */
2648:            public String getDisplayName(ULocale displayLocale) {
2649:                return getDisplayNameInternal(localeID, displayLocale.localeID);
2650:            }
2651:
2652:            /**
2653:             * Returns the locale ID localized for display in the provided locale.
2654:             * This is a cover for the ICU4C API.
2655:             * @param localeID the locale whose name is to be displayed.
2656:             * @param displayLocaleID the id of the locale in which to display the locale name.
2657:             * @return the localized locale name.
2658:             * @stable ICU 3.0
2659:             */
2660:            public static String getDisplayName(String localeID,
2661:                    String displayLocaleID) {
2662:                return getDisplayNameInternal(localeID,
2663:                        getName(displayLocaleID));
2664:            }
2665:
2666:            /**
2667:             * Returns the locale ID localized for display in the provided locale.
2668:             * This is a cover for the ICU4C API.
2669:             * @param localeID the locale whose name is to be displayed.
2670:             * @param displayLocale the locale in which to display the locale name.
2671:             * @return the localized locale name.
2672:             * @stable ICU 3.0
2673:             */
2674:            public static String getDisplayName(String localeID,
2675:                    ULocale displayLocale) {
2676:                return getDisplayNameInternal(localeID, displayLocale.localeID);
2677:            }
2678:
2679:            // displayLocaleID is canonical, localeID need not be since parsing will fix this.
2680:            private static String getDisplayNameInternal(String localeID,
2681:                    String displayLocaleID) {
2682:                // lang
2683:                // lang (script, country, variant, keyword=value, ...)
2684:                // script, country, variant, keyword=value, ...
2685:
2686:                final String[] tableNames = { "Languages", "Scripts",
2687:                        "Countries", "Variants" };
2688:
2689:                ICUResourceBundle bundle = (ICUResourceBundle) UResourceBundle
2690:                        .getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,
2691:                                displayLocaleID);
2692:
2693:                StringBuffer buf = new StringBuffer();
2694:
2695:                IDParser parser = new IDParser(localeID);
2696:                String[] names = parser.getLanguageScriptCountryVariant();
2697:
2698:                boolean haveLanguage = names[0].length() > 0;
2699:                boolean openParen = false;
2700:                for (int i = 0; i < names.length; ++i) {
2701:                    String name = names[i];
2702:                    if (name.length() > 0) {
2703:                        name = getTableString(tableNames[i], null, name, bundle);
2704:                        if (buf.length() > 0) { // need a separator
2705:                            if (haveLanguage & !openParen) {
2706:                                buf.append(" (");
2707:                                openParen = true;
2708:                            } else {
2709:                                buf.append(", ");
2710:                            }
2711:                        }
2712:                        buf.append(name);
2713:                    }
2714:                }
2715:
2716:                Map m = parser.getKeywordMap();
2717:                if (!m.isEmpty()) {
2718:                    Iterator keys = m.entrySet().iterator();
2719:                    while (keys.hasNext()) {
2720:                        if (buf.length() > 0) {
2721:                            if (haveLanguage & !openParen) {
2722:                                buf.append(" (");
2723:                                openParen = true;
2724:                            } else {
2725:                                buf.append(", ");
2726:                            }
2727:                        }
2728:                        Map.Entry e = (Map.Entry) keys.next();
2729:                        String key = (String) e.getKey();
2730:                        String val = (String) e.getValue();
2731:                        buf.append(getTableString("Keys", null, key, bundle));
2732:                        buf.append("=");
2733:                        buf.append(getTableString("Types", key, val, bundle));
2734:                    }
2735:                }
2736:
2737:                if (openParen) {
2738:                    buf.append(")");
2739:                }
2740:
2741:                return buf.toString();
2742:            }
2743:
2744:            /** 
2745:             * Selector for <tt>getLocale()</tt> indicating the locale of the
2746:             * resource containing the data.  This is always at or above the
2747:             * valid locale.  If the valid locale does not contain the
2748:             * specific data being requested, then the actual locale will be
2749:             * above the valid locale.  If the object was not constructed from
2750:             * locale data, then the valid locale is <i>null</i>.
2751:             *
2752:             * @draft ICU 2.8 (retain)
2753:             * @provisional This API might change or be removed in a future release.
2754:             */
2755:            public static Type ACTUAL_LOCALE = new Type(0);
2756:
2757:            /** 
2758:             * Selector for <tt>getLocale()</tt> indicating the most specific
2759:             * locale for which any data exists.  This is always at or above
2760:             * the requested locale, and at or below the actual locale.  If
2761:             * the requested locale does not correspond to any resource data,
2762:             * then the valid locale will be above the requested locale.  If
2763:             * the object was not constructed from locale data, then the
2764:             * actual locale is <i>null</i>.
2765:             *
2766:             * <p>Note: The valid locale will be returned correctly in ICU
2767:             * 3.0 or later.  In ICU 2.8, it is not returned correctly.
2768:             * @draft ICU 2.8 (retain)
2769:             * @provisional This API might change or be removed in a future release.
2770:             */
2771:            public static Type VALID_LOCALE = new Type(1);
2772:
2773:            /**
2774:             * Opaque selector enum for <tt>getLocale()</tt>.
2775:             * @see com.ibm.icu.util.ULocale
2776:             * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
2777:             * @see com.ibm.icu.util.ULocale#VALID_LOCALE
2778:             * @draft ICU 2.8 (retainAll)
2779:             * @provisional This API might change or be removed in a future release.
2780:             */
2781:            public static final class Type {
2782:                private int localeType;
2783:
2784:                private Type(int type) {
2785:                    localeType = type;
2786:                }
2787:            }
2788:
2789:            /**
2790:             * Based on a HTTP formatted list of acceptable locales, determine an available locale for the user.
2791:             * NullPointerException is thrown if acceptLanguageList or availableLocales is
2792:             * null.  If fallback is non-null, it will contain true if a fallback locale (one
2793:             * not in the acceptLanguageList) was returned.  The value on entry is ignored. 
2794:             * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2795:             * if a ROOT locale was used as a fallback (because nothing else in
2796:             * availableLocales matched).  No ULocale array element should be null; behavior
2797:             * is undefined if this is the case.
2798:             * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2799:             * @param availableLocales list of available locales. One of these will be returned.
2800:             * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2801:             * @return one of the locales from the availableLocales list, or null if none match
2802:             * @draft ICU 3.4
2803:             * @provisional This API might change or be removed in a future release.
2804:             */
2805:
2806:            public static ULocale acceptLanguage(String acceptLanguageList,
2807:                    ULocale[] availableLocales, boolean[] fallback) {
2808:                /**
2809:                 * @internal ICU 3.4
2810:                 */
2811:                class ULocaleAcceptLanguageQ implements  Comparable {
2812:                    private double q;
2813:                    private double serial;
2814:
2815:                    public ULocaleAcceptLanguageQ(double theq, int theserial) {
2816:                        q = theq;
2817:                        serial = theserial;
2818:                    }
2819:
2820:                    public int compareTo(Object o) {
2821:                        ULocaleAcceptLanguageQ other = (ULocaleAcceptLanguageQ) o;
2822:                        if (q > other.q) { // reverse - to sort in descending order
2823:                            return -1;
2824:                        } else if (q < other.q) {
2825:                            return 1;
2826:                        }
2827:                        if (serial < other.serial) {
2828:                            return -1;
2829:                        } else if (serial > other.serial) {
2830:                            return 1;
2831:                        } else {
2832:                            return 0; // same object
2833:                        }
2834:                    }
2835:                }
2836:
2837:                // 1st: parse out the acceptLanguageList into an array
2838:
2839:                TreeMap map = new TreeMap();
2840:
2841:                final int l = acceptLanguageList.length();
2842:                int n;
2843:                int last = -1;
2844:                for (n = 0; n < l; n++) {
2845:                    int itemEnd = acceptLanguageList.indexOf(',', n);
2846:                    if (itemEnd == -1) {
2847:                        itemEnd = l;
2848:                    }
2849:                    int paramEnd = acceptLanguageList.indexOf(';', n);
2850:                    double q = 1.0;
2851:
2852:                    if ((paramEnd != -1) && (paramEnd < itemEnd)) {
2853:                        /* semicolon (;) is closer than end (,) */
2854:                        int t = paramEnd + 1;
2855:                        while (UCharacter.isWhitespace(acceptLanguageList
2856:                                .charAt(t))) {
2857:                            t++;
2858:                        }
2859:                        if (acceptLanguageList.charAt(t) == 'q') {
2860:                            t++;
2861:                        }
2862:                        while (UCharacter.isWhitespace(acceptLanguageList
2863:                                .charAt(t))) {
2864:                            t++;
2865:                        }
2866:                        if (acceptLanguageList.charAt(t) == '=') {
2867:                            t++;
2868:                        }
2869:                        while (UCharacter.isWhitespace(acceptLanguageList
2870:                                .charAt(t))) {
2871:                            t++;
2872:                        }
2873:                        try {
2874:                            String val = acceptLanguageList.substring(t,
2875:                                    itemEnd).trim();
2876:                            q = Double.parseDouble(val);
2877:                        } catch (NumberFormatException nfe) {
2878:                            q = 1.0;
2879:                        }
2880:                    } else {
2881:                        q = 1.0; //default
2882:                        paramEnd = itemEnd;
2883:                    }
2884:
2885:                    String loc = acceptLanguageList.substring(n, paramEnd)
2886:                            .trim();
2887:                    int serial = map.size();
2888:                    ULocaleAcceptLanguageQ entry = new ULocaleAcceptLanguageQ(
2889:                            q, serial);
2890:                    map.put(entry, new ULocale(canonicalize(loc))); // sort in reverse order..   1.0, 0.9, 0.8 .. etc
2891:                    n = itemEnd; // get next item. (n++ will skip over delimiter)
2892:                }
2893:
2894:                // 2. pull out the map 
2895:                ULocale acceptList[] = (ULocale[]) map.values().toArray(
2896:                        new ULocale[map.size()]);
2897:
2898:                // 3. call the real function
2899:                return acceptLanguage(acceptList, availableLocales, fallback);
2900:            }
2901:
2902:            /**
2903:             * Based on a list of acceptable locales, determine an available locale for the user.
2904:             * NullPointerException is thrown if acceptLanguageList or availableLocales is
2905:             * null.  If fallback is non-null, it will contain true if a fallback locale (one
2906:             * not in the acceptLanguageList) was returned.  The value on entry is ignored. 
2907:             * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2908:             * if a ROOT locale was used as a fallback (because nothing else in
2909:             * availableLocales matched).  No ULocale array element should be null; behavior
2910:             * is undefined if this is the case.
2911:             * @param acceptLanguageList list of acceptable locales
2912:             * @param availableLocales list of available locales. One of these will be returned.
2913:             * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2914:             * @return one of the locales from the availableLocales list, or null if none match
2915:             * @draft ICU 3.4
2916:             * @provisional This API might change or be removed in a future release.
2917:             */
2918:
2919:            public static ULocale acceptLanguage(ULocale[] acceptLanguageList,
2920:                    ULocale[] availableLocales, boolean[] fallback) {
2921:                // fallbacklist
2922:                int i, j;
2923:                if (fallback != null) {
2924:                    fallback[0] = true;
2925:                }
2926:                for (i = 0; i < acceptLanguageList.length; i++) {
2927:                    ULocale aLocale = acceptLanguageList[i];
2928:                    boolean[] setFallback = fallback;
2929:                    do {
2930:                        for (j = 0; j < availableLocales.length; j++) {
2931:                            if (availableLocales[j].equals(aLocale)) {
2932:                                if (setFallback != null) {
2933:                                    setFallback[0] = false; // first time with this locale - not a fallback.
2934:                                }
2935:                                return availableLocales[j];
2936:                            }
2937:                        }
2938:                        Locale loc = aLocale.toLocale();
2939:                        Locale parent = LocaleUtility.fallback(loc);
2940:                        if (parent != null) {
2941:                            aLocale = new ULocale(parent);
2942:                        } else {
2943:                            aLocale = null;
2944:                        }
2945:                        setFallback = null; // Do not set fallback in later iterations
2946:                    } while (aLocale != null);
2947:                }
2948:                return null;
2949:            }
2950:
2951:            /**
2952:             * Based on a HTTP formatted list of acceptable locales, determine an available locale for the user.
2953:             * NullPointerException is thrown if acceptLanguageList or availableLocales is
2954:             * null.  If fallback is non-null, it will contain true if a fallback locale (one
2955:             * not in the acceptLanguageList) was returned.  The value on entry is ignored. 
2956:             * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2957:             * if a ROOT locale was used as a fallback (because nothing else in
2958:             * availableLocales matched).  No ULocale array element should be null; behavior
2959:             * is undefined if this is the case.
2960:             * This function will choose a locale from the ULocale.getAvailableLocales() list as available.
2961:             * @param acceptLanguageList list in HTTP "Accept-Language:" format of acceptable locales
2962:             * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2963:             * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
2964:             * @draft ICU 3.4
2965:             * @provisional This API might change or be removed in a future release.
2966:             */
2967:
2968:            public static ULocale acceptLanguage(String acceptLanguageList,
2969:                    boolean[] fallback) {
2970:                return acceptLanguage(acceptLanguageList, ULocale
2971:                        .getAvailableLocales(), fallback);
2972:            }
2973:
2974:            /**
2975:             * Based on an ordered array of acceptable locales, determine an available locale for the user.
2976:             * NullPointerException is thrown if acceptLanguageList or availableLocales is
2977:             * null.  If fallback is non-null, it will contain true if a fallback locale (one
2978:             * not in the acceptLanguageList) was returned.  The value on entry is ignored. 
2979:             * ULocale will be one of the locales in availableLocales, or the ROOT ULocale if
2980:             * if a ROOT locale was used as a fallback (because nothing else in
2981:             * availableLocales matched).  No ULocale array element should be null; behavior
2982:             * is undefined if this is the case.
2983:             * This function will choose a locale from the ULocale.getAvailableLocales() list as available.
2984:             * @param acceptLanguageList ordered array of acceptable locales (preferred are listed first)
2985:             * @param fallback if non-null, a 1-element array containing a boolean to be set with the fallback status
2986:             * @return one of the locales from the ULocale.getAvailableLocales() list, or null if none match
2987:             * @draft ICU 3.4
2988:             * @provisional This API might change or be removed in a future release.
2989:             */
2990:
2991:            public static ULocale acceptLanguage(ULocale[] acceptLanguageList,
2992:                    boolean[] fallback) {
2993:                return acceptLanguage(acceptLanguageList, ULocale
2994:                        .getAvailableLocales(), fallback);
2995:            }
2996:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.