0001: //##header
0002: /*
0003: *******************************************************************************
0004: * Copyright (C) 2004-2006, International Business Machines Corporation and *
0005: * others. All Rights Reserved. *
0006: *******************************************************************************
0007: */
0008: package com.ibm.icu.util;
0009:
0010: import java.util.ArrayList;
0011: import java.util.Arrays;
0012: import java.util.BitSet;
0013: import java.util.Date;
0014: import java.util.HashMap;
0015: import java.util.Iterator;
0016: import java.util.LinkedList;
0017: import java.util.List;
0018: import java.util.Map;
0019: import java.util.MissingResourceException;
0020: import java.util.ResourceBundle;
0021: import java.util.TreeMap; //#ifndef FOUNDATION
0022: import java.util.regex.Matcher;
0023: import java.util.regex.Pattern; //#endif
0024: import com.ibm.icu.impl.Utility;
0025: import com.ibm.icu.impl.ZoneMeta;
0026: import com.ibm.icu.text.BreakIterator;
0027: import com.ibm.icu.text.Collator;
0028: import com.ibm.icu.text.DateFormat;
0029: import com.ibm.icu.text.NumberFormat;
0030: import com.ibm.icu.text.SimpleDateFormat;
0031:
0032: /**
0033: * This convenience class provides a mechanism for bundling together different
0034: * globalization preferences. It includes:
0035: * <ul>
0036: * <li>A list of locales/languages in preference order</li>
0037: * <li>A territory</li>
0038: * <li>A currency</li>
0039: * <li>A timezone</li>
0040: * <li>A calendar</li>
0041: * <li>A collator (for language-sensitive sorting, searching, and matching).</li>
0042: * <li>Explicit overrides for date/time formats, etc.</li>
0043: * </ul>
0044: * The class will heuristically compute implicit, heuristic values for the above
0045: * based on available data if explicit values are not supplied. These implicit
0046: * values can be presented to users for confirmation, or replacement if the
0047: * values are incorrect.
0048: * <p>
0049: * To reset any explicit field so that it will get heuristic values, pass in
0050: * null. For example, myPreferences.setLocale(null);
0051: * <p>
0052: * All of the heuristics can be customized by subclasses, by overriding
0053: * getTerritory(), guessCollator(), etc.
0054: * <p>
0055: * The class also supplies display names for languages, scripts, territories,
0056: * currencies, timezones, etc. These are computed according to the
0057: * locale/language preference list. Thus, if the preference is Breton; French;
0058: * English, then the display name for a language will be returned in Breton if
0059: * available, otherwise in French if available, otherwise in English.
0060: * <p>
0061: * The codes used to reference territory, currency, etc. are as defined elsewhere
0062: * in ICU, and are taken from CLDR (which reflects RFC 3066bis usage, ISO 4217,
0063: * and the TZ Timezone database identifiers).
0064: * <p>
0065: * <b>This is at a prototype stage, and has not incorporated all the design
0066: * changes that we would like yet; further feedback is welcome.</b></p>
0067: * <p>
0068: * TODO:<ul>
0069: * <li>Add Holidays</li>
0070: * <li>Add convenience to get/take Locale as well as ULocale.</li>
0071: * <li>Add Lenient datetime formatting when that is available.</li>
0072: * <li>Should this be serializable?</li>
0073: * <li>Other utilities?</li>
0074: * </ul>
0075: * Note:
0076: * <ul>
0077: * <li>to get the display name for the first day of the week, use the calendar +
0078: * display names.</li>
0079: * <li>to get the work days, ask the calendar (when that is available).</li>
0080: * <li>to get papersize / measurement system/bidi-orientation, ask the locale
0081: * (when that is available there)</li>
0082: * <li>to get the field order in a date, and whether a time is 24hour or not,
0083: * ask the DateFormat (when that is available there)</li>
0084: * <li>it will support HOST locale when it becomes available (it is a special
0085: * locale that will ask the services to use the host platform's values).</li>
0086: * </ul>
0087: *
0088: * @draft ICU 3.6
0089: * @provisional This API might change or be removed in a future release.
0090: */
0091: public class GlobalizationPreferences implements Freezable {
0092:
0093: /**
0094: * Default constructor
0095: * @draft ICU 3.6
0096: * @provisional This API might change or be removed in a future release.
0097: */
0098: public GlobalizationPreferences() {
0099: }
0100:
0101: /**
0102: * Number Format types
0103: * @draft ICU 3.6
0104: * @provisional This API might change or be removed in a future release.
0105: */
0106: public static final int NF_NUMBER = 0, // NumberFormat.NUMBERSTYLE
0107: NF_CURRENCY = 1, // NumberFormat.CURRENCYSTYLE
0108: NF_PERCENT = 2, // NumberFormat.PERCENTSTYLE
0109: NF_SCIENTIFIC = 3, // NumberFormat.SCIENTIFICSTYLE
0110: NF_INTEGER = 4; // NumberFormat.INTEGERSTYLE
0111:
0112: private static final int NF_LIMIT = NF_INTEGER + 1;
0113:
0114: /**
0115: * Date Format types
0116: * @draft ICU 3.6
0117: * @provisional This API might change or be removed in a future release.
0118: */
0119: public static final int DF_FULL = DateFormat.FULL, // 0
0120: DF_LONG = DateFormat.LONG, // 1
0121: DF_MEDIUM = DateFormat.MEDIUM, // 2
0122: DF_SHORT = DateFormat.SHORT, // 3
0123: DF_NONE = 4;
0124:
0125: private static final int DF_LIMIT = DF_NONE + 1;
0126:
0127: /**
0128: * For selecting a choice of display names
0129: * @draft ICU 3.6
0130: * @provisional This API might change or be removed in a future release.
0131: */
0132: public static final int ID_LOCALE = 0, ID_LANGUAGE = 1,
0133: ID_SCRIPT = 2, ID_TERRITORY = 3, ID_VARIANT = 4,
0134: ID_KEYWORD = 5, ID_KEYWORD_VALUE = 6, ID_CURRENCY = 7,
0135: ID_CURRENCY_SYMBOL = 8, ID_TIMEZONE = 9;
0136:
0137: private static final int ID_LIMIT = ID_TIMEZONE + 1;
0138:
0139: /**
0140: * Break iterator types
0141: * @draft ICU 3.6
0142: * @deprecated This API is ICU internal only
0143: */
0144: public static final int BI_CHARACTER = BreakIterator.KIND_CHARACTER, // 0
0145: BI_WORD = BreakIterator.KIND_WORD, // 1
0146: BI_LINE = BreakIterator.KIND_LINE, // 2
0147: BI_SENTENCE = BreakIterator.KIND_SENTENCE, // 3
0148: BI_TITLE = BreakIterator.KIND_TITLE; // 4
0149:
0150: private static final int BI_LIMIT = BI_TITLE + 1;
0151:
0152: /**
0153: * Sets the language/locale priority list. If other information is
0154: * not (yet) available, this is used to to produce a default value
0155: * for the appropriate territory, currency, timezone, etc. The
0156: * user should be given the opportunity to correct those defaults
0157: * in case they are incorrect.
0158: *
0159: * @param inputLocales list of locales in priority order, eg {"be", "fr"}
0160: * for Breton first, then French if that fails.
0161: * @return this, for chaining
0162: * @draft ICU 3.6
0163: * @provisional This API might change or be removed in a future release.
0164: */
0165: public GlobalizationPreferences setLocales(List inputLocales) {
0166: if (isFrozen()) {
0167: throw new UnsupportedOperationException(
0168: "Attempt to modify immutable object");
0169: }
0170: locales = processLocales(inputLocales);
0171: return this ;
0172: }
0173:
0174: /**
0175: * Get a copy of the language/locale priority list
0176: *
0177: * @return a copy of the language/locale priority list.
0178: * @draft ICU 3.6
0179: * @provisional This API might change or be removed in a future release.
0180: */
0181: public List getLocales() {
0182: List result;
0183: if (locales == null) {
0184: result = guessLocales();
0185: } else {
0186: result = new ArrayList();
0187: result.addAll(locales);
0188: }
0189: return result;
0190: }
0191:
0192: /**
0193: * Convenience function for getting the locales in priority order
0194: * @param index The index (0..n) of the desired item.
0195: * @return desired item. null if index is out of range
0196: * @draft ICU 3.6
0197: * @provisional This API might change or be removed in a future release.
0198: */
0199: public ULocale getLocale(int index) {
0200: List lcls = locales;
0201: if (lcls == null) {
0202: lcls = guessLocales();
0203: }
0204: if (index >= 0 && index < lcls.size()) {
0205: return (ULocale) lcls.get(index);
0206: }
0207: return null;
0208: }
0209:
0210: /**
0211: * Convenience routine for setting the language/locale priority
0212: * list from an array.
0213: *
0214: * @see #setLocales(List locales)
0215: * @param uLocales list of locales in an array
0216: * @return this, for chaining
0217: * @draft ICU 3.6
0218: * @provisional This API might change or be removed in a future release.
0219: */
0220: public GlobalizationPreferences setLocales(ULocale[] uLocales) {
0221: if (isFrozen()) {
0222: throw new UnsupportedOperationException(
0223: "Attempt to modify immutable object");
0224: }
0225: return setLocales(Arrays.asList(uLocales));
0226: }
0227:
0228: /**
0229: * Convenience routine for setting the language/locale priority
0230: * list from a single locale/language.
0231: *
0232: * @see #setLocales(List locales)
0233: * @param uLocale single locale
0234: * @return this, for chaining
0235: * @draft ICU 3.6
0236: * @provisional This API might change or be removed in a future release.
0237: */
0238: public GlobalizationPreferences setLocale(ULocale uLocale) {
0239: if (isFrozen()) {
0240: throw new UnsupportedOperationException(
0241: "Attempt to modify immutable object");
0242: }
0243: return setLocales(new ULocale[] { uLocale });
0244: }
0245:
0246: //#ifndef FOUNDATION
0247: /**
0248: * Convenience routine for setting the locale priority list from
0249: * an Accept-Language string.
0250: * @see #setLocales(List locales)
0251: * @param acceptLanguageString Accept-Language list, as defined by
0252: * Section 14.4 of the RFC 2616 (HTTP 1.1)
0253: * @return this, for chaining
0254: * @draft ICU 3.6
0255: * @provisional This API might change or be removed in a future release.
0256: */
0257: public GlobalizationPreferences setLocales(
0258: String acceptLanguageString) {
0259: if (isFrozen()) {
0260: throw new UnsupportedOperationException(
0261: "Attempt to modify immutable object");
0262: }
0263: /*
0264: Accept-Language = "Accept-Language" ":" 1#( language-range [ ";" "q" "=" qvalue ] )
0265: x matches x-...
0266: */
0267: // reorders in quality order
0268: // don't care that it is not very efficient right now
0269: Matcher acceptMatcher = Pattern.compile(
0270: "\\s*([-_a-zA-Z]+)(;q=([.0-9]+))?\\s*").matcher("");
0271: Map reorder = new TreeMap();
0272: String[] pieces = acceptLanguageString.split(",");
0273:
0274: for (int i = 0; i < pieces.length; ++i) {
0275: Double qValue = new Double(1);
0276: try {
0277: if (!acceptMatcher.reset(pieces[i]).matches()) {
0278: throw new IllegalArgumentException();
0279: }
0280: String qValueString = acceptMatcher.group(3);
0281: if (qValueString != null) {
0282: qValue = new Double(Double
0283: .parseDouble(qValueString));
0284: }
0285: } catch (Exception e) {
0286: throw new IllegalArgumentException("element '"
0287: + pieces[i]
0288: + "' is not of the form '<locale>{;q=<number>}");
0289: }
0290: List items = (List) reorder.get(qValue);
0291: if (items == null) {
0292: reorder.put(qValue, items = new LinkedList());
0293: }
0294: items.add(0, acceptMatcher.group(1)); // reverse order, will reverse again
0295: }
0296: // now read out in reverse order
0297: List result = new ArrayList();
0298: for (Iterator it = reorder.keySet().iterator(); it.hasNext();) {
0299: Object key = it.next();
0300: List items = (List) reorder.get(key);
0301: for (Iterator it2 = items.iterator(); it2.hasNext();) {
0302: result.add(0, new ULocale((String) it2.next()));
0303: }
0304: }
0305: return setLocales(result);
0306: }
0307:
0308: //#endif
0309:
0310: /**
0311: * Convenience function to get a ResourceBundle instance using
0312: * the specified base name based on the language/locale priority list
0313: * stored in this object.
0314: *
0315: * @param baseName the base name of the resource bundle, a fully qualified
0316: * class name
0317: * @return a resource bundle for the given base name and locale based on the
0318: * language/locale priority list stored in this object
0319: * @draft ICU 3.6
0320: * @provisional This API might change or be removed in a future release.
0321: */
0322: public ResourceBundle getResourceBundle(String baseName) {
0323: return getResourceBundle(baseName, null);
0324: }
0325:
0326: /**
0327: * Convenience function to get a ResourceBundle instance using
0328: * the specified base name and class loader based on the language/locale
0329: * priority list stored in this object.
0330: *
0331: * @param baseName the base name of the resource bundle, a fully qualified
0332: * class name
0333: * @param loader the class object from which to load the resource bundle
0334: * @return a resource bundle for the given base name and locale based on the
0335: * language/locale priority list stored in this object
0336: * @draft ICU 3.6
0337: * @provisional This API might change or be removed in a future release.
0338: */
0339: public ResourceBundle getResourceBundle(String baseName,
0340: ClassLoader loader) {
0341: UResourceBundle urb = null;
0342: UResourceBundle candidate = null;
0343: String actualLocaleName = null;
0344: List fallbacks = getLocales();
0345: for (int i = 0; i < fallbacks.size(); i++) {
0346: String localeName = ((ULocale) fallbacks.get(i)).toString();
0347: if (actualLocaleName != null
0348: && localeName.equals(actualLocaleName)) {
0349: // Actual locale name in the previous round may exactly matches
0350: // with the next fallback locale
0351: urb = candidate;
0352: break;
0353: }
0354: try {
0355: if (loader == null) {
0356: candidate = UResourceBundle.getBundleInstance(
0357: baseName, localeName);
0358: } else {
0359: candidate = UResourceBundle.getBundleInstance(
0360: baseName, localeName, loader);
0361: }
0362: if (candidate != null) {
0363: actualLocaleName = candidate.getULocale().getName();
0364: if (actualLocaleName.equals(localeName)) {
0365: urb = candidate;
0366: break;
0367: }
0368: if (urb == null) {
0369: // Preserve the available bundle as the last resort
0370: urb = candidate;
0371: }
0372: }
0373: } catch (MissingResourceException mre) {
0374: actualLocaleName = null;
0375: continue;
0376: }
0377: }
0378: if (urb == null) {
0379: throw new MissingResourceException(
0380: "Can't find bundle for base name " + baseName,
0381: baseName, "");
0382: }
0383: return urb;
0384: }
0385:
0386: /**
0387: * Sets the territory, which is a valid territory according to for
0388: * RFC 3066 (or successor). If not otherwise set, default
0389: * currency and timezone values will be set from this. The user
0390: * should be given the opportunity to correct those defaults in
0391: * case they are incorrect.
0392: *
0393: * @param territory code
0394: * @return this, for chaining
0395: * @draft ICU 3.6
0396: * @provisional This API might change or be removed in a future release.
0397: */
0398: public GlobalizationPreferences setTerritory(String territory) {
0399: if (isFrozen()) {
0400: throw new UnsupportedOperationException(
0401: "Attempt to modify immutable object");
0402: }
0403: this .territory = territory; // immutable, so don't need to clone
0404: return this ;
0405: }
0406:
0407: /**
0408: * Gets the territory setting. If it wasn't explicitly set, it is
0409: * computed from the general locale setting.
0410: *
0411: * @return territory code, explicit or implicit.
0412: * @draft ICU 3.6
0413: * @provisional This API might change or be removed in a future release.
0414: */
0415: public String getTerritory() {
0416: if (territory == null) {
0417: return guessTerritory();
0418: }
0419: return territory; // immutable, so don't need to clone
0420: }
0421:
0422: /**
0423: * Sets the currency code. If this has not been set, uses default for territory.
0424: *
0425: * @param currency Valid ISO 4217 currency code.
0426: * @return this, for chaining
0427: * @draft ICU 3.6
0428: * @provisional This API might change or be removed in a future release.
0429: */
0430: public GlobalizationPreferences setCurrency(Currency currency) {
0431: if (isFrozen()) {
0432: throw new UnsupportedOperationException(
0433: "Attempt to modify immutable object");
0434: }
0435: this .currency = currency; // immutable, so don't need to clone
0436: return this ;
0437: }
0438:
0439: /**
0440: * Get a copy of the currency computed according to the settings.
0441: *
0442: * @return currency code, explicit or implicit.
0443: * @draft ICU 3.6
0444: * @provisional This API might change or be removed in a future release.
0445: */
0446: public Currency getCurrency() {
0447: if (currency == null) {
0448: return guessCurrency();
0449: }
0450: return currency; // immutable, so don't have to clone
0451: }
0452:
0453: /**
0454: * Sets the calendar. If this has not been set, uses default for territory.
0455: *
0456: * @param calendar arbitrary calendar
0457: * @return this, for chaining
0458: * @draft ICU 3.6
0459: * @provisional This API might change or be removed in a future release.
0460: */
0461: public GlobalizationPreferences setCalendar(Calendar calendar) {
0462: if (isFrozen()) {
0463: throw new UnsupportedOperationException(
0464: "Attempt to modify immutable object");
0465: }
0466: this .calendar = (Calendar) calendar.clone(); // clone for safety
0467: return this ;
0468: }
0469:
0470: /**
0471: * Get a copy of the calendar according to the settings.
0472: *
0473: * @return calendar explicit or implicit.
0474: * @draft ICU 3.6
0475: * @provisional This API might change or be removed in a future release.
0476: */
0477: public Calendar getCalendar() {
0478: if (calendar == null) {
0479: return guessCalendar();
0480: }
0481: Calendar temp = (Calendar) calendar.clone(); // clone for safety
0482: temp.setTimeZone(getTimeZone());
0483: temp.setTimeInMillis(System.currentTimeMillis());
0484: return temp;
0485: }
0486:
0487: /**
0488: * Sets the timezone ID. If this has not been set, uses default for territory.
0489: *
0490: * @param timezone a valid TZID (see UTS#35).
0491: * @return this, for chaining
0492: * @draft ICU 3.6
0493: * @provisional This API might change or be removed in a future release.
0494: */
0495: public GlobalizationPreferences setTimeZone(TimeZone timezone) {
0496: if (isFrozen()) {
0497: throw new UnsupportedOperationException(
0498: "Attempt to modify immutable object");
0499: }
0500: this .timezone = (TimeZone) timezone.clone(); // clone for safety;
0501: return this ;
0502: }
0503:
0504: /**
0505: * Get the timezone. It was either explicitly set, or is
0506: * heuristically computed from other settings.
0507: *
0508: * @return timezone, either implicitly or explicitly set
0509: * @draft ICU 3.6
0510: * @provisional This API might change or be removed in a future release.
0511: */
0512: public TimeZone getTimeZone() {
0513: if (timezone == null) {
0514: return guessTimeZone();
0515: }
0516: return (TimeZone) timezone.clone(); // clone for safety
0517: }
0518:
0519: /**
0520: * Get a copy of the collator according to the settings.
0521: *
0522: * @return collator explicit or implicit.
0523: * @draft ICU 3.6
0524: * @provisional This API might change or be removed in a future release.
0525: */
0526: public Collator getCollator() {
0527: if (collator == null) {
0528: return guessCollator();
0529: }
0530: try {
0531: return (Collator) collator.clone(); // clone for safety
0532: } catch (CloneNotSupportedException e) {
0533: throw new IllegalStateException("Error in cloning collator");
0534: }
0535: }
0536:
0537: /**
0538: * Explicitly set the collator for this object.
0539: * @param collator
0540: * @return this, for chaining
0541: * @draft ICU 3.6
0542: * @provisional This API might change or be removed in a future release.
0543: */
0544: public GlobalizationPreferences setCollator(Collator collator) {
0545: if (isFrozen()) {
0546: throw new UnsupportedOperationException(
0547: "Attempt to modify immutable object");
0548: }
0549: try {
0550: this .collator = (Collator) collator.clone(); // clone for safety
0551: } catch (CloneNotSupportedException e) {
0552: throw new IllegalStateException("Error in cloning collator");
0553: }
0554: return this ;
0555: }
0556:
0557: /**
0558: * Get a copy of the break iterator for the specified type according to the
0559: * settings.
0560: *
0561: * @param type
0562: * break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE
0563: * @return break iterator explicit or implicit
0564: * @draft ICU 3.6
0565: * @provisional This API might change or be removed in a future release.
0566: */
0567: public BreakIterator getBreakIterator(int type) {
0568: if (type < BI_CHARACTER || type >= BI_LIMIT) {
0569: throw new IllegalArgumentException(
0570: "Illegal break iterator type");
0571: }
0572: if (breakIterators == null || breakIterators[type] == null) {
0573: return guessBreakIterator(type);
0574: }
0575: return (BreakIterator) breakIterators[type].clone(); // clone for safety
0576: }
0577:
0578: /**
0579: * Explicitly set the break iterator for this object.
0580: *
0581: * @param type
0582: * break type - BI_CHARACTER or BI_WORD, BI_LINE, BI_SENTENCE, BI_TITLE
0583: * @param iterator a break iterator
0584: * @return this, for chaining
0585: * @draft ICU 3.6
0586: * @provisional This API might change or be removed in a future release.
0587: */
0588: public GlobalizationPreferences setBreakIterator(int type,
0589: BreakIterator iterator) {
0590: if (type < BI_CHARACTER || type >= BI_LIMIT) {
0591: throw new IllegalArgumentException(
0592: "Illegal break iterator type");
0593: }
0594: if (isFrozen()) {
0595: throw new UnsupportedOperationException(
0596: "Attempt to modify immutable object");
0597: }
0598: if (breakIterators == null)
0599: breakIterators = new BreakIterator[BI_LIMIT];
0600: breakIterators[type] = (BreakIterator) iterator.clone(); // clone for safety
0601: return this ;
0602: }
0603:
0604: /**
0605: * Get the display name for an ID: language, script, territory, currency, timezone...
0606: * Uses the language priority list to do so.
0607: *
0608: * @param id language code, script code, ...
0609: * @param type specifies the type of the ID: ID_LANGUAGE, etc.
0610: * @return the display name
0611: * @draft ICU 3.6
0612: * @provisional This API might change or be removed in a future release.
0613: */
0614: public String getDisplayName(String id, int type) {
0615: String result = id;
0616: for (Iterator it = getLocales().iterator(); it.hasNext();) {
0617: ULocale locale = (ULocale) it.next();
0618: if (!isAvailableLocale(locale, TYPE_GENERIC)) {
0619: continue;
0620: }
0621: switch (type) {
0622: case ID_LOCALE:
0623: result = ULocale.getDisplayName(id, locale);
0624: break;
0625: case ID_LANGUAGE:
0626: result = ULocale.getDisplayLanguage(id, locale);
0627: break;
0628: case ID_SCRIPT:
0629: result = ULocale.getDisplayScript("und-" + id, locale);
0630: break;
0631: case ID_TERRITORY:
0632: result = ULocale.getDisplayCountry("und-" + id, locale);
0633: break;
0634: case ID_VARIANT:
0635: // TODO fix variant parsing
0636: result = ULocale.getDisplayVariant("und-QQ-" + id,
0637: locale);
0638: break;
0639: case ID_KEYWORD:
0640: result = ULocale.getDisplayKeyword(id, locale);
0641: break;
0642: case ID_KEYWORD_VALUE:
0643: String[] parts = new String[2];
0644: Utility.split(id, '=', parts);
0645: result = ULocale.getDisplayKeywordValue("und@" + id,
0646: parts[0], locale);
0647: // TODO fix to tell when successful
0648: if (result.equals(parts[1])) {
0649: continue;
0650: }
0651: break;
0652: case ID_CURRENCY_SYMBOL:
0653: case ID_CURRENCY:
0654: Currency temp = new Currency(id);
0655: result = temp.getName(locale,
0656: type == ID_CURRENCY ? Currency.LONG_NAME
0657: : Currency.SYMBOL_NAME, new boolean[1]);
0658: // TODO: have method that doesn't take parameter. Add
0659: // function to determine whether string is choice
0660: // format.
0661: // TODO: have method that doesn't require us
0662: // to create a currency
0663: break;
0664: case ID_TIMEZONE:
0665: SimpleDateFormat dtf = new SimpleDateFormat("vvvv",
0666: locale);
0667: dtf.setTimeZone(TimeZone.getTimeZone(id));
0668: result = dtf.format(new Date());
0669: // TODO, have method that doesn't require us to create a timezone
0670: // fix other hacks
0671: // hack for couldn't match
0672: // note, compiling with FOUNDATION omits this check for now
0673: //#ifndef FOUNDATION
0674: if (badTimeZone.reset(result).matches())
0675: continue;
0676: //#endif
0677: break;
0678: default:
0679: throw new IllegalArgumentException("Unknown type: "
0680: + type);
0681: }
0682:
0683: // TODO need better way of seeing if we fell back to root!!
0684: // This will not work at all for lots of stuff
0685: if (!id.equals(result)) {
0686: return result;
0687: }
0688: }
0689: return result;
0690: }
0691:
0692: //#ifndef FOUNDATION
0693: // TODO remove need for this
0694: private static final Matcher badTimeZone = Pattern.compile(
0695: "[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher("");
0696:
0697: //#endif
0698:
0699: /**
0700: * Set an explicit date format. Overrides the locale priority list for
0701: * a particular combination of dateStyle and timeStyle. DF_NONE should
0702: * be used if for the style, where only the date or time format individually
0703: * is being set.
0704: *
0705: * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
0706: * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
0707: * @param format The date format
0708: * @return this, for chaining
0709: * @draft ICU 3.6
0710: * @provisional This API might change or be removed in a future release.
0711: */
0712: public GlobalizationPreferences setDateFormat(int dateStyle,
0713: int timeStyle, DateFormat format) {
0714: if (isFrozen()) {
0715: throw new UnsupportedOperationException(
0716: "Attempt to modify immutable object");
0717: }
0718: if (dateFormats == null) {
0719: dateFormats = new DateFormat[DF_LIMIT][DF_LIMIT];
0720: }
0721: dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety
0722: return this ;
0723: }
0724:
0725: /**
0726: * Gets a date format according to the current settings. If there
0727: * is an explicit (non-null) date/time format set, a copy of that
0728: * is returned. Otherwise, the language priority list is used.
0729: * DF_NONE should be used for the style, where only the date or
0730: * time format individually is being gotten.
0731: *
0732: * @param dateStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
0733: * @param timeStyle DF_FULL, DF_LONG, DF_MEDIUM, DF_SHORT or DF_NONE
0734: * @return a DateFormat, according to the above description
0735: * @draft ICU 3.6
0736: * @provisional This API might change or be removed in a future release.
0737: */
0738: public DateFormat getDateFormat(int dateStyle, int timeStyle) {
0739: if (dateStyle == DF_NONE && timeStyle == DF_NONE
0740: || dateStyle < 0 || dateStyle >= DF_LIMIT
0741: || timeStyle < 0 || timeStyle >= DF_LIMIT) {
0742: throw new IllegalArgumentException(
0743: "Illegal date format style arguments");
0744: }
0745: DateFormat result = null;
0746: if (dateFormats != null) {
0747: result = dateFormats[dateStyle][timeStyle];
0748: }
0749: if (result != null) {
0750: result = (DateFormat) result.clone(); // clone for safety
0751: // Not sure overriding configuration is what we really want...
0752: result.setTimeZone(getTimeZone());
0753: } else {
0754: result = guessDateFormat(dateStyle, timeStyle);
0755: }
0756: return result;
0757: }
0758:
0759: /**
0760: * Gets a number format according to the current settings. If
0761: * there is an explicit (non-null) number format set, a copy of
0762: * that is returned. Otherwise, the language priority list is
0763: * used.
0764: *
0765: * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER
0766: * @draft ICU 3.6
0767: * @provisional This API might change or be removed in a future release.
0768: */
0769: public NumberFormat getNumberFormat(int style) {
0770: if (style < 0 || style >= NF_LIMIT) {
0771: throw new IllegalArgumentException(
0772: "Illegal number format type");
0773: }
0774: NumberFormat result = null;
0775: if (numberFormats != null) {
0776: result = numberFormats[style];
0777: }
0778: if (result != null) {
0779: result = (NumberFormat) result.clone(); // clone for safety (later optimize)
0780: } else {
0781: result = guessNumberFormat(style);
0782: }
0783: return result;
0784: }
0785:
0786: /**
0787: * Sets a number format explicitly. Overrides the general locale settings.
0788: *
0789: * @param style NF_NUMBER, NF_CURRENCY, NF_PERCENT, NF_SCIENTIFIC, NF_INTEGER
0790: * @param format The number format
0791: * @return this, for chaining
0792: * @draft ICU 3.6
0793: * @provisional This API might change or be removed in a future release.
0794: */
0795: public GlobalizationPreferences setNumberFormat(int style,
0796: NumberFormat format) {
0797: if (isFrozen()) {
0798: throw new UnsupportedOperationException(
0799: "Attempt to modify immutable object");
0800: }
0801: if (numberFormats == null) {
0802: numberFormats = new NumberFormat[NF_LIMIT];
0803: }
0804: numberFormats[style] = (NumberFormat) format.clone(); // for safety
0805: return this ;
0806: }
0807:
0808: /**
0809: * Restore the object to the initial state.
0810: *
0811: * @return this, for chaining
0812: * @draft ICU 3.6
0813: * @provisional This API might change or be removed in a future release.
0814: */
0815: public GlobalizationPreferences reset() {
0816: if (isFrozen()) {
0817: throw new UnsupportedOperationException(
0818: "Attempt to modify immutable object");
0819: }
0820: locales = null;
0821: territory = null;
0822: calendar = null;
0823: collator = null;
0824: breakIterators = null;
0825: timezone = null;
0826: currency = null;
0827: dateFormats = null;
0828: numberFormats = null;
0829: implicitLocales = null;
0830: return this ;
0831: }
0832:
0833: /**
0834: * Process a language/locale priority list specified via <code>setLocales</code>.
0835: * The input locale list may be expanded or re-ordered to represent the prioritized
0836: * language/locale order actually used by this object by the algorithm exaplained
0837: * below.
0838: * <br>
0839: * <br>
0840: * <b>Step 1</b>: Move later occurence of more specific locale before ealier occurence of less
0841: * specific locale.
0842: * <br>
0843: * Before: en, fr_FR, en_US, en_GB
0844: * <br>
0845: * After: en_US, en_GB, en, fr_FR
0846: * <br>
0847: * <br>
0848: * <b>Step 2</b>: Append a fallback locale to each locale.
0849: * <br>
0850: * Before: en_US, en_GB, en, fr_FR
0851: * <br>
0852: * After: en_US, en, en_GB, en, en, fr_FR, fr
0853: * <br>
0854: * <br>
0855: * <b>Step 3</b>: Remove ealier occurence of duplicated locale entries.
0856: * <br>
0857: * Before: en_US, en, en_GB, en, en, fr_FR, fr
0858: * <br>
0859: * After: en_US, en_GB, en, fr_FR, fr
0860: * <br>
0861: * <br>
0862: * The final locale list is used to produce a default value for the appropriate territory,
0863: * currency, timezone, etc. The list also represents the lookup order used in
0864: * <code>getResourceBundle</code> for this object. A subclass may override this method
0865: * to customize the algorithm used for populating the locale list.
0866: *
0867: * @param inputLocales The list of input locales
0868: * @draft ICU 3.6
0869: * @provisional This API might change or be removed in a future release.
0870: */
0871: protected List processLocales(List inputLocales) {
0872: List result = new ArrayList();
0873: /*
0874: * Step 1: Relocate later occurence of more specific locale
0875: * before earlier occurence of less specific locale.
0876: *
0877: * Example:
0878: * Before - en_US, fr_FR, zh, en_US_Boston, zh_TW, zh_Hant, fr_CA
0879: * After - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
0880: */
0881: for (int i = 0; i < inputLocales.size(); i++) {
0882: ULocale uloc = (ULocale) inputLocales.get(i);
0883:
0884: String language = uloc.getLanguage();
0885: String script = uloc.getScript();
0886: String country = uloc.getCountry();
0887: String variant = uloc.getVariant();
0888:
0889: boolean bInserted = false;
0890: for (int j = 0; j < result.size(); j++) {
0891: // Check if this locale is more specific
0892: // than existing locale entries already inserted
0893: // in the destination list
0894: ULocale u = (ULocale) result.get(j);
0895: if (!u.getLanguage().equals(language)) {
0896: continue;
0897: }
0898: String s = u.getScript();
0899: String c = u.getCountry();
0900: String v = u.getVariant();
0901: if (!s.equals(script)) {
0902: if (s.length() == 0 && c.length() == 0
0903: && v.length() == 0) {
0904: result.add(j, uloc);
0905: bInserted = true;
0906: break;
0907: } else if (s.length() == 0 && c.equals(country)) {
0908: // We want to see zh_Hant_HK before zh_HK
0909: result.add(j, uloc);
0910: bInserted = true;
0911: break;
0912: } else if (script.length() == 0
0913: && country.length() > 0 && c.length() == 0) {
0914: // We want to see zh_HK before zh_Hant
0915: result.add(j, uloc);
0916: bInserted = true;
0917: break;
0918: }
0919: continue;
0920: }
0921: if (!c.equals(country)) {
0922: if (c.length() == 0 && v.length() == 0) {
0923: result.add(j, uloc);
0924: bInserted = true;
0925: break;
0926: }
0927: }
0928: if (!v.equals(variant) && v.length() == 0) {
0929: result.add(j, uloc);
0930: bInserted = true;
0931: break;
0932: }
0933: }
0934: if (!bInserted) {
0935: // Add this locale at the end of the list
0936: result.add(uloc);
0937: }
0938: }
0939:
0940: // TODO: Locale aliases might be resolved here
0941: // For example, zh_Hant_TW = zh_TW
0942:
0943: /*
0944: * Step 2: Append fallback locales for each entry
0945: *
0946: * Example:
0947: * Before - en_US_Boston, en_US, fr_FR, zh_TW, zh_Hant, zh, fr_CA
0948: * After - en_US_Boston, en_US, en, en_US, en, fr_FR, fr,
0949: * zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr
0950: */
0951: int index = 0;
0952: while (index < result.size()) {
0953: ULocale uloc = (ULocale) result.get(index);
0954: while (true) {
0955: uloc = uloc.getFallback();
0956: if (uloc.getLanguage().length() == 0) {
0957: break;
0958: }
0959: index++;
0960: result.add(index, uloc);
0961: }
0962: index++;
0963: }
0964:
0965: /*
0966: * Step 3: Remove earlier occurence of duplicated locales
0967: *
0968: * Example:
0969: * Before - en_US_Boston, en_US, en, en_US, en, fr_FR, fr,
0970: * zh_TW, zn, zh_Hant, zh, zh, fr_CA, fr
0971: * After - en_US_Boston, en_US, en, fr_FR, zh_TW, zh_Hant,
0972: * zh, fr_CA, fr
0973: */
0974: index = 0;
0975: while (index < result.size() - 1) {
0976: ULocale uloc = (ULocale) result.get(index);
0977: boolean bRemoved = false;
0978: for (int i = index + 1; i < result.size(); i++) {
0979: if (uloc.equals((ULocale) result.get(i))) {
0980: // Remove ealier one
0981: result.remove(index);
0982: bRemoved = true;
0983: break;
0984: }
0985: }
0986: if (!bRemoved) {
0987: index++;
0988: }
0989: }
0990: return result;
0991: }
0992:
0993: /**
0994: * This function can be overridden by subclasses to use different heuristics.
0995: * <b>It MUST return a 'safe' value,
0996: * one whose modification will not affect this object.</b>
0997: *
0998: * @param dateStyle
0999: * @param timeStyle
1000: * @draft ICU 3.6
1001: * @provisional This API might change or be removed in a future release.
1002: */
1003: protected DateFormat guessDateFormat(int dateStyle, int timeStyle) {
1004: DateFormat result;
1005: ULocale dfLocale = getAvailableLocale(TYPE_DATEFORMAT);
1006: if (dfLocale == null) {
1007: dfLocale = ULocale.ROOT;
1008: }
1009: if (timeStyle == DF_NONE) {
1010: result = DateFormat.getDateInstance(getCalendar(),
1011: dateStyle, dfLocale);
1012: } else if (dateStyle == DF_NONE) {
1013: result = DateFormat.getTimeInstance(getCalendar(),
1014: timeStyle, dfLocale);
1015: } else {
1016: result = DateFormat.getDateTimeInstance(getCalendar(),
1017: dateStyle, timeStyle, dfLocale);
1018: }
1019: return result;
1020: }
1021:
1022: /**
1023: * This function can be overridden by subclasses to use different heuristics.
1024: * <b>It MUST return a 'safe' value,
1025: * one whose modification will not affect this object.</b>
1026: *
1027: * @param style
1028: * @draft ICU 3.6
1029: * @provisional This API might change or be removed in a future release.
1030: */
1031: protected NumberFormat guessNumberFormat(int style) {
1032: NumberFormat result;
1033: ULocale nfLocale = getAvailableLocale(TYPE_NUMBERFORMAT);
1034: if (nfLocale == null) {
1035: nfLocale = ULocale.ROOT;
1036: }
1037: switch (style) {
1038: case NF_NUMBER:
1039: result = NumberFormat.getInstance(nfLocale);
1040: break;
1041: case NF_SCIENTIFIC:
1042: result = NumberFormat.getScientificInstance(nfLocale);
1043: break;
1044: case NF_INTEGER:
1045: result = NumberFormat.getIntegerInstance(nfLocale);
1046: break;
1047: case NF_PERCENT:
1048: result = NumberFormat.getPercentInstance(nfLocale);
1049: break;
1050: case NF_CURRENCY:
1051: result = NumberFormat.getCurrencyInstance(nfLocale);
1052: result.setCurrency(getCurrency());
1053: break;
1054: default:
1055: throw new IllegalArgumentException(
1056: "Unknown number format style");
1057: }
1058: return result;
1059: }
1060:
1061: /**
1062: * This function can be overridden by subclasses to use different heuristics.
1063: *
1064: * @draft ICU 3.6
1065: * @provisional This API might change or be removed in a future release.
1066: */
1067: protected String guessTerritory() {
1068: String result;
1069: // pass through locales to see if there is a territory.
1070: for (Iterator it = getLocales().iterator(); it.hasNext();) {
1071: ULocale locale = (ULocale) it.next();
1072: result = locale.getCountry();
1073: if (result.length() != 0) {
1074: return result;
1075: }
1076: }
1077: // if not, guess from the first language tag, or maybe from
1078: // intersection of languages, eg nl + fr => BE
1079: // TODO: fix using real data
1080: // for now, just use fixed values
1081: ULocale firstLocale = getLocale(0);
1082: String language = firstLocale.getLanguage();
1083: String script = firstLocale.getScript();
1084: result = null;
1085: if (script.length() != 0) {
1086: result = (String) language_territory_hack_map.get(language
1087: + "_" + script);
1088: }
1089: if (result == null) {
1090: result = (String) language_territory_hack_map.get(language);
1091: }
1092: if (result == null) {
1093: result = "US"; // need *some* default
1094: }
1095: return result;
1096: }
1097:
1098: /**
1099: * This function can be overridden by subclasses to use different heuristics
1100: *
1101: * @draft ICU 3.6
1102: * @provisional This API might change or be removed in a future release.
1103: */
1104: protected Currency guessCurrency() {
1105: return Currency
1106: .getInstance(new ULocale("und-" + getTerritory()));
1107: }
1108:
1109: /**
1110: * This function can be overridden by subclasses to use different heuristics
1111: * <b>It MUST return a 'safe' value,
1112: * one whose modification will not affect this object.</b>
1113: *
1114: * @draft ICU 3.6
1115: * @provisional This API might change or be removed in a future release.
1116: */
1117: protected List guessLocales() {
1118: if (implicitLocales == null) {
1119: List result = new ArrayList(1);
1120: result.add(ULocale.getDefault());
1121: implicitLocales = processLocales(result);
1122: }
1123: return implicitLocales;
1124: }
1125:
1126: /**
1127: * This function can be overridden by subclasses to use different heuristics.
1128: * <b>It MUST return a 'safe' value,
1129: * one whose modification will not affect this object.</b>
1130: *
1131: * @draft ICU 3.6
1132: * @provisional This API might change or be removed in a future release.
1133: */
1134: protected Collator guessCollator() {
1135: ULocale collLocale = getAvailableLocale(TYPE_COLLATOR);
1136: if (collLocale == null) {
1137: collLocale = ULocale.ROOT;
1138: }
1139: return Collator.getInstance(collLocale);
1140: }
1141:
1142: /**
1143: * This function can be overridden by subclasses to use different heuristics.
1144: * <b>It MUST return a 'safe' value,
1145: * one whose modification will not affect this object.</b>
1146: *
1147: * @param type
1148: * @draft ICU 3.6
1149: * @provisional This API might change or be removed in a future release.
1150: */
1151: protected BreakIterator guessBreakIterator(int type) {
1152: BreakIterator bitr = null;
1153: ULocale brkLocale = getAvailableLocale(TYPE_BREAKITERATOR);
1154: if (brkLocale == null) {
1155: brkLocale = ULocale.ROOT;
1156: }
1157: switch (type) {
1158: case BI_CHARACTER:
1159: bitr = BreakIterator.getCharacterInstance(brkLocale);
1160: break;
1161: case BI_TITLE:
1162: bitr = BreakIterator.getTitleInstance(brkLocale);
1163: break;
1164: case BI_WORD:
1165: bitr = BreakIterator.getWordInstance(brkLocale);
1166: break;
1167: case BI_LINE:
1168: bitr = BreakIterator.getLineInstance(brkLocale);
1169: break;
1170: case BI_SENTENCE:
1171: bitr = BreakIterator.getSentenceInstance(brkLocale);
1172: break;
1173: default:
1174: throw new IllegalArgumentException(
1175: "Unknown break iterator type");
1176: }
1177: return bitr;
1178: }
1179:
1180: /**
1181: * This function can be overridden by subclasses to use different heuristics.
1182: * <b>It MUST return a 'safe' value,
1183: * one whose modification will not affect this object.</b>
1184: *
1185: * @draft ICU 3.6
1186: * @provisional This API might change or be removed in a future release.
1187: */
1188: protected TimeZone guessTimeZone() {
1189: // TODO fix using real data
1190: // for single-zone countries, pick that zone
1191: // for others, pick the most populous zone
1192: // for now, just use fixed value
1193: // NOTE: in a few cases can do better by looking at language.
1194: // Eg haw+US should go to Pacific/Honolulu
1195: // fr+CA should go to America/Montreal
1196: String timezoneString = (String) territory_tzid_hack_map
1197: .get(getTerritory());
1198: if (timezoneString == null) {
1199: String[] attempt = ZoneMeta.getAvailableIDs(getTerritory());
1200: if (attempt.length == 0) {
1201: timezoneString = "Etc/GMT"; // gotta do something
1202: } else {
1203: int i;
1204: // this all needs to be fixed to use real data. But for now, do slightly better by skipping cruft
1205: for (i = 0; i < attempt.length; ++i) {
1206: if (attempt[i].indexOf("/") >= 0)
1207: break;
1208: }
1209: if (i > attempt.length)
1210: i = 0;
1211: timezoneString = attempt[i];
1212: }
1213: }
1214: return TimeZone.getTimeZone(timezoneString);
1215: }
1216:
1217: /**
1218: * This function can be overridden by subclasses to use different heuristics.
1219: * <b>It MUST return a 'safe' value,
1220: * one whose modification will not affect this object.</b>
1221: *
1222: * @draft ICU 3.6
1223: * @provisional This API might change or be removed in a future release.
1224: */
1225: protected Calendar guessCalendar() {
1226: ULocale calLocale = getAvailableLocale(TYPE_CALENDAR);
1227: if (calLocale == null) {
1228: calLocale = ULocale.US;
1229: }
1230: return Calendar.getInstance(getTimeZone(), calLocale);
1231: }
1232:
1233: // PRIVATES
1234:
1235: private List locales;
1236: private String territory;
1237: private Currency currency;
1238: private TimeZone timezone;
1239: private Calendar calendar;
1240: private Collator collator;
1241: private BreakIterator[] breakIterators;
1242: private DateFormat[][] dateFormats;
1243: private NumberFormat[] numberFormats;
1244: private List implicitLocales;
1245:
1246: {
1247: reset();
1248: }
1249:
1250: private ULocale getAvailableLocale(int type) {
1251: List locs = getLocales();
1252: ULocale result = null;
1253: for (int i = 0; i < locs.size(); i++) {
1254: ULocale l = (ULocale) locs.get(i);
1255: if (isAvailableLocale(l, type)) {
1256: result = l;
1257: break;
1258: }
1259: }
1260: return result;
1261: }
1262:
1263: private boolean isAvailableLocale(ULocale loc, int type) {
1264: BitSet bits = (BitSet) available_locales.get(loc);
1265: if (bits != null && bits.get(type)) {
1266: return true;
1267: }
1268: return false;
1269: }
1270:
1271: /*
1272: * Available locales for service types
1273: */
1274: private static final HashMap available_locales = new HashMap();
1275: private static final int TYPE_GENERIC = 0, TYPE_CALENDAR = 1,
1276: TYPE_DATEFORMAT = 2, TYPE_NUMBERFORMAT = 3,
1277: TYPE_COLLATOR = 4, TYPE_BREAKITERATOR = 5,
1278: TYPE_LIMIT = TYPE_BREAKITERATOR + 1;
1279:
1280: static {
1281: BitSet bits;
1282: ULocale[] allLocales = ULocale.getAvailableLocales();
1283: for (int i = 0; i < allLocales.length; i++) {
1284: bits = new BitSet(TYPE_LIMIT);
1285: available_locales.put(allLocales[i], bits);
1286: bits.set(TYPE_GENERIC);
1287: }
1288:
1289: ULocale[] calLocales = Calendar.getAvailableULocales();
1290: for (int i = 0; i < calLocales.length; i++) {
1291: bits = (BitSet) available_locales.get(calLocales[i]);
1292: if (bits == null) {
1293: bits = new BitSet(TYPE_LIMIT);
1294: available_locales.put(allLocales[i], bits);
1295: }
1296: bits.set(TYPE_CALENDAR);
1297: }
1298:
1299: ULocale[] dateLocales = DateFormat.getAvailableULocales();
1300: for (int i = 0; i < dateLocales.length; i++) {
1301: bits = (BitSet) available_locales.get(dateLocales[i]);
1302: if (bits == null) {
1303: bits = new BitSet(TYPE_LIMIT);
1304: available_locales.put(allLocales[i], bits);
1305: }
1306: bits.set(TYPE_DATEFORMAT);
1307: }
1308:
1309: ULocale[] numLocales = NumberFormat.getAvailableULocales();
1310: for (int i = 0; i < numLocales.length; i++) {
1311: bits = (BitSet) available_locales.get(numLocales[i]);
1312: if (bits == null) {
1313: bits = new BitSet(TYPE_LIMIT);
1314: available_locales.put(allLocales[i], bits);
1315: }
1316: bits.set(TYPE_NUMBERFORMAT);
1317: }
1318:
1319: ULocale[] collLocales = Collator.getAvailableULocales();
1320: for (int i = 0; i < collLocales.length; i++) {
1321: bits = (BitSet) available_locales.get(collLocales[i]);
1322: if (bits == null) {
1323: bits = new BitSet(TYPE_LIMIT);
1324: available_locales.put(allLocales[i], bits);
1325: }
1326: bits.set(TYPE_COLLATOR);
1327: }
1328:
1329: ULocale[] brkLocales = BreakIterator.getAvailableULocales();
1330: for (int i = 0; i < brkLocales.length; i++) {
1331: bits = (BitSet) available_locales.get(brkLocales[i]);
1332: bits.set(TYPE_BREAKITERATOR);
1333: }
1334: }
1335:
1336: /** WARNING: All of this data is temporary, until we start importing from CLDR!!!
1337: *
1338: */
1339: private static final Map language_territory_hack_map = new HashMap();
1340: private static final String[][] language_territory_hack = {
1341: { "af", "ZA" }, { "am", "ET" }, { "ar", "SA" },
1342: { "as", "IN" }, { "ay", "PE" }, { "az", "AZ" },
1343: { "bal", "PK" }, { "be", "BY" }, { "bg", "BG" },
1344: { "bn", "IN" }, { "bs", "BA" }, { "ca", "ES" },
1345: { "ch", "MP" }, { "cpe", "SL" }, { "cs", "CZ" },
1346: { "cy", "GB" }, { "da", "DK" }, { "de", "DE" },
1347: { "dv", "MV" }, { "dz", "BT" }, { "el", "GR" },
1348: { "en", "US" }, { "es", "ES" }, { "et", "EE" },
1349: { "eu", "ES" }, { "fa", "IR" }, { "fi", "FI" },
1350: { "fil", "PH" }, { "fj", "FJ" }, { "fo", "FO" },
1351: { "fr", "FR" }, { "ga", "IE" }, { "gd", "GB" },
1352: { "gl", "ES" }, { "gn", "PY" }, { "gu", "IN" },
1353: { "gv", "GB" }, { "ha", "NG" }, { "he", "IL" },
1354: { "hi", "IN" }, { "ho", "PG" }, { "hr", "HR" },
1355: { "ht", "HT" }, { "hu", "HU" }, { "hy", "AM" },
1356: { "id", "ID" }, { "is", "IS" }, { "it", "IT" },
1357: { "ja", "JP" }, { "ka", "GE" }, { "kk", "KZ" },
1358: { "kl", "GL" }, { "km", "KH" }, { "kn", "IN" },
1359: { "ko", "KR" }, { "kok", "IN" }, { "ks", "IN" },
1360: { "ku", "TR" }, { "ky", "KG" }, { "la", "VA" },
1361: { "lb", "LU" }, { "ln", "CG" }, { "lo", "LA" },
1362: { "lt", "LT" }, { "lv", "LV" }, { "mai", "IN" },
1363: { "men", "GN" }, { "mg", "MG" }, { "mh", "MH" },
1364: { "mk", "MK" }, { "ml", "IN" }, { "mn", "MN" },
1365: { "mni", "IN" }, { "mo", "MD" }, { "mr", "IN" },
1366: { "ms", "MY" }, { "mt", "MT" }, { "my", "MM" },
1367: { "na", "NR" }, { "nb", "NO" }, { "nd", "ZA" },
1368: { "ne", "NP" }, { "niu", "NU" }, { "nl", "NL" },
1369: { "nn", "NO" }, { "no", "NO" }, { "nr", "ZA" },
1370: { "nso", "ZA" }, { "ny", "MW" }, { "om", "KE" },
1371: { "or", "IN" }, { "pa", "IN" }, { "pau", "PW" },
1372: { "pl", "PL" }, { "ps", "PK" }, { "pt", "BR" },
1373: { "qu", "PE" }, { "rn", "BI" }, { "ro", "RO" },
1374: { "ru", "RU" }, { "rw", "RW" }, { "sd", "IN" },
1375: { "sg", "CF" }, { "si", "LK" }, { "sk", "SK" },
1376: { "sl", "SI" }, { "sm", "WS" }, { "so", "DJ" },
1377: { "sq", "CS" }, { "sr", "CS" }, { "ss", "ZA" },
1378: { "st", "ZA" }, { "sv", "SE" }, { "sw", "KE" },
1379: { "ta", "IN" }, { "te", "IN" }, { "tem", "SL" },
1380: { "tet", "TL" }, { "th", "TH" }, { "ti", "ET" },
1381: { "tg", "TJ" }, { "tk", "TM" }, { "tkl", "TK" },
1382: { "tvl", "TV" }, { "tl", "PH" }, { "tn", "ZA" },
1383: { "to", "TO" }, { "tpi", "PG" }, { "tr", "TR" },
1384: { "ts", "ZA" }, { "uk", "UA" }, { "ur", "IN" },
1385: { "uz", "UZ" }, { "ve", "ZA" }, { "vi", "VN" },
1386: { "wo", "SN" }, { "xh", "ZA" }, { "zh", "CN" },
1387: { "zh_Hant", "TW" }, { "zu", "ZA" }, { "aa", "ET" },
1388: { "byn", "ER" }, { "eo", "DE" }, { "gez", "ET" },
1389: { "haw", "US" }, { "iu", "CA" }, { "kw", "GB" },
1390: { "sa", "IN" }, { "sh", "HR" }, { "sid", "ET" },
1391: { "syr", "SY" }, { "tig", "ER" }, { "tt", "RU" },
1392: { "wal", "ET" }, };
1393: static {
1394: for (int i = 0; i < language_territory_hack.length; ++i) {
1395: language_territory_hack_map.put(
1396: language_territory_hack[i][0],
1397: language_territory_hack[i][1]);
1398: }
1399: }
1400:
1401: static final Map territory_tzid_hack_map = new HashMap();
1402: static final String[][] territory_tzid_hack = {
1403: { "AQ", "Antarctica/McMurdo" },
1404: { "AR", "America/Buenos_Aires" },
1405: { "AU", "Australia/Sydney" },
1406: { "BR", "America/Sao_Paulo" }, { "CA", "America/Toronto" },
1407: { "CD", "Africa/Kinshasa" }, { "CL", "America/Santiago" },
1408: { "CN", "Asia/Shanghai" }, { "EC", "America/Guayaquil" },
1409: { "ES", "Europe/Madrid" }, { "GB", "Europe/London" },
1410: { "GL", "America/Godthab" }, { "ID", "Asia/Jakarta" },
1411: { "ML", "Africa/Bamako" }, { "MX", "America/Mexico_City" },
1412: { "MY", "Asia/Kuala_Lumpur" },
1413: { "NZ", "Pacific/Auckland" }, { "PT", "Europe/Lisbon" },
1414: { "RU", "Europe/Moscow" }, { "UA", "Europe/Kiev" },
1415: { "US", "America/New_York" }, { "UZ", "Asia/Tashkent" },
1416: { "PF", "Pacific/Tahiti" }, { "FM", "Pacific/Kosrae" },
1417: { "KI", "Pacific/Tarawa" }, { "KZ", "Asia/Almaty" },
1418: { "MH", "Pacific/Majuro" }, { "MN", "Asia/Ulaanbaatar" },
1419: { "SJ", "Arctic/Longyearbyen" },
1420: { "UM", "Pacific/Midway" }, };
1421: static {
1422: for (int i = 0; i < territory_tzid_hack.length; ++i) {
1423: territory_tzid_hack_map.put(territory_tzid_hack[i][0],
1424: territory_tzid_hack[i][1]);
1425: }
1426: }
1427:
1428: // Freezable implmentation
1429:
1430: private boolean frozen;
1431:
1432: /**
1433: * @draft ICU 3.6
1434: * @provisional This API might change or be removed in a future release.
1435: */
1436: public boolean isFrozen() {
1437: return frozen;
1438: }
1439:
1440: /**
1441: * @draft ICU 3.6
1442: * @provisional This API might change or be removed in a future release.
1443: */
1444: public Object freeze() {
1445: frozen = true;
1446: return this ;
1447: }
1448:
1449: /**
1450: * @draft ICU 3.6
1451: * @provisional This API might change or be removed in a future release.
1452: */
1453: public Object cloneAsThawed() {
1454: try {
1455: GlobalizationPreferences result = (GlobalizationPreferences) clone();
1456: result.frozen = false;
1457: return result;
1458: } catch (CloneNotSupportedException e) {
1459: // will always work
1460: return null;
1461: }
1462: }
1463: }
|