blob: d260c3b5c208785d17fc643a301ee7b91c210201 [file] [log] [blame]
//##header
/*
*******************************************************************************
* Copyright (C) 2004-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
//#ifndef FOUNDATION
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//#endif
import com.ibm.icu.impl.Utility;
import com.ibm.icu.impl.ZoneMeta;
import com.ibm.icu.text.Collator;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
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>
* <p>
* TODO:<ul>
* <li>Separate out base class</li>
* <li>Add BreakIterator</li>
* <li>Add Holidays</li>
* <li>Add convenience to get/take Locale as well as ULocale.</li>
* <li>Add getResourceBundle(String baseName, ClassLoader loader);</li>
* <li>Add getFallbackLocales();</li>
* <li>Add Lenient datetime formatting when that is available.</li>
* <li>Should this be serializable?</li>
* <li>Other utilities?</li>
* </ul>
* 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>
*
* @internal
* @deprecated This API is ICU internal only.
*/
public class GlobalizationPreferences implements Freezable {
/**
* Number Format types
* @internal
* @deprecated This API is ICU internal only.
*/
public static final int CURRENCY = 0, NUMBER = 1, INTEGER = 2, SCIENTIFIC = 3,
PERCENT = 4, NUMBER_LIMIT = 5;
/**
* Supplement to DateFormat.FULL, LONG, MEDIUM, SHORT. Indicates
* that no value for one of date or time is to be used.
* @internal
* @deprecated This API is ICU internal only.
*/
public static final int NONE = 4;
/**
* For selecting a choice of display names
* @internal
* @deprecated This API is ICU internal only.
*/
public static final int
LOCALEID = 0, LANGUAGEID = 1, SCRIPTID = 2, TERRITORYID = 3, VARIANTID = 4,
KEYWORDID = 5, KEYWORD_VALUEID = 6,
CURRENCYID = 7, CURRENCY_SYMBOLID = 8, TIMEZONEID = 9, DISPLAYID_LIMIT = 10;
/**
* 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 locales list of locales in priority order, eg {"be", "fr"}
* for Breton first, then French if that fails.
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setLocales(List locales) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
if (locales.size() == 0) {
this.locales = locales.get(0);
} else {
this.locales = new ArrayList(locales); // clone for safety
}
return this;
}
/**
* Get a copy of the language/locale priority list
* @return a copy of the language/locale priority list.
* @internal
* @deprecated This API is ICU internal only.
*/
public List getLocales() {
List result = new ArrayList(); // clone for safety
if (locales == null) {
result = guessLocales();
} else if (locales instanceof ULocale) {
result.add(locales);
} else {
result.addAll((List)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.
* @internal
* @deprecated This API is ICU internal only.
*/
public ULocale getLocale(int index) {
if (locales == null) {
return (ULocale)guessLocales().get(index);
} else if (locales instanceof ULocale) {
if (index != 0) throw new IllegalArgumentException("Out of bounds: " + index);
return (ULocale)locales;
} else {
return (ULocale)((List)locales).get(index);
}
}
/**
* 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
* @internal
* @deprecated This API is ICU internal only.
*/
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
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setLocale(ULocale uLocale) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
return setLocales(new ULocale[]{uLocale});
}
//#ifndef FOUNDATION
/**
* 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
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setLocales(String acceptLanguageString) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
/*
Accept-Language = "Accept-Language" ":" 1#( language-range [ ";" "q" "=" qvalue ] )
x matches x-...
*/
// reorders in quality order
// don't care that it is not very efficient right now
Matcher acceptMatcher = Pattern.compile("\\s*([-_a-zA-Z]+)(;q=([.0-9]+))?\\s*").matcher("");
Map reorder = new TreeMap();
String[] pieces = acceptLanguageString.split(",");
for (int i = 0; i < pieces.length; ++i) {
Double qValue = new Double(1);
try {
if (!acceptMatcher.reset(pieces[i]).matches()) {
throw new IllegalArgumentException();
}
String qValueString = acceptMatcher.group(3);
if (qValueString != null) qValue = new Double(Double.parseDouble(qValueString));
} catch (Exception e) {
throw new IllegalArgumentException("element '" + pieces[i] +
"' is not of the form '<locale>{;q=<number>}");
}
List items = (List)reorder.get(qValue);
if (items == null) reorder.put(qValue, items = new LinkedList());
items.add(0, acceptMatcher.group(1)); // reverse order, will reverse again
}
// now read out in reverse order
List result = new ArrayList();
for (Iterator it = reorder.keySet().iterator(); it.hasNext();) {
Object key = it.next();
List items = (List)reorder.get(key);
for (Iterator it2 = items.iterator(); it2.hasNext();) {
result.add(0, new ULocale((String)it2.next()));
}
}
return setLocales(result);
}
//#endif
/**
* 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
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setTerritory(String territory) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.territory = territory;
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.
* @internal
* @deprecated This API is ICU internal only.
*/
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
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setCurrency(Currency currency) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.currency = currency;
return this;
}
/**
* Get a copy of the currency computed according to the settings.
* @return currency code, explicit or implicit.
* @internal
* @deprecated This API is ICU internal only.
*/
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
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setCalendar(Calendar calendar) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.calendar = calendar;
return this;
}
/**
* Get a copy of the calendar according to the settings.
* @return calendar explicit or implicit.
* @internal
* @deprecated This API is ICU internal only.
*/
public Calendar getCalendar() {
if (calendar == null) return guessCalendar();
Calendar temp = (Calendar) calendar.clone(); // clone for safety
temp.setTimeZone(getTimeZone());
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
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setTimeZone(TimeZone timezone) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.timezone = timezone;
return this;
}
/**
* Get the timezone. It was either explicitly set, or is
* heuristically computed from other settings.
* @return timezone, either implicitly or explicitly set
* @internal
* @deprecated This API is ICU internal only.
*/
public TimeZone getTimeZone() {
if (timezone == null) return guessTimeZone();
return (TimeZone) timezone.clone(); // clone for safety
}
/**
* Get a copy of the collator according to the settings.
* @return collator explicit or implicit.
* @internal
* @deprecated This API is ICU internal only.
*/
public Collator getCollator() {
if (collator == null) return guessCollator();
try {
return (Collator) collator.clone(); // clone for safety
} catch (CloneNotSupportedException e) {
throw new InternalError("Error in cloning collator");
}
}
/**
* Explicitly set the collator for this object.
* @param collator
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setCollator(Collator collator) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.collator = collator;
return this;
}
/**
* Set the date locale.
* @param dateLocale If not null, overrides the locale priority list for all the date formats.
* @return this, for chaining
*/
public GlobalizationPreferences setDateLocale(ULocale dateLocale) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.dateLocale = dateLocale;
return this;
}
/**
* Gets the date locale, to be used in computing date formats. Overrides the general locale setting.
* @return date locale. Null if none was set explicitly.
* @internal
* @deprecated This API is ICU internal only.
*/
public ULocale getDateLocale() {
return dateLocale != null ? dateLocale : getLocale(0);
}
/**
* Set the number locale.
* @param numberLocale If not null, overrides the locale priority list for all the date formats.
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setNumberLocale(ULocale numberLocale) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
this.numberLocale = numberLocale;
return this;
}
/**
* Get the current number locale setting used for getNumberFormat.
* @return number locale. Null if none was set explicitly.
* @internal
* @deprecated This API is ICU internal only.
*/
public ULocale getNumberLocale() {
return numberLocale != null ? numberLocale : getLocale(0);
}
/**
* 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: LANGUAGE, etc.
* @return the display name
* @internal
* @deprecated This API is ICU internal only.
*/
public String getDisplayName(String id, int type) {
String result = id;
for (Iterator it = getLocales().iterator(); it.hasNext();) {
ULocale locale = (ULocale) it.next();
switch (type) {
case LOCALEID:
result = ULocale.getDisplayName(id, locale);
break;
case LANGUAGEID:
result = ULocale.getDisplayLanguage(id, locale);
break;
case SCRIPTID:
result = ULocale.getDisplayScript("und-" + id, locale);
break;
case TERRITORYID:
result = ULocale.getDisplayCountry("und-" + id, locale);
break;
case VARIANTID:
// TODO fix variant parsing
result = ULocale.getDisplayVariant("und-QQ-" + id, locale);
break;
case KEYWORDID:
result = ULocale.getDisplayKeyword(id, locale);
break;
case KEYWORD_VALUEID:
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 CURRENCY_SYMBOLID:
case CURRENCYID:
Currency temp = new Currency(id);
result =temp.getName(locale, type==CURRENCYID
? 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 TIMEZONEID:
SimpleDateFormat dtf = new SimpleDateFormat("vvvv",locale);
dtf.setTimeZone(TimeZone.getTimeZone(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
// note, compiling with FOUNDATION omits this check for now
//#ifndef FOUNDATION
if (badTimeZone.reset(result).matches()) continue;
//#endif
break;
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
if (!id.equals(result)) return result;
// TODO need better way of seeing if we fell back to root!!
// This will not work at all for lots of stuff
}
return result;
}
//#ifndef FOUNDATION
// TODO remove need for this
private static final Matcher badTimeZone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher("");
//#endif
/**
* Set an explicit date format. Overrides both the date locale,
* and the locale priority list for a particular combination of
* dateStyle and timeStyle. NONE should be used if for the style,
* where only the date or time format individually is being set.
* @param dateStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
* @param timeStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
* @param format
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
if (dateFormats == null) dateFormats = new Object[NONE+1][NONE+1];
dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety
return this;
}
/**
* Set an explicit date format. Overrides both the date locale,
* and the locale priority list for a particular combination of
* dateStyle and timeStyle. NONE should be used if for the style,
* where only the date or time format individually is being set.
* @param dateStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
* @param timeStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
* @param formatPattern date pattern, eg "yyyy-MMM-dd"
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, String formatPattern) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
if (dateFormats == null) dateFormats = new Object[NONE+1][NONE+1];
// test the format to make sure it won't throw an error later
new SimpleDateFormat(formatPattern, getDateLocale());
dateFormats[dateStyle][timeStyle] = formatPattern; // 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, if there is a non-null date locale,
* that is used. Otherwise, the language priority list is
* used. NONE should be used for the style, where only the date or
* time format individually is being gotten.
* @param dateStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
* @param timeStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
* @return a DateFormat, according to the above description
* @internal
* @deprecated This API is ICU internal only.
*/
public DateFormat getDateFormat(int dateStyle, int timeStyle) {
try {
DateFormat result = null;
if (dateFormats != null) { // and override can either be a string or a pattern
Object temp = dateFormats[dateStyle][timeStyle];
if (temp instanceof DateFormat) {
result = (DateFormat) temp;
} else {
result = new SimpleDateFormat((String)temp, getDateLocale());
}
}
if (result != null) {
result = (DateFormat) result.clone(); // clone for safety
result.setCalendar(getCalendar());
} else {
// In the case of date formats, we don't have to look at more than one
// locale. May be different for other cases
// TODO Make this one function.
if (timeStyle == NONE) {
result = DateFormat.getDateInstance(getCalendar(), dateStyle, getDateLocale());
} else if (dateStyle == NONE) {
result = DateFormat.getTimeInstance(getCalendar(), timeStyle, getDateLocale());
} else {
result = DateFormat.getDateTimeInstance(getCalendar(), dateStyle, timeStyle, getDateLocale());
}
}
return result;
} catch (RuntimeException e) {
IllegalArgumentException ex = new IllegalArgumentException("Cannot create DateFormat");
//#ifndef FOUNDATION
ex.initCause(e);
//#endif
throw ex;
}
}
/**
* 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, if there is a non-null number
* locale, that is used. Otherwise, the language priority list is
* used. NONE should be used for the style, where only the date or
* time format individually is being gotten.
* @param style CURRENCY, NUMBER, INTEGER, SCIENTIFIC, PERCENT
* @internal
* @deprecated This API is ICU internal only.
*/
public NumberFormat getNumberFormat(int style) {
try {
NumberFormat result = null;
if (numberFormats != null) {
Object temp = numberFormats[style];
if (temp instanceof NumberFormat) {
result = (NumberFormat) temp;
} else {
result = new DecimalFormat((String)temp, new DecimalFormatSymbols(getDateLocale()));
}
}
if (result != null) {
result = (NumberFormat) result.clone(); // clone for safety (later optimize)
if (style == CURRENCY) {
result.setCurrency(getCurrency());
}
return result;
}
// In the case of date formats, we don't have to look at more than one
// locale. May be different for other cases
switch (style) {
case NUMBER: return NumberFormat.getInstance(getNumberLocale());
case SCIENTIFIC: return NumberFormat.getScientificInstance(getNumberLocale());
case INTEGER: return NumberFormat.getIntegerInstance(getNumberLocale());
case PERCENT: return NumberFormat.getPercentInstance(getNumberLocale());
case CURRENCY: result = NumberFormat.getCurrencyInstance(getNumberLocale());
result.setCurrency(getCurrency());
return result;
}
} catch (RuntimeException e) {}
throw new IllegalArgumentException(); // fix later
}
/**
* Sets a number format explicitly. Overrides the number locale
* and the general locale settings.
* @param style CURRENCY, NUMBER, INTEGER, SCIENTIFIC, PERCENT
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setNumberFormat(int style, DateFormat format) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
if (numberFormats == null) numberFormats = new Object[NUMBER_LIMIT];
numberFormats[style] = (NumberFormat) format.clone(); // for safety
return this;
}
/**
* Sets a number format explicitly. Overrides the number locale
* and the general locale settings.
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences setNumberFormat(int style, String formatPattern) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
if (numberFormats == null) numberFormats = new Object[NUMBER_LIMIT];
// check to make sure it compiles
new DecimalFormat((String)formatPattern, new DecimalFormatSymbols(getDateLocale()));
numberFormats[style] = formatPattern; // for safety
return this;
}
/**
* Restore the object to the initial state.
* @return this, for chaining
* @internal
* @deprecated This API is ICU internal only.
*/
public GlobalizationPreferences reset() {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify immutable object");
}
territory = null;
calendar = null;
collator = null;
timezone = null;
currency = null;
dateFormats = null;
numberFormats = null;
dateLocale = null;
numberLocale = null;
locales = null;
return this;
}
/**
* This function can be overridden by subclasses to use different heuristics.
* @internal
* @deprecated This API is ICU internal only.
*/
protected String guessTerritory() {
String result;
// pass through locales to see if there is a territory.
for (Iterator it = getLocales().iterator(); it.hasNext();) {
ULocale locale = (ULocale)it.next();
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 = (String) language_territory_hack_map.get(language + "_" + script);
}
if (result == null) result = (String) 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
* @internal
* @deprecated This API is ICU internal only.
*/
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>
* @internal
* @deprecated This API is ICU internal only.
*/
protected List guessLocales() {
List result = new ArrayList(0);
result.add(ULocale.getDefault());
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>
* @internal
* @deprecated This API is ICU internal only.
*/
protected Collator guessCollator() {
return Collator.getInstance(getLocale(0));
}
/**
* 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>
* @internal
* @deprecated This API is ICU internal only.
*/
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 = (String) territory_tzid_hack_map.get(getTerritory());
if (timezoneString == null) {
String[] attempt = ZoneMeta.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>
* @internal
* @deprecated This API is ICU internal only.
*/
protected Calendar guessCalendar() {
// TODO add better API
return Calendar.getInstance(new ULocale("und-" + getTerritory()));
}
// PRIVATES
private Object locales;
private String territory;
private Currency currency;
private TimeZone timezone;
private Calendar calendar;
private Collator collator;
private ULocale dateLocale;
private Object[][] dateFormats;
private ULocale numberLocale;
private Object[] numberFormats;
{
reset();
}
/** WARNING: All of this data is temporary, until we start importing from CLDR!!!
*
*/
private static final Map language_territory_hack_map = new HashMap();
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 territory_tzid_hack_map = new HashMap();
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]);
}
}
private boolean frozen;
/**
* @internal
* @deprecated This API is ICU internal only.
*/
public boolean isFrozen() {
return frozen;
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
public Object freeze() {
frozen = true;
return this;
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
public Object cloneAsThawed() {
try {
GlobalizationPreferences result = (GlobalizationPreferences) clone();
result.frozen = false;
return result;
} catch (CloneNotSupportedException e) {
// will always work
return null;
}
}
}