| /* |
| ******************************************************************************* |
| * Copyright (C) 2004-2011, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.util; |
| |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.MissingResourceException; |
| import java.util.ResourceBundle; |
| |
| import com.ibm.icu.impl.Utility; |
| import com.ibm.icu.text.BreakIterator; |
| import com.ibm.icu.text.Collator; |
| import com.ibm.icu.text.DateFormat; |
| import com.ibm.icu.text.NumberFormat; |
| import com.ibm.icu.text.SimpleDateFormat; |
| |
| /** |
| * This convenience class provides a mechanism for bundling together different |
| * globalization preferences. It includes: |
| * <ul> |
| * <li>A list of locales/languages in preference order</li> |
| * <li>A territory</li> |
| * <li>A currency</li> |
| * <li>A timezone</li> |
| * <li>A calendar</li> |
| * <li>A collator (for language-sensitive sorting, searching, and matching).</li> |
| * <li>Explicit overrides for date/time formats, etc.</li> |
| * </ul> |
| * The class will heuristically compute implicit, heuristic values for the above |
| * based on available data if explicit values are not supplied. These implicit |
| * values can be presented to users for confirmation, or replacement if the |
| * values are incorrect. |
| * <p> |
| * To reset any explicit field so that it will get heuristic values, pass in |
| * null. For example, myPreferences.setLocale(null); |
| * <p> |
| * All of the heuristics can be customized by subclasses, by overriding |
| * getTerritory(), guessCollator(), etc. |
| * <p> |
| * The class also supplies display names for languages, scripts, territories, |
| * currencies, timezones, etc. These are computed according to the |
| * locale/language preference list. Thus, if the preference is Breton; French; |
| * English, then the display name for a language will be returned in Breton if |
| * available, otherwise in French if available, otherwise in English. |
| * <p> |
| * The codes used to reference territory, currency, etc. are as defined elsewhere |
| * in ICU, and are taken from CLDR (which reflects RFC 3066bis usage, ISO 4217, |
| * and the TZ Timezone database identifiers). |
| * <p> |
| * <b>This is at a prototype stage, and has not incorporated all the design |
| * changes that we would like yet; further feedback is welcome.</b></p> |
| * Note: |
| * <ul> |
| * <li>to get the display name for the first day of the week, use the calendar + |
| * display names.</li> |
| * <li>to get the work days, ask the calendar (when that is available).</li> |
| * <li>to get papersize / measurement system/bidi-orientation, ask the locale |
| * (when that is available there)</li> |
| * <li>to get the field order in a date, and whether a time is 24hour or not, |
| * ask the DateFormat (when that is available there)</li> |
| * <li>it will support HOST locale when it becomes available (it is a special |
| * locale that will ask the services to use the host platform's values).</li> |
| * </ul> |
| * |
| * @draft ICU 3.6 (retainAll) |
| * @provisional This API might change or be removed in a future release. |
| */ |
| |
| //TODO: |
| // - Add Holidays |
| // - Add convenience to get/take Locale as well as ULocale. |
| // - Add Lenient datetime formatting when that is available. |
| // - Should this be serializable? |
| // - Other utilities? |
| |
| public class GlobalizationPreferences implements Freezable<GlobalizationPreferences> { |
| |
| /** |
| * Default constructor |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences(){} |
| /** |
| * Number Format type |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public static final int |
| NF_NUMBER = 0, // NumberFormat.NUMBERSTYLE |
| NF_CURRENCY = 1, // NumberFormat.CURRENCYSTYLE |
| NF_PERCENT = 2, // NumberFormat.PERCENTSTYLE |
| NF_SCIENTIFIC = 3, // NumberFormat.SCIENTIFICSTYLE |
| NF_INTEGER = 4; // NumberFormat.INTEGERSTYLE |
| |
| private static final int NF_LIMIT = NF_INTEGER + 1; |
| |
| /** |
| * Date Format type |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public static final int |
| DF_FULL = DateFormat.FULL, // 0 |
| DF_LONG = DateFormat.LONG, // 1 |
| DF_MEDIUM = DateFormat.MEDIUM, // 2 |
| DF_SHORT = DateFormat.SHORT, // 3 |
| DF_NONE = 4; |
| |
| private static final int DF_LIMIT = DF_NONE + 1; |
| |
| /** |
| * For selecting a choice of display names |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public static final int |
| ID_LOCALE = 0, |
| ID_LANGUAGE = 1, |
| ID_SCRIPT = 2, |
| ID_TERRITORY = 3, |
| ID_VARIANT = 4, |
| ID_KEYWORD = 5, |
| ID_KEYWORD_VALUE = 6, |
| ID_CURRENCY = 7, |
| ID_CURRENCY_SYMBOL = 8, |
| ID_TIMEZONE = 9; |
| |
| //private static final int ID_LIMIT = ID_TIMEZONE + 1; |
| |
| /** |
| * Break iterator type |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public static final int |
| BI_CHARACTER = BreakIterator.KIND_CHARACTER, // 0 |
| BI_WORD = BreakIterator.KIND_WORD, // 1 |
| BI_LINE = BreakIterator.KIND_LINE, // 2 |
| BI_SENTENCE = BreakIterator.KIND_SENTENCE, // 3 |
| BI_TITLE = BreakIterator.KIND_TITLE; // 4 |
| |
| private static final int BI_LIMIT = BI_TITLE + 1; |
| |
| /** |
| * Sets the language/locale priority list. If other information is |
| * not (yet) available, this is used to to produce a default value |
| * for the appropriate territory, currency, timezone, etc. The |
| * user should be given the opportunity to correct those defaults |
| * in case they are incorrect. |
| * |
| * @param inputLocales list of locales in priority order, eg {"be", "fr"} |
| * for Breton first, then French if that fails. |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setLocales(List<ULocale> inputLocales) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| locales = processLocales(inputLocales); |
| return this; |
| } |
| |
| /** |
| * Get a copy of the language/locale priority list |
| * |
| * @return a copy of the language/locale priority list. |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public List<ULocale> getLocales() { |
| List<ULocale> result; |
| if (locales == null) { |
| result = guessLocales(); |
| } else { |
| result = new ArrayList<ULocale>(); |
| result.addAll(locales); |
| } |
| return result; |
| } |
| |
| /** |
| * Convenience function for getting the locales in priority order |
| * @param index The index (0..n) of the desired item. |
| * @return desired item. null if index is out of range |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public ULocale getLocale(int index) { |
| List<ULocale> lcls = locales; |
| if (lcls == null) { |
| lcls = guessLocales(); |
| } |
| if (index >= 0 && index < lcls.size()) { |
| return lcls.get(index); |
| } |
| return null; |
| } |
| |
| /** |
| * Convenience routine for setting the language/locale priority |
| * list from an array. |
| * |
| * @see #setLocales(List locales) |
| * @param uLocales list of locales in an array |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setLocales(ULocale[] uLocales) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| return setLocales(Arrays.asList(uLocales)); |
| } |
| |
| /** |
| * Convenience routine for setting the language/locale priority |
| * list from a single locale/language. |
| * |
| * @see #setLocales(List locales) |
| * @param uLocale single locale |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setLocale(ULocale uLocale) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| return setLocales(new ULocale[]{uLocale}); |
| } |
| |
| /** |
| * Convenience routine for setting the locale priority list from |
| * an Accept-Language string. |
| * @see #setLocales(List locales) |
| * @param acceptLanguageString Accept-Language list, as defined by |
| * Section 14.4 of the RFC 2616 (HTTP 1.1) |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setLocales(String acceptLanguageString) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| ULocale[] acceptLocales = null; |
| try { |
| acceptLocales = ULocale.parseAcceptLanguage(acceptLanguageString, true); |
| } catch (ParseException pe) { |
| //TODO: revisit after 3.8 |
| throw new IllegalArgumentException("Invalid Accept-Language string"); |
| } |
| return setLocales(acceptLocales); |
| } |
| |
| /** |
| * Convenience function to get a ResourceBundle instance using |
| * the specified base name based on the language/locale priority list |
| * stored in this object. |
| * |
| * @param baseName the base name of the resource bundle, a fully qualified |
| * class name |
| * @return a resource bundle for the given base name and locale based on the |
| * language/locale priority list stored in this object |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public ResourceBundle getResourceBundle(String baseName) { |
| return getResourceBundle(baseName, null); |
| } |
| |
| /** |
| * Convenience function to get a ResourceBundle instance using |
| * the specified base name and class loader based on the language/locale |
| * priority list stored in this object. |
| * |
| * @param baseName the base name of the resource bundle, a fully qualified |
| * class name |
| * @param loader the class object from which to load the resource bundle |
| * @return a resource bundle for the given base name and locale based on the |
| * language/locale priority list stored in this object |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public ResourceBundle getResourceBundle(String baseName, ClassLoader loader) { |
| UResourceBundle urb = null; |
| UResourceBundle candidate = null; |
| String actualLocaleName = null; |
| List<ULocale> fallbacks = getLocales(); |
| for (int i = 0; i < fallbacks.size(); i++) { |
| String localeName = (fallbacks.get(i)).toString(); |
| if (actualLocaleName != null && localeName.equals(actualLocaleName)) { |
| // Actual locale name in the previous round may exactly matches |
| // with the next fallback locale |
| urb = candidate; |
| break; |
| } |
| try { |
| if (loader == null) { |
| candidate = UResourceBundle.getBundleInstance(baseName, localeName); |
| } |
| else { |
| candidate = UResourceBundle.getBundleInstance(baseName, localeName, loader); |
| } |
| if (candidate != null) { |
| actualLocaleName = candidate.getULocale().getName(); |
| if (actualLocaleName.equals(localeName)) { |
| urb = candidate; |
| break; |
| } |
| if (urb == null) { |
| // Preserve the available bundle as the last resort |
| urb = candidate; |
| } |
| } |
| } catch (MissingResourceException mre) { |
| actualLocaleName = null; |
| continue; |
| } |
| } |
| if (urb == null) { |
| throw new MissingResourceException("Can't find bundle for base name " |
| + baseName, baseName, ""); |
| } |
| return urb; |
| } |
| |
| /** |
| * Sets the territory, which is a valid territory according to for |
| * RFC 3066 (or successor). If not otherwise set, default |
| * currency and timezone values will be set from this. The user |
| * should be given the opportunity to correct those defaults in |
| * case they are incorrect. |
| * |
| * @param territory code |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setTerritory(String territory) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| this.territory = territory; // immutable, so don't need to clone |
| return this; |
| } |
| |
| /** |
| * Gets the territory setting. If it wasn't explicitly set, it is |
| * computed from the general locale setting. |
| * |
| * @return territory code, explicit or implicit. |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public String getTerritory() { |
| if (territory == null) { |
| return guessTerritory(); |
| } |
| return territory; // immutable, so don't need to clone |
| } |
| |
| /** |
| * Sets the currency code. If this has not been set, uses default for territory. |
| * |
| * @param currency Valid ISO 4217 currency code. |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setCurrency(Currency currency) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| this.currency = currency; // immutable, so don't need to clone |
| return this; |
| } |
| |
| /** |
| * Get a copy of the currency computed according to the settings. |
| * |
| * @return currency code, explicit or implicit. |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public Currency getCurrency() { |
| if (currency == null) { |
| return guessCurrency(); |
| } |
| return currency; // immutable, so don't have to clone |
| } |
| |
| /** |
| * Sets the calendar. If this has not been set, uses default for territory. |
| * |
| * @param calendar arbitrary calendar |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setCalendar(Calendar calendar) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| this.calendar = (Calendar) calendar.clone(); // clone for safety |
| return this; |
| } |
| |
| /** |
| * Get a copy of the calendar according to the settings. |
| * |
| * @return calendar explicit or implicit. |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public Calendar getCalendar() { |
| if (calendar == null) { |
| return guessCalendar(); |
| } |
| Calendar temp = (Calendar) calendar.clone(); // clone for safety |
| temp.setTimeZone(getTimeZone()); |
| temp.setTimeInMillis(System.currentTimeMillis()); |
| return temp; |
| } |
| |
| /** |
| * Sets the timezone ID. If this has not been set, uses default for territory. |
| * |
| * @param timezone a valid TZID (see UTS#35). |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setTimeZone(TimeZone timezone) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| this.timezone = (TimeZone) timezone.clone(); // clone for safety; |
| return this; |
| } |
| |
| /** |
| * Get the timezone. It was either explicitly set, or is |
| * heuristically computed from other settings. |
| * |
| * @return timezone, either implicitly or explicitly set |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public TimeZone getTimeZone() { |
| if (timezone == null) { |
| return guessTimeZone(); |
| } |
| return timezone.cloneAsThawed(); // clone for safety |
| } |
| |
| /** |
| * Get a copy of the collator according to the settings. |
| * |
| * @return collator explicit or implicit. |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public Collator getCollator() { |
| if (collator == null) { |
| return guessCollator(); |
| } |
| try { |
| return (Collator) collator.clone(); // clone for safety |
| } catch (CloneNotSupportedException e) { |
| throw new IllegalStateException("Error in cloning collator"); |
| } |
| } |
| |
| /** |
| * Explicitly set the collator for this object. |
| * @param collator The collator object to be passed. |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setCollator(Collator collator) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| try { |
| this.collator = (Collator) collator.clone(); // clone for safety |
| } catch (CloneNotSupportedException e) { |
| throw new IllegalStateException("Error in cloning collator"); |
| } |
| return this; |
| } |
| |
| /** |
| * Get a copy of the break iterator for the specified type according to the |
| * settings. |
| * |
| * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE |
| * @return break iterator explicit or implicit |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public BreakIterator getBreakIterator(int type) { |
| if (type < BI_CHARACTER || type >= BI_LIMIT) { |
| throw new IllegalArgumentException("Illegal break iterator type"); |
| } |
| if (breakIterators == null || breakIterators[type] == null) { |
| return guessBreakIterator(type); |
| } |
| return (BreakIterator) breakIterators[type].clone(); // clone for safety |
| } |
| |
| /** |
| * Explicitly set the break iterator for this object. |
| * |
| * @param type break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE |
| * @param iterator a break iterator |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setBreakIterator(int type, BreakIterator iterator) { |
| if (type < BI_CHARACTER || type >= BI_LIMIT) { |
| throw new IllegalArgumentException("Illegal break iterator type"); |
| } |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| if (breakIterators == null) |
| breakIterators = new BreakIterator[BI_LIMIT]; |
| breakIterators[type] = (BreakIterator) iterator.clone(); // clone for safety |
| return this; |
| } |
| |
| /** |
| * Get the display name for an ID: language, script, territory, currency, timezone... |
| * Uses the language priority list to do so. |
| * |
| * @param id language code, script code, ... |
| * @param type specifies the type of the ID: ID_LANGUAGE, etc. |
| * @return the display name |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public String getDisplayName(String id, int type) { |
| String result = id; |
| for (ULocale locale : getLocales()) { |
| if (!isAvailableLocale(locale, TYPE_GENERIC)) { |
| continue; |
| } |
| switch (type) { |
| case ID_LOCALE: |
| result = ULocale.getDisplayName(id, locale); |
| break; |
| case ID_LANGUAGE: |
| result = ULocale.getDisplayLanguage(id, locale); |
| break; |
| case ID_SCRIPT: |
| result = ULocale.getDisplayScript("und-" + id, locale); |
| break; |
| case ID_TERRITORY: |
| result = ULocale.getDisplayCountry("und-" + id, locale); |
| break; |
| case ID_VARIANT: |
| // TODO fix variant parsing |
| result = ULocale.getDisplayVariant("und-QQ-" + id, locale); |
| break; |
| case ID_KEYWORD: |
| result = ULocale.getDisplayKeyword(id, locale); |
| break; |
| case ID_KEYWORD_VALUE: |
| String[] parts = new String[2]; |
| Utility.split(id,'=',parts); |
| result = ULocale.getDisplayKeywordValue("und@"+id, parts[0], locale); |
| // TODO fix to tell when successful |
| if (result.equals(parts[1])) { |
| continue; |
| } |
| break; |
| case ID_CURRENCY_SYMBOL: |
| case ID_CURRENCY: |
| Currency temp = new Currency(id); |
| result =temp.getName(locale, type==ID_CURRENCY |
| ? Currency.LONG_NAME |
| : Currency.SYMBOL_NAME, new boolean[1]); |
| // TODO: have method that doesn't take parameter. Add |
| // function to determine whether string is choice |
| // format. |
| // TODO: have method that doesn't require us |
| // to create a currency |
| break; |
| case ID_TIMEZONE: |
| SimpleDateFormat dtf = new SimpleDateFormat("vvvv",locale); |
| dtf.setTimeZone(TimeZone.getFrozenTimeZone(id)); |
| result = dtf.format(new Date()); |
| // TODO, have method that doesn't require us to create a timezone |
| // fix other hacks |
| // hack for couldn't match |
| |
| boolean isBadStr = false; |
| // Matcher badTimeZone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher(""); |
| // badtzstr = badTimeZone.reset(result).matches(); |
| String teststr = result; |
| int sidx = result.indexOf('('); |
| int eidx = result.indexOf(')'); |
| if (sidx != -1 && eidx != -1 && (eidx - sidx) == 3) { |
| teststr = result.substring(sidx+1, eidx); |
| } |
| if (teststr.length() == 2) { |
| isBadStr = true; |
| for (int i = 0; i < 2; i++) { |
| char c = teststr.charAt(i); |
| if (c < 'A' || 'Z' < c) { |
| isBadStr = false; |
| break; |
| } |
| } |
| } |
| if (isBadStr) { |
| continue; |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown type: " + type); |
| } |
| |
| // TODO need better way of seeing if we fell back to root!! |
| // This will not work at all for lots of stuff |
| if (!id.equals(result)) { |
| return result; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Set an explicit date format. Overrides the locale priority list for |
| * a particular combination of dateStyle and timeStyle. DF_NONE should |
| * be used if for the style, where only the date or time format individually |
| * is being set. |
| * |
| * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE |
| * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE |
| * @param format The date format |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| if (dateFormats == null) { |
| dateFormats = new DateFormat[DF_LIMIT][DF_LIMIT]; |
| } |
| dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety |
| return this; |
| } |
| |
| /** |
| * Gets a date format according to the current settings. If there |
| * is an explicit (non-null) date/time format set, a copy of that |
| * is returned. Otherwise, the language priority list is used. |
| * DF_NONE should be used for the style, where only the date or |
| * time format individually is being gotten. |
| * |
| * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE |
| * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE |
| * @return a DateFormat, according to the above description |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public DateFormat getDateFormat(int dateStyle, int timeStyle) { |
| if (dateStyle == DF_NONE && timeStyle == DF_NONE |
| || dateStyle < 0 || dateStyle >= DF_LIMIT |
| || timeStyle < 0 || timeStyle >= DF_LIMIT) { |
| throw new IllegalArgumentException("Illegal date format style arguments"); |
| } |
| DateFormat result = null; |
| if (dateFormats != null) { |
| result = dateFormats[dateStyle][timeStyle]; |
| } |
| if (result != null) { |
| result = (DateFormat) result.clone(); // clone for safety |
| // Not sure overriding configuration is what we really want... |
| result.setTimeZone(getTimeZone()); |
| } else { |
| result = guessDateFormat(dateStyle, timeStyle); |
| } |
| return result; |
| } |
| |
| /** |
| * Gets a number format according to the current settings. If |
| * there is an explicit (non-null) number format set, a copy of |
| * that is returned. Otherwise, the language priority list is |
| * used. |
| * |
| * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public NumberFormat getNumberFormat(int style) { |
| if (style < 0 || style >= NF_LIMIT) { |
| throw new IllegalArgumentException("Illegal number format type"); |
| } |
| NumberFormat result = null; |
| if (numberFormats != null) { |
| result = numberFormats[style]; |
| } |
| if (result != null) { |
| result = (NumberFormat) result.clone(); // clone for safety (later optimize) |
| } else { |
| result = guessNumberFormat(style); |
| } |
| return result; |
| } |
| |
| /** |
| * Sets a number format explicitly. Overrides the general locale settings. |
| * |
| * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER |
| * @param format The number format |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences setNumberFormat(int style, NumberFormat format) { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| if (numberFormats == null) { |
| numberFormats = new NumberFormat[NF_LIMIT]; |
| } |
| numberFormats[style] = (NumberFormat) format.clone(); // for safety |
| return this; |
| } |
| |
| /** |
| * Restore the object to the initial state. |
| * |
| * @return this, for chaining |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences reset() { |
| if (isFrozen()) { |
| throw new UnsupportedOperationException("Attempt to modify immutable object"); |
| } |
| locales = null; |
| territory = null; |
| calendar = null; |
| collator = null; |
| breakIterators = null; |
| timezone = null; |
| currency = null; |
| dateFormats = null; |
| numberFormats = null; |
| implicitLocales = null; |
| return this; |
| } |
| |
| /** |
| * Process a language/locale priority list specified via <code>setLocales</code>. |
| * The input locale list may be expanded or re-ordered to represent the prioritized |
| * language/locale order actually used by this object by the algorithm explained |
| * below. |
| * <br> |
| * <br> |
| * <b>Step 1</b>: Move later occurrence of more specific locale before earlier |
| * occurrence of less specific locale. |
| * <br> |
| * Before: en, fr_FR, en_US, en_GB |
| * <br> |
| * After: en_US, en_GB, en, fr_FR |
| * <br> |
| * <br> |
| * <b>Step 2</b>: Append a fallback locale to each locale. |
| * <br> |
| * Before: en_US, en_GB, en, fr_FR |
| * <br> |
| * After: en_US, en, en_GB, en, en, fr_FR, fr |
| * <br> |
| * <br> |
| * <b>Step 3</b>: Remove earlier occurrence of duplicated locale entries. |
| * <br> |
| * Before: en_US, en, en_GB, en, en, fr_FR, fr |
| * <br> |
| * After: en_US, en_GB, en, fr_FR, fr |
| * <br> |
| * <br> |
| * The final locale list is used to produce a default value for the appropriate territory, |
| * currency, timezone, etc. The list also represents the lookup order used in |
| * <code>getResourceBundle</code> for this object. A subclass may override this method |
| * to customize the algorithm used for populating the locale list. |
| * |
| * @param inputLocales The list of input locales |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected List<ULocale> processLocales(List<ULocale> inputLocales) { |
| List<ULocale> result = new ArrayList<ULocale>(); |
| /* |
| * Step 1: Relocate later occurrence of more specific locale |
| * before earlier occurrence of less specific locale. |
| * |
| * Example: |
| * Before - en_US, fr_FR, zh, en_US_Boston, zh_TW, zh_Hant, fr_CA |
| * After - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA |
| */ |
| for (int i = 0; i < inputLocales.size(); i++) { |
| ULocale uloc = inputLocales.get(i); |
| |
| String language = uloc.getLanguage(); |
| String script = uloc.getScript(); |
| String country = uloc.getCountry(); |
| String variant = uloc.getVariant(); |
| |
| boolean bInserted = false; |
| for (int j = 0; j < result.size(); j++) { |
| // Check if this locale is more specific |
| // than existing locale entries already inserted |
| // in the destination list |
| ULocale u = result.get(j); |
| if (!u.getLanguage().equals(language)) { |
| continue; |
| } |
| String s = u.getScript(); |
| String c = u.getCountry(); |
| String v = u.getVariant(); |
| if (!s.equals(script)) { |
| if (s.length() == 0 && c.length() == 0 && v.length() == 0) { |
| result.add(j, uloc); |
| bInserted = true; |
| break; |
| } else if (s.length() == 0 && c.equals(country)) { |
| // We want to see zh_Hant_HK before zh_HK |
| result.add(j, uloc); |
| bInserted = true; |
| break; |
| } else if (script.length() == 0 && country.length() > 0 && c.length() == 0) { |
| // We want to see zh_HK before zh_Hant |
| result.add(j, uloc); |
| bInserted = true; |
| break; |
| } |
| continue; |
| } |
| if (!c.equals(country)) { |
| if (c.length() == 0 && v.length() == 0) { |
| result.add(j, uloc); |
| bInserted = true; |
| break; |
| } |
| } |
| if (!v.equals(variant) && v.length() == 0) { |
| result.add(j, uloc); |
| bInserted = true; |
| break; |
| } |
| } |
| if (!bInserted) { |
| // Add this locale at the end of the list |
| result.add(uloc); |
| } |
| } |
| |
| // TODO: Locale aliases might be resolved here |
| // For example, zh_Hant_TW = zh_TW |
| |
| /* |
| * Step 2: Append fallback locales for each entry |
| * |
| * Example: |
| * Before - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA |
| * After - en_US_Boston, en_US, en, en_US, en, fr_FR, fr, |
| * zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr |
| */ |
| int index = 0; |
| while (index < result.size()) { |
| ULocale uloc = result.get(index); |
| while (true) { |
| uloc = uloc.getFallback(); |
| if (uloc.getLanguage().length() == 0) { |
| break; |
| } |
| index++; |
| result.add(index, uloc); |
| } |
| index++; |
| } |
| |
| /* |
| * Step 3: Remove earlier occurrence of duplicated locales |
| * |
| * Example: |
| * Before - en_US_Boston, en_US, en, en_US, en, fr_FR, fr, |
| * zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr |
| * After - en_US_Boston, en_US, en, fr_FR, zh_TW, zh_Hant, |
| * zh, fr_CA, fr |
| */ |
| index = 0; |
| while (index < result.size() - 1) { |
| ULocale uloc = result.get(index); |
| boolean bRemoved = false; |
| for (int i = index + 1; i < result.size(); i++) { |
| if (uloc.equals(result.get(i))) { |
| // Remove earlier one |
| result.remove(index); |
| bRemoved = true; |
| break; |
| } |
| } |
| if (!bRemoved) { |
| index++; |
| } |
| } |
| return result; |
| } |
| |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @param dateStyle |
| * @param timeStyle |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected DateFormat guessDateFormat(int dateStyle, int timeStyle) { |
| DateFormat result; |
| ULocale dfLocale = getAvailableLocale(TYPE_DATEFORMAT); |
| if (dfLocale == null) { |
| dfLocale = ULocale.ROOT; |
| } |
| if (timeStyle == DF_NONE) { |
| result = DateFormat.getDateInstance(getCalendar(), dateStyle, dfLocale); |
| } else if (dateStyle == DF_NONE) { |
| result = DateFormat.getTimeInstance(getCalendar(), timeStyle, dfLocale); |
| } else { |
| result = DateFormat.getDateTimeInstance(getCalendar(), dateStyle, timeStyle, dfLocale); |
| } |
| return result; |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @param style |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected NumberFormat guessNumberFormat(int style) { |
| NumberFormat result; |
| ULocale nfLocale = getAvailableLocale(TYPE_NUMBERFORMAT); |
| if (nfLocale == null) { |
| nfLocale = ULocale.ROOT; |
| } |
| switch (style) { |
| case NF_NUMBER: |
| result = NumberFormat.getInstance(nfLocale); |
| break; |
| case NF_SCIENTIFIC: |
| result = NumberFormat.getScientificInstance(nfLocale); |
| break; |
| case NF_INTEGER: |
| result = NumberFormat.getIntegerInstance(nfLocale); |
| break; |
| case NF_PERCENT: |
| result = NumberFormat.getPercentInstance(nfLocale); |
| break; |
| case NF_CURRENCY: |
| result = NumberFormat.getCurrencyInstance(nfLocale); |
| result.setCurrency(getCurrency()); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown number format style"); |
| } |
| return result; |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected String guessTerritory() { |
| String result; |
| // pass through locales to see if there is a territory. |
| for (ULocale locale : getLocales()) { |
| result = locale.getCountry(); |
| if (result.length() != 0) { |
| return result; |
| } |
| } |
| // if not, guess from the first language tag, or maybe from |
| // intersection of languages, eg nl + fr => BE |
| // TODO: fix using real data |
| // for now, just use fixed values |
| ULocale firstLocale = getLocale(0); |
| String language = firstLocale.getLanguage(); |
| String script = firstLocale.getScript(); |
| result = null; |
| if (script.length() != 0) { |
| result = language_territory_hack_map.get(language + "_" + script); |
| } |
| if (result == null) { |
| result = language_territory_hack_map.get(language); |
| } |
| if (result == null) { |
| result = "US"; // need *some* default |
| } |
| return result; |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics |
| * |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected Currency guessCurrency() { |
| return Currency.getInstance(new ULocale("und-" + getTerritory())); |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected List<ULocale> guessLocales() { |
| if (implicitLocales == null) { |
| List<ULocale> result = new ArrayList<ULocale>(1); |
| result.add(ULocale.getDefault()); |
| implicitLocales = processLocales(result); |
| } |
| return implicitLocales; |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected Collator guessCollator() { |
| ULocale collLocale = getAvailableLocale(TYPE_COLLATOR); |
| if (collLocale == null) { |
| collLocale = ULocale.ROOT; |
| } |
| return Collator.getInstance(collLocale); |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @param type |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected BreakIterator guessBreakIterator(int type) { |
| BreakIterator bitr = null; |
| ULocale brkLocale = getAvailableLocale(TYPE_BREAKITERATOR); |
| if (brkLocale == null) { |
| brkLocale = ULocale.ROOT; |
| } |
| switch (type) { |
| case BI_CHARACTER: |
| bitr = BreakIterator.getCharacterInstance(brkLocale); |
| break; |
| case BI_TITLE: |
| bitr = BreakIterator.getTitleInstance(brkLocale); |
| break; |
| case BI_WORD: |
| bitr = BreakIterator.getWordInstance(brkLocale); |
| break; |
| case BI_LINE: |
| bitr = BreakIterator.getLineInstance(brkLocale); |
| break; |
| case BI_SENTENCE: |
| bitr = BreakIterator.getSentenceInstance(brkLocale); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown break iterator type"); |
| } |
| return bitr; |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected TimeZone guessTimeZone() { |
| // TODO fix using real data |
| // for single-zone countries, pick that zone |
| // for others, pick the most populous zone |
| // for now, just use fixed value |
| // NOTE: in a few cases can do better by looking at language. |
| // Eg haw+US should go to Pacific/Honolulu |
| // fr+CA should go to America/Montreal |
| String timezoneString = territory_tzid_hack_map.get(getTerritory()); |
| if (timezoneString == null) { |
| String[] attempt = TimeZone.getAvailableIDs(getTerritory()); |
| if (attempt.length == 0) { |
| timezoneString = "Etc/GMT"; // gotta do something |
| } else { |
| int i; |
| // this all needs to be fixed to use real data. But for now, do slightly better by skipping cruft |
| for (i = 0; i < attempt.length; ++i) { |
| if (attempt[i].indexOf("/") >= 0) break; |
| } |
| if (i > attempt.length) i = 0; |
| timezoneString = attempt[i]; |
| } |
| } |
| return TimeZone.getTimeZone(timezoneString); |
| } |
| |
| /** |
| * This function can be overridden by subclasses to use different heuristics. |
| * <b>It MUST return a 'safe' value, |
| * one whose modification will not affect this object.</b> |
| * |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| protected Calendar guessCalendar() { |
| ULocale calLocale = getAvailableLocale(TYPE_CALENDAR); |
| if (calLocale == null) { |
| calLocale = ULocale.US; |
| } |
| return Calendar.getInstance(getTimeZone(), calLocale); |
| } |
| |
| // PRIVATES |
| |
| private List<ULocale> locales; |
| private String territory; |
| private Currency currency; |
| private TimeZone timezone; |
| private Calendar calendar; |
| private Collator collator; |
| private BreakIterator[] breakIterators; |
| private DateFormat[][] dateFormats; |
| private NumberFormat[] numberFormats; |
| private List<ULocale> implicitLocales; |
| |
| { |
| reset(); |
| } |
| |
| |
| private ULocale getAvailableLocale(int type) { |
| List<ULocale> locs = getLocales(); |
| ULocale result = null; |
| for (int i = 0; i < locs.size(); i++) { |
| ULocale l = locs.get(i); |
| if (isAvailableLocale(l, type)) { |
| result = l; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| private boolean isAvailableLocale(ULocale loc, int type) { |
| BitSet bits = available_locales.get(loc); |
| if (bits != null && bits.get(type)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * Available locales for service types |
| */ |
| private static final HashMap<ULocale, BitSet> available_locales = new HashMap<ULocale, BitSet>(); |
| private static final int |
| TYPE_GENERIC = 0, |
| TYPE_CALENDAR = 1, |
| TYPE_DATEFORMAT= 2, |
| TYPE_NUMBERFORMAT = 3, |
| TYPE_COLLATOR = 4, |
| TYPE_BREAKITERATOR = 5, |
| TYPE_LIMIT = TYPE_BREAKITERATOR + 1; |
| |
| static { |
| BitSet bits; |
| ULocale[] allLocales = ULocale.getAvailableLocales(); |
| for (int i = 0; i < allLocales.length; i++) { |
| bits = new BitSet(TYPE_LIMIT); |
| available_locales.put(allLocales[i], bits); |
| bits.set(TYPE_GENERIC); |
| } |
| |
| ULocale[] calLocales = Calendar.getAvailableULocales(); |
| for (int i = 0; i < calLocales.length; i++) { |
| bits = available_locales.get(calLocales[i]); |
| if (bits == null) { |
| bits = new BitSet(TYPE_LIMIT); |
| available_locales.put(allLocales[i], bits); |
| } |
| bits.set(TYPE_CALENDAR); |
| } |
| |
| ULocale[] dateLocales = DateFormat.getAvailableULocales(); |
| for (int i = 0; i < dateLocales.length; i++) { |
| bits = available_locales.get(dateLocales[i]); |
| if (bits == null) { |
| bits = new BitSet(TYPE_LIMIT); |
| available_locales.put(allLocales[i], bits); |
| } |
| bits.set(TYPE_DATEFORMAT); |
| } |
| |
| ULocale[] numLocales = NumberFormat.getAvailableULocales(); |
| for (int i = 0; i < numLocales.length; i++) { |
| bits = available_locales.get(numLocales[i]); |
| if (bits == null) { |
| bits = new BitSet(TYPE_LIMIT); |
| available_locales.put(allLocales[i], bits); |
| } |
| bits.set(TYPE_NUMBERFORMAT); |
| } |
| |
| ULocale[] collLocales = Collator.getAvailableULocales(); |
| for (int i = 0; i < collLocales.length; i++) { |
| bits = available_locales.get(collLocales[i]); |
| if (bits == null) { |
| bits = new BitSet(TYPE_LIMIT); |
| available_locales.put(allLocales[i], bits); |
| } |
| bits.set(TYPE_COLLATOR); |
| } |
| |
| ULocale[] brkLocales = BreakIterator.getAvailableULocales(); |
| for (int i = 0; i < brkLocales.length; i++) { |
| bits = available_locales.get(brkLocales[i]); |
| bits.set(TYPE_BREAKITERATOR); |
| } |
| } |
| |
| /** WARNING: All of this data is temporary, until we start importing from CLDR!!! |
| * |
| */ |
| private static final Map<String, String> language_territory_hack_map = new HashMap<String, String>(); |
| private static final String[][] language_territory_hack = { |
| {"af", "ZA"}, |
| {"am", "ET"}, |
| {"ar", "SA"}, |
| {"as", "IN"}, |
| {"ay", "PE"}, |
| {"az", "AZ"}, |
| {"bal", "PK"}, |
| {"be", "BY"}, |
| {"bg", "BG"}, |
| {"bn", "IN"}, |
| {"bs", "BA"}, |
| {"ca", "ES"}, |
| {"ch", "MP"}, |
| {"cpe", "SL"}, |
| {"cs", "CZ"}, |
| {"cy", "GB"}, |
| {"da", "DK"}, |
| {"de", "DE"}, |
| {"dv", "MV"}, |
| {"dz", "BT"}, |
| {"el", "GR"}, |
| {"en", "US"}, |
| {"es", "ES"}, |
| {"et", "EE"}, |
| {"eu", "ES"}, |
| {"fa", "IR"}, |
| {"fi", "FI"}, |
| {"fil", "PH"}, |
| {"fj", "FJ"}, |
| {"fo", "FO"}, |
| {"fr", "FR"}, |
| {"ga", "IE"}, |
| {"gd", "GB"}, |
| {"gl", "ES"}, |
| {"gn", "PY"}, |
| {"gu", "IN"}, |
| {"gv", "GB"}, |
| {"ha", "NG"}, |
| {"he", "IL"}, |
| {"hi", "IN"}, |
| {"ho", "PG"}, |
| {"hr", "HR"}, |
| {"ht", "HT"}, |
| {"hu", "HU"}, |
| {"hy", "AM"}, |
| {"id", "ID"}, |
| {"is", "IS"}, |
| {"it", "IT"}, |
| {"ja", "JP"}, |
| {"ka", "GE"}, |
| {"kk", "KZ"}, |
| {"kl", "GL"}, |
| {"km", "KH"}, |
| {"kn", "IN"}, |
| {"ko", "KR"}, |
| {"kok", "IN"}, |
| {"ks", "IN"}, |
| {"ku", "TR"}, |
| {"ky", "KG"}, |
| {"la", "VA"}, |
| {"lb", "LU"}, |
| {"ln", "CG"}, |
| {"lo", "LA"}, |
| {"lt", "LT"}, |
| {"lv", "LV"}, |
| {"mai", "IN"}, |
| {"men", "GN"}, |
| {"mg", "MG"}, |
| {"mh", "MH"}, |
| {"mk", "MK"}, |
| {"ml", "IN"}, |
| {"mn", "MN"}, |
| {"mni", "IN"}, |
| {"mo", "MD"}, |
| {"mr", "IN"}, |
| {"ms", "MY"}, |
| {"mt", "MT"}, |
| {"my", "MM"}, |
| {"na", "NR"}, |
| {"nb", "NO"}, |
| {"nd", "ZA"}, |
| {"ne", "NP"}, |
| {"niu", "NU"}, |
| {"nl", "NL"}, |
| {"nn", "NO"}, |
| {"no", "NO"}, |
| {"nr", "ZA"}, |
| {"nso", "ZA"}, |
| {"ny", "MW"}, |
| {"om", "KE"}, |
| {"or", "IN"}, |
| {"pa", "IN"}, |
| {"pau", "PW"}, |
| {"pl", "PL"}, |
| {"ps", "PK"}, |
| {"pt", "BR"}, |
| {"qu", "PE"}, |
| {"rn", "BI"}, |
| {"ro", "RO"}, |
| {"ru", "RU"}, |
| {"rw", "RW"}, |
| {"sd", "IN"}, |
| {"sg", "CF"}, |
| {"si", "LK"}, |
| {"sk", "SK"}, |
| {"sl", "SI"}, |
| {"sm", "WS"}, |
| {"so", "DJ"}, |
| {"sq", "CS"}, |
| {"sr", "CS"}, |
| {"ss", "ZA"}, |
| {"st", "ZA"}, |
| {"sv", "SE"}, |
| {"sw", "KE"}, |
| {"ta", "IN"}, |
| {"te", "IN"}, |
| {"tem", "SL"}, |
| {"tet", "TL"}, |
| {"th", "TH"}, |
| {"ti", "ET"}, |
| {"tg", "TJ"}, |
| {"tk", "TM"}, |
| {"tkl", "TK"}, |
| {"tvl", "TV"}, |
| {"tl", "PH"}, |
| {"tn", "ZA"}, |
| {"to", "TO"}, |
| {"tpi", "PG"}, |
| {"tr", "TR"}, |
| {"ts", "ZA"}, |
| {"uk", "UA"}, |
| {"ur", "IN"}, |
| {"uz", "UZ"}, |
| {"ve", "ZA"}, |
| {"vi", "VN"}, |
| {"wo", "SN"}, |
| {"xh", "ZA"}, |
| {"zh", "CN"}, |
| {"zh_Hant", "TW"}, |
| {"zu", "ZA"}, |
| {"aa", "ET"}, |
| {"byn", "ER"}, |
| {"eo", "DE"}, |
| {"gez", "ET"}, |
| {"haw", "US"}, |
| {"iu", "CA"}, |
| {"kw", "GB"}, |
| {"sa", "IN"}, |
| {"sh", "HR"}, |
| {"sid", "ET"}, |
| {"syr", "SY"}, |
| {"tig", "ER"}, |
| {"tt", "RU"}, |
| {"wal", "ET"}, }; |
| static { |
| for (int i = 0; i < language_territory_hack.length; ++i) { |
| language_territory_hack_map.put(language_territory_hack[i][0],language_territory_hack[i][1]); |
| } |
| } |
| |
| static final Map<String, String> territory_tzid_hack_map = new HashMap<String, String>(); |
| static final String[][] territory_tzid_hack = { |
| {"AQ", "Antarctica/McMurdo"}, |
| {"AR", "America/Buenos_Aires"}, |
| {"AU", "Australia/Sydney"}, |
| {"BR", "America/Sao_Paulo"}, |
| {"CA", "America/Toronto"}, |
| {"CD", "Africa/Kinshasa"}, |
| {"CL", "America/Santiago"}, |
| {"CN", "Asia/Shanghai"}, |
| {"EC", "America/Guayaquil"}, |
| {"ES", "Europe/Madrid"}, |
| {"GB", "Europe/London"}, |
| {"GL", "America/Godthab"}, |
| {"ID", "Asia/Jakarta"}, |
| {"ML", "Africa/Bamako"}, |
| {"MX", "America/Mexico_City"}, |
| {"MY", "Asia/Kuala_Lumpur"}, |
| {"NZ", "Pacific/Auckland"}, |
| {"PT", "Europe/Lisbon"}, |
| {"RU", "Europe/Moscow"}, |
| {"UA", "Europe/Kiev"}, |
| {"US", "America/New_York"}, |
| {"UZ", "Asia/Tashkent"}, |
| {"PF", "Pacific/Tahiti"}, |
| {"FM", "Pacific/Kosrae"}, |
| {"KI", "Pacific/Tarawa"}, |
| {"KZ", "Asia/Almaty"}, |
| {"MH", "Pacific/Majuro"}, |
| {"MN", "Asia/Ulaanbaatar"}, |
| {"SJ", "Arctic/Longyearbyen"}, |
| {"UM", "Pacific/Midway"}, |
| }; |
| static { |
| for (int i = 0; i < territory_tzid_hack.length; ++i) { |
| territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1]); |
| } |
| } |
| |
| // Freezable implementation |
| |
| private boolean frozen; |
| |
| /** |
| * @draft ICU 3.6 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public boolean isFrozen() { |
| return frozen; |
| } |
| |
| /** |
| * @draft ICU 4.4 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences freeze() { |
| frozen = true; |
| return this; |
| } |
| |
| /** |
| * @draft ICU 4.4 |
| * @provisional This API might change or be removed in a future release. |
| */ |
| public GlobalizationPreferences cloneAsThawed() { |
| try { |
| GlobalizationPreferences result = (GlobalizationPreferences) clone(); |
| result.frozen = false; |
| return result; |
| } catch (CloneNotSupportedException e) { |
| // will always work |
| return null; |
| } |
| } |
| } |
| |