However the Locale class needs to be simplified - it is suprising how a class used in a trival way may get bloated javac
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 28 Sep 2013 02:45:17 +0200
branchjavac
changeset 1320e49c4c2c3737
parent 1319 be64f0e582bc
child 1321 7a78a84ab583
However the Locale class needs to be simplified - it is suprising how a class used in a trival way may get bloated
rt/emul/compact/src/main/java/java/util/Locale.java
     1.1 --- a/rt/emul/compact/src/main/java/java/util/Locale.java	Sat Sep 28 02:44:17 2013 +0200
     1.2 +++ b/rt/emul/compact/src/main/java/java/util/Locale.java	Sat Sep 28 02:45:17 2013 +0200
     1.3 @@ -45,23 +45,6 @@
     1.4  import java.io.ObjectOutputStream;
     1.5  import java.io.ObjectStreamField;
     1.6  import java.io.Serializable;
     1.7 -import java.security.AccessController;
     1.8 -import java.text.MessageFormat;
     1.9 -import java.util.spi.LocaleNameProvider;
    1.10 -
    1.11 -import sun.security.action.GetPropertyAction;
    1.12 -import sun.util.LocaleServiceProviderPool;
    1.13 -import sun.util.locale.BaseLocale;
    1.14 -import sun.util.locale.InternalLocaleBuilder;
    1.15 -import sun.util.locale.LanguageTag;
    1.16 -import sun.util.locale.LocaleExtensions;
    1.17 -import sun.util.locale.LocaleObjectCache;
    1.18 -import sun.util.locale.LocaleSyntaxException;
    1.19 -import sun.util.locale.LocaleUtils;
    1.20 -import sun.util.locale.ParseStatus;
    1.21 -import sun.util.locale.UnicodeLocaleExtension;
    1.22 -import sun.util.resources.LocaleData;
    1.23 -import sun.util.resources.OpenListResourceBundle;
    1.24  
    1.25  /**
    1.26   * A <code>Locale</code> object represents a specific geographical, political,
    1.27 @@ -408,8 +391,6 @@
    1.28   */
    1.29  public final class Locale implements Cloneable, Serializable {
    1.30  
    1.31 -    static private final  Cache LOCALECACHE = new Cache();
    1.32 -
    1.33      /** Useful constant for language.
    1.34       */
    1.35      static public final Locale ENGLISH = createConstant("en", "");
    1.36 @@ -533,14 +514,10 @@
    1.37      private static final int DISPLAY_COUNTRY  = 1;
    1.38      private static final int DISPLAY_VARIANT  = 2;
    1.39      private static final int DISPLAY_SCRIPT   = 3;
    1.40 +    private String language;
    1.41 +    private String country;
    1.42 +    private String variant;
    1.43  
    1.44 -    /**
    1.45 -     * Private constructor used by getInstance method
    1.46 -     */
    1.47 -    private Locale(BaseLocale baseLocale, LocaleExtensions extensions) {
    1.48 -        this.baseLocale = baseLocale;
    1.49 -        this.localeExtensions = extensions;
    1.50 -    }
    1.51  
    1.52      /**
    1.53       * Construct a locale from language, country and variant.
    1.54 @@ -572,8 +549,9 @@
    1.55          if (language== null || country == null || variant == null) {
    1.56              throw new NullPointerException();
    1.57          }
    1.58 -        baseLocale = BaseLocale.getInstance(convertOldISOCodes(language), "", country, variant);
    1.59 -        localeExtensions = getCompatibilityExtensions(language, "", country, variant);
    1.60 +        this.language = language;
    1.61 +        this.country = country;
    1.62 +        this.variant = variant;
    1.63      }
    1.64  
    1.65      /**
    1.66 @@ -631,96 +609,7 @@
    1.67       * constants due to making shortcuts.
    1.68       */
    1.69      private static Locale createConstant(String lang, String country) {
    1.70 -        BaseLocale base = BaseLocale.createInstance(lang, country);
    1.71 -        return getInstance(base, null);
    1.72 -    }
    1.73 -
    1.74 -    /**
    1.75 -     * Returns a <code>Locale</code> constructed from the given
    1.76 -     * <code>language</code>, <code>country</code> and
    1.77 -     * <code>variant</code>. If the same <code>Locale</code> instance
    1.78 -     * is available in the cache, then that instance is
    1.79 -     * returned. Otherwise, a new <code>Locale</code> instance is
    1.80 -     * created and cached.
    1.81 -     *
    1.82 -     * @param language lowercase 2 to 8 language code.
    1.83 -     * @param country uppercase two-letter ISO-3166 code and numric-3 UN M.49 area code.
    1.84 -     * @param variant vendor and browser specific code. See class description.
    1.85 -     * @return the <code>Locale</code> instance requested
    1.86 -     * @exception NullPointerException if any argument is null.
    1.87 -     */
    1.88 -    static Locale getInstance(String language, String country, String variant) {
    1.89 -        return getInstance(language, "", country, variant, null);
    1.90 -    }
    1.91 -
    1.92 -    static Locale getInstance(String language, String script, String country,
    1.93 -                                      String variant, LocaleExtensions extensions) {
    1.94 -        if (language== null || script == null || country == null || variant == null) {
    1.95 -            throw new NullPointerException();
    1.96 -        }
    1.97 -
    1.98 -        if (extensions == null) {
    1.99 -            extensions = getCompatibilityExtensions(language, script, country, variant);
   1.100 -        }
   1.101 -
   1.102 -        BaseLocale baseloc = BaseLocale.getInstance(language, script, country, variant);
   1.103 -        return getInstance(baseloc, extensions);
   1.104 -    }
   1.105 -
   1.106 -    static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) {
   1.107 -        LocaleKey key = new LocaleKey(baseloc, extensions);
   1.108 -        return LOCALECACHE.get(key);
   1.109 -    }
   1.110 -
   1.111 -    private static class Cache extends LocaleObjectCache<LocaleKey, Locale> {
   1.112 -        private Cache() {
   1.113 -        }
   1.114 -
   1.115 -        @Override
   1.116 -        protected Locale createObject(LocaleKey key) {
   1.117 -            return new Locale(key.base, key.exts);
   1.118 -        }
   1.119 -    }
   1.120 -
   1.121 -    private static final class LocaleKey {
   1.122 -        private final BaseLocale base;
   1.123 -        private final LocaleExtensions exts;
   1.124 -        private final int hash;
   1.125 -
   1.126 -        private LocaleKey(BaseLocale baseLocale, LocaleExtensions extensions) {
   1.127 -            base = baseLocale;
   1.128 -            exts = extensions;
   1.129 -
   1.130 -            // Calculate the hash value here because it's always used.
   1.131 -            int h = base.hashCode();
   1.132 -            if (exts != null) {
   1.133 -                h ^= exts.hashCode();
   1.134 -            }
   1.135 -            hash = h;
   1.136 -        }
   1.137 -
   1.138 -        @Override
   1.139 -        public boolean equals(Object obj) {
   1.140 -            if (this == obj) {
   1.141 -                return true;
   1.142 -            }
   1.143 -            if (!(obj instanceof LocaleKey)) {
   1.144 -                return false;
   1.145 -            }
   1.146 -            LocaleKey other = (LocaleKey)obj;
   1.147 -            if (hash != other.hash || !base.equals(other.base)) {
   1.148 -                return false;
   1.149 -            }
   1.150 -            if (exts == null) {
   1.151 -                return other.exts == null;
   1.152 -            }
   1.153 -            return exts.equals(other.exts);
   1.154 -        }
   1.155 -
   1.156 -        @Override
   1.157 -        public int hashCode() {
   1.158 -            return hash;
   1.159 -        }
   1.160 +        return new Locale(lang, country);
   1.161      }
   1.162  
   1.163      /**
   1.164 @@ -736,104 +625,7 @@
   1.165       * @return the default locale for this instance of the Java Virtual Machine
   1.166       */
   1.167      public static Locale getDefault() {
   1.168 -        // do not synchronize this method - see 4071298
   1.169 -        // it's OK if more than one default locale happens to be created
   1.170 -        if (defaultLocale == null) {
   1.171 -            initDefault();
   1.172 -        }
   1.173 -        return defaultLocale;
   1.174 -    }
   1.175 -
   1.176 -    /**
   1.177 -     * Gets the current value of the default locale for the specified Category
   1.178 -     * for this instance of the Java Virtual Machine.
   1.179 -     * <p>
   1.180 -     * The Java Virtual Machine sets the default locale during startup based
   1.181 -     * on the host environment. It is used by many locale-sensitive methods
   1.182 -     * if no locale is explicitly specified. It can be changed using the
   1.183 -     * setDefault(Locale.Category, Locale) method.
   1.184 -     *
   1.185 -     * @param category - the specified category to get the default locale
   1.186 -     * @throws NullPointerException - if category is null
   1.187 -     * @return the default locale for the specified Category for this instance
   1.188 -     *     of the Java Virtual Machine
   1.189 -     * @see #setDefault(Locale.Category, Locale)
   1.190 -     * @since 1.7
   1.191 -     */
   1.192 -    public static Locale getDefault(Locale.Category category) {
   1.193 -        // do not synchronize this method - see 4071298
   1.194 -        // it's OK if more than one default locale happens to be created
   1.195 -        switch (category) {
   1.196 -        case DISPLAY:
   1.197 -            if (defaultDisplayLocale == null) {
   1.198 -                initDefault(category);
   1.199 -            }
   1.200 -            return defaultDisplayLocale;
   1.201 -        case FORMAT:
   1.202 -            if (defaultFormatLocale == null) {
   1.203 -                initDefault(category);
   1.204 -            }
   1.205 -            return defaultFormatLocale;
   1.206 -        default:
   1.207 -            assert false: "Unknown Category";
   1.208 -        }
   1.209 -        return getDefault();
   1.210 -    }
   1.211 -
   1.212 -    private static void initDefault() {
   1.213 -        String language, region, script, country, variant;
   1.214 -        language = AccessController.doPrivileged(
   1.215 -            new GetPropertyAction("user.language", "en"));
   1.216 -        // for compatibility, check for old user.region property
   1.217 -        region = AccessController.doPrivileged(
   1.218 -            new GetPropertyAction("user.region"));
   1.219 -        if (region != null) {
   1.220 -            // region can be of form country, country_variant, or _variant
   1.221 -            int i = region.indexOf('_');
   1.222 -            if (i >= 0) {
   1.223 -                country = region.substring(0, i);
   1.224 -                variant = region.substring(i + 1);
   1.225 -            } else {
   1.226 -                country = region;
   1.227 -                variant = "";
   1.228 -            }
   1.229 -            script = "";
   1.230 -        } else {
   1.231 -            script = AccessController.doPrivileged(
   1.232 -                new GetPropertyAction("user.script", ""));
   1.233 -            country = AccessController.doPrivileged(
   1.234 -                new GetPropertyAction("user.country", ""));
   1.235 -            variant = AccessController.doPrivileged(
   1.236 -                new GetPropertyAction("user.variant", ""));
   1.237 -        }
   1.238 -        defaultLocale = getInstance(language, script, country, variant, null);
   1.239 -    }
   1.240 -
   1.241 -    private static void initDefault(Locale.Category category) {
   1.242 -        // make sure defaultLocale is initialized
   1.243 -        if (defaultLocale == null) {
   1.244 -            initDefault();
   1.245 -        }
   1.246 -
   1.247 -        Locale defaultCategoryLocale = getInstance(
   1.248 -            AccessController.doPrivileged(
   1.249 -                new GetPropertyAction(category.languageKey, defaultLocale.getLanguage())),
   1.250 -            AccessController.doPrivileged(
   1.251 -                new GetPropertyAction(category.scriptKey, defaultLocale.getScript())),
   1.252 -            AccessController.doPrivileged(
   1.253 -                new GetPropertyAction(category.countryKey, defaultLocale.getCountry())),
   1.254 -            AccessController.doPrivileged(
   1.255 -                new GetPropertyAction(category.variantKey, defaultLocale.getVariant())),
   1.256 -            null);
   1.257 -
   1.258 -        switch (category) {
   1.259 -        case DISPLAY:
   1.260 -            defaultDisplayLocale = defaultCategoryLocale;
   1.261 -            break;
   1.262 -        case FORMAT:
   1.263 -            defaultFormatLocale = defaultCategoryLocale;
   1.264 -            break;
   1.265 -        }
   1.266 +        return Locale.US;
   1.267      }
   1.268  
   1.269      /**
   1.270 @@ -864,60 +656,8 @@
   1.271       * @see SecurityManager#checkPermission
   1.272       * @see java.util.PropertyPermission
   1.273       */
   1.274 -    public static synchronized void setDefault(Locale newLocale) {
   1.275 -        setDefault(Category.DISPLAY, newLocale);
   1.276 -        setDefault(Category.FORMAT, newLocale);
   1.277 -        defaultLocale = newLocale;
   1.278 -    }
   1.279 -
   1.280 -    /**
   1.281 -     * Sets the default locale for the specified Category for this instance
   1.282 -     * of the Java Virtual Machine. This does not affect the host locale.
   1.283 -     * <p>
   1.284 -     * If there is a security manager, its checkPermission method is called
   1.285 -     * with a PropertyPermission("user.language", "write") permission before
   1.286 -     * the default locale is changed.
   1.287 -     * <p>
   1.288 -     * The Java Virtual Machine sets the default locale during startup based
   1.289 -     * on the host environment. It is used by many locale-sensitive methods
   1.290 -     * if no locale is explicitly specified.
   1.291 -     * <p>
   1.292 -     * Since changing the default locale may affect many different areas of
   1.293 -     * functionality, this method should only be used if the caller is
   1.294 -     * prepared to reinitialize locale-sensitive code running within the
   1.295 -     * same Java Virtual Machine.
   1.296 -     * <p>
   1.297 -     *
   1.298 -     * @param category - the specified category to set the default locale
   1.299 -     * @param newLocale - the new default locale
   1.300 -     * @throws SecurityException - if a security manager exists and its
   1.301 -     *     checkPermission method doesn't allow the operation.
   1.302 -     * @throws NullPointerException - if category and/or newLocale is null
   1.303 -     * @see SecurityManager#checkPermission(java.security.Permission)
   1.304 -     * @see PropertyPermission
   1.305 -     * @see #getDefault(Locale.Category)
   1.306 -     * @since 1.7
   1.307 -     */
   1.308 -    public static synchronized void setDefault(Locale.Category category,
   1.309 -        Locale newLocale) {
   1.310 -        if (category == null)
   1.311 -            throw new NullPointerException("Category cannot be NULL");
   1.312 -        if (newLocale == null)
   1.313 -            throw new NullPointerException("Can't set default locale to NULL");
   1.314 -
   1.315 -        SecurityManager sm = System.getSecurityManager();
   1.316 -        if (sm != null) sm.checkPermission(new PropertyPermission
   1.317 -                        ("user.language", "write"));
   1.318 -        switch (category) {
   1.319 -        case DISPLAY:
   1.320 -            defaultDisplayLocale = newLocale;
   1.321 -            break;
   1.322 -        case FORMAT:
   1.323 -            defaultFormatLocale = newLocale;
   1.324 -            break;
   1.325 -        default:
   1.326 -            assert false: "Unknown Category";
   1.327 -        }
   1.328 +    public static void setDefault(Locale newLocale) {
   1.329 +        throw new SecurityException();
   1.330      }
   1.331  
   1.332      /**
   1.333 @@ -931,57 +671,7 @@
   1.334       * @return An array of installed locales.
   1.335       */
   1.336      public static Locale[] getAvailableLocales() {
   1.337 -        return LocaleServiceProviderPool.getAllAvailableLocales();
   1.338 -    }
   1.339 -
   1.340 -    /**
   1.341 -     * Returns a list of all 2-letter country codes defined in ISO 3166.
   1.342 -     * Can be used to create Locales.
   1.343 -     * <p>
   1.344 -     * <b>Note:</b> The <code>Locale</code> class also supports other codes for
   1.345 -     * country (region), such as 3-letter numeric UN M.49 area codes.
   1.346 -     * Therefore, the list returned by this method does not contain ALL valid
   1.347 -     * codes that can be used to create Locales.
   1.348 -     */
   1.349 -    public static String[] getISOCountries() {
   1.350 -        if (isoCountries == null) {
   1.351 -            isoCountries = getISO2Table(LocaleISOData.isoCountryTable);
   1.352 -        }
   1.353 -        String[] result = new String[isoCountries.length];
   1.354 -        System.arraycopy(isoCountries, 0, result, 0, isoCountries.length);
   1.355 -        return result;
   1.356 -    }
   1.357 -
   1.358 -    /**
   1.359 -     * Returns a list of all 2-letter language codes defined in ISO 639.
   1.360 -     * Can be used to create Locales.
   1.361 -     * <p>
   1.362 -     * <b>Note:</b>
   1.363 -     * <ul>
   1.364 -     * <li>ISO 639 is not a stable standard&mdash; some languages' codes have changed.
   1.365 -     * The list this function returns includes both the new and the old codes for the
   1.366 -     * languages whose codes have changed.
   1.367 -     * <li>The <code>Locale</code> class also supports language codes up to
   1.368 -     * 8 characters in length.  Therefore, the list returned by this method does
   1.369 -     * not contain ALL valid codes that can be used to create Locales.
   1.370 -     * </ul>
   1.371 -     */
   1.372 -    public static String[] getISOLanguages() {
   1.373 -        if (isoLanguages == null) {
   1.374 -            isoLanguages = getISO2Table(LocaleISOData.isoLanguageTable);
   1.375 -        }
   1.376 -        String[] result = new String[isoLanguages.length];
   1.377 -        System.arraycopy(isoLanguages, 0, result, 0, isoLanguages.length);
   1.378 -        return result;
   1.379 -    }
   1.380 -
   1.381 -    private static final String[] getISO2Table(String table) {
   1.382 -        int len = table.length() / 5;
   1.383 -        String[] isoTable = new String[len];
   1.384 -        for (int i = 0, j = 0; i < len; i++, j += 5) {
   1.385 -            isoTable[i] = table.substring(j, j + 2);
   1.386 -        }
   1.387 -        return isoTable;
   1.388 +        return new Locale[] { Locale.US };
   1.389      }
   1.390  
   1.391      /**
   1.392 @@ -1004,7 +694,7 @@
   1.393       * @see #getDisplayLanguage
   1.394       */
   1.395      public String getLanguage() {
   1.396 -        return baseLocale.getLanguage();
   1.397 +        return language;
   1.398      }
   1.399  
   1.400      /**
   1.401 @@ -1018,7 +708,7 @@
   1.402       * @since 1.7
   1.403       */
   1.404      public String getScript() {
   1.405 -        return baseLocale.getScript();
   1.406 +        return "";
   1.407      }
   1.408  
   1.409      /**
   1.410 @@ -1030,7 +720,7 @@
   1.411       * @see #getDisplayCountry
   1.412       */
   1.413      public String getCountry() {
   1.414 -        return baseLocale.getRegion();
   1.415 +        return country;
   1.416      }
   1.417  
   1.418      /**
   1.419 @@ -1040,7 +730,11 @@
   1.420       * @see #getDisplayVariant
   1.421       */
   1.422      public String getVariant() {
   1.423 -        return baseLocale.getVariant();
   1.424 +        return variant;
   1.425 +    }
   1.426 +    
   1.427 +    String getRegion() {
   1.428 +        return getCountry();
   1.429      }
   1.430  
   1.431      /**
   1.432 @@ -1059,10 +753,7 @@
   1.433       * @since 1.7
   1.434       */
   1.435      public String getExtension(char key) {
   1.436 -        if (!LocaleExtensions.isValidKey(key)) {
   1.437 -            throw new IllegalArgumentException("Ill-formed extension key: " + key);
   1.438 -        }
   1.439 -        return (localeExtensions == null) ? null : localeExtensions.getExtensionValue(key);
   1.440 +        return null;
   1.441      }
   1.442  
   1.443      /**
   1.444 @@ -1075,10 +766,7 @@
   1.445       * @since 1.7
   1.446       */
   1.447      public Set<Character> getExtensionKeys() {
   1.448 -        if (localeExtensions == null) {
   1.449 -            return Collections.emptySet();
   1.450 -        }
   1.451 -        return localeExtensions.getKeys();
   1.452 +        return Collections.emptySet();
   1.453      }
   1.454  
   1.455      /**
   1.456 @@ -1090,10 +778,7 @@
   1.457       * @since 1.7
   1.458       */
   1.459      public Set<String> getUnicodeLocaleAttributes() {
   1.460 -        if (localeExtensions == null) {
   1.461 -            return Collections.emptySet();
   1.462 -        }
   1.463 -        return localeExtensions.getUnicodeLocaleAttributes();
   1.464 +        return Collections.emptySet();
   1.465      }
   1.466  
   1.467      /**
   1.468 @@ -1111,10 +796,7 @@
   1.469       * @since 1.7
   1.470       */
   1.471      public String getUnicodeLocaleType(String key) {
   1.472 -        if (!UnicodeLocaleExtension.isKey(key)) {
   1.473 -            throw new IllegalArgumentException("Ill-formed Unicode locale key: " + key);
   1.474 -        }
   1.475 -        return (localeExtensions == null) ? null : localeExtensions.getUnicodeLocaleType(key);
   1.476 +        return null;
   1.477      }
   1.478  
   1.479      /**
   1.480 @@ -1126,32 +808,10 @@
   1.481       * @since 1.7
   1.482       */
   1.483      public Set<String> getUnicodeLocaleKeys() {
   1.484 -        if (localeExtensions == null) {
   1.485 -            return Collections.emptySet();
   1.486 -        }
   1.487 -        return localeExtensions.getUnicodeLocaleKeys();
   1.488 +        return Collections.emptySet();
   1.489      }
   1.490  
   1.491      /**
   1.492 -     * Package locale method returning the Locale's BaseLocale,
   1.493 -     * used by ResourceBundle
   1.494 -     * @return base locale of this Locale
   1.495 -     */
   1.496 -    BaseLocale getBaseLocale() {
   1.497 -        return baseLocale;
   1.498 -    }
   1.499 -
   1.500 -    /**
   1.501 -     * Package private method returning the Locale's LocaleExtensions,
   1.502 -     * used by ResourceBundle.
   1.503 -     * @return locale exnteions of this Locale,
   1.504 -     *         or {@code null} if no extensions are defined
   1.505 -     */
   1.506 -     LocaleExtensions getLocaleExtensions() {
   1.507 -         return localeExtensions;
   1.508 -     }
   1.509 -
   1.510 -    /**
   1.511       * Returns a string representation of this <code>Locale</code>
   1.512       * object, consisting of language, country, variant, script,
   1.513       * and extensions as below:
   1.514 @@ -1195,11 +855,12 @@
   1.515       */
   1.516      @Override
   1.517      public final String toString() {
   1.518 +        Locale baseLocale = this;
   1.519          boolean l = (baseLocale.getLanguage().length() != 0);
   1.520          boolean s = (baseLocale.getScript().length() != 0);
   1.521          boolean r = (baseLocale.getRegion().length() != 0);
   1.522          boolean v = (baseLocale.getVariant().length() != 0);
   1.523 -        boolean e = (localeExtensions != null && localeExtensions.getID().length() != 0);
   1.524 +        boolean e = false; //(localeExtensions != null && localeExtensions.getID().length() != 0);
   1.525  
   1.526          StringBuilder result = new StringBuilder(baseLocale.getLanguage());
   1.527          if (r || (l && (v || s || e))) {
   1.528 @@ -1221,635 +882,13 @@
   1.529              if (!s) {
   1.530                  result.append('#');
   1.531              }
   1.532 -            result.append(localeExtensions.getID());
   1.533 +//            result.append(localeExtensions.getID());
   1.534          }
   1.535  
   1.536          return result.toString();
   1.537      }
   1.538  
   1.539 -    /**
   1.540 -     * Returns a well-formed IETF BCP 47 language tag representing
   1.541 -     * this locale.
   1.542 -     *
   1.543 -     * <p>If this <code>Locale</code> has a language, country, or
   1.544 -     * variant that does not satisfy the IETF BCP 47 language tag
   1.545 -     * syntax requirements, this method handles these fields as
   1.546 -     * described below:
   1.547 -     *
   1.548 -     * <p><b>Language:</b> If language is empty, or not <a
   1.549 -     * href="#def_language" >well-formed</a> (for example "a" or
   1.550 -     * "e2"), it will be emitted as "und" (Undetermined).
   1.551 -     *
   1.552 -     * <p><b>Country:</b> If country is not <a
   1.553 -     * href="#def_region">well-formed</a> (for example "12" or "USA"),
   1.554 -     * it will be omitted.
   1.555 -     *
   1.556 -     * <p><b>Variant:</b> If variant <b>is</b> <a
   1.557 -     * href="#def_variant">well-formed</a>, each sub-segment
   1.558 -     * (delimited by '-' or '_') is emitted as a subtag.  Otherwise:
   1.559 -     * <ul>
   1.560 -     *
   1.561 -     * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
   1.562 -     * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
   1.563 -     * ill-formed sub-segment and all following will be appended to
   1.564 -     * the private use subtag.  The first appended subtag will be
   1.565 -     * "lvariant", followed by the sub-segments in order, separated by
   1.566 -     * hyphen. For example, "x-lvariant-WIN",
   1.567 -     * "Oracle-x-lvariant-JDK-Standard-Edition".
   1.568 -     *
   1.569 -     * <li>if any sub-segment does not match
   1.570 -     * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
   1.571 -     * and the problematic sub-segment and all following sub-segments
   1.572 -     * will be omitted.  If the remainder is non-empty, it will be
   1.573 -     * emitted as a private use subtag as above (even if the remainder
   1.574 -     * turns out to be well-formed).  For example,
   1.575 -     * "Solaris_isjustthecoolestthing" is emitted as
   1.576 -     * "x-lvariant-Solaris", not as "solaris".</li></ul>
   1.577 -     *
   1.578 -     * <p><b>Special Conversions:</b> Java supports some old locale
   1.579 -     * representations, including deprecated ISO language codes,
   1.580 -     * for compatibility. This method performs the following
   1.581 -     * conversions:
   1.582 -     * <ul>
   1.583 -     *
   1.584 -     * <li>Deprecated ISO language codes "iw", "ji", and "in" are
   1.585 -     * converted to "he", "yi", and "id", respectively.
   1.586 -     *
   1.587 -     * <li>A locale with language "no", country "NO", and variant
   1.588 -     * "NY", representing Norwegian Nynorsk (Norway), is converted
   1.589 -     * to a language tag "nn-NO".</li></ul>
   1.590 -     *
   1.591 -     * <p><b>Note:</b> Although the language tag created by this
   1.592 -     * method is well-formed (satisfies the syntax requirements
   1.593 -     * defined by the IETF BCP 47 specification), it is not
   1.594 -     * necessarily a valid BCP 47 language tag.  For example,
   1.595 -     * <pre>
   1.596 -     *   new Locale("xx", "YY").toLanguageTag();</pre>
   1.597 -     *
   1.598 -     * will return "xx-YY", but the language subtag "xx" and the
   1.599 -     * region subtag "YY" are invalid because they are not registered
   1.600 -     * in the IANA Language Subtag Registry.
   1.601 -     *
   1.602 -     * @return a BCP47 language tag representing the locale
   1.603 -     * @see #forLanguageTag(String)
   1.604 -     * @since 1.7
   1.605 -     */
   1.606 -    public String toLanguageTag() {
   1.607 -        LanguageTag tag = LanguageTag.parseLocale(baseLocale, localeExtensions);
   1.608 -        StringBuilder buf = new StringBuilder();
   1.609 -
   1.610 -        String subtag = tag.getLanguage();
   1.611 -        if (subtag.length() > 0) {
   1.612 -            buf.append(LanguageTag.canonicalizeLanguage(subtag));
   1.613 -        }
   1.614 -
   1.615 -        subtag = tag.getScript();
   1.616 -        if (subtag.length() > 0) {
   1.617 -            buf.append(LanguageTag.SEP);
   1.618 -            buf.append(LanguageTag.canonicalizeScript(subtag));
   1.619 -        }
   1.620 -
   1.621 -        subtag = tag.getRegion();
   1.622 -        if (subtag.length() > 0) {
   1.623 -            buf.append(LanguageTag.SEP);
   1.624 -            buf.append(LanguageTag.canonicalizeRegion(subtag));
   1.625 -        }
   1.626 -
   1.627 -        List<String>subtags = tag.getVariants();
   1.628 -        for (String s : subtags) {
   1.629 -            buf.append(LanguageTag.SEP);
   1.630 -            // preserve casing
   1.631 -            buf.append(s);
   1.632 -        }
   1.633 -
   1.634 -        subtags = tag.getExtensions();
   1.635 -        for (String s : subtags) {
   1.636 -            buf.append(LanguageTag.SEP);
   1.637 -            buf.append(LanguageTag.canonicalizeExtension(s));
   1.638 -        }
   1.639 -
   1.640 -        subtag = tag.getPrivateuse();
   1.641 -        if (subtag.length() > 0) {
   1.642 -            if (buf.length() > 0) {
   1.643 -                buf.append(LanguageTag.SEP);
   1.644 -            }
   1.645 -            buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
   1.646 -            // preserve casing
   1.647 -            buf.append(subtag);
   1.648 -        }
   1.649 -
   1.650 -        return buf.toString();
   1.651 -    }
   1.652 -
   1.653 -    /**
   1.654 -     * Returns a locale for the specified IETF BCP 47 language tag string.
   1.655 -     *
   1.656 -     * <p>If the specified language tag contains any ill-formed subtags,
   1.657 -     * the first such subtag and all following subtags are ignored.  Compare
   1.658 -     * to {@link Locale.Builder#setLanguageTag} which throws an exception
   1.659 -     * in this case.
   1.660 -     *
   1.661 -     * <p>The following <b>conversions</b> are performed:<ul>
   1.662 -     *
   1.663 -     * <li>The language code "und" is mapped to language "".
   1.664 -     *
   1.665 -     * <li>The language codes "he", "yi", and "id" are mapped to "iw",
   1.666 -     * "ji", and "in" respectively. (This is the same canonicalization
   1.667 -     * that's done in Locale's constructors.)
   1.668 -     *
   1.669 -     * <li>The portion of a private use subtag prefixed by "lvariant",
   1.670 -     * if any, is removed and appended to the variant field in the
   1.671 -     * result locale (without case normalization).  If it is then
   1.672 -     * empty, the private use subtag is discarded:
   1.673 -     *
   1.674 -     * <pre>
   1.675 -     *     Locale loc;
   1.676 -     *     loc = Locale.forLanguageTag("en-US-x-lvariant-POSIX");
   1.677 -     *     loc.getVariant(); // returns "POSIX"
   1.678 -     *     loc.getExtension('x'); // returns null
   1.679 -     *
   1.680 -     *     loc = Locale.forLanguageTag("de-POSIX-x-URP-lvariant-Abc-Def");
   1.681 -     *     loc.getVariant(); // returns "POSIX_Abc_Def"
   1.682 -     *     loc.getExtension('x'); // returns "urp"
   1.683 -     * </pre>
   1.684 -     *
   1.685 -     * <li>When the languageTag argument contains an extlang subtag,
   1.686 -     * the first such subtag is used as the language, and the primary
   1.687 -     * language subtag and other extlang subtags are ignored:
   1.688 -     *
   1.689 -     * <pre>
   1.690 -     *     Locale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
   1.691 -     *     Locale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
   1.692 -     * </pre>
   1.693 -     *
   1.694 -     * <li>Case is normalized except for variant tags, which are left
   1.695 -     * unchanged.  Language is normalized to lower case, script to
   1.696 -     * title case, country to upper case, and extensions to lower
   1.697 -     * case.
   1.698 -     *
   1.699 -     * <li>If, after processing, the locale would exactly match either
   1.700 -     * ja_JP_JP or th_TH_TH with no extensions, the appropriate
   1.701 -     * extensions are added as though the constructor had been called:
   1.702 -     *
   1.703 -     * <pre>
   1.704 -     *    Locale.forLanguageTag("ja-JP-x-lvariant-JP").toLanguageTag();
   1.705 -     *    // returns "ja-JP-u-ca-japanese-x-lvariant-JP"
   1.706 -     *    Locale.forLanguageTag("th-TH-x-lvariant-TH").toLanguageTag();
   1.707 -     *    // returns "th-TH-u-nu-thai-x-lvariant-TH"
   1.708 -     * <pre></ul>
   1.709 -     *
   1.710 -     * <p>This implements the 'Language-Tag' production of BCP47, and
   1.711 -     * so supports grandfathered (regular and irregular) as well as
   1.712 -     * private use language tags.  Stand alone private use tags are
   1.713 -     * represented as empty language and extension 'x-whatever',
   1.714 -     * and grandfathered tags are converted to their canonical replacements
   1.715 -     * where they exist.
   1.716 -     *
   1.717 -     * <p>Grandfathered tags with canonical replacements are as follows:
   1.718 -     *
   1.719 -     * <table>
   1.720 -     * <tbody align="center">
   1.721 -     * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>modern replacement</th></tr>
   1.722 -     * <tr><td>art-lojban</td><td>&nbsp;</td><td>jbo</td></tr>
   1.723 -     * <tr><td>i-ami</td><td>&nbsp;</td><td>ami</td></tr>
   1.724 -     * <tr><td>i-bnn</td><td>&nbsp;</td><td>bnn</td></tr>
   1.725 -     * <tr><td>i-hak</td><td>&nbsp;</td><td>hak</td></tr>
   1.726 -     * <tr><td>i-klingon</td><td>&nbsp;</td><td>tlh</td></tr>
   1.727 -     * <tr><td>i-lux</td><td>&nbsp;</td><td>lb</td></tr>
   1.728 -     * <tr><td>i-navajo</td><td>&nbsp;</td><td>nv</td></tr>
   1.729 -     * <tr><td>i-pwn</td><td>&nbsp;</td><td>pwn</td></tr>
   1.730 -     * <tr><td>i-tao</td><td>&nbsp;</td><td>tao</td></tr>
   1.731 -     * <tr><td>i-tay</td><td>&nbsp;</td><td>tay</td></tr>
   1.732 -     * <tr><td>i-tsu</td><td>&nbsp;</td><td>tsu</td></tr>
   1.733 -     * <tr><td>no-bok</td><td>&nbsp;</td><td>nb</td></tr>
   1.734 -     * <tr><td>no-nyn</td><td>&nbsp;</td><td>nn</td></tr>
   1.735 -     * <tr><td>sgn-BE-FR</td><td>&nbsp;</td><td>sfb</td></tr>
   1.736 -     * <tr><td>sgn-BE-NL</td><td>&nbsp;</td><td>vgt</td></tr>
   1.737 -     * <tr><td>sgn-CH-DE</td><td>&nbsp;</td><td>sgg</td></tr>
   1.738 -     * <tr><td>zh-guoyu</td><td>&nbsp;</td><td>cmn</td></tr>
   1.739 -     * <tr><td>zh-hakka</td><td>&nbsp;</td><td>hak</td></tr>
   1.740 -     * <tr><td>zh-min-nan</td><td>&nbsp;</td><td>nan</td></tr>
   1.741 -     * <tr><td>zh-xiang</td><td>&nbsp;</td><td>hsn</td></tr>
   1.742 -     * </tbody>
   1.743 -     * </table>
   1.744 -     *
   1.745 -     * <p>Grandfathered tags with no modern replacement will be
   1.746 -     * converted as follows:
   1.747 -     *
   1.748 -     * <table>
   1.749 -     * <tbody align="center">
   1.750 -     * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>converts to</th></tr>
   1.751 -     * <tr><td>cel-gaulish</td><td>&nbsp;</td><td>xtg-x-cel-gaulish</td></tr>
   1.752 -     * <tr><td>en-GB-oed</td><td>&nbsp;</td><td>en-GB-x-oed</td></tr>
   1.753 -     * <tr><td>i-default</td><td>&nbsp;</td><td>en-x-i-default</td></tr>
   1.754 -     * <tr><td>i-enochian</td><td>&nbsp;</td><td>und-x-i-enochian</td></tr>
   1.755 -     * <tr><td>i-mingo</td><td>&nbsp;</td><td>see-x-i-mingo</td></tr>
   1.756 -     * <tr><td>zh-min</td><td>&nbsp;</td><td>nan-x-zh-min</td></tr>
   1.757 -     * </tbody>
   1.758 -     * </table>
   1.759 -     *
   1.760 -     * <p>For a list of all grandfathered tags, see the
   1.761 -     * IANA Language Subtag Registry (search for "Type: grandfathered").
   1.762 -     *
   1.763 -     * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
   1.764 -     * and <code>forLanguageTag</code> will round-trip.
   1.765 -     *
   1.766 -     * @param languageTag the language tag
   1.767 -     * @return The locale that best represents the language tag.
   1.768 -     * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
   1.769 -     * @see #toLanguageTag()
   1.770 -     * @see java.util.Locale.Builder#setLanguageTag(String)
   1.771 -     * @since 1.7
   1.772 -     */
   1.773 -    public static Locale forLanguageTag(String languageTag) {
   1.774 -        LanguageTag tag = LanguageTag.parse(languageTag, null);
   1.775 -        InternalLocaleBuilder bldr = new InternalLocaleBuilder();
   1.776 -        bldr.setLanguageTag(tag);
   1.777 -        BaseLocale base = bldr.getBaseLocale();
   1.778 -        LocaleExtensions exts = bldr.getLocaleExtensions();
   1.779 -        if (exts == null && base.getVariant().length() > 0) {
   1.780 -            exts = getCompatibilityExtensions(base.getLanguage(), base.getScript(),
   1.781 -                                              base.getRegion(), base.getVariant());
   1.782 -        }
   1.783 -        return getInstance(base, exts);
   1.784 -    }
   1.785 -
   1.786 -    /**
   1.787 -     * Returns a three-letter abbreviation of this locale's language.
   1.788 -     * If the language matches an ISO 639-1 two-letter code, the
   1.789 -     * corresponding ISO 639-2/T three-letter lowercase code is
   1.790 -     * returned.  The ISO 639-2 language codes can be found on-line,
   1.791 -     * see "Codes for the Representation of Names of Languages Part 2:
   1.792 -     * Alpha-3 Code".  If the locale specifies a three-letter
   1.793 -     * language, the language is returned as is.  If the locale does
   1.794 -     * not specify a language the empty string is returned.
   1.795 -     *
   1.796 -     * @return A three-letter abbreviation of this locale's language.
   1.797 -     * @exception MissingResourceException Throws MissingResourceException if
   1.798 -     * three-letter language abbreviation is not available for this locale.
   1.799 -     */
   1.800 -    public String getISO3Language() throws MissingResourceException {
   1.801 -        String lang = baseLocale.getLanguage();
   1.802 -        if (lang.length() == 3) {
   1.803 -            return lang;
   1.804 -        }
   1.805 -
   1.806 -        String language3 = getISO3Code(lang, LocaleISOData.isoLanguageTable);
   1.807 -        if (language3 == null) {
   1.808 -            throw new MissingResourceException("Couldn't find 3-letter language code for "
   1.809 -                    + lang, "FormatData_" + toString(), "ShortLanguage");
   1.810 -        }
   1.811 -        return language3;
   1.812 -    }
   1.813 -
   1.814 -    /**
   1.815 -     * Returns a three-letter abbreviation for this locale's country.
   1.816 -     * If the country matches an ISO 3166-1 alpha-2 code, the
   1.817 -     * corresponding ISO 3166-1 alpha-3 uppercase code is returned.
   1.818 -     * If the locale doesn't specify a country, this will be the empty
   1.819 -     * string.
   1.820 -     *
   1.821 -     * <p>The ISO 3166-1 codes can be found on-line.
   1.822 -     *
   1.823 -     * @return A three-letter abbreviation of this locale's country.
   1.824 -     * @exception MissingResourceException Throws MissingResourceException if the
   1.825 -     * three-letter country abbreviation is not available for this locale.
   1.826 -     */
   1.827 -    public String getISO3Country() throws MissingResourceException {
   1.828 -        String country3 = getISO3Code(baseLocale.getRegion(), LocaleISOData.isoCountryTable);
   1.829 -        if (country3 == null) {
   1.830 -            throw new MissingResourceException("Couldn't find 3-letter country code for "
   1.831 -                    + baseLocale.getRegion(), "FormatData_" + toString(), "ShortCountry");
   1.832 -        }
   1.833 -        return country3;
   1.834 -    }
   1.835 -
   1.836 -    private static final String getISO3Code(String iso2Code, String table) {
   1.837 -        int codeLength = iso2Code.length();
   1.838 -        if (codeLength == 0) {
   1.839 -            return "";
   1.840 -        }
   1.841 -
   1.842 -        int tableLength = table.length();
   1.843 -        int index = tableLength;
   1.844 -        if (codeLength == 2) {
   1.845 -            char c1 = iso2Code.charAt(0);
   1.846 -            char c2 = iso2Code.charAt(1);
   1.847 -            for (index = 0; index < tableLength; index += 5) {
   1.848 -                if (table.charAt(index) == c1
   1.849 -                    && table.charAt(index + 1) == c2) {
   1.850 -                    break;
   1.851 -                }
   1.852 -            }
   1.853 -        }
   1.854 -        return index < tableLength ? table.substring(index + 2, index + 5) : null;
   1.855 -    }
   1.856 -
   1.857 -    /**
   1.858 -     * Returns a name for the locale's language that is appropriate for display to the
   1.859 -     * user.
   1.860 -     * If possible, the name returned will be localized for the default locale.
   1.861 -     * For example, if the locale is fr_FR and the default locale
   1.862 -     * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
   1.863 -     * the default locale is fr_FR, getDisplayLanguage() will return "anglais".
   1.864 -     * If the name returned cannot be localized for the default locale,
   1.865 -     * (say, we don't have a Japanese name for Croatian),
   1.866 -     * this function falls back on the English name, and uses the ISO code as a last-resort
   1.867 -     * value.  If the locale doesn't specify a language, this function returns the empty string.
   1.868 -     */
   1.869 -    public final String getDisplayLanguage() {
   1.870 -        return getDisplayLanguage(getDefault(Category.DISPLAY));
   1.871 -    }
   1.872 -
   1.873 -    /**
   1.874 -     * Returns a name for the locale's language that is appropriate for display to the
   1.875 -     * user.
   1.876 -     * If possible, the name returned will be localized according to inLocale.
   1.877 -     * For example, if the locale is fr_FR and inLocale
   1.878 -     * is en_US, getDisplayLanguage() will return "French"; if the locale is en_US and
   1.879 -     * inLocale is fr_FR, getDisplayLanguage() will return "anglais".
   1.880 -     * If the name returned cannot be localized according to inLocale,
   1.881 -     * (say, we don't have a Japanese name for Croatian),
   1.882 -     * this function falls back on the English name, and finally
   1.883 -     * on the ISO code as a last-resort value.  If the locale doesn't specify a language,
   1.884 -     * this function returns the empty string.
   1.885 -     *
   1.886 -     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
   1.887 -     */
   1.888 -    public String getDisplayLanguage(Locale inLocale) {
   1.889 -        return getDisplayString(baseLocale.getLanguage(), inLocale, DISPLAY_LANGUAGE);
   1.890 -    }
   1.891 -
   1.892 -    /**
   1.893 -     * Returns a name for the the locale's script that is appropriate for display to
   1.894 -     * the user. If possible, the name will be localized for the default locale.  Returns
   1.895 -     * the empty string if this locale doesn't specify a script code.
   1.896 -     *
   1.897 -     * @return the display name of the script code for the current default locale
   1.898 -     * @since 1.7
   1.899 -     */
   1.900 -    public String getDisplayScript() {
   1.901 -        return getDisplayScript(getDefault());
   1.902 -    }
   1.903 -
   1.904 -    /**
   1.905 -     * Returns a name for the locale's script that is appropriate
   1.906 -     * for display to the user. If possible, the name will be
   1.907 -     * localized for the given locale. Returns the empty string if
   1.908 -     * this locale doesn't specify a script code.
   1.909 -     *
   1.910 -     * @return the display name of the script code for the current default locale
   1.911 -     * @throws NullPointerException if <code>inLocale</code> is <code>null</code>
   1.912 -     * @since 1.7
   1.913 -     */
   1.914 -    public String getDisplayScript(Locale inLocale) {
   1.915 -        return getDisplayString(baseLocale.getScript(), inLocale, DISPLAY_SCRIPT);
   1.916 -    }
   1.917 -
   1.918 -    /**
   1.919 -     * Returns a name for the locale's country that is appropriate for display to the
   1.920 -     * user.
   1.921 -     * If possible, the name returned will be localized for the default locale.
   1.922 -     * For example, if the locale is fr_FR and the default locale
   1.923 -     * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
   1.924 -     * the default locale is fr_FR, getDisplayCountry() will return "Etats-Unis".
   1.925 -     * If the name returned cannot be localized for the default locale,
   1.926 -     * (say, we don't have a Japanese name for Croatia),
   1.927 -     * this function falls back on the English name, and uses the ISO code as a last-resort
   1.928 -     * value.  If the locale doesn't specify a country, this function returns the empty string.
   1.929 -     */
   1.930 -    public final String getDisplayCountry() {
   1.931 -        return getDisplayCountry(getDefault(Category.DISPLAY));
   1.932 -    }
   1.933 -
   1.934 -    /**
   1.935 -     * Returns a name for the locale's country that is appropriate for display to the
   1.936 -     * user.
   1.937 -     * If possible, the name returned will be localized according to inLocale.
   1.938 -     * For example, if the locale is fr_FR and inLocale
   1.939 -     * is en_US, getDisplayCountry() will return "France"; if the locale is en_US and
   1.940 -     * inLocale is fr_FR, getDisplayCountry() will return "Etats-Unis".
   1.941 -     * If the name returned cannot be localized according to inLocale.
   1.942 -     * (say, we don't have a Japanese name for Croatia),
   1.943 -     * this function falls back on the English name, and finally
   1.944 -     * on the ISO code as a last-resort value.  If the locale doesn't specify a country,
   1.945 -     * this function returns the empty string.
   1.946 -     *
   1.947 -     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
   1.948 -     */
   1.949 -    public String getDisplayCountry(Locale inLocale) {
   1.950 -        return getDisplayString(baseLocale.getRegion(), inLocale, DISPLAY_COUNTRY);
   1.951 -    }
   1.952 -
   1.953 -    private String getDisplayString(String code, Locale inLocale, int type) {
   1.954 -        if (code.length() == 0) {
   1.955 -            return "";
   1.956 -        }
   1.957 -
   1.958 -        if (inLocale == null) {
   1.959 -            throw new NullPointerException();
   1.960 -        }
   1.961 -
   1.962 -        try {
   1.963 -            OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
   1.964 -            String key = (type == DISPLAY_VARIANT ? "%%"+code : code);
   1.965 -            String result = null;
   1.966 -
   1.967 -            // Check whether a provider can provide an implementation that's closer
   1.968 -            // to the requested locale than what the Java runtime itself can provide.
   1.969 -            LocaleServiceProviderPool pool =
   1.970 -                LocaleServiceProviderPool.getPool(LocaleNameProvider.class);
   1.971 -            if (pool.hasProviders()) {
   1.972 -                result = pool.getLocalizedObject(
   1.973 -                                    LocaleNameGetter.INSTANCE,
   1.974 -                                    inLocale, bundle, key,
   1.975 -                                    type, code);
   1.976 -            }
   1.977 -
   1.978 -            if (result == null) {
   1.979 -                result = bundle.getString(key);
   1.980 -            }
   1.981 -
   1.982 -            if (result != null) {
   1.983 -                return result;
   1.984 -            }
   1.985 -        }
   1.986 -        catch (Exception e) {
   1.987 -            // just fall through
   1.988 -        }
   1.989 -        return code;
   1.990 -    }
   1.991 -
   1.992 -    /**
   1.993 -     * Returns a name for the locale's variant code that is appropriate for display to the
   1.994 -     * user.  If possible, the name will be localized for the default locale.  If the locale
   1.995 -     * doesn't specify a variant code, this function returns the empty string.
   1.996 -     */
   1.997 -    public final String getDisplayVariant() {
   1.998 -        return getDisplayVariant(getDefault(Category.DISPLAY));
   1.999 -    }
  1.1000 -
  1.1001 -    /**
  1.1002 -     * Returns a name for the locale's variant code that is appropriate for display to the
  1.1003 -     * user.  If possible, the name will be localized for inLocale.  If the locale
  1.1004 -     * doesn't specify a variant code, this function returns the empty string.
  1.1005 -     *
  1.1006 -     * @exception NullPointerException if <code>inLocale</code> is <code>null</code>
  1.1007 -     */
  1.1008 -    public String getDisplayVariant(Locale inLocale) {
  1.1009 -        if (baseLocale.getVariant().length() == 0)
  1.1010 -            return "";
  1.1011 -
  1.1012 -        OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
  1.1013 -
  1.1014 -        String names[] = getDisplayVariantArray(bundle, inLocale);
  1.1015 -
  1.1016 -        // Get the localized patterns for formatting a list, and use
  1.1017 -        // them to format the list.
  1.1018 -        String listPattern = null;
  1.1019 -        String listCompositionPattern = null;
  1.1020 -        try {
  1.1021 -            listPattern = bundle.getString("ListPattern");
  1.1022 -            listCompositionPattern = bundle.getString("ListCompositionPattern");
  1.1023 -        } catch (MissingResourceException e) {
  1.1024 -        }
  1.1025 -        return formatList(names, listPattern, listCompositionPattern);
  1.1026 -    }
  1.1027 -
  1.1028 -    /**
  1.1029 -     * Returns a name for the locale that is appropriate for display to the
  1.1030 -     * user. This will be the values returned by getDisplayLanguage(),
  1.1031 -     * getDisplayScript(), getDisplayCountry(), and getDisplayVariant() assembled
  1.1032 -     * into a single string. The the non-empty values are used in order,
  1.1033 -     * with the second and subsequent names in parentheses.  For example:
  1.1034 -     * <blockquote>
  1.1035 -     * language (script, country, variant)<br>
  1.1036 -     * language (country)<br>
  1.1037 -     * language (variant)<br>
  1.1038 -     * script (country)<br>
  1.1039 -     * country<br>
  1.1040 -     * </blockquote>
  1.1041 -     * depending on which fields are specified in the locale.  If the
  1.1042 -     * language, sacript, country, and variant fields are all empty,
  1.1043 -     * this function returns the empty string.
  1.1044 -     */
  1.1045 -    public final String getDisplayName() {
  1.1046 -        return getDisplayName(getDefault(Category.DISPLAY));
  1.1047 -    }
  1.1048 -
  1.1049 -    /**
  1.1050 -     * Returns a name for the locale that is appropriate for display
  1.1051 -     * to the user.  This will be the values returned by
  1.1052 -     * getDisplayLanguage(), getDisplayScript(),getDisplayCountry(),
  1.1053 -     * and getDisplayVariant() assembled into a single string.
  1.1054 -     * The non-empty values are used in order,
  1.1055 -     * with the second and subsequent names in parentheses.  For example:
  1.1056 -     * <blockquote>
  1.1057 -     * language (script, country, variant)<br>
  1.1058 -     * language (country)<br>
  1.1059 -     * language (variant)<br>
  1.1060 -     * script (country)<br>
  1.1061 -     * country<br>
  1.1062 -     * </blockquote>
  1.1063 -     * depending on which fields are specified in the locale.  If the
  1.1064 -     * language, script, country, and variant fields are all empty,
  1.1065 -     * this function returns the empty string.
  1.1066 -     *
  1.1067 -     * @throws NullPointerException if <code>inLocale</code> is <code>null</code>
  1.1068 -     */
  1.1069 -    public String getDisplayName(Locale inLocale) {
  1.1070 -        OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale);
  1.1071 -
  1.1072 -        String languageName = getDisplayLanguage(inLocale);
  1.1073 -        String scriptName = getDisplayScript(inLocale);
  1.1074 -        String countryName = getDisplayCountry(inLocale);
  1.1075 -        String[] variantNames = getDisplayVariantArray(bundle, inLocale);
  1.1076 -
  1.1077 -        // Get the localized patterns for formatting a display name.
  1.1078 -        String displayNamePattern = null;
  1.1079 -        String listPattern = null;
  1.1080 -        String listCompositionPattern = null;
  1.1081 -        try {
  1.1082 -            displayNamePattern = bundle.getString("DisplayNamePattern");
  1.1083 -            listPattern = bundle.getString("ListPattern");
  1.1084 -            listCompositionPattern = bundle.getString("ListCompositionPattern");
  1.1085 -        } catch (MissingResourceException e) {
  1.1086 -        }
  1.1087 -
  1.1088 -        // The display name consists of a main name, followed by qualifiers.
  1.1089 -        // Typically, the format is "MainName (Qualifier, Qualifier)" but this
  1.1090 -        // depends on what pattern is stored in the display locale.
  1.1091 -        String   mainName       = null;
  1.1092 -        String[] qualifierNames = null;
  1.1093 -
  1.1094 -        // The main name is the language, or if there is no language, the script,
  1.1095 -        // then if no script, the country. If there is no language/script/country
  1.1096 -        // (an anomalous situation) then the display name is simply the variant's
  1.1097 -        // display name.
  1.1098 -        if (languageName.length() == 0 && scriptName.length() == 0 && countryName.length() == 0) {
  1.1099 -            if (variantNames.length == 0) {
  1.1100 -                return "";
  1.1101 -            } else {
  1.1102 -                return formatList(variantNames, listPattern, listCompositionPattern);
  1.1103 -            }
  1.1104 -        }
  1.1105 -        ArrayList<String> names = new ArrayList<>(4);
  1.1106 -        if (languageName.length() != 0) {
  1.1107 -            names.add(languageName);
  1.1108 -        }
  1.1109 -        if (scriptName.length() != 0) {
  1.1110 -            names.add(scriptName);
  1.1111 -        }
  1.1112 -        if (countryName.length() != 0) {
  1.1113 -            names.add(countryName);
  1.1114 -        }
  1.1115 -        if (variantNames.length != 0) {
  1.1116 -            for (String var : variantNames) {
  1.1117 -                names.add(var);
  1.1118 -            }
  1.1119 -        }
  1.1120 -
  1.1121 -        // The first one in the main name
  1.1122 -        mainName = names.get(0);
  1.1123 -
  1.1124 -        // Others are qualifiers
  1.1125 -        int numNames = names.size();
  1.1126 -        qualifierNames = (numNames > 1) ?
  1.1127 -                names.subList(1, numNames).toArray(new String[numNames - 1]) : new String[0];
  1.1128 -
  1.1129 -        // Create an array whose first element is the number of remaining
  1.1130 -        // elements.  This serves as a selector into a ChoiceFormat pattern from
  1.1131 -        // the resource.  The second and third elements are the main name and
  1.1132 -        // the qualifier; if there are no qualifiers, the third element is
  1.1133 -        // unused by the format pattern.
  1.1134 -        Object[] displayNames = {
  1.1135 -            new Integer(qualifierNames.length != 0 ? 2 : 1),
  1.1136 -            mainName,
  1.1137 -            // We could also just call formatList() and have it handle the empty
  1.1138 -            // list case, but this is more efficient, and we want it to be
  1.1139 -            // efficient since all the language-only locales will not have any
  1.1140 -            // qualifiers.
  1.1141 -            qualifierNames.length != 0 ? formatList(qualifierNames, listPattern, listCompositionPattern) : null
  1.1142 -        };
  1.1143 -
  1.1144 -        if (displayNamePattern != null) {
  1.1145 -            return new MessageFormat(displayNamePattern).format(displayNames);
  1.1146 -        }
  1.1147 -        else {
  1.1148 -            // If we cannot get the message format pattern, then we use a simple
  1.1149 -            // hard-coded pattern.  This should not occur in practice unless the
  1.1150 -            // installation is missing some core files (FormatData etc.).
  1.1151 -            StringBuilder result = new StringBuilder();
  1.1152 -            result.append((String)displayNames[1]);
  1.1153 -            if (displayNames.length > 2) {
  1.1154 -                result.append(" (");
  1.1155 -                result.append((String)displayNames[2]);
  1.1156 -                result.append(')');
  1.1157 -            }
  1.1158 -            return result.toString();
  1.1159 -        }
  1.1160 -    }
  1.1161 -
  1.1162 +    
  1.1163      /**
  1.1164       * Overrides Cloneable.
  1.1165       */
  1.1166 @@ -1870,691 +909,34 @@
  1.1167       */
  1.1168      @Override
  1.1169      public int hashCode() {
  1.1170 -        int hc = hashCodeValue;
  1.1171 -        if (hc == 0) {
  1.1172 -            hc = baseLocale.hashCode();
  1.1173 -            if (localeExtensions != null) {
  1.1174 -                hc ^= localeExtensions.hashCode();
  1.1175 -            }
  1.1176 -            hashCodeValue = hc;
  1.1177 -        }
  1.1178 -        return hc;
  1.1179 +        int hash = 3;
  1.1180 +        hash = 43 * hash + Objects.hashCode(this.language);
  1.1181 +        hash = 43 * hash + Objects.hashCode(this.country);
  1.1182 +        hash = 43 * hash + Objects.hashCode(this.variant);
  1.1183 +        return hash;
  1.1184      }
  1.1185  
  1.1186      // Overrides
  1.1187 -
  1.1188 -    /**
  1.1189 -     * Returns true if this Locale is equal to another object.  A Locale is
  1.1190 -     * deemed equal to another Locale with identical language, script, country,
  1.1191 -     * variant and extensions, and unequal to all other objects.
  1.1192 -     *
  1.1193 -     * @return true if this Locale is equal to the specified object.
  1.1194 -     */
  1.1195      @Override
  1.1196      public boolean equals(Object obj) {
  1.1197 -        if (this == obj)                      // quick check
  1.1198 -            return true;
  1.1199 -        if (!(obj instanceof Locale))
  1.1200 -            return false;
  1.1201 -        BaseLocale otherBase = ((Locale)obj).baseLocale;
  1.1202 -        if (!baseLocale.equals(otherBase)) {
  1.1203 +        if (obj == null) {
  1.1204              return false;
  1.1205          }
  1.1206 -        if (localeExtensions == null) {
  1.1207 -            return ((Locale)obj).localeExtensions == null;
  1.1208 +        if (getClass() != obj.getClass()) {
  1.1209 +            return false;
  1.1210          }
  1.1211 -        return localeExtensions.equals(((Locale)obj).localeExtensions);
  1.1212 +        final Locale other = (Locale) obj;
  1.1213 +        if (!Objects.equals(this.language, other.language)) {
  1.1214 +            return false;
  1.1215 +        }
  1.1216 +        if (!Objects.equals(this.country, other.country)) {
  1.1217 +            return false;
  1.1218 +        }
  1.1219 +        if (!Objects.equals(this.variant, other.variant)) {
  1.1220 +            return false;
  1.1221 +        }
  1.1222 +        return true;
  1.1223      }
  1.1224  
  1.1225 -    // ================= privates =====================================
  1.1226  
  1.1227 -    private transient BaseLocale baseLocale;
  1.1228 -    private transient LocaleExtensions localeExtensions;
  1.1229 -
  1.1230 -    /**
  1.1231 -     * Calculated hashcode
  1.1232 -     */
  1.1233 -    private transient volatile int hashCodeValue = 0;
  1.1234 -
  1.1235 -    private static Locale defaultLocale = null;
  1.1236 -    private static Locale defaultDisplayLocale = null;
  1.1237 -    private static Locale defaultFormatLocale = null;
  1.1238 -
  1.1239 -    /**
  1.1240 -     * Return an array of the display names of the variant.
  1.1241 -     * @param bundle the ResourceBundle to use to get the display names
  1.1242 -     * @return an array of display names, possible of zero length.
  1.1243 -     */
  1.1244 -    private String[] getDisplayVariantArray(OpenListResourceBundle bundle, Locale inLocale) {
  1.1245 -        // Split the variant name into tokens separated by '_'.
  1.1246 -        StringTokenizer tokenizer = new StringTokenizer(baseLocale.getVariant(), "_");
  1.1247 -        String[] names = new String[tokenizer.countTokens()];
  1.1248 -
  1.1249 -        // For each variant token, lookup the display name.  If
  1.1250 -        // not found, use the variant name itself.
  1.1251 -        for (int i=0; i<names.length; ++i) {
  1.1252 -            names[i] = getDisplayString(tokenizer.nextToken(),
  1.1253 -                                inLocale, DISPLAY_VARIANT);
  1.1254 -        }
  1.1255 -
  1.1256 -        return names;
  1.1257 -    }
  1.1258 -
  1.1259 -    /**
  1.1260 -     * Format a list using given pattern strings.
  1.1261 -     * If either of the patterns is null, then a the list is
  1.1262 -     * formatted by concatenation with the delimiter ','.
  1.1263 -     * @param stringList the list of strings to be formatted.
  1.1264 -     * @param listPattern should create a MessageFormat taking 0-3 arguments
  1.1265 -     * and formatting them into a list.
  1.1266 -     * @param listCompositionPattern should take 2 arguments
  1.1267 -     * and is used by composeList.
  1.1268 -     * @return a string representing the list.
  1.1269 -     */
  1.1270 -    private static String formatList(String[] stringList, String listPattern, String listCompositionPattern) {
  1.1271 -        // If we have no list patterns, compose the list in a simple,
  1.1272 -        // non-localized way.
  1.1273 -        if (listPattern == null || listCompositionPattern == null) {
  1.1274 -            StringBuffer result = new StringBuffer();
  1.1275 -            for (int i=0; i<stringList.length; ++i) {
  1.1276 -                if (i>0) result.append(',');
  1.1277 -                result.append(stringList[i]);
  1.1278 -            }
  1.1279 -            return result.toString();
  1.1280 -        }
  1.1281 -
  1.1282 -        // Compose the list down to three elements if necessary
  1.1283 -        if (stringList.length > 3) {
  1.1284 -            MessageFormat format = new MessageFormat(listCompositionPattern);
  1.1285 -            stringList = composeList(format, stringList);
  1.1286 -        }
  1.1287 -
  1.1288 -        // Rebuild the argument list with the list length as the first element
  1.1289 -        Object[] args = new Object[stringList.length + 1];
  1.1290 -        System.arraycopy(stringList, 0, args, 1, stringList.length);
  1.1291 -        args[0] = new Integer(stringList.length);
  1.1292 -
  1.1293 -        // Format it using the pattern in the resource
  1.1294 -        MessageFormat format = new MessageFormat(listPattern);
  1.1295 -        return format.format(args);
  1.1296 -    }
  1.1297 -
  1.1298 -    /**
  1.1299 -     * Given a list of strings, return a list shortened to three elements.
  1.1300 -     * Shorten it by applying the given format to the first two elements
  1.1301 -     * recursively.
  1.1302 -     * @param format a format which takes two arguments
  1.1303 -     * @param list a list of strings
  1.1304 -     * @return if the list is three elements or shorter, the same list;
  1.1305 -     * otherwise, a new list of three elements.
  1.1306 -     */
  1.1307 -    private static String[] composeList(MessageFormat format, String[] list) {
  1.1308 -        if (list.length <= 3) return list;
  1.1309 -
  1.1310 -        // Use the given format to compose the first two elements into one
  1.1311 -        String[] listItems = { list[0], list[1] };
  1.1312 -        String newItem = format.format(listItems);
  1.1313 -
  1.1314 -        // Form a new list one element shorter
  1.1315 -        String[] newList = new String[list.length-1];
  1.1316 -        System.arraycopy(list, 2, newList, 1, newList.length-1);
  1.1317 -        newList[0] = newItem;
  1.1318 -
  1.1319 -        // Recurse
  1.1320 -        return composeList(format, newList);
  1.1321 -    }
  1.1322 -
  1.1323 -    /**
  1.1324 -     * @serialField language    String
  1.1325 -     *      language subtag in lower case. (See <a href="java/util/Locale.html#getLanguage()">getLanguage()</a>)
  1.1326 -     * @serialField country     String
  1.1327 -     *      country subtag in upper case. (See <a href="java/util/Locale.html#getCountry()">getCountry()</a>)
  1.1328 -     * @serialField variant     String
  1.1329 -     *      variant subtags separated by LOWLINE characters. (See <a href="java/util/Locale.html#getVariant()">getVariant()</a>)
  1.1330 -     * @serialField hashcode    int
  1.1331 -     *      deprecated, for forward compatibility only
  1.1332 -     * @serialField script      String
  1.1333 -     *      script subtag in title case (See <a href="java/util/Locale.html#getScript()">getScript()</a>)
  1.1334 -     * @serialField extensions  String
  1.1335 -     *      canonical representation of extensions, that is,
  1.1336 -     *      BCP47 extensions in alphabetical order followed by
  1.1337 -     *      BCP47 private use subtags, all in lower case letters
  1.1338 -     *      separated by HYPHEN-MINUS characters.
  1.1339 -     *      (See <a href="java/util/Locale.html#getExtensionKeys()">getExtensionKeys()</a>,
  1.1340 -     *      <a href="java/util/Locale.html#getExtension(char)">getExtension(char)</a>)
  1.1341 -     */
  1.1342 -    private static final ObjectStreamField[] serialPersistentFields = {
  1.1343 -        new ObjectStreamField("language", String.class),
  1.1344 -        new ObjectStreamField("country", String.class),
  1.1345 -        new ObjectStreamField("variant", String.class),
  1.1346 -        new ObjectStreamField("hashcode", int.class),
  1.1347 -        new ObjectStreamField("script", String.class),
  1.1348 -        new ObjectStreamField("extensions", String.class),
  1.1349 -    };
  1.1350 -
  1.1351 -    /**
  1.1352 -     * Serializes this <code>Locale</code> to the specified <code>ObjectOutputStream</code>.
  1.1353 -     * @param out the <code>ObjectOutputStream</code> to write
  1.1354 -     * @throws IOException
  1.1355 -     * @since 1.7
  1.1356 -     */
  1.1357 -    private void writeObject(ObjectOutputStream out) throws IOException {
  1.1358 -        ObjectOutputStream.PutField fields = out.putFields();
  1.1359 -        fields.put("language", baseLocale.getLanguage());
  1.1360 -        fields.put("script", baseLocale.getScript());
  1.1361 -        fields.put("country", baseLocale.getRegion());
  1.1362 -        fields.put("variant", baseLocale.getVariant());
  1.1363 -        fields.put("extensions", localeExtensions == null ? "" : localeExtensions.getID());
  1.1364 -        fields.put("hashcode", -1); // place holder just for backward support
  1.1365 -        out.writeFields();
  1.1366 -    }
  1.1367 -
  1.1368 -    /**
  1.1369 -     * Deserializes this <code>Locale</code>.
  1.1370 -     * @param in the <code>ObjectInputStream</code> to read
  1.1371 -     * @throws IOException
  1.1372 -     * @throws ClassNotFoundException
  1.1373 -     * @throws IllformdLocaleException
  1.1374 -     * @since 1.7
  1.1375 -     */
  1.1376 -    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  1.1377 -        ObjectInputStream.GetField fields = in.readFields();
  1.1378 -        String language = (String)fields.get("language", "");
  1.1379 -        String script = (String)fields.get("script", "");
  1.1380 -        String country = (String)fields.get("country", "");
  1.1381 -        String variant = (String)fields.get("variant", "");
  1.1382 -        String extStr = (String)fields.get("extensions", "");
  1.1383 -        baseLocale = BaseLocale.getInstance(convertOldISOCodes(language), script, country, variant);
  1.1384 -        if (extStr.length() > 0) {
  1.1385 -            try {
  1.1386 -                InternalLocaleBuilder bldr = new InternalLocaleBuilder();
  1.1387 -                bldr.setExtensions(extStr);
  1.1388 -                localeExtensions = bldr.getLocaleExtensions();
  1.1389 -            } catch (LocaleSyntaxException e) {
  1.1390 -                throw new IllformedLocaleException(e.getMessage());
  1.1391 -            }
  1.1392 -        } else {
  1.1393 -            localeExtensions = null;
  1.1394 -        }
  1.1395 -    }
  1.1396 -
  1.1397 -    /**
  1.1398 -     * Returns a cached <code>Locale</code> instance equivalent to
  1.1399 -     * the deserialized <code>Locale</code>. When serialized
  1.1400 -     * language, country and variant fields read from the object data stream
  1.1401 -     * are exactly "ja", "JP", "JP" or "th", "TH", "TH" and script/extensions
  1.1402 -     * fields are empty, this method supplies <code>UNICODE_LOCALE_EXTENSION</code>
  1.1403 -     * "ca"/"japanese" (calendar type is "japanese") or "nu"/"thai" (number script
  1.1404 -     * type is "thai"). See <a href="Locale.html#special_cases_constructor"/>Special Cases</a>
  1.1405 -     * for more information.
  1.1406 -     *
  1.1407 -     * @return an instance of <code>Locale</code> equivalent to
  1.1408 -     * the deserialized <code>Locale</code>.
  1.1409 -     * @throws java.io.ObjectStreamException
  1.1410 -     */
  1.1411 -    private Object readResolve() throws java.io.ObjectStreamException {
  1.1412 -        return getInstance(baseLocale.getLanguage(), baseLocale.getScript(),
  1.1413 -                baseLocale.getRegion(), baseLocale.getVariant(), localeExtensions);
  1.1414 -    }
  1.1415 -
  1.1416 -    private static volatile String[] isoLanguages = null;
  1.1417 -
  1.1418 -    private static volatile String[] isoCountries = null;
  1.1419 -
  1.1420 -    private static String convertOldISOCodes(String language) {
  1.1421 -        // we accept both the old and the new ISO codes for the languages whose ISO
  1.1422 -        // codes have changed, but we always store the OLD code, for backward compatibility
  1.1423 -        language = LocaleUtils.toLowerString(language).intern();
  1.1424 -        if (language == "he") {
  1.1425 -            return "iw";
  1.1426 -        } else if (language == "yi") {
  1.1427 -            return "ji";
  1.1428 -        } else if (language == "id") {
  1.1429 -            return "in";
  1.1430 -        } else {
  1.1431 -            return language;
  1.1432 -        }
  1.1433 -    }
  1.1434 -
  1.1435 -    private static LocaleExtensions getCompatibilityExtensions(String language,
  1.1436 -                                                               String script,
  1.1437 -                                                               String country,
  1.1438 -                                                               String variant) {
  1.1439 -        LocaleExtensions extensions = null;
  1.1440 -        // Special cases for backward compatibility support
  1.1441 -        if (LocaleUtils.caseIgnoreMatch(language, "ja")
  1.1442 -                && script.length() == 0
  1.1443 -                && LocaleUtils.caseIgnoreMatch(country, "jp")
  1.1444 -                && "JP".equals(variant)) {
  1.1445 -            // ja_JP_JP -> u-ca-japanese (calendar = japanese)
  1.1446 -            extensions = LocaleExtensions.CALENDAR_JAPANESE;
  1.1447 -        } else if (LocaleUtils.caseIgnoreMatch(language, "th")
  1.1448 -                && script.length() == 0
  1.1449 -                && LocaleUtils.caseIgnoreMatch(country, "th")
  1.1450 -                && "TH".equals(variant)) {
  1.1451 -            // th_TH_TH -> u-nu-thai (numbersystem = thai)
  1.1452 -            extensions = LocaleExtensions.NUMBER_THAI;
  1.1453 -        }
  1.1454 -        return extensions;
  1.1455 -    }
  1.1456 -
  1.1457 -    /**
  1.1458 -     * Obtains a localized locale names from a LocaleNameProvider
  1.1459 -     * implementation.
  1.1460 -     */
  1.1461 -    private static class LocaleNameGetter
  1.1462 -        implements LocaleServiceProviderPool.LocalizedObjectGetter<LocaleNameProvider, String> {
  1.1463 -        private static final LocaleNameGetter INSTANCE = new LocaleNameGetter();
  1.1464 -
  1.1465 -        public String getObject(LocaleNameProvider localeNameProvider,
  1.1466 -                                Locale locale,
  1.1467 -                                String key,
  1.1468 -                                Object... params) {
  1.1469 -            assert params.length == 2;
  1.1470 -            int type = (Integer)params[0];
  1.1471 -            String code = (String)params[1];
  1.1472 -
  1.1473 -            switch(type) {
  1.1474 -            case DISPLAY_LANGUAGE:
  1.1475 -                return localeNameProvider.getDisplayLanguage(code, locale);
  1.1476 -            case DISPLAY_COUNTRY:
  1.1477 -                return localeNameProvider.getDisplayCountry(code, locale);
  1.1478 -            case DISPLAY_VARIANT:
  1.1479 -                return localeNameProvider.getDisplayVariant(code, locale);
  1.1480 -            case DISPLAY_SCRIPT:
  1.1481 -                return localeNameProvider.getDisplayScript(code, locale);
  1.1482 -            default:
  1.1483 -                assert false; // shouldn't happen
  1.1484 -            }
  1.1485 -
  1.1486 -            return null;
  1.1487 -        }
  1.1488 -    }
  1.1489 -
  1.1490 -    /**
  1.1491 -     * Enum for locale categories.  These locale categories are used to get/set
  1.1492 -     * the default locale for the specific functionality represented by the
  1.1493 -     * category.
  1.1494 -     *
  1.1495 -     * @see #getDefault(Locale.Category)
  1.1496 -     * @see #setDefault(Locale.Category, Locale)
  1.1497 -     * @since 1.7
  1.1498 -     */
  1.1499 -    public enum Category {
  1.1500 -
  1.1501 -        /**
  1.1502 -         * Category used to represent the default locale for
  1.1503 -         * displaying user interfaces.
  1.1504 -         */
  1.1505 -        DISPLAY("user.language.display",
  1.1506 -                "user.script.display",
  1.1507 -                "user.country.display",
  1.1508 -                "user.variant.display"),
  1.1509 -
  1.1510 -        /**
  1.1511 -         * Category used to represent the default locale for
  1.1512 -         * formatting dates, numbers, and/or currencies.
  1.1513 -         */
  1.1514 -        FORMAT("user.language.format",
  1.1515 -               "user.script.format",
  1.1516 -               "user.country.format",
  1.1517 -               "user.variant.format");
  1.1518 -
  1.1519 -        Category(String languageKey, String scriptKey, String countryKey, String variantKey) {
  1.1520 -            this.languageKey = languageKey;
  1.1521 -            this.scriptKey = scriptKey;
  1.1522 -            this.countryKey = countryKey;
  1.1523 -            this.variantKey = variantKey;
  1.1524 -        }
  1.1525 -
  1.1526 -        final String languageKey;
  1.1527 -        final String scriptKey;
  1.1528 -        final String countryKey;
  1.1529 -        final String variantKey;
  1.1530 -    }
  1.1531 -
  1.1532 -    /**
  1.1533 -     * <code>Builder</code> is used to build instances of <code>Locale</code>
  1.1534 -     * from values configured by the setters.  Unlike the <code>Locale</code>
  1.1535 -     * constructors, the <code>Builder</code> checks if a value configured by a
  1.1536 -     * setter satisfies the syntax requirements defined by the <code>Locale</code>
  1.1537 -     * class.  A <code>Locale</code> object created by a <code>Builder</code> is
  1.1538 -     * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
  1.1539 -     * without losing information.
  1.1540 -     *
  1.1541 -     * <p><b>Note:</b> The <code>Locale</code> class does not provide any
  1.1542 -     * syntactic restrictions on variant, while BCP 47 requires each variant
  1.1543 -     * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
  1.1544 -     * alphanumerics.  The method <code>setVariant</code> throws
  1.1545 -     * <code>IllformedLocaleException</code> for a variant that does not satisfy
  1.1546 -     * this restriction. If it is necessary to support such a variant, use a
  1.1547 -     * Locale constructor.  However, keep in mind that a <code>Locale</code>
  1.1548 -     * object created this way might lose the variant information when
  1.1549 -     * transformed to a BCP 47 language tag.
  1.1550 -     *
  1.1551 -     * <p>The following example shows how to create a <code>Locale</code> object
  1.1552 -     * with the <code>Builder</code>.
  1.1553 -     * <blockquote>
  1.1554 -     * <pre>
  1.1555 -     *     Locale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
  1.1556 -     * </pre>
  1.1557 -     * </blockquote>
  1.1558 -     *
  1.1559 -     * <p>Builders can be reused; <code>clear()</code> resets all
  1.1560 -     * fields to their default values.
  1.1561 -     *
  1.1562 -     * @see Locale#forLanguageTag
  1.1563 -     * @since 1.7
  1.1564 -     */
  1.1565 -    public static final class Builder {
  1.1566 -        private final InternalLocaleBuilder localeBuilder;
  1.1567 -
  1.1568 -        /**
  1.1569 -         * Constructs an empty Builder. The default value of all
  1.1570 -         * fields, extensions, and private use information is the
  1.1571 -         * empty string.
  1.1572 -         */
  1.1573 -        public Builder() {
  1.1574 -            localeBuilder = new InternalLocaleBuilder();
  1.1575 -        }
  1.1576 -
  1.1577 -        /**
  1.1578 -         * Resets the <code>Builder</code> to match the provided
  1.1579 -         * <code>locale</code>.  Existing state is discarded.
  1.1580 -         *
  1.1581 -         * <p>All fields of the locale must be well-formed, see {@link Locale}.
  1.1582 -         *
  1.1583 -         * <p>Locales with any ill-formed fields cause
  1.1584 -         * <code>IllformedLocaleException</code> to be thrown, except for the
  1.1585 -         * following three cases which are accepted for compatibility
  1.1586 -         * reasons:<ul>
  1.1587 -         * <li>Locale("ja", "JP", "JP") is treated as "ja-JP-u-ca-japanese"
  1.1588 -         * <li>Locale("th", "TH", "TH") is treated as "th-TH-u-nu-thai"
  1.1589 -         * <li>Locale("no", "NO", "NY") is treated as "nn-NO"</ul>
  1.1590 -         *
  1.1591 -         * @param locale the locale
  1.1592 -         * @return This builder.
  1.1593 -         * @throws IllformedLocaleException if <code>locale</code> has
  1.1594 -         * any ill-formed fields.
  1.1595 -         * @throws NullPointerException if <code>locale</code> is null.
  1.1596 -         */
  1.1597 -        public Builder setLocale(Locale locale) {
  1.1598 -            try {
  1.1599 -                localeBuilder.setLocale(locale.baseLocale, locale.localeExtensions);
  1.1600 -            } catch (LocaleSyntaxException e) {
  1.1601 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1602 -            }
  1.1603 -            return this;
  1.1604 -        }
  1.1605 -
  1.1606 -        /**
  1.1607 -         * Resets the Builder to match the provided IETF BCP 47
  1.1608 -         * language tag.  Discards the existing state.  Null and the
  1.1609 -         * empty string cause the builder to be reset, like {@link
  1.1610 -         * #clear}.  Grandfathered tags (see {@link
  1.1611 -         * Locale#forLanguageTag}) are converted to their canonical
  1.1612 -         * form before being processed.  Otherwise, the language tag
  1.1613 -         * must be well-formed (see {@link Locale}) or an exception is
  1.1614 -         * thrown (unlike <code>Locale.forLanguageTag</code>, which
  1.1615 -         * just discards ill-formed and following portions of the
  1.1616 -         * tag).
  1.1617 -         *
  1.1618 -         * @param languageTag the language tag
  1.1619 -         * @return This builder.
  1.1620 -         * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
  1.1621 -         * @see Locale#forLanguageTag(String)
  1.1622 -         */
  1.1623 -        public Builder setLanguageTag(String languageTag) {
  1.1624 -            ParseStatus sts = new ParseStatus();
  1.1625 -            LanguageTag tag = LanguageTag.parse(languageTag, sts);
  1.1626 -            if (sts.isError()) {
  1.1627 -                throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
  1.1628 -            }
  1.1629 -            localeBuilder.setLanguageTag(tag);
  1.1630 -            return this;
  1.1631 -        }
  1.1632 -
  1.1633 -        /**
  1.1634 -         * Sets the language.  If <code>language</code> is the empty string or
  1.1635 -         * null, the language in this <code>Builder</code> is removed.  Otherwise,
  1.1636 -         * the language must be <a href="./Locale.html#def_language">well-formed</a>
  1.1637 -         * or an exception is thrown.
  1.1638 -         *
  1.1639 -         * <p>The typical language value is a two or three-letter language
  1.1640 -         * code as defined in ISO639.
  1.1641 -         *
  1.1642 -         * @param language the language
  1.1643 -         * @return This builder.
  1.1644 -         * @throws IllformedLocaleException if <code>language</code> is ill-formed
  1.1645 -         */
  1.1646 -        public Builder setLanguage(String language) {
  1.1647 -            try {
  1.1648 -                localeBuilder.setLanguage(language);
  1.1649 -            } catch (LocaleSyntaxException e) {
  1.1650 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1651 -            }
  1.1652 -            return this;
  1.1653 -        }
  1.1654 -
  1.1655 -        /**
  1.1656 -         * Sets the script. If <code>script</code> is null or the empty string,
  1.1657 -         * the script in this <code>Builder</code> is removed.
  1.1658 -         * Otherwise, the script must be <a href="./Locale.html#def_script">well-formed</a> or an
  1.1659 -         * exception is thrown.
  1.1660 -         *
  1.1661 -         * <p>The typical script value is a four-letter script code as defined by ISO 15924.
  1.1662 -         *
  1.1663 -         * @param script the script
  1.1664 -         * @return This builder.
  1.1665 -         * @throws IllformedLocaleException if <code>script</code> is ill-formed
  1.1666 -         */
  1.1667 -        public Builder setScript(String script) {
  1.1668 -            try {
  1.1669 -                localeBuilder.setScript(script);
  1.1670 -            } catch (LocaleSyntaxException e) {
  1.1671 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1672 -            }
  1.1673 -            return this;
  1.1674 -        }
  1.1675 -
  1.1676 -        /**
  1.1677 -         * Sets the region.  If region is null or the empty string, the region
  1.1678 -         * in this <code>Builder</code> is removed.  Otherwise,
  1.1679 -         * the region must be <a href="./Locale.html#def_region">well-formed</a> or an
  1.1680 -         * exception is thrown.
  1.1681 -         *
  1.1682 -         * <p>The typical region value is a two-letter ISO 3166 code or a
  1.1683 -         * three-digit UN M.49 area code.
  1.1684 -         *
  1.1685 -         * <p>The country value in the <code>Locale</code> created by the
  1.1686 -         * <code>Builder</code> is always normalized to upper case.
  1.1687 -         *
  1.1688 -         * @param region the region
  1.1689 -         * @return This builder.
  1.1690 -         * @throws IllformedLocaleException if <code>region</code> is ill-formed
  1.1691 -         */
  1.1692 -        public Builder setRegion(String region) {
  1.1693 -            try {
  1.1694 -                localeBuilder.setRegion(region);
  1.1695 -            } catch (LocaleSyntaxException e) {
  1.1696 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1697 -            }
  1.1698 -            return this;
  1.1699 -        }
  1.1700 -
  1.1701 -        /**
  1.1702 -         * Sets the variant.  If variant is null or the empty string, the
  1.1703 -         * variant in this <code>Builder</code> is removed.  Otherwise, it
  1.1704 -         * must consist of one or more <a href="./Locale.html#def_variant">well-formed</a>
  1.1705 -         * subtags, or an exception is thrown.
  1.1706 -         *
  1.1707 -         * <p><b>Note:</b> This method checks if <code>variant</code>
  1.1708 -         * satisfies the IETF BCP 47 variant subtag's syntax requirements,
  1.1709 -         * and normalizes the value to lowercase letters.  However,
  1.1710 -         * the <code>Locale</code> class does not impose any syntactic
  1.1711 -         * restriction on variant, and the variant value in
  1.1712 -         * <code>Locale</code> is case sensitive.  To set such a variant,
  1.1713 -         * use a Locale constructor.
  1.1714 -         *
  1.1715 -         * @param variant the variant
  1.1716 -         * @return This builder.
  1.1717 -         * @throws IllformedLocaleException if <code>variant</code> is ill-formed
  1.1718 -         */
  1.1719 -        public Builder setVariant(String variant) {
  1.1720 -            try {
  1.1721 -                localeBuilder.setVariant(variant);
  1.1722 -            } catch (LocaleSyntaxException e) {
  1.1723 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1724 -            }
  1.1725 -            return this;
  1.1726 -        }
  1.1727 -
  1.1728 -        /**
  1.1729 -         * Sets the extension for the given key. If the value is null or the
  1.1730 -         * empty string, the extension is removed.  Otherwise, the extension
  1.1731 -         * must be <a href="./Locale.html#def_extensions">well-formed</a> or an exception
  1.1732 -         * is thrown.
  1.1733 -         *
  1.1734 -         * <p><b>Note:</b> The key {@link Locale#UNICODE_LOCALE_EXTENSION
  1.1735 -         * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
  1.1736 -         * Setting a value for this key replaces any existing Unicode locale key/type
  1.1737 -         * pairs with those defined in the extension.
  1.1738 -         *
  1.1739 -         * <p><b>Note:</b> The key {@link Locale#PRIVATE_USE_EXTENSION
  1.1740 -         * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
  1.1741 -         * well-formed, the value for this key needs only to have subtags of one to
  1.1742 -         * eight alphanumeric characters, not two to eight as in the general case.
  1.1743 -         *
  1.1744 -         * @param key the extension key
  1.1745 -         * @param value the extension value
  1.1746 -         * @return This builder.
  1.1747 -         * @throws IllformedLocaleException if <code>key</code> is illegal
  1.1748 -         * or <code>value</code> is ill-formed
  1.1749 -         * @see #setUnicodeLocaleKeyword(String, String)
  1.1750 -         */
  1.1751 -        public Builder setExtension(char key, String value) {
  1.1752 -            try {
  1.1753 -                localeBuilder.setExtension(key, value);
  1.1754 -            } catch (LocaleSyntaxException e) {
  1.1755 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1756 -            }
  1.1757 -            return this;
  1.1758 -        }
  1.1759 -
  1.1760 -        /**
  1.1761 -         * Sets the Unicode locale keyword type for the given key.  If the type
  1.1762 -         * is null, the Unicode keyword is removed.  Otherwise, the key must be
  1.1763 -         * non-null and both key and type must be <a
  1.1764 -         * href="./Locale.html#def_locale_extension">well-formed</a> or an exception
  1.1765 -         * is thrown.
  1.1766 -         *
  1.1767 -         * <p>Keys and types are converted to lower case.
  1.1768 -         *
  1.1769 -         * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
  1.1770 -         * replaces all Unicode locale keywords with those defined in the
  1.1771 -         * extension.
  1.1772 -         *
  1.1773 -         * @param key the Unicode locale key
  1.1774 -         * @param type the Unicode locale type
  1.1775 -         * @return This builder.
  1.1776 -         * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
  1.1777 -         * is ill-formed
  1.1778 -         * @throws NullPointerException if <code>key</code> is null
  1.1779 -         * @see #setExtension(char, String)
  1.1780 -         */
  1.1781 -        public Builder setUnicodeLocaleKeyword(String key, String type) {
  1.1782 -            try {
  1.1783 -                localeBuilder.setUnicodeLocaleKeyword(key, type);
  1.1784 -            } catch (LocaleSyntaxException e) {
  1.1785 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1786 -            }
  1.1787 -            return this;
  1.1788 -        }
  1.1789 -
  1.1790 -        /**
  1.1791 -         * Adds a unicode locale attribute, if not already present, otherwise
  1.1792 -         * has no effect.  The attribute must not be null and must be <a
  1.1793 -         * href="./Locale.html#def_locale_extension">well-formed</a> or an exception
  1.1794 -         * is thrown.
  1.1795 -         *
  1.1796 -         * @param attribute the attribute
  1.1797 -         * @return This builder.
  1.1798 -         * @throws NullPointerException if <code>attribute</code> is null
  1.1799 -         * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
  1.1800 -         * @see #setExtension(char, String)
  1.1801 -         */
  1.1802 -        public Builder addUnicodeLocaleAttribute(String attribute) {
  1.1803 -            try {
  1.1804 -                localeBuilder.addUnicodeLocaleAttribute(attribute);
  1.1805 -            } catch (LocaleSyntaxException e) {
  1.1806 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1807 -            }
  1.1808 -            return this;
  1.1809 -        }
  1.1810 -
  1.1811 -        /**
  1.1812 -         * Removes a unicode locale attribute, if present, otherwise has no
  1.1813 -         * effect.  The attribute must not be null and must be <a
  1.1814 -         * href="./Locale.html#def_locale_extension">well-formed</a> or an exception
  1.1815 -         * is thrown.
  1.1816 -         *
  1.1817 -         * <p>Attribute comparision for removal is case-insensitive.
  1.1818 -         *
  1.1819 -         * @param attribute the attribute
  1.1820 -         * @return This builder.
  1.1821 -         * @throws NullPointerException if <code>attribute</code> is null
  1.1822 -         * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
  1.1823 -         * @see #setExtension(char, String)
  1.1824 -         */
  1.1825 -        public Builder removeUnicodeLocaleAttribute(String attribute) {
  1.1826 -            try {
  1.1827 -                localeBuilder.removeUnicodeLocaleAttribute(attribute);
  1.1828 -            } catch (LocaleSyntaxException e) {
  1.1829 -                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
  1.1830 -            }
  1.1831 -            return this;
  1.1832 -        }
  1.1833 -
  1.1834 -        /**
  1.1835 -         * Resets the builder to its initial, empty state.
  1.1836 -         *
  1.1837 -         * @return This builder.
  1.1838 -         */
  1.1839 -        public Builder clear() {
  1.1840 -            localeBuilder.clear();
  1.1841 -            return this;
  1.1842 -        }
  1.1843 -
  1.1844 -        /**
  1.1845 -         * Resets the extensions to their initial, empty state.
  1.1846 -         * Language, script, region and variant are unchanged.
  1.1847 -         *
  1.1848 -         * @return This builder.
  1.1849 -         * @see #setExtension(char, String)
  1.1850 -         */
  1.1851 -        public Builder clearExtensions() {
  1.1852 -            localeBuilder.clearExtensions();
  1.1853 -            return this;
  1.1854 -        }
  1.1855 -
  1.1856 -        /**
  1.1857 -         * Returns an instance of <code>Locale</code> created from the fields set
  1.1858 -         * on this builder.
  1.1859 -         *
  1.1860 -         * <p>This applies the conversions listed in {@link Locale#forLanguageTag}
  1.1861 -         * when constructing a Locale. (Grandfathered tags are handled in
  1.1862 -         * {@link #setLanguageTag}.)
  1.1863 -         *
  1.1864 -         * @return A Locale.
  1.1865 -         */
  1.1866 -        public Locale build() {
  1.1867 -            BaseLocale baseloc = localeBuilder.getBaseLocale();
  1.1868 -            LocaleExtensions extensions = localeBuilder.getLocaleExtensions();
  1.1869 -            if (extensions == null && baseloc.getVariant().length() > 0) {
  1.1870 -                extensions = getCompatibilityExtensions(baseloc.getLanguage(), baseloc.getScript(),
  1.1871 -                        baseloc.getRegion(), baseloc.getVariant());
  1.1872 -            }
  1.1873 -            return Locale.getInstance(baseloc, extensions);
  1.1874 -        }
  1.1875 -    }
  1.1876  }