src/share/classes/java/util/Locale.java

Print this page
rev 5615 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o Jigsaw. by Naoto Sato and Masayoshi Okutsu)

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -48,21 +48,20 @@
 import java.security.AccessController;
 import java.text.MessageFormat;
 import java.util.spi.LocaleNameProvider;
 
 import sun.security.action.GetPropertyAction;
-import sun.util.LocaleServiceProviderPool;
+import sun.util.locale.provider.LocaleServiceProviderPool;
 import sun.util.locale.BaseLocale;
 import sun.util.locale.InternalLocaleBuilder;
 import sun.util.locale.LanguageTag;
 import sun.util.locale.LocaleExtensions;
 import sun.util.locale.LocaleObjectCache;
 import sun.util.locale.LocaleSyntaxException;
 import sun.util.locale.LocaleUtils;
 import sun.util.locale.ParseStatus;
-import sun.util.locale.UnicodeLocaleExtension;
-import sun.util.resources.LocaleData;
+import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.resources.OpenListResourceBundle;
 
 /**
  * A <code>Locale</code> object represents a specific geographical, political,
  * or cultural region. An operation that requires a <code>Locale</code> to perform

@@ -963,11 +962,11 @@
         String[] result = new String[isoLanguages.length];
         System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
         return result;
     }
 
-    private static final String[] getISO2Table(String table) {
+    private static String[] getISO2Table(String table) {
         int len = table.length() / 5;
         String[] isoTable = new String[len];
         for (int i = 0, j = 0; i < len; i++, j += 5) {
             isoTable[i] = table.substring(j, j + 2);
         }

@@ -1032,10 +1031,34 @@
     public String getVariant() {
         return baseLocale.getVariant();
     }
 
     /**
+     * Returns {@code true} if this {@code Locale} has any <a href="#def_extensions">
+     * extensions</a>.
+     *
+     * @return {@code true} if this {@code Locale} has any extensions
+     * @since 1.8
+     */
+    public boolean hasExtensions() {
+        return localeExtensions != null;
+    }
+
+    /**
+     * Returns a copy of this {@code Locale} with no <a href="#def_extensions">
+     * extensions</a>. If this {@code Locale} has no extensions, this {@code Locale}
+     * is returned.
+     *
+     * @return a copy of this {@code Locale} with no extensions, or {@code this}
+     *         if {@code this} has no extensions
+     * @since 1.8
+     */
+    public Locale stripExtensions() {
+        return hasExtensions() ? Locale.getInstance(baseLocale, null) : this;
+    }
+
+    /**
      * Returns the extension (or private use) value associated with
      * the specified key, or null if there is no extension
      * associated with the key. To be well-formed, the key must be one
      * of <code>[0-9A-Za-z]</code>. Keys are case-insensitive, so
      * for example 'z' and 'Z' represent the same extension.

@@ -1050,11 +1073,11 @@
      */
     public String getExtension(char key) {
         if (!LocaleExtensions.isValidKey(key)) {
             throw new IllegalArgumentException("Ill-formed extension key: " + key);
         }
-        return (localeExtensions == null) ? null : localeExtensions.getExtensionValue(key);
+        return hasExtensions() ? localeExtensions.getExtensionValue(key) : null;
     }
 
     /**
      * Returns the set of extension keys associated with this locale, or the
      * empty set if it has no extensions. The returned set is unmodifiable.

@@ -1063,11 +1086,11 @@
      * @return The set of extension keys, or the empty set if this locale has
      * no extensions.
      * @since 1.7
      */
     public Set<Character> getExtensionKeys() {
-        if (localeExtensions == null) {
+        if (!hasExtensions()) {
             return Collections.emptySet();
         }
         return localeExtensions.getKeys();
     }
 

@@ -1078,11 +1101,11 @@
      *
      * @return The set of attributes.
      * @since 1.7
      */
     public Set<String> getUnicodeLocaleAttributes() {
-        if (localeExtensions == null) {
+        if (!hasExtensions()) {
             return Collections.emptySet();
         }
         return localeExtensions.getUnicodeLocaleAttributes();
     }
 

@@ -1099,14 +1122,14 @@
      * @throws IllegalArgumentException if the key is not well-formed
      * @throws NullPointerException if <code>key</code> is null
      * @since 1.7
      */
     public String getUnicodeLocaleType(String key) {
-        if (!UnicodeLocaleExtension.isKey(key)) {
+        if (!isUnicodeExtensionKey(key)) {
             throw new IllegalArgumentException("Ill-formed Unicode locale key: " + key);
         }
-        return (localeExtensions == null) ? null : localeExtensions.getUnicodeLocaleType(key);
+        return hasExtensions() ? localeExtensions.getUnicodeLocaleType(key) : null;
     }
 
     /**
      * Returns the set of Unicode locale keys defined by this locale, or the empty set if
      * this locale has none.  The returned set is immutable.  Keys are all lower case.

@@ -1283,10 +1306,14 @@
      * @return a BCP47 language tag representing the locale
      * @see #forLanguageTag(String)
      * @since 1.7
      */
     public String toLanguageTag() {
+        if (languageTag != null) {
+            return languageTag;
+        }
+
         LanguageTag tag = LanguageTag.parseLocale(baseLocale, localeExtensions);
         StringBuilder buf = new StringBuilder();
 
         String subtag = tag.getLanguage();
         if (subtag.length() > 0) {

@@ -1326,12 +1353,18 @@
             buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
             // preserve casing
             buf.append(subtag);
         }
 
-        return buf.toString();
+        String langTag = buf.toString();
+        synchronized (this) {
+            if (languageTag == null) {
+                languageTag = langTag;
     }
+        }
+        return languageTag;
+    }
 
     /**
      * Returns a locale for the specified IETF BCP 47 language tag string.
      *
      * <p>If the specified language tag contains any ill-formed subtags,

@@ -1512,11 +1545,11 @@
                     + baseLocale.getRegion(), "FormatData_" + toString(), "ShortCountry");
         }
         return country3;
     }
 
-    private static final String getISO3Code(String iso2Code, String table) {
+    private static String getISO3Code(String iso2Code, String table) {
         int codeLength = iso2Code.length();
         if (codeLength == 0) {
             return "";
         }
 

@@ -1638,37 +1671,20 @@
 
         if (inLocale == null) {
             throw new NullPointerException();
         }
 
-        try {
-            OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
-            String key = (type == DISPLAY_VARIANT ? "%%"+code : code);
-            String result = null;
-
-            // Check whether a provider can provide an implementation that's closer
-            // to the requested locale than what the Java runtime itself can provide.
             LocaleServiceProviderPool pool =
                 LocaleServiceProviderPool.getPool(LocaleNameProvider.class);
-            if (pool.hasProviders()) {
-                result = pool.getLocalizedObject(
+        String key = (type == DISPLAY_VARIANT ? "%%"+code : code);
+        String result = pool.getLocalizedObject(
                                     LocaleNameGetter.INSTANCE,
-                                    inLocale, bundle, key,
-                                    type, code);
-            }
-
-            if (result == null) {
-                result = bundle.getString(key);
-            }
-
+                                inLocale, key, type, code);
             if (result != null) {
                 return result;
             }
-        }
-        catch (Exception e) {
-            // just fall through
-        }
+
         return code;
     }
 
     /**
      * Returns a name for the locale's variant code that is appropriate for display to the

@@ -1688,11 +1704,11 @@
      */
     public String getDisplayVariant(Locale inLocale) {
         if (baseLocale.getVariant().length() == 0)
             return "";
 
-        OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
+        OpenListResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(inLocale);
 
         String names[] = getDisplayVariantArray(bundle, inLocale);
 
         // Get the localized patterns for formatting a list, and use
         // them to format the list.

@@ -1746,11 +1762,11 @@
      * this function returns the empty string.
      *
      * @throws NullPointerException if <code>inLocale</code> is <code>null</code>
      */
     public String getDisplayName(Locale inLocale) {
-        OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
+        OpenListResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(inLocale);
 
         String languageName = getDisplayLanguage(inLocale);
         String scriptName = getDisplayScript(inLocale);
         String countryName = getDisplayCountry(inLocale);
         String[] variantNames = getDisplayVariantArray(bundle, inLocale);

@@ -1792,14 +1808,12 @@
         }
         if (countryName.length() != 0) {
             names.add(countryName);
         }
         if (variantNames.length != 0) {
-            for (String var : variantNames) {
-                names.add(var);
+            names.addAll(Arrays.asList(variantNames));
             }
-        }
 
         // The first one in the main name
         mainName = names.get(0);
 
         // Others are qualifiers

@@ -1841,10 +1855,11 @@
     }
 
     /**
      * Overrides Cloneable.
      */
+    @Override
     public Object clone()
     {
         try {
             Locale that = (Locale)super.clone();
             return that;

@@ -1908,10 +1923,12 @@
 
     private volatile static Locale defaultLocale = initDefault();
     private volatile static Locale defaultDisplayLocale = null;
     private volatile static Locale defaultFormatLocale = null;
 
+    private transient volatile String languageTag;
+
     /**
      * Return an array of the display names of the variant.
      * @param bundle the ResourceBundle to use to get the display names
      * @return an array of display names, possible of zero length.
      */

@@ -1943,13 +1960,15 @@
      */
     private static String formatList(String[] stringList, String listPattern, String listCompositionPattern) {
         // If we have no list patterns, compose the list in a simple,
         // non-localized way.
         if (listPattern == null || listCompositionPattern == null) {
-            StringBuffer result = new StringBuffer();
-            for (int i=0; i<stringList.length; ++i) {
-                if (i>0) result.append(',');
+            StringBuilder result = new StringBuilder();
+            for (int i = 0; i < stringList.length; ++i) {
+                if (i > 0) {
+                    result.append(',');
+                }
                 result.append(stringList[i]);
             }
             return result.toString();
         }
 

@@ -1992,10 +2011,17 @@
 
         // Recurse
         return composeList(format, newList);
     }
 
+    // Duplicate of sun.util.locale.UnicodeLocaleExtension.isKey in order to
+    // avoid its class loading.
+    private static boolean isUnicodeExtensionKey(String s) {
+        // 2alphanum
+        return (s.length() == 2) && LocaleUtils.isAlphaNumericString(s);
+    }
+
     /**
      * @serialField language    String
      *      language subtag in lower case. (See <a href="java/util/Locale.html#getLanguage()">getLanguage()</a>)
      * @serialField country     String
      *      country subtag in upper case. (See <a href="java/util/Locale.html#getCountry()">getCountry()</a>)

@@ -2134,10 +2160,11 @@
      */
     private static class LocaleNameGetter
         implements LocaleServiceProviderPool.LocalizedObjectGetter<LocaleNameProvider, String> {
         private static final LocaleNameGetter INSTANCE = new LocaleNameGetter();
 
+        @Override
         public String getObject(LocaleNameProvider localeNameProvider,
                                 Locale locale,
                                 String key,
                                 Object... params) {
             assert params.length == 2;