]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/collate/src/com/ibm/icu/util/GlobalizationPreferences.java
Clean up imports.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / collate / src / com / ibm / icu / util / GlobalizationPreferences.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 2004-2011, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6 */
7 package com.ibm.icu.util;
8
9 import java.text.ParseException;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.BitSet;
13 import java.util.Date;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.MissingResourceException;
18 import java.util.ResourceBundle;
19
20 import com.ibm.icu.impl.Utility;
21 import com.ibm.icu.text.BreakIterator;
22 import com.ibm.icu.text.Collator;
23 import com.ibm.icu.text.DateFormat;
24 import com.ibm.icu.text.NumberFormat;
25 import com.ibm.icu.text.SimpleDateFormat;
26
27 /**
28  * This convenience class provides a mechanism for bundling together different
29  * globalization preferences. It includes:
30  * <ul>
31  * <li>A list of locales/languages in preference order</li>
32  * <li>A territory</li>
33  * <li>A currency</li>
34  * <li>A timezone</li>
35  * <li>A calendar</li>
36  * <li>A collator (for language-sensitive sorting, searching, and matching).</li>
37  * <li>Explicit overrides for date/time formats, etc.</li>
38  * </ul>
39  * The class will heuristically compute implicit, heuristic values for the above
40  * based on available data if explicit values are not supplied. These implicit
41  * values can be presented to users for confirmation, or replacement if the
42  * values are incorrect.
43  * <p>
44  * To reset any explicit field so that it will get heuristic values, pass in
45  * null. For example, myPreferences.setLocale(null);
46  * <p>
47  * All of the heuristics can be customized by subclasses, by overriding
48  * getTerritory(), guessCollator(), etc.
49  * <p>
50  * The class also supplies display names for languages, scripts, territories,
51  * currencies, timezones, etc. These are computed according to the
52  * locale/language preference list. Thus, if the preference is Breton; French;
53  * English, then the display name for a language will be returned in Breton if
54  * available, otherwise in French if available, otherwise in English.
55  * <p>
56  * The codes used to reference territory, currency, etc. are as defined elsewhere
57  * in ICU, and are taken from CLDR (which reflects RFC 3066bis usage, ISO 4217,
58  * and the TZ Timezone database identifiers).
59  * <p>
60  * <b>This is at a prototype stage, and has not incorporated all the design
61  * changes that we would like yet; further feedback is welcome.</b></p>
62  * Note:
63  * <ul>
64  * <li>to get the display name for the first day of the week, use the calendar +
65  * display names.</li>
66  * <li>to get the work days, ask the calendar (when that is available).</li>
67  * <li>to get papersize / measurement system/bidi-orientation, ask the locale
68  * (when that is available there)</li>
69  * <li>to get the field order in a date, and whether a time is 24hour or not,
70  * ask the DateFormat (when that is available there)</li>
71  * <li>it will support HOST locale when it becomes available (it is a special
72  * locale that will ask the services to use the host platform's values).</li>
73  * </ul>
74  *
75  * @draft ICU 3.6 (retainAll)
76  * @provisional This API might change or be removed in a future release.
77  */
78
79 //TODO:
80 // - Add Holidays
81 // - Add convenience to get/take Locale as well as ULocale.
82 // - Add Lenient datetime formatting when that is available.
83 // - Should this be serializable?
84 // - Other utilities?
85
86 public class GlobalizationPreferences implements Freezable<GlobalizationPreferences> {
87     
88     /**
89      * Default constructor
90      * @draft ICU 3.6
91      * @provisional This API might change or be removed in a future release.
92      */
93     public GlobalizationPreferences(){}
94     /**
95      * Number Format type
96      * @draft ICU 3.6
97      * @provisional This API might change or be removed in a future release.
98      */
99     public static final int 
100         NF_NUMBER = 0,      // NumberFormat.NUMBERSTYLE
101         NF_CURRENCY = 1,    // NumberFormat.CURRENCYSTYLE
102         NF_PERCENT = 2,     // NumberFormat.PERCENTSTYLE
103         NF_SCIENTIFIC = 3,  // NumberFormat.SCIENTIFICSTYLE
104         NF_INTEGER = 4;     // NumberFormat.INTEGERSTYLE
105
106     private static final int NF_LIMIT = NF_INTEGER + 1;
107
108     /**
109      * Date Format type
110      * @draft ICU 3.6
111      * @provisional This API might change or be removed in a future release.
112      */
113     public static final int
114         DF_FULL = DateFormat.FULL,      // 0
115         DF_LONG = DateFormat.LONG,      // 1
116         DF_MEDIUM = DateFormat.MEDIUM,  // 2
117         DF_SHORT = DateFormat.SHORT,    // 3
118         DF_NONE = 4;
119
120     private static final int DF_LIMIT = DF_NONE + 1;
121
122     /**
123      * For selecting a choice of display names
124      * @draft ICU 3.6
125      * @provisional This API might change or be removed in a future release.
126      */
127     public static final int
128         ID_LOCALE = 0,
129         ID_LANGUAGE = 1,
130         ID_SCRIPT = 2,
131         ID_TERRITORY = 3,
132         ID_VARIANT = 4, 
133         ID_KEYWORD = 5,
134         ID_KEYWORD_VALUE = 6,
135         ID_CURRENCY = 7,
136         ID_CURRENCY_SYMBOL = 8,
137         ID_TIMEZONE = 9;
138
139     //private static final int ID_LIMIT = ID_TIMEZONE + 1;
140
141     /**
142      * Break iterator type
143      * @draft ICU 3.6
144      * @provisional This API might change or be removed in a future release.
145      */
146     public static final int
147         BI_CHARACTER = BreakIterator.KIND_CHARACTER,    // 0
148         BI_WORD = BreakIterator.KIND_WORD,              // 1
149         BI_LINE = BreakIterator.KIND_LINE,              // 2
150         BI_SENTENCE = BreakIterator.KIND_SENTENCE,      // 3
151         BI_TITLE = BreakIterator.KIND_TITLE;            // 4
152
153     private static final int BI_LIMIT = BI_TITLE + 1;
154
155     /**
156      * Sets the language/locale priority list. If other information is
157      * not (yet) available, this is used to to produce a default value
158      * for the appropriate territory, currency, timezone, etc.  The
159      * user should be given the opportunity to correct those defaults
160      * in case they are incorrect.
161      * 
162      * @param inputLocales list of locales in priority order, eg {"be", "fr"} 
163      *     for Breton first, then French if that fails.
164      * @return this, for chaining
165      * @draft ICU 3.6
166      * @provisional This API might change or be removed in a future release.
167      */
168     public GlobalizationPreferences setLocales(List<ULocale> inputLocales) {
169         if (isFrozen()) {
170             throw new UnsupportedOperationException("Attempt to modify immutable object");
171         }
172         locales = processLocales(inputLocales);
173         return this;
174     }
175
176     /**
177      * Get a copy of the language/locale priority list
178      * 
179      * @return a copy of the language/locale priority list.
180      * @draft ICU 3.6
181      * @provisional This API might change or be removed in a future release.
182      */
183     public List<ULocale> getLocales() {
184         List<ULocale> result;
185         if (locales == null) {
186             result = guessLocales();
187         } else {
188             result = new ArrayList<ULocale>(); 
189             result.addAll(locales);
190         }
191         return result;
192     }
193
194     /**
195      * Convenience function for getting the locales in priority order
196      * @param index The index (0..n) of the desired item.
197      * @return desired item. null if index is out of range
198      * @draft ICU 3.6
199      * @provisional This API might change or be removed in a future release.
200      */
201     public ULocale getLocale(int index) {
202         List<ULocale> lcls = locales;
203         if (lcls == null) {
204             lcls = guessLocales();
205         }
206         if (index >= 0 && index < lcls.size()) {
207             return lcls.get(index);
208         }
209         return null;
210     }
211
212     /**
213      * Convenience routine for setting the language/locale priority
214      * list from an array.
215      * 
216      * @see #setLocales(List locales)
217      * @param uLocales list of locales in an array
218      * @return this, for chaining
219      * @draft ICU 3.6
220      * @provisional This API might change or be removed in a future release.
221      */
222     public GlobalizationPreferences setLocales(ULocale[] uLocales) {
223         if (isFrozen()) {
224             throw new UnsupportedOperationException("Attempt to modify immutable object");
225         }
226         return setLocales(Arrays.asList(uLocales));
227     }
228
229     /**
230      * Convenience routine for setting the language/locale priority
231      * list from a single locale/language.
232      * 
233      * @see #setLocales(List locales)
234      * @param uLocale single locale
235      * @return this, for chaining
236      * @draft ICU 3.6
237      * @provisional This API might change or be removed in a future release.
238      */
239     public GlobalizationPreferences setLocale(ULocale uLocale) {
240         if (isFrozen()) {
241             throw new UnsupportedOperationException("Attempt to modify immutable object");
242         }
243         return setLocales(new ULocale[]{uLocale});
244     }
245
246     /**
247      * Convenience routine for setting the locale priority list from
248      * an Accept-Language string.
249      * @see #setLocales(List locales)
250      * @param acceptLanguageString Accept-Language list, as defined by 
251      *     Section 14.4 of the RFC 2616 (HTTP 1.1)
252      * @return this, for chaining
253      * @draft ICU 3.6
254      * @provisional This API might change or be removed in a future release.
255      */
256     public GlobalizationPreferences setLocales(String acceptLanguageString) {
257         if (isFrozen()) {
258             throw new UnsupportedOperationException("Attempt to modify immutable object");
259         }
260         ULocale[] acceptLocales = null;
261         try {
262             acceptLocales = ULocale.parseAcceptLanguage(acceptLanguageString, true);
263         } catch (ParseException pe) {
264             //TODO: revisit after 3.8
265             throw new IllegalArgumentException("Invalid Accept-Language string");
266         }
267         return setLocales(acceptLocales);
268     }
269
270     /**
271      * Convenience function to get a ResourceBundle instance using
272      * the specified base name based on the language/locale priority list
273      * stored in this object.
274      *  
275      * @param baseName the base name of the resource bundle, a fully qualified
276      * class name
277      * @return a resource bundle for the given base name and locale based on the
278      * language/locale priority list stored in this object
279      * @draft ICU 3.6
280      * @provisional This API might change or be removed in a future release.
281      */
282     public ResourceBundle getResourceBundle(String baseName) {
283         return getResourceBundle(baseName, null);
284     }
285
286     /**
287      * Convenience function to get a ResourceBundle instance using
288      * the specified base name and class loader based on the language/locale
289      * priority list stored in this object.
290      *  
291      * @param baseName the base name of the resource bundle, a fully qualified
292      * class name
293      * @param loader the class object from which to load the resource bundle
294      * @return a resource bundle for the given base name and locale based on the
295      * language/locale priority list stored in this object
296      * @draft ICU 3.6
297      * @provisional This API might change or be removed in a future release.
298      */
299     public ResourceBundle getResourceBundle(String baseName, ClassLoader loader) {
300         UResourceBundle urb = null;
301         UResourceBundle candidate = null;
302         String actualLocaleName = null;
303         List<ULocale> fallbacks = getLocales();
304         for (int i = 0; i < fallbacks.size(); i++) {
305             String localeName = (fallbacks.get(i)).toString();
306             if (actualLocaleName != null && localeName.equals(actualLocaleName)) {
307                 // Actual locale name in the previous round may exactly matches
308                 // with the next fallback locale
309                 urb = candidate;
310                 break;
311             }
312             try {
313                 if (loader == null) {
314                     candidate = UResourceBundle.getBundleInstance(baseName, localeName);
315                 }
316                 else {
317                     candidate = UResourceBundle.getBundleInstance(baseName, localeName, loader);
318                 }
319                 if (candidate != null) {
320                     actualLocaleName = candidate.getULocale().getName();
321                     if (actualLocaleName.equals(localeName)) {
322                         urb = candidate;
323                         break;
324                     }
325                     if (urb == null) {
326                         // Preserve the available bundle as the last resort
327                         urb = candidate;
328                     }
329                 }
330             } catch (MissingResourceException mre) {
331                 actualLocaleName = null;
332                 continue;
333             }
334         }
335         if (urb == null) {
336             throw new MissingResourceException("Can't find bundle for base name "
337                     + baseName, baseName, "");
338         }
339         return urb;
340     }
341     
342     /**
343      * Sets the territory, which is a valid territory according to for
344      * RFC 3066 (or successor).  If not otherwise set, default
345      * currency and timezone values will be set from this.  The user
346      * should be given the opportunity to correct those defaults in
347      * case they are incorrect.
348      * 
349      * @param territory code
350      * @return this, for chaining
351      * @draft ICU 3.6
352      * @provisional This API might change or be removed in a future release.
353      */
354     public GlobalizationPreferences setTerritory(String territory) {
355         if (isFrozen()) {
356             throw new UnsupportedOperationException("Attempt to modify immutable object");
357         }
358         this.territory = territory; // immutable, so don't need to clone
359         return this;
360     }
361
362     /**
363      * Gets the territory setting. If it wasn't explicitly set, it is
364      * computed from the general locale setting.
365      * 
366      * @return territory code, explicit or implicit.
367      * @draft ICU 3.6
368      * @provisional This API might change or be removed in a future release.
369      */
370     public String getTerritory() {
371         if (territory == null) {
372             return guessTerritory();
373         }
374         return territory; // immutable, so don't need to clone
375     }
376
377     /**
378      * Sets the currency code. If this has not been set, uses default for territory.
379      * 
380      * @param currency Valid ISO 4217 currency code.
381      * @return this, for chaining
382      * @draft ICU 3.6
383      * @provisional This API might change or be removed in a future release.
384      */
385     public GlobalizationPreferences setCurrency(Currency currency) {
386         if (isFrozen()) {
387             throw new UnsupportedOperationException("Attempt to modify immutable object");
388         }
389         this.currency = currency; // immutable, so don't need to clone
390         return this;
391     }
392
393     /**
394      * Get a copy of the currency computed according to the settings.
395      * 
396      * @return currency code, explicit or implicit.
397      * @draft ICU 3.6
398      * @provisional This API might change or be removed in a future release.
399      */
400     public Currency getCurrency() {
401         if (currency == null) {
402             return guessCurrency();
403         }
404         return currency; // immutable, so don't have to clone
405     }
406
407     /**
408      * Sets the calendar. If this has not been set, uses default for territory.
409      * 
410      * @param calendar arbitrary calendar
411      * @return this, for chaining
412      * @draft ICU 3.6
413      * @provisional This API might change or be removed in a future release.
414      */
415     public GlobalizationPreferences setCalendar(Calendar calendar) {
416         if (isFrozen()) {
417             throw new UnsupportedOperationException("Attempt to modify immutable object");
418         }
419         this.calendar = (Calendar) calendar.clone(); // clone for safety
420         return this;
421     }
422
423     /**
424      * Get a copy of the calendar according to the settings. 
425      * 
426      * @return calendar explicit or implicit.
427      * @draft ICU 3.6
428      * @provisional This API might change or be removed in a future release.
429      */
430     public Calendar getCalendar() {
431         if (calendar == null) {
432             return guessCalendar();
433         }
434         Calendar temp = (Calendar) calendar.clone(); // clone for safety
435         temp.setTimeZone(getTimeZone());
436         temp.setTimeInMillis(System.currentTimeMillis());
437         return temp;
438     }
439
440     /**
441      * Sets the timezone ID.  If this has not been set, uses default for territory.
442      * 
443      * @param timezone a valid TZID (see UTS#35).
444      * @return this, for chaining
445      * @draft ICU 3.6
446      * @provisional This API might change or be removed in a future release.
447      */
448     public GlobalizationPreferences setTimeZone(TimeZone timezone) {
449         if (isFrozen()) {
450             throw new UnsupportedOperationException("Attempt to modify immutable object");
451         }
452         this.timezone = (TimeZone) timezone.clone(); // clone for safety;
453         return this;
454     }
455
456     /**
457      * Get the timezone. It was either explicitly set, or is
458      * heuristically computed from other settings.
459      * 
460      * @return timezone, either implicitly or explicitly set
461      * @draft ICU 3.6
462      * @provisional This API might change or be removed in a future release.
463      */
464     public TimeZone getTimeZone() {
465         if (timezone == null) {
466             return guessTimeZone();
467         }
468         return timezone.cloneAsThawed(); // clone for safety
469     }
470
471     /**
472      * Get a copy of the collator according to the settings. 
473      * 
474      * @return collator explicit or implicit.
475      * @draft ICU 3.6
476      * @provisional This API might change or be removed in a future release.
477      */
478     public Collator getCollator() {
479         if (collator == null) {
480             return guessCollator();
481         }
482         try {
483             return (Collator) collator.clone();  // clone for safety
484         } catch (CloneNotSupportedException e) {
485             throw new IllegalStateException("Error in cloning collator");
486         }
487     }
488
489     /**
490      * Explicitly set the collator for this object.
491      * @param collator The collator object to be passed.
492      * @return this, for chaining
493      * @draft ICU 3.6
494      * @provisional This API might change or be removed in a future release.
495      */
496     public GlobalizationPreferences setCollator(Collator collator) {
497         if (isFrozen()) {
498             throw new UnsupportedOperationException("Attempt to modify immutable object");
499         }
500         try {
501             this.collator = (Collator) collator.clone(); // clone for safety         
502         } catch (CloneNotSupportedException e) {
503                 throw new IllegalStateException("Error in cloning collator");
504         }
505         return this;
506     }
507
508     /**
509      * Get a copy of the break iterator for the specified type according to the
510      * settings.
511      * 
512      * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE
513      * @return break iterator explicit or implicit
514      * @draft ICU 3.6
515      * @provisional This API might change or be removed in a future release.
516      */
517     public BreakIterator getBreakIterator(int type) {
518         if (type < BI_CHARACTER || type >= BI_LIMIT) {
519             throw new IllegalArgumentException("Illegal break iterator type");
520         }
521         if (breakIterators == null || breakIterators[type] == null) {
522             return guessBreakIterator(type);
523         }
524         return (BreakIterator) breakIterators[type].clone(); // clone for safety
525     }
526
527     /**
528      * Explicitly set the break iterator for this object.
529      * 
530      * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE
531      * @param iterator a break iterator
532      * @return this, for chaining
533      * @draft ICU 3.6
534      * @provisional This API might change or be removed in a future release.
535      */
536     public GlobalizationPreferences setBreakIterator(int type, BreakIterator iterator) {
537         if (type < BI_CHARACTER || type >= BI_LIMIT) {
538             throw new IllegalArgumentException("Illegal break iterator type");
539         }
540         if (isFrozen()) {
541             throw new UnsupportedOperationException("Attempt to modify immutable object");
542         }
543         if (breakIterators == null)
544             breakIterators = new BreakIterator[BI_LIMIT];
545         breakIterators[type] = (BreakIterator) iterator.clone(); // clone for safety
546         return this;
547     }
548
549     /**
550      * Get the display name for an ID: language, script, territory, currency, timezone...
551      * Uses the language priority list to do so.
552      * 
553      * @param id language code, script code, ...
554      * @param type specifies the type of the ID: ID_LANGUAGE, etc.
555      * @return the display name
556      * @draft ICU 3.6
557      * @provisional This API might change or be removed in a future release.
558      */
559     public String getDisplayName(String id, int type) {
560         String result = id;
561         for (ULocale locale : getLocales()) {
562             if (!isAvailableLocale(locale, TYPE_GENERIC)) {
563                 continue;
564             }
565             switch (type) {
566             case ID_LOCALE:
567                 result = ULocale.getDisplayName(id, locale); 
568                 break;
569             case ID_LANGUAGE:
570                 result = ULocale.getDisplayLanguage(id, locale); 
571                 break;
572             case ID_SCRIPT:
573                 result = ULocale.getDisplayScript("und-" + id, locale); 
574                 break;
575             case ID_TERRITORY:
576                 result = ULocale.getDisplayCountry("und-" + id, locale); 
577                 break;
578             case ID_VARIANT:
579                 // TODO fix variant parsing
580                 result = ULocale.getDisplayVariant("und-QQ-" + id, locale); 
581                 break;
582             case ID_KEYWORD:
583                 result = ULocale.getDisplayKeyword(id, locale); 
584                 break;
585             case ID_KEYWORD_VALUE:
586                 String[] parts = new String[2];
587                 Utility.split(id,'=',parts);
588                 result = ULocale.getDisplayKeywordValue("und@"+id, parts[0], locale);
589                 // TODO fix to tell when successful
590                 if (result.equals(parts[1])) {
591                     continue;
592                 }
593                 break;
594             case ID_CURRENCY_SYMBOL:
595             case ID_CURRENCY:
596                 Currency temp = new Currency(id);
597                 result =temp.getName(locale, type==ID_CURRENCY 
598                                      ? Currency.LONG_NAME 
599                                      : Currency.SYMBOL_NAME, new boolean[1]);
600                 // TODO: have method that doesn't take parameter. Add
601                 // function to determine whether string is choice
602                 // format.  
603                 // TODO: have method that doesn't require us
604                 // to create a currency
605                 break;
606             case ID_TIMEZONE:
607                 SimpleDateFormat dtf = new SimpleDateFormat("vvvv",locale);
608                 dtf.setTimeZone(TimeZone.getFrozenTimeZone(id));
609                 result = dtf.format(new Date());
610                 // TODO, have method that doesn't require us to create a timezone
611                 // fix other hacks
612                 // hack for couldn't match
613                 
614                 boolean isBadStr = false;
615                 // Matcher badTimeZone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher("");
616                 // badtzstr = badTimeZone.reset(result).matches();
617                 String teststr = result;
618                 int sidx = result.indexOf('(');
619                 int eidx = result.indexOf(')');
620                 if (sidx != -1 && eidx != -1 && (eidx - sidx) == 3) {
621                     teststr = result.substring(sidx+1, eidx);
622                 }
623                 if (teststr.length() == 2) {
624                     isBadStr = true;
625                     for (int i = 0; i < 2; i++) {
626                         char c = teststr.charAt(i);
627                         if (c < 'A' || 'Z' < c) {
628                             isBadStr = false;
629                             break;
630                         }
631                     }
632                 }
633                 if (isBadStr) {
634                     continue;
635                 }
636                 break;
637             default:
638                 throw new IllegalArgumentException("Unknown type: " + type);
639             }
640
641             // TODO need better way of seeing if we fell back to root!!
642             // This will not work at all for lots of stuff
643             if (!id.equals(result)) {
644                 return result;
645             }
646         }
647         return result;
648     }
649
650     /**
651      * Set an explicit date format. Overrides the locale priority list for
652      * a particular combination of dateStyle and timeStyle. DF_NONE should
653      * be used if for the style, where only the date or time format individually
654      * is being set.
655      * 
656      * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
657      * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
658      * @param format The date format
659      * @return this, for chaining
660      * @draft ICU 3.6
661      * @provisional This API might change or be removed in a future release.
662      */
663     public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) {
664         if (isFrozen()) {
665             throw new UnsupportedOperationException("Attempt to modify immutable object");
666         }
667         if (dateFormats == null) {
668             dateFormats = new DateFormat[DF_LIMIT][DF_LIMIT];
669         }
670         dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety
671         return this;
672     }
673
674     /**
675      * Gets a date format according to the current settings. If there
676      * is an explicit (non-null) date/time format set, a copy of that
677      * is returned. Otherwise, the language priority list is used.
678      * DF_NONE should be used for the style, where only the date or
679      * time format individually is being gotten.
680      * 
681      * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
682      * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
683      * @return a DateFormat, according to the above description
684      * @draft ICU 3.6
685      * @provisional This API might change or be removed in a future release.
686      */
687     public DateFormat getDateFormat(int dateStyle, int timeStyle) {
688         if (dateStyle == DF_NONE && timeStyle == DF_NONE
689                 || dateStyle < 0 || dateStyle >= DF_LIMIT
690                 || timeStyle < 0 || timeStyle >= DF_LIMIT) {
691             throw new IllegalArgumentException("Illegal date format style arguments");
692         }
693         DateFormat result = null;
694         if (dateFormats != null) {
695             result = dateFormats[dateStyle][timeStyle];
696         }
697         if (result != null) {
698             result = (DateFormat) result.clone(); // clone for safety
699             // Not sure overriding configuration is what we really want...
700             result.setTimeZone(getTimeZone());
701         } else {
702             result = guessDateFormat(dateStyle, timeStyle);
703         }
704         return result;
705     }
706
707     /**
708      * Gets a number format according to the current settings.  If
709      * there is an explicit (non-null) number format set, a copy of
710      * that is returned.  Otherwise, the language priority list is
711      * used.
712      * 
713      * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER
714      * @draft ICU 3.6
715      * @provisional This API might change or be removed in a future release.
716      */
717     public NumberFormat getNumberFormat(int style) {
718         if (style < 0 || style >= NF_LIMIT) {
719             throw new IllegalArgumentException("Illegal number format type");
720         }
721         NumberFormat result = null;
722         if (numberFormats != null) {
723             result = numberFormats[style];
724         }
725         if (result != null) {
726             result = (NumberFormat) result.clone(); // clone for safety (later optimize)
727         } else {
728             result = guessNumberFormat(style);
729         }
730         return result;
731     }
732
733     /**
734      * Sets a number format explicitly. Overrides the general locale settings.
735      * 
736      * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER
737      * @param format The number format
738      * @return this, for chaining
739      * @draft ICU 3.6
740      * @provisional This API might change or be removed in a future release.
741      */
742     public GlobalizationPreferences setNumberFormat(int style, NumberFormat format) {
743         if (isFrozen()) {
744             throw new UnsupportedOperationException("Attempt to modify immutable object");
745         }
746         if (numberFormats == null) {
747             numberFormats = new NumberFormat[NF_LIMIT];
748         }
749         numberFormats[style] = (NumberFormat) format.clone(); // for safety
750         return this;
751     }
752
753     /**
754      * Restore the object to the initial state.
755      * 
756      * @return this, for chaining
757      * @draft ICU 3.6
758      * @provisional This API might change or be removed in a future release.
759      */
760     public GlobalizationPreferences reset() {
761         if (isFrozen()) {
762             throw new UnsupportedOperationException("Attempt to modify immutable object");
763         }
764         locales = null;
765         territory = null;
766         calendar = null;
767         collator = null;
768         breakIterators = null;
769         timezone = null;
770         currency = null;
771         dateFormats = null;
772         numberFormats = null;
773         implicitLocales = null;
774         return this;
775     }
776
777     /**
778      * Process a language/locale priority list specified via <code>setLocales</code>.
779      * The input locale list may be expanded or re-ordered to represent the prioritized
780      * language/locale order actually used by this object by the algorithm explained
781      * below.
782      * <br>
783      * <br>
784      * <b>Step 1</b>: Move later occurrence of more specific locale before earlier
785      * occurrence of less specific locale.
786      * <br>
787      * Before: en, fr_FR, en_US, en_GB
788      * <br>
789      * After: en_US, en_GB, en, fr_FR
790      * <br>
791      * <br>
792      * <b>Step 2</b>: Append a fallback locale to each locale.
793      * <br>
794      * Before: en_US, en_GB, en, fr_FR
795      * <br>
796      * After: en_US, en, en_GB, en, en, fr_FR, fr
797      * <br>
798      * <br>
799      * <b>Step 3</b>: Remove earlier occurrence of duplicated locale entries.
800      * <br>
801      * Before: en_US, en, en_GB, en, en, fr_FR, fr
802      * <br>
803      * After: en_US, en_GB, en, fr_FR, fr
804      * <br> 
805      * <br>
806      * The final locale list is used to produce a default value for the appropriate territory,
807      * currency, timezone, etc.  The list also represents the lookup order used in
808      * <code>getResourceBundle</code> for this object.  A subclass may override this method
809      * to customize the algorithm used for populating the locale list.
810      * 
811      * @param inputLocales The list of input locales
812      * @draft ICU 3.6
813      * @provisional This API might change or be removed in a future release.
814      */
815     protected List<ULocale> processLocales(List<ULocale> inputLocales) {
816         List<ULocale> result = new ArrayList<ULocale>();
817         /*
818          * Step 1: Relocate later occurrence of more specific locale
819          * before earlier occurrence of less specific locale.
820          *
821          * Example:
822          *   Before - en_US, fr_FR, zh, en_US_Boston, zh_TW, zh_Hant, fr_CA
823          *   After  - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
824          */
825         for (int i = 0; i < inputLocales.size(); i++) {
826             ULocale uloc = inputLocales.get(i);
827
828             String language = uloc.getLanguage();
829             String script = uloc.getScript();
830             String country = uloc.getCountry();
831             String variant = uloc.getVariant();
832
833             boolean bInserted = false;
834             for (int j = 0; j < result.size(); j++) {
835                 // Check if this locale is more specific
836                 // than existing locale entries already inserted
837                 // in the destination list
838                 ULocale u = result.get(j);
839                 if (!u.getLanguage().equals(language)) {
840                     continue;
841                 }
842                 String s = u.getScript();
843                 String c = u.getCountry();
844                 String v = u.getVariant();
845                 if (!s.equals(script)) {
846                     if (s.length() == 0 && c.length() == 0 && v.length() == 0) {
847                         result.add(j, uloc);
848                         bInserted = true;
849                         break;
850                     } else if (s.length() == 0 && c.equals(country)) {
851                         // We want to see zh_Hant_HK before zh_HK
852                         result.add(j, uloc);
853                         bInserted = true;
854                         break;                      
855                     } else if (script.length() == 0 && country.length() > 0 && c.length() == 0) {
856                         // We want to see zh_HK before zh_Hant
857                         result.add(j, uloc);
858                         bInserted = true;
859                         break;
860                     }
861                     continue;
862                 }
863                 if (!c.equals(country)) {
864                     if (c.length() == 0 && v.length() == 0) {
865                         result.add(j, uloc);
866                         bInserted = true;
867                         break;
868                     }
869                 }
870                 if (!v.equals(variant) && v.length() == 0) {
871                     result.add(j, uloc);
872                     bInserted = true;
873                     break;
874                 }
875             }
876             if (!bInserted) {
877                 // Add this locale at the end of the list
878                 result.add(uloc);
879             }
880         }
881
882         // TODO: Locale aliases might be resolved here
883         // For example, zh_Hant_TW = zh_TW
884
885         /*
886          * Step 2: Append fallback locales for each entry
887          *
888          * Example:
889          *   Before - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
890          *   After  - en_US_Boston, en_US, en, en_US, en, fr_FR, fr,
891          *            zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr
892          */
893         int index = 0;
894         while (index < result.size()) {
895             ULocale uloc = result.get(index);
896             while (true) {
897                 uloc = uloc.getFallback();
898                 if (uloc.getLanguage().length() == 0) {
899                     break;
900                 }
901                 index++;
902                 result.add(index, uloc);
903             }
904             index++;
905         }
906
907         /*
908          * Step 3: Remove earlier occurrence of duplicated locales
909          * 
910          * Example:
911          *   Before - en_US_Boston, en_US, en, en_US, en, fr_FR, fr,
912          *            zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr
913          *   After  - en_US_Boston, en_US, en, fr_FR, zh_TW, zh_Hant,
914          *            zh, fr_CA, fr
915          */
916         index = 0;
917         while (index < result.size() - 1) {
918             ULocale uloc = result.get(index);
919             boolean bRemoved = false;
920             for (int i = index + 1; i < result.size(); i++) {
921                 if (uloc.equals(result.get(i))) {
922                     // Remove earlier one
923                     result.remove(index);
924                     bRemoved = true;
925                     break;
926                 }
927             }
928             if (!bRemoved) {
929                 index++;
930             }
931         }
932         return result;
933     }
934
935     
936     /**
937      * This function can be overridden by subclasses to use different heuristics.
938      * <b>It MUST return a 'safe' value,
939      * one whose modification will not affect this object.</b>
940      * 
941      * @param dateStyle
942      * @param timeStyle
943      * @draft ICU 3.6
944      * @provisional This API might change or be removed in a future release.
945      */
946     protected DateFormat guessDateFormat(int dateStyle, int timeStyle) {
947         DateFormat result;
948         ULocale dfLocale = getAvailableLocale(TYPE_DATEFORMAT);
949         if (dfLocale == null) {
950             dfLocale = ULocale.ROOT;
951         }
952         if (timeStyle == DF_NONE) {
953             result = DateFormat.getDateInstance(getCalendar(), dateStyle, dfLocale);
954         } else if (dateStyle == DF_NONE) {
955             result = DateFormat.getTimeInstance(getCalendar(), timeStyle, dfLocale);
956         } else {
957             result = DateFormat.getDateTimeInstance(getCalendar(), dateStyle, timeStyle, dfLocale);
958         }
959         return result;
960     }
961
962     /**
963      * This function can be overridden by subclasses to use different heuristics.
964      * <b>It MUST return a 'safe' value,
965      * one whose modification will not affect this object.</b>
966      * 
967      * @param style
968      * @draft ICU 3.6
969      * @provisional This API might change or be removed in a future release.
970      */
971     protected NumberFormat guessNumberFormat(int style) {
972         NumberFormat result;
973         ULocale nfLocale = getAvailableLocale(TYPE_NUMBERFORMAT);
974         if (nfLocale == null) {
975             nfLocale = ULocale.ROOT;
976         }
977         switch (style) {
978         case NF_NUMBER:
979             result = NumberFormat.getInstance(nfLocale);
980             break;
981         case NF_SCIENTIFIC:
982             result = NumberFormat.getScientificInstance(nfLocale);
983             break;
984         case NF_INTEGER:
985             result = NumberFormat.getIntegerInstance(nfLocale);
986             break;
987         case NF_PERCENT:
988             result = NumberFormat.getPercentInstance(nfLocale);
989             break;
990         case NF_CURRENCY:
991             result = NumberFormat.getCurrencyInstance(nfLocale);
992             result.setCurrency(getCurrency());
993             break;
994         default:
995             throw new IllegalArgumentException("Unknown number format style");
996         }
997         return result;
998     }
999
1000     /**
1001      * This function can be overridden by subclasses to use different heuristics.
1002      * 
1003      * @draft ICU 3.6
1004      * @provisional This API might change or be removed in a future release.
1005      */
1006     protected String guessTerritory() {
1007         String result;
1008         // pass through locales to see if there is a territory.
1009         for (ULocale locale : getLocales()) {
1010             result = locale.getCountry();
1011             if (result.length() != 0) {
1012                 return result;
1013             }
1014         }
1015         // if not, guess from the first language tag, or maybe from
1016         // intersection of languages, eg nl + fr => BE
1017         // TODO: fix using real data
1018         // for now, just use fixed values
1019         ULocale firstLocale = getLocale(0);
1020         String language = firstLocale.getLanguage();
1021         String script = firstLocale.getScript();
1022         result = null;
1023         if (script.length() != 0) {
1024             result = language_territory_hack_map.get(language + "_" + script);
1025         }
1026         if (result == null) {
1027             result = language_territory_hack_map.get(language);
1028         }
1029         if (result == null) {
1030             result = "US"; // need *some* default
1031         }
1032         return result;
1033     }
1034
1035     /**
1036      * This function can be overridden by subclasses to use different heuristics
1037      * 
1038      * @draft ICU 3.6
1039      * @provisional This API might change or be removed in a future release.
1040      */
1041     protected Currency guessCurrency() {
1042         return Currency.getInstance(new ULocale("und-" + getTerritory()));
1043     }
1044
1045     /**
1046      * This function can be overridden by subclasses to use different heuristics
1047      * <b>It MUST return a 'safe' value,
1048      * one whose modification will not affect this object.</b>
1049      * 
1050      * @draft ICU 3.6
1051      * @provisional This API might change or be removed in a future release.
1052      */
1053     protected List<ULocale> guessLocales() {
1054         if (implicitLocales == null) {
1055             List<ULocale> result = new ArrayList<ULocale>(1);
1056             result.add(ULocale.getDefault());
1057             implicitLocales = processLocales(result);
1058         }
1059         return implicitLocales;
1060     }
1061
1062     /**
1063      * This function can be overridden by subclasses to use different heuristics.
1064      * <b>It MUST return a 'safe' value,
1065      * one whose modification will not affect this object.</b>
1066      * 
1067      * @draft ICU 3.6
1068      * @provisional This API might change or be removed in a future release.
1069      */
1070     protected Collator guessCollator() {
1071         ULocale collLocale = getAvailableLocale(TYPE_COLLATOR);
1072         if (collLocale == null) {
1073             collLocale = ULocale.ROOT;
1074         }
1075         return Collator.getInstance(collLocale);
1076     }
1077
1078     /**
1079      * This function can be overridden by subclasses to use different heuristics.
1080      * <b>It MUST return a 'safe' value,
1081      * one whose modification will not affect this object.</b>
1082      * 
1083      * @param type
1084      * @draft ICU 3.6
1085      * @provisional This API might change or be removed in a future release.
1086      */
1087     protected BreakIterator guessBreakIterator(int type) {
1088         BreakIterator bitr = null;
1089         ULocale brkLocale = getAvailableLocale(TYPE_BREAKITERATOR);
1090         if (brkLocale == null) {
1091             brkLocale = ULocale.ROOT;
1092         }
1093         switch (type) {
1094         case BI_CHARACTER:
1095             bitr = BreakIterator.getCharacterInstance(brkLocale);
1096             break;
1097         case BI_TITLE:
1098             bitr = BreakIterator.getTitleInstance(brkLocale);
1099             break;
1100         case BI_WORD:
1101             bitr = BreakIterator.getWordInstance(brkLocale);
1102             break;
1103         case BI_LINE:
1104             bitr = BreakIterator.getLineInstance(brkLocale);
1105             break;
1106         case BI_SENTENCE:
1107             bitr = BreakIterator.getSentenceInstance(brkLocale);
1108             break;
1109         default:
1110             throw new IllegalArgumentException("Unknown break iterator type");
1111         }
1112         return bitr;
1113     }
1114
1115     /**
1116      * This function can be overridden by subclasses to use different heuristics.
1117      * <b>It MUST return a 'safe' value,
1118      * one whose modification will not affect this object.</b>
1119      * 
1120      * @draft ICU 3.6
1121      * @provisional This API might change or be removed in a future release.
1122      */
1123     protected TimeZone guessTimeZone() {
1124         // TODO fix using real data
1125         // for single-zone countries, pick that zone
1126         // for others, pick the most populous zone
1127         // for now, just use fixed value
1128         // NOTE: in a few cases can do better by looking at language. 
1129         // Eg haw+US should go to Pacific/Honolulu
1130         // fr+CA should go to America/Montreal
1131         String timezoneString = territory_tzid_hack_map.get(getTerritory());
1132         if (timezoneString == null) {
1133             String[] attempt = TimeZone.getAvailableIDs(getTerritory());
1134             if (attempt.length == 0) {
1135                 timezoneString = "Etc/GMT"; // gotta do something
1136             } else {
1137                 int i;
1138                 // this all needs to be fixed to use real data. But for now, do slightly better by skipping cruft
1139                 for (i = 0; i < attempt.length; ++i) {
1140                     if (attempt[i].indexOf("/") >= 0) break;
1141                 }
1142                 if (i > attempt.length) i = 0;
1143                 timezoneString = attempt[i];
1144             }
1145         }
1146         return TimeZone.getTimeZone(timezoneString);
1147     }
1148
1149     /**
1150      * This function can be overridden by subclasses to use different heuristics.
1151      * <b>It MUST return a 'safe' value,
1152      * one whose modification will not affect this object.</b>
1153      * 
1154      * @draft ICU 3.6
1155      * @provisional This API might change or be removed in a future release.
1156      */
1157     protected Calendar guessCalendar() {
1158         ULocale calLocale = getAvailableLocale(TYPE_CALENDAR);
1159         if (calLocale == null) {
1160             calLocale = ULocale.US;
1161         }
1162         return Calendar.getInstance(getTimeZone(), calLocale);
1163     }
1164     
1165     // PRIVATES
1166     
1167     private List<ULocale> locales;
1168     private String territory;
1169     private Currency currency;
1170     private TimeZone timezone;
1171     private Calendar calendar;
1172     private Collator collator;
1173     private BreakIterator[] breakIterators;
1174     private DateFormat[][] dateFormats;
1175     private NumberFormat[] numberFormats;
1176     private List<ULocale> implicitLocales;
1177     
1178     {
1179         reset();
1180     }
1181
1182
1183     private ULocale getAvailableLocale(int type) {
1184         List<ULocale> locs = getLocales();
1185         ULocale result = null;
1186         for (int i = 0; i < locs.size(); i++) {
1187             ULocale l = locs.get(i);
1188             if (isAvailableLocale(l, type)) {
1189                 result = l;
1190                 break;
1191             }
1192         }
1193         return result;
1194     }
1195
1196     private boolean isAvailableLocale(ULocale loc, int type) {
1197         BitSet bits = available_locales.get(loc);
1198         if (bits != null && bits.get(type)) {
1199             return true;
1200         }
1201         return false;        
1202     }
1203     
1204     /*
1205      * Available locales for service types
1206      */
1207     private static final HashMap<ULocale, BitSet> available_locales = new HashMap<ULocale, BitSet>();
1208     private static final int
1209         TYPE_GENERIC = 0,
1210         TYPE_CALENDAR = 1,
1211         TYPE_DATEFORMAT= 2,
1212         TYPE_NUMBERFORMAT = 3,
1213         TYPE_COLLATOR = 4,
1214         TYPE_BREAKITERATOR = 5,
1215         TYPE_LIMIT = TYPE_BREAKITERATOR + 1;
1216
1217     static {
1218         BitSet bits;
1219         ULocale[] allLocales = ULocale.getAvailableLocales();
1220         for (int i = 0; i < allLocales.length; i++) {
1221             bits = new BitSet(TYPE_LIMIT);
1222             available_locales.put(allLocales[i], bits);
1223             bits.set(TYPE_GENERIC);
1224         }
1225
1226         ULocale[] calLocales = Calendar.getAvailableULocales();
1227         for (int i = 0; i < calLocales.length; i++) {
1228             bits = available_locales.get(calLocales[i]);
1229             if (bits == null) {
1230                 bits = new BitSet(TYPE_LIMIT);
1231                 available_locales.put(allLocales[i], bits);
1232             }
1233             bits.set(TYPE_CALENDAR);
1234         }
1235
1236         ULocale[] dateLocales = DateFormat.getAvailableULocales();
1237         for (int i = 0; i < dateLocales.length; i++) {
1238             bits = available_locales.get(dateLocales[i]);
1239             if (bits == null) {
1240                 bits = new BitSet(TYPE_LIMIT);
1241                 available_locales.put(allLocales[i], bits);
1242             }
1243             bits.set(TYPE_DATEFORMAT);
1244         }
1245
1246         ULocale[] numLocales = NumberFormat.getAvailableULocales();
1247         for (int i = 0; i < numLocales.length; i++) {
1248             bits = available_locales.get(numLocales[i]);
1249             if (bits == null) {
1250                 bits = new BitSet(TYPE_LIMIT);
1251                 available_locales.put(allLocales[i], bits);
1252             }
1253             bits.set(TYPE_NUMBERFORMAT);
1254         }
1255
1256         ULocale[] collLocales = Collator.getAvailableULocales();
1257         for (int i = 0; i < collLocales.length; i++) {
1258             bits = available_locales.get(collLocales[i]);
1259             if (bits == null) {
1260                 bits = new BitSet(TYPE_LIMIT);
1261                 available_locales.put(allLocales[i], bits);
1262             }
1263             bits.set(TYPE_COLLATOR);
1264         }
1265
1266         ULocale[] brkLocales = BreakIterator.getAvailableULocales();
1267         for (int i = 0; i < brkLocales.length; i++) {
1268             bits = available_locales.get(brkLocales[i]);
1269             bits.set(TYPE_BREAKITERATOR);
1270         }
1271     }
1272
1273     /** WARNING: All of this data is temporary, until we start importing from CLDR!!!
1274      * 
1275      */
1276     private static final Map<String, String> language_territory_hack_map = new HashMap<String, String>();
1277     private static final String[][] language_territory_hack = {
1278         {"af", "ZA"},
1279         {"am", "ET"},
1280         {"ar", "SA"},
1281         {"as", "IN"},
1282         {"ay", "PE"},
1283         {"az", "AZ"},
1284         {"bal", "PK"},
1285         {"be", "BY"},
1286         {"bg", "BG"},
1287         {"bn", "IN"},
1288         {"bs", "BA"},
1289         {"ca", "ES"},
1290         {"ch", "MP"},
1291         {"cpe", "SL"},
1292         {"cs", "CZ"},
1293         {"cy", "GB"},
1294         {"da", "DK"},
1295         {"de", "DE"},
1296         {"dv", "MV"},
1297         {"dz", "BT"},
1298         {"el", "GR"},
1299         {"en", "US"},
1300         {"es", "ES"},
1301         {"et", "EE"},
1302         {"eu", "ES"},
1303         {"fa", "IR"},
1304         {"fi", "FI"},
1305         {"fil", "PH"},
1306         {"fj", "FJ"},
1307         {"fo", "FO"},
1308         {"fr", "FR"},
1309         {"ga", "IE"},
1310         {"gd", "GB"},
1311         {"gl", "ES"},
1312         {"gn", "PY"},
1313         {"gu", "IN"},
1314         {"gv", "GB"},
1315         {"ha", "NG"},
1316         {"he", "IL"},
1317         {"hi", "IN"},
1318         {"ho", "PG"},
1319         {"hr", "HR"},
1320         {"ht", "HT"},
1321         {"hu", "HU"},
1322         {"hy", "AM"},
1323         {"id", "ID"},
1324         {"is", "IS"},
1325         {"it", "IT"},
1326         {"ja", "JP"},
1327         {"ka", "GE"},
1328         {"kk", "KZ"},
1329         {"kl", "GL"},
1330         {"km", "KH"},
1331         {"kn", "IN"},
1332         {"ko", "KR"},
1333         {"kok", "IN"},
1334         {"ks", "IN"},
1335         {"ku", "TR"},
1336         {"ky", "KG"},
1337         {"la", "VA"},
1338         {"lb", "LU"},
1339         {"ln", "CG"},
1340         {"lo", "LA"},
1341         {"lt", "LT"},
1342         {"lv", "LV"},
1343         {"mai", "IN"},
1344         {"men", "GN"},
1345         {"mg", "MG"},
1346         {"mh", "MH"},
1347         {"mk", "MK"},
1348         {"ml", "IN"},
1349         {"mn", "MN"},
1350         {"mni", "IN"},
1351         {"mo", "MD"},
1352         {"mr", "IN"},
1353         {"ms", "MY"},
1354         {"mt", "MT"},
1355         {"my", "MM"},
1356         {"na", "NR"},
1357         {"nb", "NO"},
1358         {"nd", "ZA"},
1359         {"ne", "NP"},
1360         {"niu", "NU"},
1361         {"nl", "NL"},
1362         {"nn", "NO"},
1363         {"no", "NO"},
1364         {"nr", "ZA"},
1365         {"nso", "ZA"},
1366         {"ny", "MW"},
1367         {"om", "KE"},
1368         {"or", "IN"},
1369         {"pa", "IN"},
1370         {"pau", "PW"},
1371         {"pl", "PL"},
1372         {"ps", "PK"},
1373         {"pt", "BR"},
1374         {"qu", "PE"},
1375         {"rn", "BI"},
1376         {"ro", "RO"},
1377         {"ru", "RU"},
1378         {"rw", "RW"},
1379         {"sd", "IN"},
1380         {"sg", "CF"},
1381         {"si", "LK"},
1382         {"sk", "SK"},
1383         {"sl", "SI"},
1384         {"sm", "WS"},
1385         {"so", "DJ"},
1386         {"sq", "CS"},
1387         {"sr", "CS"},
1388         {"ss", "ZA"},
1389         {"st", "ZA"},
1390         {"sv", "SE"},
1391         {"sw", "KE"},
1392         {"ta", "IN"},
1393         {"te", "IN"},
1394         {"tem", "SL"},
1395         {"tet", "TL"},
1396         {"th", "TH"},
1397         {"ti", "ET"},
1398         {"tg", "TJ"},
1399         {"tk", "TM"},
1400         {"tkl", "TK"},
1401         {"tvl", "TV"},
1402         {"tl", "PH"},
1403         {"tn", "ZA"},
1404         {"to", "TO"},
1405         {"tpi", "PG"},
1406         {"tr", "TR"},
1407         {"ts", "ZA"},
1408         {"uk", "UA"},
1409         {"ur", "IN"},
1410         {"uz", "UZ"},
1411         {"ve", "ZA"},
1412         {"vi", "VN"},
1413         {"wo", "SN"},
1414         {"xh", "ZA"},
1415         {"zh", "CN"},
1416         {"zh_Hant", "TW"},
1417         {"zu", "ZA"},
1418         {"aa", "ET"},
1419         {"byn", "ER"},
1420         {"eo", "DE"},
1421         {"gez", "ET"},
1422         {"haw", "US"},
1423         {"iu", "CA"},
1424         {"kw", "GB"},
1425         {"sa", "IN"},
1426         {"sh", "HR"},
1427         {"sid", "ET"},
1428         {"syr", "SY"},
1429         {"tig", "ER"},
1430         {"tt", "RU"},
1431         {"wal", "ET"},  };
1432     static {
1433         for (int i = 0; i < language_territory_hack.length; ++i) {
1434             language_territory_hack_map.put(language_territory_hack[i][0],language_territory_hack[i][1]);
1435         }
1436     }
1437
1438     static final Map<String, String> territory_tzid_hack_map = new HashMap<String, String>();
1439     static final String[][] territory_tzid_hack = {
1440         {"AQ", "Antarctica/McMurdo"},
1441         {"AR", "America/Buenos_Aires"},
1442         {"AU", "Australia/Sydney"},
1443         {"BR", "America/Sao_Paulo"},
1444         {"CA", "America/Toronto"},
1445         {"CD", "Africa/Kinshasa"},
1446         {"CL", "America/Santiago"},
1447         {"CN", "Asia/Shanghai"},
1448         {"EC", "America/Guayaquil"},
1449         {"ES", "Europe/Madrid"},
1450         {"GB", "Europe/London"},
1451         {"GL", "America/Godthab"},
1452         {"ID", "Asia/Jakarta"},
1453         {"ML", "Africa/Bamako"},
1454         {"MX", "America/Mexico_City"},
1455         {"MY", "Asia/Kuala_Lumpur"},
1456         {"NZ", "Pacific/Auckland"},
1457         {"PT", "Europe/Lisbon"},
1458         {"RU", "Europe/Moscow"},
1459         {"UA", "Europe/Kiev"},
1460         {"US", "America/New_York"},
1461         {"UZ", "Asia/Tashkent"},
1462         {"PF", "Pacific/Tahiti"},
1463         {"FM", "Pacific/Kosrae"},
1464         {"KI", "Pacific/Tarawa"},
1465         {"KZ", "Asia/Almaty"},
1466         {"MH", "Pacific/Majuro"},
1467         {"MN", "Asia/Ulaanbaatar"},
1468         {"SJ", "Arctic/Longyearbyen"},
1469         {"UM", "Pacific/Midway"},   
1470     };
1471     static {
1472         for (int i = 0; i < territory_tzid_hack.length; ++i) {
1473             territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1]);
1474         }
1475     }
1476
1477     // Freezable implementation
1478     
1479     private boolean frozen;
1480
1481     /**
1482      * @draft ICU 3.6
1483      * @provisional This API might change or be removed in a future release.
1484      */
1485     public boolean isFrozen() {
1486         return frozen;
1487     }
1488
1489     /**
1490      * @draft ICU 4.4
1491      * @provisional This API might change or be removed in a future release.
1492      */
1493     public GlobalizationPreferences freeze() {
1494         frozen = true;
1495         return this;
1496     }
1497
1498     /**
1499      * @draft ICU 4.4
1500      * @provisional This API might change or be removed in a future release.
1501      */
1502     public GlobalizationPreferences cloneAsThawed() {
1503         try {
1504             GlobalizationPreferences result = (GlobalizationPreferences) clone();
1505             result.frozen = false;
1506             return result;
1507         } catch (CloneNotSupportedException e) {
1508             // will always work
1509             return null;
1510         }
1511     }
1512 }
1513