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