< prev index next >

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

Print this page
rev 47480 : [mq]: 8176841

*** 46,55 **** --- 46,56 ---- import java.io.ObjectStreamField; import java.io.Serializable; import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.spi.LocaleNameProvider; + import java.util.stream.Collectors; import sun.security.action.GetPropertyAction; import sun.util.locale.BaseLocale; import sun.util.locale.InternalLocaleBuilder; import sun.util.locale.LanguageTag;
*** 60,69 **** --- 61,71 ---- import sun.util.locale.LocaleUtils; import sun.util.locale.ParseStatus; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleResources; import sun.util.locale.provider.LocaleServiceProviderPool; + import sun.util.locale.provider.TimeZoneNameUtility; /** * A <code>Locale</code> object represents a specific geographical, political, * or cultural region. An operation that requires a <code>Locale</code> to perform * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
*** 667,676 **** --- 669,680 ---- */ private static final int DISPLAY_LANGUAGE = 0; private static final int DISPLAY_COUNTRY = 1; private static final int DISPLAY_VARIANT = 2; private static final int DISPLAY_SCRIPT = 3; + private static final int DISPLAY_UEXT_KEY = 4; + private static final int DISPLAY_UEXT_TYPE = 5; /** * Private constructor used by getInstance method */ private Locale(BaseLocale baseLocale, LocaleExtensions extensions) {
*** 940,964 **** script = props.getProperty("user.script", ""); country = props.getProperty("user.country", ""); variant = props.getProperty("user.variant", ""); } ! return getInstance(language, script, country, variant, null); } private static Locale initDefault(Locale.Category category) { Properties props = GetPropertyAction.privilegedGetProperties(); return getInstance( props.getProperty(category.languageKey, defaultLocale.getLanguage()), props.getProperty(category.scriptKey, defaultLocale.getScript()), props.getProperty(category.countryKey, defaultLocale.getCountry()), props.getProperty(category.variantKey, defaultLocale.getVariant()), ! null); } /** * Sets the default locale for this instance of the Java Virtual Machine. * This does not affect the host locale. --- 944,986 ---- script = props.getProperty("user.script", ""); country = props.getProperty("user.country", ""); variant = props.getProperty("user.variant", ""); } ! return getInstance(language, script, country, variant, ! getDefaultExtensions(props.getProperty("user.extensions", "")) ! .orElse(null)); } private static Locale initDefault(Locale.Category category) { Properties props = GetPropertyAction.privilegedGetProperties(); + return getInstance( props.getProperty(category.languageKey, defaultLocale.getLanguage()), props.getProperty(category.scriptKey, defaultLocale.getScript()), props.getProperty(category.countryKey, defaultLocale.getCountry()), props.getProperty(category.variantKey, defaultLocale.getVariant()), ! getDefaultExtensions(props.getProperty(category.extensionsKey, "")) ! .orElse(defaultLocale.getLocaleExtensions())); ! } ! ! private static Optional<LocaleExtensions> getDefaultExtensions(String extensionsProp) { ! LocaleExtensions exts = null; ! ! try { ! exts = new InternalLocaleBuilder() ! .setExtensions(extensionsProp) ! .getLocaleExtensions(); ! } catch (LocaleSyntaxException e) { ! // just ignore this incorrect property ! } ! ! return Optional.ofNullable(exts); } /** * Sets the default locale for this instance of the Java Virtual Machine. * This does not affect the host locale.
*** 1769,1779 **** * @param inLocale The locale for which to retrieve the display language. * @return The name of the display language appropriate to the given locale. * @exception NullPointerException if <code>inLocale</code> is <code>null</code> */ public String getDisplayLanguage(Locale inLocale) { ! return getDisplayString(baseLocale.getLanguage(), inLocale, DISPLAY_LANGUAGE); } /** * Returns a name for the locale's script that is appropriate for display to * the user. If possible, the name will be localized for the default --- 1791,1801 ---- * @param inLocale The locale for which to retrieve the display language. * @return The name of the display language appropriate to the given locale. * @exception NullPointerException if <code>inLocale</code> is <code>null</code> */ public String getDisplayLanguage(Locale inLocale) { ! return getDisplayString(baseLocale.getLanguage(), null, inLocale, DISPLAY_LANGUAGE); } /** * Returns a name for the locale's script that is appropriate for display to * the user. If possible, the name will be localized for the default
*** 1799,1809 **** * {@link Locale.Category#DISPLAY DISPLAY} locale * @throws NullPointerException if <code>inLocale</code> is <code>null</code> * @since 1.7 */ public String getDisplayScript(Locale inLocale) { ! return getDisplayString(baseLocale.getScript(), inLocale, DISPLAY_SCRIPT); } /** * Returns a name for the locale's country that is appropriate for display to the * user. --- 1821,1831 ---- * {@link Locale.Category#DISPLAY DISPLAY} locale * @throws NullPointerException if <code>inLocale</code> is <code>null</code> * @since 1.7 */ public String getDisplayScript(Locale inLocale) { ! return getDisplayString(baseLocale.getScript(), null, inLocale, DISPLAY_SCRIPT); } /** * Returns a name for the locale's country that is appropriate for display to the * user.
*** 1842,1874 **** * @param inLocale The locale for which to retrieve the display country. * @return The name of the country appropriate to the given locale. * @exception NullPointerException if <code>inLocale</code> is <code>null</code> */ public String getDisplayCountry(Locale inLocale) { ! return getDisplayString(baseLocale.getRegion(), inLocale, DISPLAY_COUNTRY); } ! private String getDisplayString(String code, Locale inLocale, int type) { ! if (code.length() == 0) { ! return ""; ! } ! if (inLocale == null) { ! throw new NullPointerException(); } LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(LocaleNameProvider.class); ! String key = (type == DISPLAY_VARIANT ? "%%"+code : code); String result = pool.getLocalizedObject( LocaleNameGetter.INSTANCE, ! inLocale, key, type, code); ! if (result != null) { ! return result; ! } ! ! return code; } /** * Returns a name for the locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for the default --- 1864,1891 ---- * @param inLocale The locale for which to retrieve the display country. * @return The name of the country appropriate to the given locale. * @exception NullPointerException if <code>inLocale</code> is <code>null</code> */ public String getDisplayCountry(Locale inLocale) { ! return getDisplayString(baseLocale.getRegion(), null, inLocale, DISPLAY_COUNTRY); } ! private String getDisplayString(String code, String cat, Locale inLocale, int type) { ! Objects.requireNonNull(inLocale); ! Objects.requireNonNull(code); ! if (code.isEmpty()) { ! return ""; } LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(LocaleNameProvider.class); ! String rbKey = (type == DISPLAY_VARIANT ? "%%"+code : code); String result = pool.getLocalizedObject( LocaleNameGetter.INSTANCE, ! inLocale, rbKey, type, code, cat); ! return result != null ? result : code; } /** * Returns a name for the locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for the default
*** 1892,1924 **** */ public String getDisplayVariant(Locale inLocale) { if (baseLocale.getVariant().length() == 0) return ""; ! LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(inLocale); String names[] = getDisplayVariantArray(inLocale); // Get the localized patterns for formatting a list, and use // them to format the list. return formatList(names, - lr.getLocaleName("ListPattern"), lr.getLocaleName("ListCompositionPattern")); } /** * Returns a name for the locale that is appropriate for display to the * user. This will be the values returned by getDisplayLanguage(), ! * getDisplayScript(), getDisplayCountry(), and getDisplayVariant() assembled ! * into a single string. The non-empty values are used in order, ! * with the second and subsequent names in parentheses. For example: * <blockquote> ! * language (script, country, variant)<br> ! * language (country)<br> ! * language (variant)<br> ! * script (country)<br> ! * country<br> * </blockquote> * depending on which fields are specified in the locale. If the * language, script, country, and variant fields are all empty, * this function returns the empty string. * --- 1909,1943 ---- */ public String getDisplayVariant(Locale inLocale) { if (baseLocale.getVariant().length() == 0) return ""; ! LocaleResources lr = LocaleProviderAdapter ! .getResourceBundleBased() ! .getLocaleResources(inLocale); String names[] = getDisplayVariantArray(inLocale); // Get the localized patterns for formatting a list, and use // them to format the list. return formatList(names, lr.getLocaleName("ListCompositionPattern")); } /** * Returns a name for the locale that is appropriate for display to the * user. This will be the values returned by getDisplayLanguage(), ! * getDisplayScript(), getDisplayCountry(), getDisplayVariant() and ! * optional <a href="./Locale.html#def_locale_extension">Unicode extensions</a> ! * assembled into a single string. The non-empty values are used in order, with ! * the second and subsequent names in parentheses. For example: * <blockquote> ! * language (script, country, variant(, extension)*)<br> ! * language (country(, extension)*)<br> ! * language (variant(, extension)*)<br> ! * script (country(, extension)*)<br> ! * country (extension)*<br> * </blockquote> * depending on which fields are specified in the locale. If the * language, script, country, and variant fields are all empty, * this function returns the empty string. *
*** 1929,1968 **** } /** * Returns a name for the locale that is appropriate for display * to the user. This will be the values returned by ! * getDisplayLanguage(), getDisplayScript(),getDisplayCountry(), ! * and getDisplayVariant() assembled into a single string. ! * The non-empty values are used in order, ! * with the second and subsequent names in parentheses. For example: * <blockquote> ! * language (script, country, variant)<br> ! * language (country)<br> ! * language (variant)<br> ! * script (country)<br> ! * country<br> * </blockquote> * depending on which fields are specified in the locale. If the * language, script, country, and variant fields are all empty, * this function returns the empty string. * * @param inLocale The locale for which to retrieve the display name. * @return The name of the locale appropriate to display. * @throws NullPointerException if <code>inLocale</code> is <code>null</code> */ public String getDisplayName(Locale inLocale) { ! LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(inLocale); String languageName = getDisplayLanguage(inLocale); String scriptName = getDisplayScript(inLocale); String countryName = getDisplayCountry(inLocale); String[] variantNames = getDisplayVariantArray(inLocale); // Get the localized patterns for formatting a display name. String displayNamePattern = lr.getLocaleName("DisplayNamePattern"); - String listPattern = lr.getLocaleName("ListPattern"); String listCompositionPattern = lr.getLocaleName("ListCompositionPattern"); // The display name consists of a main name, followed by qualifiers. // Typically, the format is "MainName (Qualifier, Qualifier)" but this // depends on what pattern is stored in the display locale. --- 1948,1989 ---- } /** * Returns a name for the locale that is appropriate for display * to the user. This will be the values returned by ! * getDisplayLanguage(), getDisplayScript(),getDisplayCountry() ! * getDisplayVariant(), and optional <a href="./Locale.html#def_locale_extension"> ! * Unicode extensions</a> assembled into a single string. The non-empty ! * values are used in order, with the second and subsequent names in ! * parentheses. For example: * <blockquote> ! * language (script, country, variant(, extension)*)<br> ! * language (country(, extension)*)<br> ! * language (variant(, extension)*)<br> ! * script (country(, extension)*)<br> ! * country (extension)*<br> * </blockquote> * depending on which fields are specified in the locale. If the * language, script, country, and variant fields are all empty, * this function returns the empty string. * * @param inLocale The locale for which to retrieve the display name. * @return The name of the locale appropriate to display. * @throws NullPointerException if <code>inLocale</code> is <code>null</code> */ public String getDisplayName(Locale inLocale) { ! LocaleResources lr = LocaleProviderAdapter ! .getResourceBundleBased() ! .getLocaleResources(inLocale); String languageName = getDisplayLanguage(inLocale); String scriptName = getDisplayScript(inLocale); String countryName = getDisplayCountry(inLocale); String[] variantNames = getDisplayVariantArray(inLocale); // Get the localized patterns for formatting a display name. String displayNamePattern = lr.getLocaleName("DisplayNamePattern"); String listCompositionPattern = lr.getLocaleName("ListCompositionPattern"); // The display name consists of a main name, followed by qualifiers. // Typically, the format is "MainName (Qualifier, Qualifier)" but this // depends on what pattern is stored in the display locale.
*** 1975,1985 **** // display name. if (languageName.length() == 0 && scriptName.length() == 0 && countryName.length() == 0) { if (variantNames.length == 0) { return ""; } else { ! return formatList(variantNames, listPattern, listCompositionPattern); } } ArrayList<String> names = new ArrayList<>(4); if (languageName.length() != 0) { names.add(languageName); --- 1996,2006 ---- // display name. if (languageName.length() == 0 && scriptName.length() == 0 && countryName.length() == 0) { if (variantNames.length == 0) { return ""; } else { ! return formatList(variantNames, listCompositionPattern); } } ArrayList<String> names = new ArrayList<>(4); if (languageName.length() != 0) { names.add(languageName);
*** 1992,2001 **** --- 2013,2032 ---- } if (variantNames.length != 0) { names.addAll(Arrays.asList(variantNames)); } + // add Unicode extensions + if (localeExtensions != null) { + localeExtensions.getUnicodeLocaleAttributes().stream() + .map(key -> getDisplayString(key, null, inLocale, DISPLAY_UEXT_KEY)) + .forEach(names::add); + localeExtensions.getUnicodeLocaleKeys().stream() + .map(key -> getDisplayKeyTypeExtensionString(key, lr, inLocale)) + .forEach(names::add); + } + // The first one in the main name mainName = names.get(0); // Others are qualifiers int numNames = names.size();
*** 2012,2022 **** mainName, // We could also just call formatList() and have it handle the empty // list case, but this is more efficient, and we want it to be // efficient since all the language-only locales will not have any // qualifiers. ! qualifierNames.length != 0 ? formatList(qualifierNames, listPattern, listCompositionPattern) : null }; if (displayNamePattern != null) { return new MessageFormat(displayNamePattern).format(displayNames); } --- 2043,2053 ---- mainName, // We could also just call formatList() and have it handle the empty // list case, but this is more efficient, and we want it to be // efficient since all the language-only locales will not have any // qualifiers. ! qualifierNames.length != 0 ? formatList(qualifierNames, listCompositionPattern) : null }; if (displayNamePattern != null) { return new MessageFormat(displayNamePattern).format(displayNames); }
*** 2119,2196 **** String[] names = new String[tokenizer.countTokens()]; // For each variant token, lookup the display name. If // not found, use the variant name itself. for (int i=0; i<names.length; ++i) { ! names[i] = getDisplayString(tokenizer.nextToken(), inLocale, DISPLAY_VARIANT); } return names; } /** * Format a list using given pattern strings. * If either of the patterns is null, then a the list is * formatted by concatenation with the delimiter ','. * @param stringList the list of strings to be formatted. - * @param listPattern should create a MessageFormat taking 0-3 arguments * and formatting them into a list. ! * @param listCompositionPattern should take 2 arguments ! * and is used by composeList. * @return a string representing the list. */ ! private static String formatList(String[] stringList, String listPattern, String listCompositionPattern) { // If we have no list patterns, compose the list in a simple, // non-localized way. ! if (listPattern == null || listCompositionPattern == null) { ! StringJoiner sj = new StringJoiner(","); ! for (int i = 0; i < stringList.length; ++i) { ! sj.add(stringList[i]); ! } ! return sj.toString(); } ! // Compose the list down to three elements if necessary ! if (stringList.length > 3) { ! MessageFormat format = new MessageFormat(listCompositionPattern); ! stringList = composeList(format, stringList); } ! ! // Rebuild the argument list with the list length as the first element ! Object[] args = new Object[stringList.length + 1]; ! System.arraycopy(stringList, 0, args, 1, stringList.length); ! args[0] = stringList.length; ! ! // Format it using the pattern in the resource ! MessageFormat format = new MessageFormat(listPattern); ! return format.format(args); } - - /** - * Given a list of strings, return a list shortened to three elements. - * Shorten it by applying the given format to the first two elements - * recursively. - * @param format a format which takes two arguments - * @param list a list of strings - * @return if the list is three elements or shorter, the same list; - * otherwise, a new list of three elements. - */ - private static String[] composeList(MessageFormat format, String[] list) { - if (list.length <= 3) return list; - - // Use the given format to compose the first two elements into one - String[] listItems = { list[0], list[1] }; - String newItem = format.format(listItems); - - // Form a new list one element shorter - String[] newList = new String[list.length-1]; - System.arraycopy(list, 2, newList, 1, newList.length-1); - newList[0] = newItem; - - // Recurse - return composeList(format, newList); } // Duplicate of sun.util.locale.UnicodeLocaleExtension.isKey in order to // avoid its class loading. private static boolean isUnicodeExtensionKey(String s) { --- 2150,2230 ---- String[] names = new String[tokenizer.countTokens()]; // For each variant token, lookup the display name. If // not found, use the variant name itself. for (int i=0; i<names.length; ++i) { ! names[i] = getDisplayString(tokenizer.nextToken(), null, inLocale, DISPLAY_VARIANT); } return names; } + private String getDisplayKeyTypeExtensionString(String key, LocaleResources lr, Locale inLocale) { + String type = localeExtensions.getUnicodeLocaleType(key); + String ret = getDisplayString(type, key, inLocale, DISPLAY_UEXT_TYPE); + + if (ret == null || ret.equals(type)) { + // no localization for this type. try combining key/type separately + String displayType = type; + switch (key) { + case "cu": + displayType = lr.getCurrencyName(type.toLowerCase(Locale.ROOT)); + break; + case "rg": + if (type != null && + // UN M.49 code should not be allowed here + type.matches("^[a-zA-Z]{2}[zZ]{4}$")) { + displayType = lr.getLocaleName(type.substring(0, 2).toUpperCase(Locale.ROOT)); + } + break; + case "tz": + displayType = TimeZoneNameUtility.retrieveGenericDisplayName( + TimeZoneNameUtility.convertLDMLShortID(type).orElse(type), + TimeZone.LONG, inLocale); + break; + } + ret = MessageFormat.format(lr.getLocaleName("ListKeyTypePattern"), + getDisplayString(key, null, inLocale, DISPLAY_UEXT_KEY), displayType); + } + + return ret; + } + /** * Format a list using given pattern strings. * If either of the patterns is null, then a the list is * formatted by concatenation with the delimiter ','. * @param stringList the list of strings to be formatted. * and formatting them into a list. ! * @param pattern should take 2 arguments for reduction * @return a string representing the list. */ ! private static String formatList(String[] stringList, String pattern) { // If we have no list patterns, compose the list in a simple, // non-localized way. ! if (pattern == null) { ! return Arrays.stream(stringList).collect(Collectors.joining(",")); } ! switch (stringList.length) { ! case 0: ! return ""; ! case 1: ! return stringList[0]; ! default: ! return Arrays.stream(stringList).reduce("", ! (s1, s2) -> { ! if (s1.equals("")) { ! return s2; } ! if (s2.equals("")) { ! return s1; ! } ! return MessageFormat.format(pattern, s1, s2); ! }); } } // Duplicate of sun.util.locale.UnicodeLocaleExtension.isKey in order to // avoid its class loading. private static boolean isUnicodeExtensionKey(String s) {
*** 2343,2365 **** @Override public String getObject(LocaleNameProvider localeNameProvider, Locale locale, String key, Object... params) { ! assert params.length == 2; int type = (Integer)params[0]; String code = (String)params[1]; switch(type) { case DISPLAY_LANGUAGE: return localeNameProvider.getDisplayLanguage(code, locale); case DISPLAY_COUNTRY: return localeNameProvider.getDisplayCountry(code, locale); case DISPLAY_VARIANT: return localeNameProvider.getDisplayVariant(code, locale); case DISPLAY_SCRIPT: return localeNameProvider.getDisplayScript(code, locale); default: assert false; // shouldn't happen } return null; --- 2377,2404 ---- @Override public String getObject(LocaleNameProvider localeNameProvider, Locale locale, String key, Object... params) { ! assert params.length == 3; int type = (Integer)params[0]; String code = (String)params[1]; + String cat = (String)params[2]; switch(type) { case DISPLAY_LANGUAGE: return localeNameProvider.getDisplayLanguage(code, locale); case DISPLAY_COUNTRY: return localeNameProvider.getDisplayCountry(code, locale); case DISPLAY_VARIANT: return localeNameProvider.getDisplayVariant(code, locale); case DISPLAY_SCRIPT: return localeNameProvider.getDisplayScript(code, locale); + case DISPLAY_UEXT_KEY: + return localeNameProvider.getDisplayUnicodeExtensionKey(code, locale); + case DISPLAY_UEXT_TYPE: + return localeNameProvider.getDisplayUnicodeExtensionType(code, cat, locale); default: assert false; // shouldn't happen } return null;
*** 2382,2413 **** * displaying user interfaces. */ DISPLAY("user.language.display", "user.script.display", "user.country.display", ! "user.variant.display"), /** * Category used to represent the default locale for * formatting dates, numbers, and/or currencies. */ FORMAT("user.language.format", "user.script.format", "user.country.format", ! "user.variant.format"); ! Category(String languageKey, String scriptKey, String countryKey, String variantKey) { this.languageKey = languageKey; this.scriptKey = scriptKey; this.countryKey = countryKey; this.variantKey = variantKey; } final String languageKey; final String scriptKey; final String countryKey; final String variantKey; } /** * <code>Builder</code> is used to build instances of <code>Locale</code> * from values configured by the setters. Unlike the <code>Locale</code> --- 2421,2457 ---- * displaying user interfaces. */ DISPLAY("user.language.display", "user.script.display", "user.country.display", ! "user.variant.display", ! "user.extensions.display"), /** * Category used to represent the default locale for * formatting dates, numbers, and/or currencies. */ FORMAT("user.language.format", "user.script.format", "user.country.format", ! "user.variant.format", ! "user.extensions.format"); ! Category(String languageKey, String scriptKey, String countryKey, ! String variantKey, String extensionsKey) { this.languageKey = languageKey; this.scriptKey = scriptKey; this.countryKey = countryKey; this.variantKey = variantKey; + this.extensionsKey = extensionsKey; } final String languageKey; final String scriptKey; final String countryKey; final String variantKey; + final String extensionsKey; } /** * <code>Builder</code> is used to build instances of <code>Locale</code> * from values configured by the setters. Unlike the <code>Locale</code>
< prev index next >