0001: /*
0002: * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: /*
0027: * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
0028: * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
0029: *
0030: * The original version of this source code and documentation
0031: * is copyrighted and owned by Taligent, Inc., a wholly-owned
0032: * subsidiary of IBM. These materials are provided under terms
0033: * of a License Agreement between Taligent and Sun. This technology
0034: * is protected by multiple US and International patents.
0035: *
0036: * This notice and attribution to Taligent may not be removed.
0037: * Taligent is a registered trademark of Taligent, Inc.
0038: *
0039: */
0040:
0041: package java.util;
0042:
0043: import java.io.*;
0044: import java.security.AccessController;
0045: import java.text.MessageFormat;
0046: import java.util.List;
0047: import java.util.concurrent.ConcurrentHashMap;
0048: import java.util.spi.LocaleNameProvider;
0049: import java.util.spi.LocaleServiceProvider;
0050: import sun.security.action.GetPropertyAction;
0051: import sun.util.LocaleServiceProviderPool;
0052: import sun.util.resources.LocaleData;
0053: import sun.util.resources.OpenListResourceBundle;
0054:
0055: /**
0056: *
0057: * A <code>Locale</code> object represents a specific geographical, political,
0058: * or cultural region. An operation that requires a <code>Locale</code> to perform
0059: * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
0060: * to tailor information for the user. For example, displaying a number
0061: * is a locale-sensitive operation--the number should be formatted
0062: * according to the customs/conventions of the user's native country,
0063: * region, or culture.
0064: *
0065: * <P>
0066: * Create a <code>Locale</code> object using the constructors in this class:
0067: * <blockquote>
0068: * <pre>
0069: * Locale(String language)
0070: * Locale(String language, String country)
0071: * Locale(String language, String country, String variant)
0072: * </pre>
0073: * </blockquote>
0074: * The language argument is a valid <STRONG>ISO Language Code.</STRONG>
0075: * These codes are the lower-case, two-letter codes as defined by ISO-639.
0076: * You can find a full list of these codes at a number of sites, such as:
0077: * <BR><a href ="http://www.loc.gov/standards/iso639-2/php/English_list.php">
0078: * <code>http://www.loc.gov/standards/iso639-2/php/English_list.php</code></a>
0079: *
0080: * <P>
0081: * The country argument is a valid <STRONG>ISO Country Code.</STRONG> These
0082: * codes are the upper-case, two-letter codes as defined by ISO-3166.
0083: * You can find a full list of these codes at a number of sites, such as:
0084: * <BR><a href="http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">
0085: * <code>http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html</code></a>
0086: *
0087: * <P>
0088: * The variant argument is a vendor or browser-specific code.
0089: * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
0090: * Where there are two variants, separate them with an underscore, and
0091: * put the most important one first. For example, a Traditional Spanish collation
0092: * might construct a locale with parameters for language, country and variant as:
0093: * "es", "ES", "Traditional_WIN".
0094: *
0095: * <P>
0096: * Because a <code>Locale</code> object is just an identifier for a region,
0097: * no validity check is performed when you construct a <code>Locale</code>.
0098: * If you want to see whether particular resources are available for the
0099: * <code>Locale</code> you construct, you must query those resources. For
0100: * example, ask the <code>NumberFormat</code> for the locales it supports
0101: * using its <code>getAvailableLocales</code> method.
0102: * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
0103: * locale, you get back the best available match, not necessarily
0104: * precisely what you asked for. For more information, look at
0105: * {@link ResourceBundle}.
0106: *
0107: * <P>
0108: * The <code>Locale</code> class provides a number of convenient constants
0109: * that you can use to create <code>Locale</code> objects for commonly used
0110: * locales. For example, the following creates a <code>Locale</code> object
0111: * for the United States:
0112: * <blockquote>
0113: * <pre>
0114: * Locale.US
0115: * </pre>
0116: * </blockquote>
0117: *
0118: * <P>
0119: * Once you've created a <code>Locale</code> you can query it for information about
0120: * itself. Use <code>getCountry</code> to get the ISO Country Code and
0121: * <code>getLanguage</code> to get the ISO Language Code. You can
0122: * use <code>getDisplayCountry</code> to get the
0123: * name of the country suitable for displaying to the user. Similarly,
0124: * you can use <code>getDisplayLanguage</code> to get the name of
0125: * the language suitable for displaying to the user. Interestingly,
0126: * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
0127: * and have two versions: one that uses the default locale and one
0128: * that uses the locale specified as an argument.
0129: *
0130: * <P>
0131: * The Java Platform provides a number of classes that perform locale-sensitive
0132: * operations. For example, the <code>NumberFormat</code> class formats
0133: * numbers, currency, or percentages in a locale-sensitive manner. Classes
0134: * such as <code>NumberFormat</code> have a number of convenience methods
0135: * for creating a default object of that type. For example, the
0136: * <code>NumberFormat</code> class provides these three convenience methods
0137: * for creating a default <code>NumberFormat</code> object:
0138: * <blockquote>
0139: * <pre>
0140: * NumberFormat.getInstance()
0141: * NumberFormat.getCurrencyInstance()
0142: * NumberFormat.getPercentInstance()
0143: * </pre>
0144: * </blockquote>
0145: * These methods have two variants; one with an explicit locale
0146: * and one without; the latter using the default locale.
0147: * <blockquote>
0148: * <pre>
0149: * NumberFormat.getInstance(myLocale)
0150: * NumberFormat.getCurrencyInstance(myLocale)
0151: * NumberFormat.getPercentInstance(myLocale)
0152: * </pre>
0153: * </blockquote>
0154: * A <code>Locale</code> is the mechanism for identifying the kind of object
0155: * (<code>NumberFormat</code>) that you would like to get. The locale is
0156: * <STRONG>just</STRONG> a mechanism for identifying objects,
0157: * <STRONG>not</STRONG> a container for the objects themselves.
0158: *
0159: * @see ResourceBundle
0160: * @see java.text.Format
0161: * @see java.text.NumberFormat
0162: * @see java.text.Collator
0163: * @author Mark Davis
0164: * @since 1.1
0165: */
0166:
0167: public final class Locale implements Cloneable, Serializable {
0168:
0169: // cache to store singleton Locales
0170: private final static ConcurrentHashMap<String, Locale> cache = new ConcurrentHashMap<String, Locale>(
0171: 32);
0172:
0173: /** Useful constant for language.
0174: */
0175: static public final Locale ENGLISH = createSingleton("en__", "en",
0176: "");
0177:
0178: /** Useful constant for language.
0179: */
0180: static public final Locale FRENCH = createSingleton("fr__", "fr",
0181: "");
0182:
0183: /** Useful constant for language.
0184: */
0185: static public final Locale GERMAN = createSingleton("de__", "de",
0186: "");
0187:
0188: /** Useful constant for language.
0189: */
0190: static public final Locale ITALIAN = createSingleton("it__", "it",
0191: "");
0192:
0193: /** Useful constant for language.
0194: */
0195: static public final Locale JAPANESE = createSingleton("ja__", "ja",
0196: "");
0197:
0198: /** Useful constant for language.
0199: */
0200: static public final Locale KOREAN = createSingleton("ko__", "ko",
0201: "");
0202:
0203: /** Useful constant for language.
0204: */
0205: static public final Locale CHINESE = createSingleton("zh__", "zh",
0206: "");
0207:
0208: /** Useful constant for language.
0209: */
0210: static public final Locale SIMPLIFIED_CHINESE = createSingleton(
0211: "zh_CN_", "zh", "CN");
0212:
0213: /** Useful constant for language.
0214: */
0215: static public final Locale TRADITIONAL_CHINESE = createSingleton(
0216: "zh_TW_", "zh", "TW");
0217:
0218: /** Useful constant for country.
0219: */
0220: static public final Locale FRANCE = createSingleton("fr_FR_", "fr",
0221: "FR");
0222:
0223: /** Useful constant for country.
0224: */
0225: static public final Locale GERMANY = createSingleton("de_DE_",
0226: "de", "DE");
0227:
0228: /** Useful constant for country.
0229: */
0230: static public final Locale ITALY = createSingleton("it_IT_", "it",
0231: "IT");
0232:
0233: /** Useful constant for country.
0234: */
0235: static public final Locale JAPAN = createSingleton("ja_JP_", "ja",
0236: "JP");
0237:
0238: /** Useful constant for country.
0239: */
0240: static public final Locale KOREA = createSingleton("ko_KR_", "ko",
0241: "KR");
0242:
0243: /** Useful constant for country.
0244: */
0245: static public final Locale CHINA = SIMPLIFIED_CHINESE;
0246:
0247: /** Useful constant for country.
0248: */
0249: static public final Locale PRC = SIMPLIFIED_CHINESE;
0250:
0251: /** Useful constant for country.
0252: */
0253: static public final Locale TAIWAN = TRADITIONAL_CHINESE;
0254:
0255: /** Useful constant for country.
0256: */
0257: static public final Locale UK = createSingleton("en_GB_", "en",
0258: "GB");
0259:
0260: /** Useful constant for country.
0261: */
0262: static public final Locale US = createSingleton("en_US_", "en",
0263: "US");
0264:
0265: /** Useful constant for country.
0266: */
0267: static public final Locale CANADA = createSingleton("en_CA_", "en",
0268: "CA");
0269:
0270: /** Useful constant for country.
0271: */
0272: static public final Locale CANADA_FRENCH = createSingleton(
0273: "fr_CA_", "fr", "CA");
0274:
0275: /**
0276: * Useful constant for the root locale. The root locale is the locale whose
0277: * language, country, and variant are empty ("") strings. This is regarded
0278: * as the base locale of all locales, and is used as the language/country
0279: * neutral locale for the locale sensitive operations.
0280: *
0281: * @since 1.6
0282: */
0283: static public final Locale ROOT = createSingleton("__", "", "");
0284:
0285: /** serialization ID
0286: */
0287: static final long serialVersionUID = 9149081749638150636L;
0288:
0289: /**
0290: * Display types for retrieving localized names from the name providers.
0291: */
0292: private static final int DISPLAY_LANGUAGE = 0;
0293: private static final int DISPLAY_COUNTRY = 1;
0294: private static final int DISPLAY_VARIANT = 2;
0295:
0296: /**
0297: * Construct a locale from language, country, variant.
0298: * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
0299: * (specifically iw, ji, and in) have changed. This constructor accepts both the
0300: * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
0301: * API on Locale will return only the OLD codes.
0302: * @param language lowercase two-letter ISO-639 code.
0303: * @param country uppercase two-letter ISO-3166 code.
0304: * @param variant vendor and browser specific code. See class description.
0305: * @exception NullPointerException thrown if any argument is null.
0306: */
0307: public Locale(String language, String country, String variant) {
0308: this .language = convertOldISOCodes(language);
0309: this .country = toUpperCase(country).intern();
0310: this .variant = variant.intern();
0311: }
0312:
0313: /**
0314: * Construct a locale from language, country.
0315: * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
0316: * (specifically iw, ji, and in) have changed. This constructor accepts both the
0317: * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
0318: * API on Locale will return only the OLD codes.
0319: * @param language lowercase two-letter ISO-639 code.
0320: * @param country uppercase two-letter ISO-3166 code.
0321: * @exception NullPointerException thrown if either argument is null.
0322: */
0323: public Locale(String language, String country) {
0324: this (language, country, "");
0325: }
0326:
0327: /**
0328: * Construct a locale from a language code.
0329: * NOTE: ISO 639 is not a stable standard; some of the language codes it defines
0330: * (specifically iw, ji, and in) have changed. This constructor accepts both the
0331: * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other
0332: * API on Locale will return only the OLD codes.
0333: * @param language lowercase two-letter ISO-639 code.
0334: * @exception NullPointerException thrown if argument is null.
0335: * @since 1.4
0336: */
0337: public Locale(String language) {
0338: this (language, "", "");
0339: }
0340:
0341: /**
0342: * Constructs a <code>Locale</code> using <code>language</code>
0343: * and <code>country</code>. This constructor assumes that
0344: * <code>language</code> and <code>contry</code> are interned and
0345: * it is invoked by createSingleton only. (flag is just for
0346: * avoiding the conflict with the public constructors.
0347: */
0348: private Locale(String language, String country, boolean flag) {
0349: this .language = language;
0350: this .country = country;
0351: this .variant = "";
0352: }
0353:
0354: /**
0355: * Creates a <code>Locale</code> instance with the given
0356: * <code>language</code> and <code>counry</code> and puts the
0357: * instance under the given <code>key</code> in the cache. This
0358: * method must be called only when initializing the Locale
0359: * constants.
0360: */
0361: private static Locale createSingleton(String key, String language,
0362: String country) {
0363: Locale locale = new Locale(language, country, false);
0364: cache.put(key, locale);
0365: return locale;
0366: }
0367:
0368: /**
0369: * Returns a <code>Locale</code> constructed from the given
0370: * <code>language</code>, <code>country</code> and
0371: * <code>variant</code>. If the same <code>Locale</code> instance
0372: * is available in the cache, then that instance is
0373: * returned. Otherwise, a new <code>Locale</code> instance is
0374: * created and cached.
0375: *
0376: * @param language lowercase two-letter ISO-639 code.
0377: * @param country uppercase two-letter ISO-3166 code.
0378: * @param variant vendor and browser specific code. See class description.
0379: * @return the <code>Locale</code> instance requested
0380: * @exception NullPointerException if any argument is null.
0381: */
0382: static Locale getInstance(String language, String country,
0383: String variant) {
0384: if (language == null || country == null || variant == null) {
0385: throw new NullPointerException();
0386: }
0387:
0388: StringBuilder sb = new StringBuilder();
0389: sb.append(language).append('_').append(country).append('_')
0390: .append(variant);
0391: String key = sb.toString();
0392: Locale locale = cache.get(key);
0393: if (locale == null) {
0394: locale = new Locale(language, country, variant);
0395: Locale l = cache.putIfAbsent(key, locale);
0396: if (l != null) {
0397: locale = l;
0398: }
0399: }
0400: return locale;
0401: }
0402:
0403: /**
0404: * Gets the current value of the default locale for this instance
0405: * of the Java Virtual Machine.
0406: * <p>
0407: * The Java Virtual Machine sets the default locale during startup
0408: * based on the host environment. It is used by many locale-sensitive
0409: * methods if no locale is explicitly specified.
0410: * It can be changed using the
0411: * {@link #setDefault(java.util.Locale) setDefault} method.
0412: *
0413: * @return the default locale for this instance of the Java Virtual Machine
0414: */
0415: public static Locale getDefault() {
0416: // do not synchronize this method - see 4071298
0417: // it's OK if more than one default locale happens to be created
0418: if (defaultLocale == null) {
0419: String language, region, country, variant;
0420: language = (String) AccessController
0421: .doPrivileged(new GetPropertyAction(
0422: "user.language", "en"));
0423: // for compatibility, check for old user.region property
0424: region = (String) AccessController
0425: .doPrivileged(new GetPropertyAction("user.region"));
0426: if (region != null) {
0427: // region can be of form country, country_variant, or _variant
0428: int i = region.indexOf('_');
0429: if (i >= 0) {
0430: country = region.substring(0, i);
0431: variant = region.substring(i + 1);
0432: } else {
0433: country = region;
0434: variant = "";
0435: }
0436: } else {
0437: country = (String) AccessController
0438: .doPrivileged(new GetPropertyAction(
0439: "user.country", ""));
0440: variant = (String) AccessController
0441: .doPrivileged(new GetPropertyAction(
0442: "user.variant", ""));
0443: }
0444: defaultLocale = getInstance(language, country, variant);
0445: }
0446: return defaultLocale;
0447: }
0448:
0449: /**
0450: * Sets the default locale for this instance of the Java Virtual Machine.
0451: * This does not affect the host locale.
0452: * <p>
0453: * If there is a security manager, its <code>checkPermission</code>
0454: * method is called with a <code>PropertyPermission("user.language", "write")</code>
0455: * permission before the default locale is changed.
0456: * <p>
0457: * The Java Virtual Machine sets the default locale during startup
0458: * based on the host environment. It is used by many locale-sensitive
0459: * methods if no locale is explicitly specified.
0460: * <p>
0461: * Since changing the default locale may affect many different areas
0462: * of functionality, this method should only be used if the caller
0463: * is prepared to reinitialize locale-sensitive code running
0464: * within the same Java Virtual Machine.
0465: *
0466: * @throws SecurityException
0467: * if a security manager exists and its
0468: * <code>checkPermission</code> method doesn't allow the operation.
0469: * @throws NullPointerException if <code>newLocale</code> is null
0470: * @param newLocale the new default locale
0471: * @see SecurityManager#checkPermission
0472: * @see java.util.PropertyPermission
0473: */
0474: public static synchronized void setDefault(Locale newLocale) {
0475: if (newLocale == null)
0476: throw new NullPointerException(
0477: "Can't set default locale to NULL");
0478:
0479: SecurityManager sm = System.getSecurityManager();
0480: if (sm != null)
0481: sm.checkPermission(new PropertyPermission("user.language",
0482: "write"));
0483: defaultLocale = newLocale;
0484: }
0485:
0486: /**
0487: * Returns an array of all installed locales.
0488: * The returned array represents the union of locales supported
0489: * by the Java runtime environment and by installed
0490: * {@link java.util.spi.LocaleServiceProvider LocaleServiceProvider}
0491: * implementations. It must contain at least a <code>Locale</code>
0492: * instance equal to {@link java.util.Locale#US Locale.US}.
0493: *
0494: * @return An array of installed locales.
0495: */
0496: public static Locale[] getAvailableLocales() {
0497: return LocaleServiceProviderPool.getAllAvailableLocales();
0498: }
0499:
0500: /**
0501: * Returns a list of all 2-letter country codes defined in ISO 3166.
0502: * Can be used to create Locales.
0503: */
0504: public static String[] getISOCountries() {
0505: if (isoCountries == null) {
0506: isoCountries = getISO2Table(LocaleISOData.isoCountryTable);
0507: }
0508: String[] result = new String[isoCountries.length];
0509: System.arraycopy(isoCountries, 0, result, 0,
0510: isoCountries.length);
0511: return result;
0512: }
0513:
0514: /**
0515: * Returns a list of all 2-letter language codes defined in ISO 639.
0516: * Can be used to create Locales.
0517: * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
0518: * The list this function returns includes both the new and the old codes for the
0519: * languages whose codes have changed.]
0520: */
0521: public static String[] getISOLanguages() {
0522: if (isoLanguages == null) {
0523: isoLanguages = getISO2Table(LocaleISOData.isoLanguageTable);
0524: }
0525: String[] result = new String[isoLanguages.length];
0526: System.arraycopy(isoLanguages, 0, result, 0,
0527: isoLanguages.length);
0528: return result;
0529: }
0530:
0531: private static final String[] getISO2Table(String table) {
0532: int len = table.length() / 5;
0533: String[] isoTable = new String[len];
0534: for (int i = 0, j = 0; i < len; i++, j += 5) {
0535: isoTable[i] = table.substring(j, j + 2);
0536: }
0537: return isoTable;
0538: }
0539:
0540: /**
0541: * Returns the language code for this locale, which will either be the empty string
0542: * or a lowercase ISO 639 code.
0543: * <p>NOTE: ISO 639 is not a stable standard-- some languages' codes have changed.
0544: * Locale's constructor recognizes both the new and the old codes for the languages
0545: * whose codes have changed, but this function always returns the old code. If you
0546: * want to check for a specific language whose code has changed, don't do <pre>
0547: * if (locale.getLanguage().equals("he"))
0548: * ...
0549: * </pre>Instead, do<pre>
0550: * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage()))
0551: * ...</pre>
0552: * @see #getDisplayLanguage
0553: */
0554: public String getLanguage() {
0555: return language;
0556: }
0557:
0558: /**
0559: * Returns the country/region code for this locale, which will
0560: * either be the empty string or an uppercase ISO 3166 2-letter code.
0561: * @see #getDisplayCountry
0562: */
0563: public String getCountry() {
0564: return country;
0565: }
0566:
0567: /**
0568: * Returns the variant code for this locale.
0569: * @see #getDisplayVariant
0570: */
0571: public String getVariant() {
0572: return variant;
0573: }
0574:
0575: /**
0576: * Getter for the programmatic name of the entire locale,
0577: * with the language, country and variant separated by underbars.
0578: * Language is always lower case, and country is always upper case.
0579: * If the language is missing, the string will begin with an underbar.
0580: * If both the language and country fields are missing, this function
0581: * will return the empty string, even if the variant field is filled in
0582: * (you can't have a locale with just a variant-- the variant must accompany
0583: * a valid language or country code).
0584: * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC"
0585: * @see #getDisplayName
0586: */
0587: public final String toString() {
0588: boolean l = language.length() != 0;
0589: boolean c = country.length() != 0;
0590: boolean v = variant.length() != 0;
0591: StringBuilder result = new StringBuilder(language);
0592: if (c || (l && v)) {
0593: result.append('_').append(country); // This may just append '_'
0594: }
0595: if (v && (l || c)) {
0596: result.append('_').append(variant);
0597: }
0598: return result.toString();
0599: }
0600:
0601: /**
0602: * Returns a three-letter abbreviation for this locale's language. If the locale
0603: * doesn't specify a language, this will be the empty string. Otherwise, this will
0604: * be a lowercase ISO 639-2/T language code.
0605: * The ISO 639-2 language codes can be found on-line at
0606: * <a href="http://www.loc.gov/standards/iso639-2/englangn.html">
0607: * <code>http://www.loc.gov/standards/iso639-2/englangn.html</code>.</a>
0608: * @exception MissingResourceException Throws MissingResourceException if the
0609: * three-letter language abbreviation is not available for this locale.
0610: */
0611: public String getISO3Language() throws MissingResourceException {
0612: String language3 = getISO3Code(language,
0613: LocaleISOData.isoLanguageTable);
0614: if (language3 == null) {
0615: throw new MissingResourceException(
0616: "Couldn't find 3-letter language code for "
0617: + language, "FormatData_" + toString(),
0618: "ShortLanguage");
0619: }
0620: return language3;
0621: }
0622:
0623: /**
0624: * Returns a three-letter abbreviation for this locale's country. If the locale
0625: * doesn't specify a country, this will be the empty string. Otherwise, this will
0626: * be an uppercase ISO 3166 3-letter country code.
0627: * The ISO 3166-2 country codes can be found on-line at
0628: * <a href="http://www.davros.org/misc/iso3166.txt">
0629: * <code>http://www.davros.org/misc/iso3166.txt</code>.</a>
0630: * @exception MissingResourceException Throws MissingResourceException if the
0631: * three-letter country abbreviation is not available for this locale.
0632: */
0633: public String getISO3Country() throws MissingResourceException {
0634: String country3 = getISO3Code(country,
0635: LocaleISOData.isoCountryTable);
0636: if (country3 == null) {
0637: throw new MissingResourceException(
0638: "Couldn't find 3-letter country code for "
0639: + country, "FormatData_" + toString(),
0640: "ShortCountry");
0641: }
0642: return country3;
0643: }
0644:
0645: private static final String getISO3Code(String iso2Code,
0646: String table) {
0647: int codeLength = iso2Code.length();
0648: if (codeLength == 0) {
0649: return "";
0650: }
0651:
0652: int tableLength = table.length();
0653: int index = tableLength;
0654: if (codeLength == 2) {
0655: char c1 = iso2Code.charAt(0);
0656: char c2 = iso2Code.charAt(1);
0657: for (index = 0; index < tableLength; index += 5) {
0658: if (table.charAt(index) == c1
0659: && table.charAt(index + 1) == c2) {
0660: break;
0661: }
0662: }
0663: }
0664: return index < tableLength ? table.substring(index + 2,
0665: index + 5) : null;
0666: }
0667:
0668: /**
0669: * Returns a name for the locale's language that is appropriate for display to the
0670: * user.
0671: * If possible, the name returned will be localized for the default locale.
0672: * For example, if the locale is fr_FR and the default locale
0673: * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
0674: * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
0675: * If the name returned cannot be localized for the default locale,
0676: * (say, we don't have a Japanese name for Croatian),
0677: * this function falls back on the English name, and uses the ISO code as a last-resort
0678: * value. If the locale doesn't specify a language, this function returns the empty string.
0679: */
0680: public final String getDisplayLanguage() {
0681: return getDisplayLanguage(getDefault());
0682: }
0683:
0684: /**
0685: * Returns a name for the locale's language that is appropriate for display to the
0686: * user.
0687: * If possible, the name returned will be localized according to inLocale.
0688: * For example, if the locale is fr_FR and inLocale
0689: * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
0690: * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
0691: * If the name returned cannot be localized according to inLocale,
0692: * (say, we don't have a Japanese name for Croatian),
0693: * this function falls back on the English name, and finally
0694: * on the ISO code as a last-resort value. If the locale doesn't specify a language,
0695: * this function returns the empty string.
0696: *
0697: * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
0698: */
0699: public String getDisplayLanguage(Locale inLocale) {
0700: return getDisplayString(language, inLocale, DISPLAY_LANGUAGE);
0701: }
0702:
0703: /**
0704: * Returns a name for the locale's country that is appropriate for display to the
0705: * user.
0706: * If possible, the name returned will be localized for the default locale.
0707: * For example, if the locale is fr_FR and the default locale
0708: * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
0709: * the default locale is fr_FR, getDisplayCountry() will return "Etats-Unis".
0710: * If the name returned cannot be localized for the default locale,
0711: * (say, we don't have a Japanese name for Croatia),
0712: * this function falls back on the English name, and uses the ISO code as a last-resort
0713: * value. If the locale doesn't specify a country, this function returns the empty string.
0714: */
0715: public final String getDisplayCountry() {
0716: return getDisplayCountry(getDefault());
0717: }
0718:
0719: /**
0720: * Returns a name for the locale's country that is appropriate for display to the
0721: * user.
0722: * If possible, the name returned will be localized according to inLocale.
0723: * For example, if the locale is fr_FR and inLocale
0724: * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
0725: * inLocale is fr_FR, getDisplayCountry() will return "Etats-Unis".
0726: * If the name returned cannot be localized according to inLocale.
0727: * (say, we don't have a Japanese name for Croatia),
0728: * this function falls back on the English name, and finally
0729: * on the ISO code as a last-resort value. If the locale doesn't specify a country,
0730: * this function returns the empty string.
0731: *
0732: * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
0733: */
0734: public String getDisplayCountry(Locale inLocale) {
0735: return getDisplayString(country, inLocale, DISPLAY_COUNTRY);
0736: }
0737:
0738: private String getDisplayString(String code, Locale inLocale,
0739: int type) {
0740: if (code.length() == 0) {
0741: return "";
0742: }
0743:
0744: if (inLocale == null) {
0745: throw new NullPointerException();
0746: }
0747:
0748: try {
0749: OpenListResourceBundle bundle = LocaleData
0750: .getLocaleNames(inLocale);
0751: String key = (type == DISPLAY_VARIANT ? "%%" + code : code);
0752: String result = null;
0753:
0754: // Check whether a provider can provide an implementation that's closer
0755: // to the requested locale than what the Java runtime itself can provide.
0756: LocaleServiceProviderPool pool = LocaleServiceProviderPool
0757: .getPool(LocaleNameProvider.class);
0758: if (pool.hasProviders()) {
0759: result = pool.getLocalizedObject(
0760: LocaleNameGetter.INSTANCE, inLocale, bundle,
0761: key, type, code);
0762: }
0763:
0764: if (result == null) {
0765: result = bundle.getString(key);
0766: }
0767:
0768: if (result != null) {
0769: return result;
0770: }
0771: } catch (Exception e) {
0772: // just fall through
0773: }
0774: return code;
0775: }
0776:
0777: /**
0778: * Returns a name for the locale's variant code that is appropriate for display to the
0779: * user. If possible, the name will be localized for the default locale. If the locale
0780: * doesn't specify a variant code, this function returns the empty string.
0781: */
0782: public final String getDisplayVariant() {
0783: return getDisplayVariant(getDefault());
0784: }
0785:
0786: /**
0787: * Returns a name for the locale's variant code that is appropriate for display to the
0788: * user. If possible, the name will be localized for inLocale. If the locale
0789: * doesn't specify a variant code, this function returns the empty string.
0790: *
0791: * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
0792: */
0793: public String getDisplayVariant(Locale inLocale) {
0794: if (variant.length() == 0)
0795: return "";
0796:
0797: OpenListResourceBundle bundle = LocaleData
0798: .getLocaleNames(inLocale);
0799:
0800: String names[] = getDisplayVariantArray(bundle, inLocale);
0801:
0802: // Get the localized patterns for formatting a list, and use
0803: // them to format the list.
0804: String listPattern = null;
0805: String listCompositionPattern = null;
0806: try {
0807: listPattern = bundle.getString("ListPattern");
0808: listCompositionPattern = bundle
0809: .getString("ListCompositionPattern");
0810: } catch (MissingResourceException e) {
0811: }
0812: return formatList(names, listPattern, listCompositionPattern);
0813: }
0814:
0815: /**
0816: * Returns a name for the locale that is appropriate for display to the
0817: * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
0818: * and getDisplayVariant() assembled into a single string. The display name will have
0819: * one of the following forms:<p><blockquote>
0820: * language (country, variant)<p>
0821: * language (country)<p>
0822: * language (variant)<p>
0823: * country (variant)<p>
0824: * language<p>
0825: * country<p>
0826: * variant<p></blockquote>
0827: * depending on which fields are specified in the locale. If the language, country,
0828: * and variant fields are all empty, this function returns the empty string.
0829: */
0830: public final String getDisplayName() {
0831: return getDisplayName(getDefault());
0832: }
0833:
0834: /**
0835: * Returns a name for the locale that is appropriate for display to the
0836: * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(),
0837: * and getDisplayVariant() assembled into a single string. The display name will have
0838: * one of the following forms:<p><blockquote>
0839: * language (country, variant)<p>
0840: * language (country)<p>
0841: * language (variant)<p>
0842: * country (variant)<p>
0843: * language<p>
0844: * country<p>
0845: * variant<p></blockquote>
0846: * depending on which fields are specified in the locale. If the language, country,
0847: * and variant fields are all empty, this function returns the empty string.
0848: *
0849: * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
0850: */
0851: public String getDisplayName(Locale inLocale) {
0852: OpenListResourceBundle bundle = LocaleData
0853: .getLocaleNames(inLocale);
0854:
0855: String languageName = getDisplayLanguage(inLocale);
0856: String countryName = getDisplayCountry(inLocale);
0857: String[] variantNames = getDisplayVariantArray(bundle, inLocale);
0858:
0859: // Get the localized patterns for formatting a display name.
0860: String displayNamePattern = null;
0861: String listPattern = null;
0862: String listCompositionPattern = null;
0863: try {
0864: displayNamePattern = bundle.getString("DisplayNamePattern");
0865: listPattern = bundle.getString("ListPattern");
0866: listCompositionPattern = bundle
0867: .getString("ListCompositionPattern");
0868: } catch (MissingResourceException e) {
0869: }
0870:
0871: // The display name consists of a main name, followed by qualifiers.
0872: // Typically, the format is "MainName (Qualifier, Qualifier)" but this
0873: // depends on what pattern is stored in the display locale.
0874: String mainName = null;
0875: String[] qualifierNames = null;
0876:
0877: // The main name is the language, or if there is no language, the country.
0878: // If there is neither language nor country (an anomalous situation) then
0879: // the display name is simply the variant's display name.
0880: if (languageName.length() != 0) {
0881: mainName = languageName;
0882: if (countryName.length() != 0) {
0883: qualifierNames = new String[variantNames.length + 1];
0884: System.arraycopy(variantNames, 0, qualifierNames, 1,
0885: variantNames.length);
0886: qualifierNames[0] = countryName;
0887: } else
0888: qualifierNames = variantNames;
0889: } else if (countryName.length() != 0) {
0890: mainName = countryName;
0891: qualifierNames = variantNames;
0892: } else {
0893: return formatList(variantNames, listPattern,
0894: listCompositionPattern);
0895: }
0896:
0897: // Create an array whose first element is the number of remaining
0898: // elements. This serves as a selector into a ChoiceFormat pattern from
0899: // the resource. The second and third elements are the main name and
0900: // the qualifier; if there are no qualifiers, the third element is
0901: // unused by the format pattern.
0902: Object[] displayNames = {
0903: new Integer(qualifierNames.length != 0 ? 2 : 1),
0904: mainName,
0905: // We could also just call formatList() and have it handle the empty
0906: // list case, but this is more efficient, and we want it to be
0907: // efficient since all the language-only locales will not have any
0908: // qualifiers.
0909: qualifierNames.length != 0 ? formatList(qualifierNames,
0910: listPattern, listCompositionPattern) : null };
0911:
0912: if (displayNamePattern != null) {
0913: return new MessageFormat(displayNamePattern)
0914: .format(displayNames);
0915: } else {
0916: // If we cannot get the message format pattern, then we use a simple
0917: // hard-coded pattern. This should not occur in practice unless the
0918: // installation is missing some core files (FormatData etc.).
0919: StringBuilder result = new StringBuilder();
0920: result.append((String) displayNames[1]);
0921: if (displayNames.length > 2) {
0922: result.append(" (");
0923: result.append((String) displayNames[2]);
0924: result.append(')');
0925: }
0926: return result.toString();
0927: }
0928: }
0929:
0930: /**
0931: * Overrides Cloneable
0932: */
0933: public Object clone() {
0934: try {
0935: Locale that = (Locale) super .clone();
0936: return that;
0937: } catch (CloneNotSupportedException e) {
0938: throw new InternalError();
0939: }
0940: }
0941:
0942: /**
0943: * Override hashCode.
0944: * Since Locales are often used in hashtables, caches the value
0945: * for speed.
0946: */
0947: public int hashCode() {
0948: int hc = hashCodeValue;
0949: if (hc == 0) {
0950: hc = (language.hashCode() << 8) ^ country.hashCode()
0951: ^ (variant.hashCode() << 4);
0952: hashCodeValue = hc;
0953: }
0954: return hc;
0955: }
0956:
0957: // Overrides
0958:
0959: /**
0960: * Returns true if this Locale is equal to another object. A Locale is
0961: * deemed equal to another Locale with identical language, country,
0962: * and variant, and unequal to all other objects.
0963: *
0964: * @return true if this Locale is equal to the specified object.
0965: */
0966:
0967: public boolean equals(Object obj) {
0968: if (this == obj) // quick check
0969: return true;
0970: if (!(obj instanceof Locale))
0971: return false;
0972: Locale other = (Locale) obj;
0973: return language == other.language && country == other.country
0974: && variant == other.variant;
0975: }
0976:
0977: // ================= privates =====================================
0978:
0979: // XXX instance and class variables. For now keep these separate, since it is
0980: // faster to match. Later, make into single string.
0981:
0982: /**
0983: * @serial
0984: * @see #getLanguage
0985: */
0986: private final String language;
0987:
0988: /**
0989: * @serial
0990: * @see #getCountry
0991: */
0992: private final String country;
0993:
0994: /**
0995: * @serial
0996: * @see #getVariant
0997: */
0998: private final String variant;
0999:
1000: /**
1001: * Placeholder for the object's hash code. Always -1.
1002: * @serial
1003: */
1004: private volatile int hashcode = -1; // lazy evaluate
1005:
1006: /**
1007: * Calculated hashcode to fix 4518797.
1008: */
1009: private transient volatile int hashCodeValue = 0;
1010:
1011: private static Locale defaultLocale = null;
1012:
1013: /**
1014: * Return an array of the display names of the variant.
1015: * @param bundle the ResourceBundle to use to get the display names
1016: * @return an array of display names, possible of zero length.
1017: */
1018: private String[] getDisplayVariantArray(
1019: OpenListResourceBundle bundle, Locale inLocale) {
1020: // Split the variant name into tokens separated by '_'.
1021: StringTokenizer tokenizer = new StringTokenizer(variant, "_");
1022: String[] names = new String[tokenizer.countTokens()];
1023:
1024: // For each variant token, lookup the display name. If
1025: // not found, use the variant name itself.
1026: for (int i = 0; i < names.length; ++i) {
1027: names[i] = getDisplayString(tokenizer.nextToken(),
1028: inLocale, DISPLAY_VARIANT);
1029: }
1030:
1031: return names;
1032: }
1033:
1034: /**
1035: * Format a list using given pattern strings.
1036: * If either of the patterns is null, then a the list is
1037: * formatted by concatenation with the delimiter ','.
1038: * @param stringList the list of strings to be formatted.
1039: * @param listPattern should create a MessageFormat taking 0-3 arguments
1040: * and formatting them into a list.
1041: * @param listCompositionPattern should take 2 arguments
1042: * and is used by composeList.
1043: * @return a string representing the list.
1044: */
1045: private static String formatList(String[] stringList,
1046: String listPattern, String listCompositionPattern) {
1047: // If we have no list patterns, compose the list in a simple,
1048: // non-localized way.
1049: if (listPattern == null || listCompositionPattern == null) {
1050: StringBuffer result = new StringBuffer();
1051: for (int i = 0; i < stringList.length; ++i) {
1052: if (i > 0)
1053: result.append(',');
1054: result.append(stringList[i]);
1055: }
1056: return result.toString();
1057: }
1058:
1059: // Compose the list down to three elements if necessary
1060: if (stringList.length > 3) {
1061: MessageFormat format = new MessageFormat(
1062: listCompositionPattern);
1063: stringList = composeList(format, stringList);
1064: }
1065:
1066: // Rebuild the argument list with the list length as the first element
1067: Object[] args = new Object[stringList.length + 1];
1068: System.arraycopy(stringList, 0, args, 1, stringList.length);
1069: args[0] = new Integer(stringList.length);
1070:
1071: // Format it using the pattern in the resource
1072: MessageFormat format = new MessageFormat(listPattern);
1073: return format.format(args);
1074: }
1075:
1076: /**
1077: * Given a list of strings, return a list shortened to three elements.
1078: * Shorten it by applying the given format to the first two elements
1079: * recursively.
1080: * @param format a format which takes two arguments
1081: * @param list a list of strings
1082: * @return if the list is three elements or shorter, the same list;
1083: * otherwise, a new list of three elements.
1084: */
1085: private static String[] composeList(MessageFormat format,
1086: String[] list) {
1087: if (list.length <= 3)
1088: return list;
1089:
1090: // Use the given format to compose the first two elements into one
1091: String[] listItems = { list[0], list[1] };
1092: String newItem = format.format(listItems);
1093:
1094: // Form a new list one element shorter
1095: String[] newList = new String[list.length - 1];
1096: System.arraycopy(list, 2, newList, 1, newList.length - 1);
1097: newList[0] = newItem;
1098:
1099: // Recurse
1100: return composeList(format, newList);
1101: }
1102:
1103: /**
1104: * Replace the deserialized Locale object with a newly
1105: * created object. Newer language codes are replaced with older ISO
1106: * codes. The country and variant codes are replaced with internalized
1107: * String copies.
1108: */
1109: private Object readResolve() throws java.io.ObjectStreamException {
1110: return getInstance(language, country, variant);
1111: }
1112:
1113: private static volatile String[] isoLanguages = null;
1114:
1115: private static volatile String[] isoCountries = null;
1116:
1117: /*
1118: * Locale needs its own, locale insensitive version of toLowerCase to
1119: * avoid circularity problems between Locale and String.
1120: * The most straightforward algorithm is used. Look at optimizations later.
1121: */
1122: private String toLowerCase(String str) {
1123: char[] buf = new char[str.length()];
1124: for (int i = 0; i < buf.length; i++) {
1125: buf[i] = Character.toLowerCase(str.charAt(i));
1126: }
1127: return new String(buf);
1128: }
1129:
1130: /*
1131: * Locale needs its own, locale insensitive version of toUpperCase to
1132: * avoid circularity problems between Locale and String.
1133: * The most straightforward algorithm is used. Look at optimizations later.
1134: */
1135: private String toUpperCase(String str) {
1136: char[] buf = new char[str.length()];
1137: for (int i = 0; i < buf.length; i++) {
1138: buf[i] = Character.toUpperCase(str.charAt(i));
1139: }
1140: return new String(buf);
1141: }
1142:
1143: private String convertOldISOCodes(String language) {
1144: // we accept both the old and the new ISO codes for the languages whose ISO
1145: // codes have changed, but we always store the OLD code, for backward compatibility
1146: language = toLowerCase(language).intern();
1147: if (language == "he") {
1148: return "iw";
1149: } else if (language == "yi") {
1150: return "ji";
1151: } else if (language == "id") {
1152: return "in";
1153: } else {
1154: return language;
1155: }
1156: }
1157:
1158: /**
1159: * Obtains a localized locale names from a LocaleNameProvider
1160: * implementation.
1161: */
1162: private static class LocaleNameGetter
1163: implements
1164: LocaleServiceProviderPool.LocalizedObjectGetter<LocaleNameProvider, String> {
1165: private static final LocaleNameGetter INSTANCE = new LocaleNameGetter();
1166:
1167: public String getObject(LocaleNameProvider localeNameProvider,
1168: Locale locale, String key, Object... params) {
1169: assert params.length == 2;
1170: int type = (Integer) params[0];
1171: String code = (String) params[1];
1172:
1173: switch (type) {
1174: case DISPLAY_LANGUAGE:
1175: return localeNameProvider.getDisplayLanguage(code,
1176: locale);
1177: case DISPLAY_COUNTRY:
1178: return localeNameProvider.getDisplayCountry(code,
1179: locale);
1180: case DISPLAY_VARIANT:
1181: return localeNameProvider.getDisplayVariant(code,
1182: locale);
1183: default:
1184: assert false; // shouldn't happen
1185: }
1186:
1187: return null;
1188: }
1189: }
1190: }
|