mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-16 05:15:22 +00:00
7069824: Support for BCP47 locale matching
Reviewed-by: naoto, okutsu
This commit is contained in:
parent
152b0d6143
commit
5a3a2dc308
@ -55,6 +55,7 @@ import sun.util.locale.BaseLocale;
|
||||
import sun.util.locale.InternalLocaleBuilder;
|
||||
import sun.util.locale.LanguageTag;
|
||||
import sun.util.locale.LocaleExtensions;
|
||||
import sun.util.locale.LocaleMatcher;
|
||||
import sun.util.locale.LocaleObjectCache;
|
||||
import sun.util.locale.LocaleSyntaxException;
|
||||
import sun.util.locale.LocaleUtils;
|
||||
@ -71,10 +72,11 @@ import sun.util.resources.OpenListResourceBundle;
|
||||
* according to the customs and conventions of the user's native country,
|
||||
* region, or culture.
|
||||
*
|
||||
* <p> The <code>Locale</code> class implements identifiers
|
||||
* interchangeable with BCP 47 (IETF BCP 47, "Tags for Identifying
|
||||
* Languages"), with support for the LDML (UTS#35, "Unicode Locale
|
||||
* Data Markup Language") BCP 47-compatible extensions for locale data
|
||||
* <p> The {@code Locale} class implements IETF BCP 47 which is composed of
|
||||
* <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 "Matching of Language
|
||||
* Tags"</a> and <a href="http://tools.ietf.org/html/rfc5646">RFC 5646 "Tags
|
||||
* for Identifying Languages"</a> with support for the LDML (UTS#35, "Unicode
|
||||
* Locale Data Markup Language") BCP 47-compatible extensions for locale data
|
||||
* exchange.
|
||||
*
|
||||
* <p> A <code>Locale</code> object logically consists of the fields
|
||||
@ -267,6 +269,77 @@ import sun.util.resources.OpenListResourceBundle;
|
||||
* </pre>
|
||||
* </blockquote>
|
||||
*
|
||||
* <h4><a name="LocaleMatching">Locale Matching</a></h4>
|
||||
*
|
||||
* <p>If an application or a system is internationalized and provides localized
|
||||
* resources for multiple locales, it sometimes needs to find one or more
|
||||
* locales (or language tags) which meet each user's specific preferences. Note
|
||||
* that a term "language tag" is used interchangeably with "locale" in this
|
||||
* locale matching documentation.
|
||||
*
|
||||
* <p>In order to do matching a user's preferred locales to a set of language
|
||||
* tags, <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 Matching of
|
||||
* Language Tags</a> defines two mechanisms: filtering and lookup.
|
||||
* <em>Filtering</em> is used to get all matching locales, whereas
|
||||
* <em>lookup</em> is to choose the best matching locale.
|
||||
* Matching is done case-insensitively. These matching mechanisms are described
|
||||
* in the following sections.
|
||||
*
|
||||
* <p>A user's preference is called a <em>Language Priority List</em> and is
|
||||
* expressed as a list of language ranges. There are syntactically two types of
|
||||
* language ranges: basic and extended. See
|
||||
* {@link Locale.LanguageRange Locale.LanguageRange} for details.
|
||||
*
|
||||
* <h5>Filtering</h5>
|
||||
*
|
||||
* <p>The filtering operation returns all matching language tags. It is defined
|
||||
* in RFC 4647 as follows:
|
||||
* "In filtering, each language range represents the least specific language
|
||||
* tag (that is, the language tag with fewest number of subtags) that is an
|
||||
* acceptable match. All of the language tags in the matching set of tags will
|
||||
* have an equal or greater number of subtags than the language range. Every
|
||||
* non-wildcard subtag in the language range will appear in every one of the
|
||||
* matching language tags."
|
||||
*
|
||||
* <p>There are two types of filtering: filtering for basic language ranges
|
||||
* (called "basic filtering") and filtering for extended language ranges
|
||||
* (called "extended filtering"). They may return different results by what
|
||||
* kind of language ranges are included in the given Language Priority List.
|
||||
* {@link Locale.FilteringMode} is a parameter to specify how filtering should
|
||||
* be done.
|
||||
*
|
||||
* <h5>Lookup</h5>
|
||||
*
|
||||
* <p>The lookup operation returns the best matching language tags. It is
|
||||
* defined in RFC 4647 as follows:
|
||||
* "By contrast with filtering, each language range represents the most
|
||||
* specific tag that is an acceptable match. The first matching tag found,
|
||||
* according to the user's priority, is considered the closest match and is the
|
||||
* item returned."
|
||||
*
|
||||
* <p>For example, if a Language Priority List consists of two language ranges,
|
||||
* {@code "zh-Hant-TW"} and {@code "en-US"}, in prioritized order, lookup
|
||||
* method progressively searches the language tags below in order to find the
|
||||
* best matching language tag.
|
||||
* <blockquote>
|
||||
* <pre>
|
||||
* 1. zh-Hant-TW
|
||||
* 2. zh-Hant
|
||||
* 3. zh
|
||||
* 4. en-US
|
||||
* 5. en
|
||||
* </pre>
|
||||
* </blockquote>
|
||||
* If there is a language tag which matches completely to a language range
|
||||
* above, the language tag is returned.
|
||||
*
|
||||
* <p>{@code "*"} is the special language range, and it is ignored in lookup.
|
||||
*
|
||||
* <p>If multiple language tags match as a result of the subtag {@code '*'}
|
||||
* included in a language range, the first matching language tag returned by
|
||||
* an {@link Iterator} over a {@link Collection} of language tags is treated as
|
||||
* the best matching one.
|
||||
*
|
||||
* <h4>Use of Locale</h4>
|
||||
*
|
||||
* <p>Once you've created a <code>Locale</code> you can query it for information
|
||||
@ -2574,4 +2647,611 @@ public final class Locale implements Cloneable, Serializable {
|
||||
return Locale.getInstance(baseloc, extensions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This enum provides constants to select a filtering mode for locale
|
||||
* matching. Refer to <a href="http://tools.ietf.org/html/rfc4647">RFC 4647
|
||||
* Matching of Language Tags</a> for details.
|
||||
*
|
||||
* <p>As an example, think of two Language Priority Lists each of which
|
||||
* includes only one language range and a set of following language tags:
|
||||
*
|
||||
* <pre>
|
||||
* de (German)
|
||||
* de-DE (German, Germany)
|
||||
* de-Deva (German, in Devanagari script)
|
||||
* de-Deva-DE (German, in Devanagari script, Germany)
|
||||
* de-DE-1996 (German, Germany, orthography of 1996)
|
||||
* de-Latn-DE (German, in Latin script, Germany)
|
||||
* de-Latn-DE-1996 (German, in Latin script, Germany, orthography of 1996)
|
||||
* </pre>
|
||||
*
|
||||
* The filtering method will behave as follows:
|
||||
*
|
||||
* <table cellpadding=2>
|
||||
* <tr>
|
||||
* <th>Filtering Mode</th>
|
||||
* <th>Language Priority List: {@code "de-DE"}</th>
|
||||
* <th>Language Priority List: {@code "de-*-DE"}</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td valign=top>
|
||||
* {@link FilteringMode#AUTOSELECT_FILTERING AUTOSELECT_FILTERING}
|
||||
* </td>
|
||||
* <td valign=top>
|
||||
* Performs <em>basic</em> filtering and returns {@code "de-DE"} and
|
||||
* {@code "de-DE-1996"}.
|
||||
* </td>
|
||||
* <td valign=top>
|
||||
* Performs <em>extended</em> filtering and returns {@code "de-DE"},
|
||||
* {@code "de-Deva-DE"}, {@code "de-DE-1996"}, {@code "de-Latn-DE"}, and
|
||||
* {@code "de-Latn-DE-1996"}.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td valign=top>
|
||||
* {@link FilteringMode#EXTENDED_FILTERING EXTENDED_FILTERING}
|
||||
* </td>
|
||||
* <td valign=top>
|
||||
* Performs <em>extended</em> filtering and returns {@code "de-DE"},
|
||||
* {@code "de-Deva-DE"}, {@code "de-DE-1996"}, {@code "de-Latn-DE"}, and
|
||||
* {@code "de-Latn-DE-1996"}.
|
||||
* </td>
|
||||
* <td valign=top>Same as above.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td valign=top>
|
||||
* {@link FilteringMode#IGNORE_EXTENDED_RANGES IGNORE_EXTENDED_RANGES}
|
||||
* </td>
|
||||
* <td valign=top>
|
||||
* Performs <em>basic</em> filtering and returns {@code "de-DE"} and
|
||||
* {@code "de-DE-1996"}.
|
||||
* </td>
|
||||
* <td valign=top>
|
||||
* Performs <em>basic</em> filtering and returns {@code null} because
|
||||
* nothing matches.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td valign=top>
|
||||
* {@link FilteringMode#MAP_EXTENDED_RANGES MAP_EXTENDED_RANGES}
|
||||
* </td>
|
||||
* <td valign=top>Same as above.</td>
|
||||
* <td valign=top>
|
||||
* Performs <em>basic</em> filtering and returns {@code "de-DE"} and
|
||||
* {@code "de-DE-1996"} because {@code "de-*-DE"} is mapped to
|
||||
* {@code "de-DE"}.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td valign=top>
|
||||
* {@link FilteringMode#REJECT_EXTENDED_RANGES REJECT_EXTENDED_RANGES}
|
||||
* </td>
|
||||
* <td valign=top>Same as above.</td>
|
||||
* <td valign=top>
|
||||
* Throws {@link IllegalArgumentException} because {@code "de-*-DE"} is
|
||||
* not a valid basic language range.
|
||||
* </td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @see #filter(List, Collection, FilteringMode)
|
||||
* @see #filterTags(List, Collection, FilteringMode)
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static enum FilteringMode {
|
||||
/**
|
||||
* Specifies automatic filtering mode based on the given Language
|
||||
* Priority List consisting of language ranges. If all of the ranges
|
||||
* are basic, basic filtering is selected. Otherwise, extended
|
||||
* filtering is selected.
|
||||
*/
|
||||
AUTOSELECT_FILTERING,
|
||||
|
||||
/**
|
||||
* Specifies extended filtering.
|
||||
*/
|
||||
EXTENDED_FILTERING,
|
||||
|
||||
/**
|
||||
* Specifies basic filtering: Note that any extended language ranges
|
||||
* included in the given Language Priority List are ignored.
|
||||
*/
|
||||
IGNORE_EXTENDED_RANGES,
|
||||
|
||||
/**
|
||||
* Specifies basic filtering: If any extended language ranges are
|
||||
* included in the given Language Priority List, they are mapped to the
|
||||
* basic language range. Specifically, a language range starting with a
|
||||
* subtag {@code "*"} is treated as a language range {@code "*"}. For
|
||||
* example, {@code "*-US"} is treated as {@code "*"}. If {@code "*"} is
|
||||
* not the first subtag, {@code "*"} and extra {@code "-"} are removed.
|
||||
* For example, {@code "ja-*-JP"} is mapped to {@code "ja-JP"}.
|
||||
*/
|
||||
MAP_EXTENDED_RANGES,
|
||||
|
||||
/**
|
||||
* Specifies basic filtering: If any extended language ranges are
|
||||
* included in the given Language Priority List, the list is rejected
|
||||
* and the filtering method throws {@link IllegalArgumentException}.
|
||||
*/
|
||||
REJECT_EXTENDED_RANGES
|
||||
};
|
||||
|
||||
/**
|
||||
* This class expresses a <em>Language Range</em> defined in
|
||||
* <a href="http://tools.ietf.org/html/rfc4647">RFC 4647 Matching of
|
||||
* Language Tags</a>. A language range is an identifier which is used to
|
||||
* select language tag(s) meeting specific requirements by using the
|
||||
* mechanisms described in <a href="Locale.html#LocaleMatching">Locale
|
||||
* Matching</a>. A list which represents a user's preferences and consists
|
||||
* of language ranges is called a <em>Language Priority List</em>.
|
||||
*
|
||||
* <p>There are two types of language ranges: basic and extended. In RFC
|
||||
* 4647, the syntax of language ranges is expressed in
|
||||
* <a href="http://tools.ietf.org/html/rfc4234">ABNF</a> as follows:
|
||||
* <blockquote>
|
||||
* <pre>
|
||||
* basic-language-range = (1*8ALPHA *("-" 1*8alphanum)) / "*"
|
||||
* extended-language-range = (1*8ALPHA / "*")
|
||||
* *("-" (1*8alphanum / "*"))
|
||||
* alphanum = ALPHA / DIGIT
|
||||
* </pre>
|
||||
* </blockquote>
|
||||
* For example, {@code "en"} (English), {@code "ja-JP"} (Japanese, Japan),
|
||||
* {@code "*"} (special language range which matches any language tag) are
|
||||
* basic language ranges, whereas {@code "*-CH"} (any languages,
|
||||
* Switzerland), {@code "es-*"} (Spanish, any regions), and
|
||||
* {@code "zh-Hant-*"} (Traditional Chinese, any regions) are extended
|
||||
* language ranges.
|
||||
*
|
||||
* @see #filter
|
||||
* @see #filterTags
|
||||
* @see #lookup
|
||||
* @see #lookupTag
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static final class LanguageRange {
|
||||
|
||||
/**
|
||||
* A constant holding the maximum value of weight, 1.0, which indicates
|
||||
* that the language range is a good fit for the user.
|
||||
*/
|
||||
public static final double MAX_WEIGHT = 1.0;
|
||||
|
||||
/**
|
||||
* A constant holding the minimum value of weight, 0.0, which indicates
|
||||
* that the language range is not a good fit for the user.
|
||||
*/
|
||||
public static final double MIN_WEIGHT = 0.0;
|
||||
|
||||
private final String range;
|
||||
private final double weight;
|
||||
|
||||
private volatile int hash = 0;
|
||||
|
||||
/**
|
||||
* Constructs a {@code LanguageRange} using the given {@code range}.
|
||||
* Note that no validation is done against the IANA Language Subtag
|
||||
* Registry at time of construction.
|
||||
*
|
||||
* <p>This is equivalent to {@code LanguageRange(range, MAX_WEIGHT)}.
|
||||
*
|
||||
* @param range a language range
|
||||
* @throws NullPointerException if the given {@code range} is
|
||||
* {@code null}
|
||||
*/
|
||||
public LanguageRange(String range) {
|
||||
this(range, MAX_WEIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code LanguageRange} using the given {@code range} and
|
||||
* {@code weight}. Note that no validation is done against the IANA
|
||||
* Language Subtag Registry at time of construction.
|
||||
*
|
||||
* @param range a language range
|
||||
* @param weight a weight value between {@code MIN_WEIGHT} and
|
||||
* {@code MAX_WEIGHT}
|
||||
* @throws NullPointerException if the given {@code range} is
|
||||
* {@code null}
|
||||
* @throws IllegalArgumentException if the given {@code weight} is less
|
||||
* than {@code MIN_WEIGHT} or greater than {@code MAX_WEIGHT}
|
||||
*/
|
||||
public LanguageRange(String range, double weight) {
|
||||
if (range == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) {
|
||||
throw new IllegalArgumentException("weight=" + weight);
|
||||
}
|
||||
|
||||
range = range.toLowerCase();
|
||||
|
||||
// Do syntax check.
|
||||
boolean isIllFormed = false;
|
||||
String[] subtags = range.split("-");
|
||||
if (isSubtagIllFormed(subtags[0], true)
|
||||
|| range.endsWith("-")) {
|
||||
isIllFormed = true;
|
||||
} else {
|
||||
for (int i = 1; i < subtags.length; i++) {
|
||||
if (isSubtagIllFormed(subtags[i], false)) {
|
||||
isIllFormed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isIllFormed) {
|
||||
throw new IllegalArgumentException("range=" + range);
|
||||
}
|
||||
|
||||
this.range = range;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
private static boolean isSubtagIllFormed(String subtag,
|
||||
boolean isFirstSubtag) {
|
||||
if (subtag.equals("") || subtag.length() > 8) {
|
||||
return true;
|
||||
} else if (subtag.equals("*")) {
|
||||
return false;
|
||||
}
|
||||
char[] charArray = subtag.toCharArray();
|
||||
if (isFirstSubtag) { // ALPHA
|
||||
for (char c : charArray) {
|
||||
if (c < 'a' || c > 'z') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else { // ALPHA / DIGIT
|
||||
for (char c : charArray) {
|
||||
if (c < '0' || (c > '9' && c < 'a') || c > 'z') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language range of this {@code LanguageRange}.
|
||||
*
|
||||
* @return the language range.
|
||||
*/
|
||||
public String getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the weight of this {@code LanguageRange}.
|
||||
*
|
||||
* @return the weight value.
|
||||
*/
|
||||
public double getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given {@code ranges} to generate a Language Priority List.
|
||||
*
|
||||
* <p>This method performs a syntactic check for each language range in
|
||||
* the given {@code ranges} but doesn't do validation using the IANA
|
||||
* Language Subtag Registry.
|
||||
*
|
||||
* <p>The {@code ranges} to be given can take one of the following
|
||||
* forms:
|
||||
*
|
||||
* <pre>
|
||||
* "Accept-Language: ja,en;q=0.4" (weighted list with Accept-Language prefix)
|
||||
* "ja,en;q=0.4" (weighted list)
|
||||
* "ja,en" (prioritized list)
|
||||
* </pre>
|
||||
*
|
||||
* In a weighted list, each language range is given a weight value.
|
||||
* The weight value is identical to the "quality value" in
|
||||
* <a href="http://tools.ietf.org/html/rfc2616">RFC 2616</a>, and it
|
||||
* expresses how much the user prefers the language. A weight value is
|
||||
* specified after a corresponding language range followed by
|
||||
* {@code ";q="}, and the default weight value is {@code MAX_WEIGHT}
|
||||
* when it is omitted.
|
||||
*
|
||||
* <p>Unlike a weighted list, language ranges in a prioritized list
|
||||
* are sorted in the descending order based on its priority. The first
|
||||
* language range has the highest priority and meets the user's
|
||||
* preference most.
|
||||
*
|
||||
* <p>In either case, language ranges are sorted in descending order in
|
||||
* the Language Priority List based on priority or weight. If a
|
||||
* language range appears in the given {@code ranges} more than once,
|
||||
* only the first one is included on the Language Priority List.
|
||||
*
|
||||
* <p>The returned list consists of language ranges from the given
|
||||
* {@code ranges} and their equivalents found in the IANA Language
|
||||
* Subtag Registry. For example, if the given {@code ranges} is
|
||||
* {@code "Accept-Language: iw,en-us;q=0.7,en;q=0.3"}, the elements in
|
||||
* the list to be returned are:
|
||||
*
|
||||
* <pre>
|
||||
* <b>Range</b> <b>Weight</b>
|
||||
* "iw" (older tag for Hebrew) 1.0
|
||||
* "he" (new preferred code for Hebrew) 1.0
|
||||
* "en-us" (English, United States) 0.7
|
||||
* "en" (English) 0.3
|
||||
* </pre>
|
||||
*
|
||||
* Two language ranges, {@code "iw"} and {@code "he"}, have the same
|
||||
* highest priority in the list. By adding {@code "he"} to the user's
|
||||
* Language Priority List, locale-matching method can find Hebrew as a
|
||||
* matching locale (or language tag) even if the application or system
|
||||
* offers only {@code "he"} as a supported locale (or language tag).
|
||||
*
|
||||
* @param ranges a list of comma-separated language ranges or a list of
|
||||
* language ranges in the form of the "Accept-Language" header
|
||||
* defined in <a href="http://tools.ietf.org/html/rfc2616">RFC
|
||||
* 2616</a>
|
||||
* @return a Language Priority List consisting of language ranges
|
||||
* included in the given {@code ranges} and their equivalent
|
||||
* language ranges if available. The list is modifiable.
|
||||
* @throws NullPointerException if {@code ranges} is null
|
||||
* @throws IllegalArgumentException if a language range or a weight
|
||||
* found in the given {@code ranges} is ill-formed
|
||||
*/
|
||||
public static List<LanguageRange> parse(String ranges) {
|
||||
return LocaleMatcher.parse(ranges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given {@code ranges} to generate a Language Priority
|
||||
* List, and then customizes the list using the given {@code map}.
|
||||
* This method is equivalent to
|
||||
* {@code mapEquivalents(parse(ranges), map)}.
|
||||
*
|
||||
* @param ranges a list of comma-separated language ranges or a list
|
||||
* of language ranges in the form of the "Accept-Language" header
|
||||
* defined in <a href="http://tools.ietf.org/html/rfc2616">RFC
|
||||
* 2616</a>
|
||||
* @param map a map containing information to customize language ranges
|
||||
* @return a Language Priority List with customization. The list is
|
||||
* @throws NullPointerException if {@code ranges} is null
|
||||
* @throws IllegalArgumentException if a language range or a weight
|
||||
* found in the given {@code ranges} is ill-formed
|
||||
* @see #parse(String)
|
||||
* @see #mapEquivalents
|
||||
*/
|
||||
public static List<LanguageRange> parse(String ranges,
|
||||
Map<String, List<String>> map) {
|
||||
return mapEquivalents(parse(ranges), map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new customized Language Priority List using the given
|
||||
* {@code priorityList} and {@code map}. If the given {@code map} is
|
||||
* empty, this method returns a copy of the given {@code priorityList}.
|
||||
*
|
||||
* <p>In the map, a key represents a language range whereas a value is
|
||||
* a list of equivalents of it. {@code '*'} cannot be used in the map.
|
||||
* Each equivalent language range has the same weight value as its
|
||||
* original language range.
|
||||
*
|
||||
* <pre>
|
||||
* An example of map:
|
||||
* <b>Key</b> <b>Value</b>
|
||||
* "zh" (Chinese) "zh",
|
||||
* "zh-Hans"(Simplified Chinese)
|
||||
* "zh-HK" (Chinese, Hong Kong) "zh-HK"
|
||||
* "zh-TW" (Chinese, Taiwan) "zh-TW"
|
||||
* </pre>
|
||||
*
|
||||
* The customization is performed after modification using the IANA
|
||||
* Language Subtag Registry.
|
||||
*
|
||||
* <p>For example, if a user's Language Priority List consists of five
|
||||
* language ranges ({@code "zh"}, {@code "zh-CN"}, {@code "en"},
|
||||
* {@code "zh-TW"}, and {@code "zh-HK"}), the newly generated Language
|
||||
* Priority List which is customized using the above map example will
|
||||
* consists of {@code "zh"}, {@code "zh-Hans"}, {@code "zh-CN"},
|
||||
* {@code "zh-Hans-CN"}, {@code "en"}, {@code "zh-TW"}, and
|
||||
* {@code "zh-HK"}.
|
||||
*
|
||||
* <p>{@code "zh-HK"} and {@code "zh-TW"} aren't converted to
|
||||
* {@code "zh-Hans-HK"} nor {@code "zh-Hans-TW"} even if they are
|
||||
* included in the Language Priority List. In this example, mapping
|
||||
* is used to clearly distinguish Simplified Chinese and Traditional
|
||||
* Chinese.
|
||||
*
|
||||
* <p>If the {@code "zh"}-to-{@code "zh"} mapping isn't included in the
|
||||
* map, a simple replacement will be performed and the customized list
|
||||
* won't include {@code "zh"} and {@code "zh-CN"}.
|
||||
*
|
||||
* @param priorityList user's Language Priority List
|
||||
* @param map a map containing information to customize language ranges
|
||||
* @return a new Language Priority List with customization. The list is
|
||||
* modifiable.
|
||||
* @throws NullPointerException if {@code priorityList} is {@code null}
|
||||
* @see #parse(String, Map)
|
||||
*/
|
||||
public static List<LanguageRange> mapEquivalents(
|
||||
List<LanguageRange>priorityList,
|
||||
Map<String, List<String>> map) {
|
||||
return LocaleMatcher.mapEquivalents(priorityList, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*
|
||||
* @return a hash code value for this object.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hash == 0) {
|
||||
int result = 17;
|
||||
result = 37*result + range.hashCode();
|
||||
long bitsWeight = Double.doubleToLongBits(weight);
|
||||
result = 37*result + (int)(bitsWeight ^ (bitsWeight >>> 32));
|
||||
hash = result;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this object to the specified object. The result is true if
|
||||
* and only if the argument is not {@code null} and is a
|
||||
* {@code LanguageRange} object that contains the same {@code range}
|
||||
* and {@code weight} values as this object.
|
||||
*
|
||||
* @param obj the object to compare with
|
||||
* @return {@code true} if this object's {@code range} and
|
||||
* {@code weight} are the same as the {@code obj}'s; {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof LanguageRange)) {
|
||||
return false;
|
||||
}
|
||||
LanguageRange other = (LanguageRange)obj;
|
||||
return hash == other.hash
|
||||
&& range.equals(other.range)
|
||||
&& weight == other.weight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of matching {@code Locale} instances using the filtering
|
||||
* mechanism defined in RFC 4647.
|
||||
*
|
||||
* @param priorityList user's Language Priority List in which each language
|
||||
* tag is sorted in descending order based on priority or weight
|
||||
* @param locales {@code Locale} instances used for matching
|
||||
* @param mode filtering mode
|
||||
* @return a list of {@code Locale} instances for matching language tags
|
||||
* sorted in descending order based on priority or weight, or an empty
|
||||
* list if nothing matches. The list is modifiable.
|
||||
* @throws NullPointerException if {@code priorityList} or {@code locales}
|
||||
* is {@code null}
|
||||
* @throws IllegalArgumentException if one or more extended language ranges
|
||||
* are included in the given list when
|
||||
* {@link FilteringMode#REJECT_EXTENDED_RANGES} is specified
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static List<Locale> filter(List<LanguageRange> priorityList,
|
||||
Collection<Locale> locales,
|
||||
FilteringMode mode) {
|
||||
return LocaleMatcher.filter(priorityList, locales, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of matching {@code Locale} instances using the filtering
|
||||
* mechanism defined in RFC 4647. This is equivalent to
|
||||
* {@link #filter(List, Collection, FilteringMode)} when {@code mode} is
|
||||
* {@link FilteringMode#AUTOSELECT_FILTERING}.
|
||||
*
|
||||
* @param priorityList user's Language Priority List in which each language
|
||||
* tag is sorted in descending order based on priority or weight
|
||||
* @param locales {@code Locale} instances used for matching
|
||||
* @return a list of {@code Locale} instances for matching language tags
|
||||
* sorted in descending order based on priority or weight, or an empty
|
||||
* list if nothing matches. The list is modifiable.
|
||||
* @throws NullPointerException if {@code priorityList} or {@code locales}
|
||||
* is {@code null}
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static List<Locale> filter(List<LanguageRange> priorityList,
|
||||
Collection<Locale> locales) {
|
||||
return filter(priorityList, locales, FilteringMode.AUTOSELECT_FILTERING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of matching languages tags using the basic filtering
|
||||
* mechanism defined in RFC 4647.
|
||||
*
|
||||
* @param priorityList user's Language Priority List in which each language
|
||||
* tag is sorted in descending order based on priority or weight
|
||||
* @param tags language tags
|
||||
* @param mode filtering mode
|
||||
* @return a list of matching language tags sorted in descending order
|
||||
* based on priority or weight, or an empty list if nothing matches.
|
||||
* The list is modifiable.
|
||||
* @throws NullPointerException if {@code priorityList} or {@code tags} is
|
||||
* {@code null}
|
||||
* @throws IllegalArgumentException if one or more extended language ranges
|
||||
* are included in the given list when
|
||||
* {@link FilteringMode#REJECT_EXTENDED_RANGES} is specified
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static List<String> filterTags(List<LanguageRange> priorityList,
|
||||
Collection<String> tags,
|
||||
FilteringMode mode) {
|
||||
return LocaleMatcher.filterTags(priorityList, tags, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of matching languages tags using the basic filtering
|
||||
* mechanism defined in RFC 4647. This is equivalent to
|
||||
* {@link #filterTags(List, Collection, FilteringMode)} when {@code mode}
|
||||
* is {@link FilteringMode#AUTOSELECT_FILTERING}.
|
||||
*
|
||||
* @param priorityList user's Language Priority List in which each language
|
||||
* tag is sorted in descending order based on priority or weight
|
||||
* @param tags language tags
|
||||
* @return a list of matching language tags sorted in descending order
|
||||
* based on priority or weight, or an empty list if nothing matches.
|
||||
* The list is modifiable.
|
||||
* @throws NullPointerException if {@code priorityList} or {@code tags} is
|
||||
* {@code null}
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static List<String> filterTags(List<LanguageRange> priorityList,
|
||||
Collection<String> tags) {
|
||||
return filterTags(priorityList, tags, FilteringMode.AUTOSELECT_FILTERING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Locale} instance for the best-matching language
|
||||
* tag using the lookup mechanism defined in RFC 4647.
|
||||
*
|
||||
* @param priorityList user's Language Priority List in which each language
|
||||
* tag is sorted in descending order based on priority or weight
|
||||
* @param locales {@code Locale} instances used for matching
|
||||
* @return the best matching <code>Locale</code> instance chosen based on
|
||||
* priority or weight, or {@code null} if nothing matches.
|
||||
* @throws NullPointerException if {@code priorityList} or {@code tags} is
|
||||
* {@code null}
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static Locale lookup(List<LanguageRange> priorityList,
|
||||
Collection<Locale> locales) {
|
||||
return LocaleMatcher.lookup(priorityList, locales);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best-matching language tag using the lookup mechanism
|
||||
* defined in RFC 4647.
|
||||
*
|
||||
* @param priorityList user's Language Priority List in which each language
|
||||
* tag is sorted in descending order based on priority or weight
|
||||
* @param tags language tangs used for matching
|
||||
* @return the best matching language tag chosen based on priority or
|
||||
* weight, or {@code null} if nothing matches.
|
||||
* @throws NullPointerException if {@code priorityList} or {@code tags} is
|
||||
* {@code null}
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
public static String lookupTag(List<LanguageRange> priorityList,
|
||||
Collection<String> tags) {
|
||||
return LocaleMatcher.lookupTag(priorityList, tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
204
jdk/src/share/classes/sun/util/locale/LocaleEquivalentMaps.java
Normal file
204
jdk/src/share/classes/sun/util/locale/LocaleEquivalentMaps.java
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.util.locale;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Locale equivalent map for BCP47 Locale matching
|
||||
*/
|
||||
final class LocaleEquivalentMaps {
|
||||
|
||||
static final Map<String, String> singleEquivMap;
|
||||
static final Map<String, String[]> multiEquivsMap;
|
||||
static final Map<String, String> regionVariantEquivMap;
|
||||
|
||||
static {
|
||||
singleEquivMap = new HashMap<>();
|
||||
multiEquivsMap = new HashMap<>();
|
||||
regionVariantEquivMap = new HashMap<>();
|
||||
|
||||
// This is an auto-generated file and should not be manually edited.
|
||||
// LSR Revision: 2012-09-04
|
||||
singleEquivMap.put("ami", "i-ami");
|
||||
singleEquivMap.put("art-lojban", "jbo");
|
||||
singleEquivMap.put("ase", "sgn-us");
|
||||
singleEquivMap.put("ayx", "nun");
|
||||
singleEquivMap.put("bfi", "sgn-gb");
|
||||
singleEquivMap.put("bjd", "drl");
|
||||
singleEquivMap.put("bnn", "i-bnn");
|
||||
singleEquivMap.put("bzs", "sgn-br");
|
||||
singleEquivMap.put("cjr", "mom");
|
||||
singleEquivMap.put("cka", "cmr");
|
||||
singleEquivMap.put("cmk", "xch");
|
||||
singleEquivMap.put("cmn-hans", "zh-cmn-hans");
|
||||
singleEquivMap.put("cmn-hant", "zh-cmn-hant");
|
||||
singleEquivMap.put("cmr", "cka");
|
||||
singleEquivMap.put("csn", "sgn-co");
|
||||
singleEquivMap.put("dev", "gav");
|
||||
singleEquivMap.put("drh", "khk");
|
||||
singleEquivMap.put("drl", "bjd");
|
||||
singleEquivMap.put("dse", "sgn-nl");
|
||||
singleEquivMap.put("dsl", "sgn-dk");
|
||||
singleEquivMap.put("fsl", "sgn-fr");
|
||||
singleEquivMap.put("gan", "zh-gan");
|
||||
singleEquivMap.put("gav", "dev");
|
||||
singleEquivMap.put("gsg", "sgn-de");
|
||||
singleEquivMap.put("gss", "sgn-gr");
|
||||
singleEquivMap.put("he", "iw");
|
||||
singleEquivMap.put("hle", "sca");
|
||||
singleEquivMap.put("hrr", "jal");
|
||||
singleEquivMap.put("hsn", "zh-xiang");
|
||||
singleEquivMap.put("i-ami", "ami");
|
||||
singleEquivMap.put("i-bnn", "bnn");
|
||||
singleEquivMap.put("i-klingon", "tlh");
|
||||
singleEquivMap.put("i-lux", "lb");
|
||||
singleEquivMap.put("i-navajo", "nv");
|
||||
singleEquivMap.put("i-pwn", "pwn");
|
||||
singleEquivMap.put("i-tao", "tao");
|
||||
singleEquivMap.put("i-tay", "tay");
|
||||
singleEquivMap.put("i-tsu", "tsu");
|
||||
singleEquivMap.put("ibi", "opa");
|
||||
singleEquivMap.put("id", "in");
|
||||
singleEquivMap.put("in", "id");
|
||||
singleEquivMap.put("ise", "sgn-it");
|
||||
singleEquivMap.put("isg", "sgn-ie");
|
||||
singleEquivMap.put("iw", "he");
|
||||
singleEquivMap.put("jal", "hrr");
|
||||
singleEquivMap.put("jbo", "art-lojban");
|
||||
singleEquivMap.put("ji", "yi");
|
||||
singleEquivMap.put("jsl", "sgn-jp");
|
||||
singleEquivMap.put("jv", "jw");
|
||||
singleEquivMap.put("jw", "jv");
|
||||
singleEquivMap.put("kgh", "kml");
|
||||
singleEquivMap.put("khk", "drh");
|
||||
singleEquivMap.put("kml", "kgh");
|
||||
singleEquivMap.put("lb", "i-lux");
|
||||
singleEquivMap.put("lcq", "ppr");
|
||||
singleEquivMap.put("lrr", "yma");
|
||||
singleEquivMap.put("mfs", "sgn-mx");
|
||||
singleEquivMap.put("mo", "ro");
|
||||
singleEquivMap.put("mom", "cjr");
|
||||
singleEquivMap.put("nan", "zh-min-nan");
|
||||
singleEquivMap.put("nb", "no-bok");
|
||||
singleEquivMap.put("ncs", "sgn-ni");
|
||||
singleEquivMap.put("nn", "no-nyn");
|
||||
singleEquivMap.put("no-bok", "nb");
|
||||
singleEquivMap.put("no-nyn", "nn");
|
||||
singleEquivMap.put("nsl", "sgn-no");
|
||||
singleEquivMap.put("nun", "ayx");
|
||||
singleEquivMap.put("nv", "i-navajo");
|
||||
singleEquivMap.put("opa", "ibi");
|
||||
singleEquivMap.put("ppr", "lcq");
|
||||
singleEquivMap.put("psr", "sgn-pt");
|
||||
singleEquivMap.put("pwn", "i-pwn");
|
||||
singleEquivMap.put("ras", "tie");
|
||||
singleEquivMap.put("ro", "mo");
|
||||
singleEquivMap.put("sca", "hle");
|
||||
singleEquivMap.put("sfb", "sgn-be-fr");
|
||||
singleEquivMap.put("sfs", "sgn-za");
|
||||
singleEquivMap.put("sgg", "sgn-ch-de");
|
||||
singleEquivMap.put("sgn-be-fr", "sfb");
|
||||
singleEquivMap.put("sgn-be-nl", "vgt");
|
||||
singleEquivMap.put("sgn-br", "bzs");
|
||||
singleEquivMap.put("sgn-ch-de", "sgg");
|
||||
singleEquivMap.put("sgn-co", "csn");
|
||||
singleEquivMap.put("sgn-de", "gsg");
|
||||
singleEquivMap.put("sgn-dk", "dsl");
|
||||
singleEquivMap.put("sgn-es", "ssp");
|
||||
singleEquivMap.put("sgn-fr", "fsl");
|
||||
singleEquivMap.put("sgn-gb", "bfi");
|
||||
singleEquivMap.put("sgn-gr", "gss");
|
||||
singleEquivMap.put("sgn-ie", "isg");
|
||||
singleEquivMap.put("sgn-it", "ise");
|
||||
singleEquivMap.put("sgn-jp", "jsl");
|
||||
singleEquivMap.put("sgn-mx", "mfs");
|
||||
singleEquivMap.put("sgn-ni", "ncs");
|
||||
singleEquivMap.put("sgn-nl", "dse");
|
||||
singleEquivMap.put("sgn-no", "nsl");
|
||||
singleEquivMap.put("sgn-pt", "psr");
|
||||
singleEquivMap.put("sgn-se", "swl");
|
||||
singleEquivMap.put("sgn-us", "ase");
|
||||
singleEquivMap.put("sgn-za", "sfs");
|
||||
singleEquivMap.put("ssp", "sgn-es");
|
||||
singleEquivMap.put("swl", "sgn-se");
|
||||
singleEquivMap.put("tao", "i-tao");
|
||||
singleEquivMap.put("tay", "i-tay");
|
||||
singleEquivMap.put("tie", "ras");
|
||||
singleEquivMap.put("tkk", "twm");
|
||||
singleEquivMap.put("tlh", "i-klingon");
|
||||
singleEquivMap.put("tlw", "weo");
|
||||
singleEquivMap.put("tsu", "i-tsu");
|
||||
singleEquivMap.put("twm", "tkk");
|
||||
singleEquivMap.put("vgt", "sgn-be-nl");
|
||||
singleEquivMap.put("weo", "tlw");
|
||||
singleEquivMap.put("wuu", "zh-wuu");
|
||||
singleEquivMap.put("xch", "cmk");
|
||||
singleEquivMap.put("yi", "ji");
|
||||
singleEquivMap.put("yma", "lrr");
|
||||
singleEquivMap.put("yue", "zh-yue");
|
||||
singleEquivMap.put("zh-cmn-hans", "cmn-hans");
|
||||
singleEquivMap.put("zh-cmn-hant", "cmn-hant");
|
||||
singleEquivMap.put("zh-gan", "gan");
|
||||
singleEquivMap.put("zh-min-nan", "nan");
|
||||
singleEquivMap.put("zh-wuu", "wuu");
|
||||
singleEquivMap.put("zh-xiang", "hsn");
|
||||
singleEquivMap.put("zh-yue", "yue");
|
||||
|
||||
multiEquivsMap.put("ccq", new String[] {"rki", "ybd"});
|
||||
multiEquivsMap.put("cmn", new String[] {"zh-guoyu", "zh-cmn"});
|
||||
multiEquivsMap.put("drw", new String[] {"prs", "tnf"});
|
||||
multiEquivsMap.put("hak", new String[] {"i-hak", "zh-hakka"});
|
||||
multiEquivsMap.put("i-hak", new String[] {"hak", "zh-hakka"});
|
||||
multiEquivsMap.put("mry", new String[] {"mst", "myt"});
|
||||
multiEquivsMap.put("mst", new String[] {"mry", "myt"});
|
||||
multiEquivsMap.put("myt", new String[] {"mry", "mst"});
|
||||
multiEquivsMap.put("prs", new String[] {"drw", "tnf"});
|
||||
multiEquivsMap.put("rki", new String[] {"ccq", "ybd"});
|
||||
multiEquivsMap.put("tnf", new String[] {"prs", "drw"});
|
||||
multiEquivsMap.put("ybd", new String[] {"rki", "ccq"});
|
||||
multiEquivsMap.put("zh-cmn", new String[] {"cmn", "zh-guoyu"});
|
||||
multiEquivsMap.put("zh-guoyu", new String[] {"cmn", "zh-cmn"});
|
||||
multiEquivsMap.put("zh-hakka", new String[] {"hak", "i-hak"});
|
||||
|
||||
regionVariantEquivMap.put("-alalc97", "-heploc");
|
||||
regionVariantEquivMap.put("-bu", "-mm");
|
||||
regionVariantEquivMap.put("-cd", "-zr");
|
||||
regionVariantEquivMap.put("-dd", "-de");
|
||||
regionVariantEquivMap.put("-de", "-dd");
|
||||
regionVariantEquivMap.put("-fr", "-fx");
|
||||
regionVariantEquivMap.put("-fx", "-fr");
|
||||
regionVariantEquivMap.put("-heploc", "-alalc97");
|
||||
regionVariantEquivMap.put("-mm", "-bu");
|
||||
regionVariantEquivMap.put("-tl", "-tp");
|
||||
regionVariantEquivMap.put("-tp", "-tl");
|
||||
regionVariantEquivMap.put("-yd", "-ye");
|
||||
regionVariantEquivMap.put("-ye", "-yd");
|
||||
regionVariantEquivMap.put("-zr", "-cd");
|
||||
}
|
||||
|
||||
}
|
||||
455
jdk/src/share/classes/sun/util/locale/LocaleMatcher.java
Normal file
455
jdk/src/share/classes/sun/util/locale/LocaleMatcher.java
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.util.locale;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Locale.*;
|
||||
import static java.util.Locale.FilteringMode.*;
|
||||
import static java.util.Locale.LanguageRange.*;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation for BCP47 Locale matching
|
||||
*
|
||||
*/
|
||||
public final class LocaleMatcher {
|
||||
|
||||
public static List<Locale> filter(List<LanguageRange> priorityList,
|
||||
Collection<Locale> locales,
|
||||
FilteringMode mode) {
|
||||
if (priorityList.isEmpty() || locales.isEmpty()) {
|
||||
return new ArrayList<>(); // need to return a empty mutable List
|
||||
}
|
||||
|
||||
// Create a list of language tags to be matched.
|
||||
List<String> tags = new ArrayList<>();
|
||||
for (Locale locale : locales) {
|
||||
tags.add(locale.toLanguageTag());
|
||||
}
|
||||
|
||||
// Filter language tags.
|
||||
List<String> filteredTags = filterTags(priorityList, tags, mode);
|
||||
|
||||
// Create a list of matching locales.
|
||||
List<Locale> filteredLocales = new ArrayList<>(filteredTags.size());
|
||||
for (String tag : filteredTags) {
|
||||
filteredLocales.add(Locale.forLanguageTag(tag));
|
||||
}
|
||||
|
||||
return filteredLocales;
|
||||
}
|
||||
|
||||
public static List<String> filterTags(List<LanguageRange> priorityList,
|
||||
Collection<String> tags,
|
||||
FilteringMode mode) {
|
||||
if (priorityList.isEmpty() || tags.isEmpty()) {
|
||||
return new ArrayList<>(); // need to return a empty mutable List
|
||||
}
|
||||
|
||||
ArrayList<LanguageRange> list;
|
||||
if (mode == EXTENDED_FILTERING) {
|
||||
return filterExtended(priorityList, tags);
|
||||
} else {
|
||||
list = new ArrayList<>();
|
||||
for (LanguageRange lr : priorityList) {
|
||||
String range = lr.getRange();
|
||||
if (range.startsWith("*-")
|
||||
|| range.indexOf("-*") != -1) { // Extended range
|
||||
if (mode == AUTOSELECT_FILTERING) {
|
||||
return filterExtended(priorityList, tags);
|
||||
} else if (mode == MAP_EXTENDED_RANGES) {
|
||||
if (range.charAt(0) == '*') {
|
||||
range = "*";
|
||||
} else {
|
||||
range = range.replaceAll("-[*]", "");
|
||||
}
|
||||
list.add(new LanguageRange(range, lr.getWeight()));
|
||||
} else if (mode == REJECT_EXTENDED_RANGES) {
|
||||
throw new IllegalArgumentException("An extended range \""
|
||||
+ range
|
||||
+ "\" found in REJECT_EXTENDED_RANGES mode.");
|
||||
}
|
||||
} else { // Basic range
|
||||
list.add(lr);
|
||||
}
|
||||
}
|
||||
|
||||
return filterBasic(list, tags);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> filterBasic(List<LanguageRange> priorityList,
|
||||
Collection<String> tags) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (LanguageRange lr : priorityList) {
|
||||
String range = lr.getRange();
|
||||
if (range.equals("*")) {
|
||||
return new ArrayList<String>(tags);
|
||||
} else {
|
||||
for (String tag : tags) {
|
||||
tag = tag.toLowerCase();
|
||||
if (tag.startsWith(range)) {
|
||||
int len = range.length();
|
||||
if ((tag.length() == len || tag.charAt(len) == '-')
|
||||
&& !list.contains(tag)) {
|
||||
list.add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<String> filterExtended(List<LanguageRange> priorityList,
|
||||
Collection<String> tags) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (LanguageRange lr : priorityList) {
|
||||
String range = lr.getRange();
|
||||
if (range.equals("*")) {
|
||||
return new ArrayList<String>(tags);
|
||||
}
|
||||
String[] rangeSubtags = range.split("-");
|
||||
for (String tag : tags) {
|
||||
tag = tag.toLowerCase();
|
||||
String[] tagSubtags = tag.split("-");
|
||||
if (!rangeSubtags[0].equals(tagSubtags[0])
|
||||
&& !rangeSubtags[0].equals("*")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int rangeIndex = 1;
|
||||
int tagIndex = 1;
|
||||
|
||||
while (rangeIndex < rangeSubtags.length
|
||||
&& tagIndex < tagSubtags.length) {
|
||||
if (rangeSubtags[rangeIndex].equals("*")) {
|
||||
rangeIndex++;
|
||||
} else if (rangeSubtags[rangeIndex].equals(tagSubtags[tagIndex])) {
|
||||
rangeIndex++;
|
||||
tagIndex++;
|
||||
} else if (tagSubtags[tagIndex].length() == 1
|
||||
&& !tagSubtags[tagIndex].equals("*")) {
|
||||
break;
|
||||
} else {
|
||||
tagIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (rangeSubtags.length == rangeIndex && !list.contains(tag)) {
|
||||
list.add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Locale lookup(List<LanguageRange> priorityList,
|
||||
Collection<Locale> locales) {
|
||||
if (priorityList.isEmpty() || locales.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a list of language tags to be matched.
|
||||
List<String> tags = new ArrayList<>();
|
||||
for (Locale locale : locales) {
|
||||
tags.add(locale.toLanguageTag());
|
||||
}
|
||||
|
||||
// Look up a language tags.
|
||||
String lookedUpTag = lookupTag(priorityList, tags);
|
||||
|
||||
if (lookedUpTag == null) {
|
||||
return null;
|
||||
} else {
|
||||
return Locale.forLanguageTag(lookedUpTag);
|
||||
}
|
||||
}
|
||||
|
||||
public static String lookupTag(List<LanguageRange> priorityList,
|
||||
Collection<String> tags) {
|
||||
if (priorityList.isEmpty() || tags.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (LanguageRange lr : priorityList) {
|
||||
String range = lr.getRange();
|
||||
|
||||
// Special language range ("*") is ignored in lookup.
|
||||
if (range.equals("*")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String rangeForRegex = range.replaceAll("\\x2A", "\\\\p{Alnum}*");
|
||||
while (rangeForRegex.length() > 0) {
|
||||
for (String tag : tags) {
|
||||
tag = tag.toLowerCase();
|
||||
if (tag.matches(rangeForRegex)) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate from the end....
|
||||
int index = rangeForRegex.lastIndexOf('-');
|
||||
if (index >= 0) {
|
||||
rangeForRegex = rangeForRegex.substring(0, index);
|
||||
|
||||
// if range ends with an extension key, truncate it.
|
||||
if (rangeForRegex.lastIndexOf('-') == rangeForRegex.length()-2) {
|
||||
rangeForRegex =
|
||||
rangeForRegex.substring(0, rangeForRegex.length()-2);
|
||||
}
|
||||
} else {
|
||||
rangeForRegex = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<LanguageRange> parse(String ranges) {
|
||||
ranges = ranges.replaceAll(" ", "").toLowerCase();
|
||||
if (ranges.startsWith("accept-language:")) {
|
||||
ranges = ranges.substring(16); // delete unnecessary prefix
|
||||
}
|
||||
|
||||
String[] langRanges = ranges.split(",");
|
||||
List<LanguageRange> list = new ArrayList<>(langRanges.length);
|
||||
List<String> tempList = new ArrayList<>();
|
||||
int numOfRanges = 0;
|
||||
|
||||
for (String range : langRanges) {
|
||||
int index;
|
||||
String r;
|
||||
double w;
|
||||
|
||||
if ((index = range.indexOf(";q=")) == -1) {
|
||||
r = range;
|
||||
w = MAX_WEIGHT;
|
||||
} else {
|
||||
r = range.substring(0, index);
|
||||
index += 3;
|
||||
try {
|
||||
w = Double.parseDouble(range.substring(index));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalArgumentException("weight=\""
|
||||
+ range.substring(index)
|
||||
+ "\" for language range \"" + r + "\"");
|
||||
}
|
||||
|
||||
if (w < MIN_WEIGHT || w > MAX_WEIGHT) {
|
||||
throw new IllegalArgumentException("weight=" + w
|
||||
+ " for language range \"" + r
|
||||
+ "\". It must be between " + MIN_WEIGHT
|
||||
+ " and " + MAX_WEIGHT + ".");
|
||||
}
|
||||
}
|
||||
|
||||
if (!tempList.contains(r)) {
|
||||
LanguageRange lr = new LanguageRange(r, w);
|
||||
index = numOfRanges;
|
||||
for (int j = 0; j < numOfRanges; j++) {
|
||||
if (list.get(j).getWeight() < w) {
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
list.add(index, lr);
|
||||
numOfRanges++;
|
||||
tempList.add(r);
|
||||
|
||||
// Check if the range has an equivalent using IANA LSR data.
|
||||
// If yes, add it to the User's Language Priority List as well.
|
||||
|
||||
// aa-XX -> aa-YY
|
||||
String equivalent;
|
||||
if ((equivalent = getEquivalentForRegionAndVariant(r)) != null
|
||||
&& !tempList.contains(equivalent)) {
|
||||
list.add(index+1, new LanguageRange(equivalent, w));
|
||||
numOfRanges++;
|
||||
tempList.add(equivalent);
|
||||
}
|
||||
|
||||
String[] equivalents;
|
||||
if ((equivalents = getEquivalentsForLanguage(r)) != null) {
|
||||
for (String equiv: equivalents) {
|
||||
// aa-XX -> bb-XX(, cc-XX)
|
||||
if (!tempList.contains(equiv)) {
|
||||
list.add(index+1, new LanguageRange(equiv, w));
|
||||
numOfRanges++;
|
||||
tempList.add(equiv);
|
||||
}
|
||||
|
||||
// bb-XX -> bb-YY(, cc-YY)
|
||||
equivalent = getEquivalentForRegionAndVariant(equiv);
|
||||
if (equivalent != null
|
||||
&& !tempList.contains(equivalent)) {
|
||||
list.add(index+1, new LanguageRange(equivalent, w));
|
||||
numOfRanges++;
|
||||
tempList.add(equivalent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static String[] getEquivalentsForLanguage(String range) {
|
||||
String r = range;
|
||||
|
||||
while (r.length() > 0) {
|
||||
if (LocaleEquivalentMaps.singleEquivMap.containsKey(r)) {
|
||||
String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
|
||||
// Return immediately for performance if the first matching
|
||||
// subtag is found.
|
||||
return new String[] {range.replaceFirst(r, equiv)};
|
||||
} else if (LocaleEquivalentMaps.multiEquivsMap.containsKey(r)) {
|
||||
String[] equivs = LocaleEquivalentMaps.multiEquivsMap.get(r);
|
||||
for (int i = 0; i < equivs.length; i++) {
|
||||
equivs[i] = range.replaceFirst(r, equivs[i]);
|
||||
}
|
||||
return equivs;
|
||||
}
|
||||
|
||||
// Truncate the last subtag simply.
|
||||
int index = r.lastIndexOf('-');
|
||||
if (index == -1) {
|
||||
break;
|
||||
}
|
||||
r = r.substring(0, index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getEquivalentForRegionAndVariant(String range) {
|
||||
int extensionKeyIndex = getExtentionKeyIndex(range);
|
||||
|
||||
for (String subtag : LocaleEquivalentMaps.regionVariantEquivMap.keySet()) {
|
||||
int index;
|
||||
if ((index = range.indexOf(subtag)) != -1) {
|
||||
// Check if the matching text is a valid region or variant.
|
||||
if (extensionKeyIndex != Integer.MIN_VALUE
|
||||
&& index > extensionKeyIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = index + subtag.length();
|
||||
if (range.length() == len || range.charAt(len) == '-') {
|
||||
return range.replaceFirst(subtag, LocaleEquivalentMaps.regionVariantEquivMap.get(subtag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getExtentionKeyIndex(String s) {
|
||||
char[] c = s.toCharArray();
|
||||
int index = Integer.MIN_VALUE;
|
||||
for (int i = 1; i < c.length; i++) {
|
||||
if (c[i] == '-') {
|
||||
if (i - index == 2) {
|
||||
return index;
|
||||
} else {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static List<LanguageRange> mapEquivalents(
|
||||
List<LanguageRange>priorityList,
|
||||
Map<String, List<String>> map) {
|
||||
if (priorityList.isEmpty()) {
|
||||
return new ArrayList<>(); // need to return a empty mutable List
|
||||
}
|
||||
if (map == null || map.isEmpty()) {
|
||||
return new ArrayList<LanguageRange>(priorityList);
|
||||
}
|
||||
|
||||
// Create a map, key=originalKey.toLowerCaes(), value=originalKey
|
||||
Map<String, String> keyMap = new HashMap<>();
|
||||
for (String key : map.keySet()) {
|
||||
keyMap.put(key.toLowerCase(), key);
|
||||
}
|
||||
|
||||
List<LanguageRange> list = new ArrayList<>();
|
||||
for (LanguageRange lr : priorityList) {
|
||||
String range = lr.getRange();
|
||||
String r = range;
|
||||
boolean hasEquivalent = false;
|
||||
|
||||
while (r.length() > 0) {
|
||||
if (keyMap.containsKey(r)) {
|
||||
hasEquivalent = true;
|
||||
List<String> equivalents = map.get(keyMap.get(r));
|
||||
if (equivalents != null) {
|
||||
int len = r.length();
|
||||
for (String equivalent : equivalents) {
|
||||
list.add(new LanguageRange(equivalent.toLowerCase()
|
||||
+ range.substring(len),
|
||||
lr.getWeight()));
|
||||
}
|
||||
}
|
||||
// Return immediately if the first matching subtag is found.
|
||||
break;
|
||||
}
|
||||
|
||||
// Truncate the last subtag simply.
|
||||
int index = r.lastIndexOf('-');
|
||||
if (index == -1) {
|
||||
break;
|
||||
}
|
||||
r = r.substring(0, index);
|
||||
}
|
||||
|
||||
if (!hasEquivalent) {
|
||||
list.add(lr);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private LocaleMatcher() {}
|
||||
|
||||
}
|
||||
1006
jdk/test/java/util/Locale/Bug7069824.java
Normal file
1006
jdk/test/java/util/Locale/Bug7069824.java
Normal file
File diff suppressed because it is too large
Load Diff
251
jdk/test/java/util/Locale/tools/EquivMapsGenerator.java
Normal file
251
jdk/test/java/util/Locale/tools/EquivMapsGenerator.java
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
public class EquivMapsGenerator {
|
||||
|
||||
/*
|
||||
* IANA Language Subtag Registry file downloaded from
|
||||
* http://www.iana.org/assignments/language-subtag-registry
|
||||
*/
|
||||
private static final String DEFAULT_LSR_FILE =
|
||||
"language-subtag-registry.txt";
|
||||
|
||||
private static boolean verbose = false;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String fileLSR = DEFAULT_LSR_FILE;
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String s = args[i];
|
||||
if (s.equals("-lsr")) {
|
||||
fileLSR = args[++i];
|
||||
} else if (s.equals("-verbose")) {
|
||||
verbose = true;
|
||||
}
|
||||
}
|
||||
|
||||
readLSRfile(fileLSR);
|
||||
generateEquivalentMap();
|
||||
generateSourceCode();
|
||||
}
|
||||
|
||||
private static String LSRrevisionDate;
|
||||
private static Map<String, StringBuilder> initialLanguageMap =
|
||||
new TreeMap<>();
|
||||
private static Map<String, StringBuilder> initialRegionVariantMap =
|
||||
new TreeMap<>();
|
||||
|
||||
private static Map<String, String> sortedLanguageMap1 = new TreeMap<>();
|
||||
private static Map<String, String[]> sortedLanguageMap2 = new TreeMap<>();
|
||||
private static Map<String, String> sortedRegionVariantMap =
|
||||
new TreeMap<>();
|
||||
|
||||
private static void readLSRfile(String filename) throws Exception {
|
||||
String type = null;
|
||||
String tag = null;
|
||||
String preferred = null;
|
||||
int mappingNum = 0;
|
||||
|
||||
for (String line : Files.readAllLines(Paths.get(filename),
|
||||
Charset.forName("UTF-8"))) {
|
||||
line = line.toLowerCase();
|
||||
int index = line.indexOf(' ')+1;
|
||||
if (line.startsWith("file-date:")) {
|
||||
LSRrevisionDate = line.substring(index);
|
||||
if (verbose) {
|
||||
System.out.println("LSR revision date=" + LSRrevisionDate);
|
||||
}
|
||||
} else if (line.startsWith("type:")) {
|
||||
type = line.substring(index);
|
||||
} else if (line.startsWith("tag:") || line.startsWith("subtag:")) {
|
||||
tag = line.substring(index);
|
||||
} else if (line.startsWith("preferred-value:")
|
||||
&& !type.equals("extlang")) {
|
||||
preferred = line.substring(index);
|
||||
mappingNum++;
|
||||
processDeprecatedData(type, tag, preferred);
|
||||
} else if (line.equals("%%")) {
|
||||
type = null;
|
||||
tag = null;
|
||||
preferred = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
System.out.println("readLSRfile(" + filename + ")");
|
||||
System.out.println(" Total number of mapping=" + mappingNum);
|
||||
System.out.println("\n Map for language. Size="
|
||||
+ initialLanguageMap.size());
|
||||
|
||||
for (String key : initialLanguageMap.keySet()) {
|
||||
System.out.println(" " + key + ": \""
|
||||
+ initialLanguageMap.get(key) + "\"");
|
||||
}
|
||||
|
||||
System.out.println("\n Map for region and variant. Size="
|
||||
+ initialRegionVariantMap.size());
|
||||
|
||||
for (String key : initialRegionVariantMap.keySet()) {
|
||||
System.out.println(" " + key + ": \""
|
||||
+ initialRegionVariantMap.get(key) + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processDeprecatedData(String type,
|
||||
String tag,
|
||||
String preferred) {
|
||||
StringBuilder sb;
|
||||
if (type.equals("region") || type.equals("variant")) {
|
||||
if (!initialRegionVariantMap.containsKey(preferred)) {
|
||||
sb = new StringBuilder("-");
|
||||
sb.append(preferred);
|
||||
sb.append(",-");
|
||||
sb.append(tag);
|
||||
initialRegionVariantMap.put("-"+preferred, sb);
|
||||
} else {
|
||||
throw new RuntimeException("New case, need implementation."
|
||||
+ " A region/variant subtag \"" + preferred
|
||||
+ "\" is registered for more than one subtags.");
|
||||
}
|
||||
} else { // language, grandfahered, and redundant
|
||||
if (!initialLanguageMap.containsKey(preferred)) {
|
||||
sb = new StringBuilder(preferred);
|
||||
sb.append(',');
|
||||
sb.append(tag);
|
||||
initialLanguageMap.put(preferred, sb);
|
||||
} else {
|
||||
sb = initialLanguageMap.get(preferred);
|
||||
sb.append(',');
|
||||
sb.append(tag);
|
||||
initialLanguageMap.put(preferred, sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateEquivalentMap() {
|
||||
String[] subtags;
|
||||
for (String preferred : initialLanguageMap.keySet()) {
|
||||
subtags = initialLanguageMap.get(preferred).toString().split(",");
|
||||
|
||||
if (subtags.length == 2) {
|
||||
sortedLanguageMap1.put(subtags[0], subtags[1]);
|
||||
sortedLanguageMap1.put(subtags[1], subtags[0]);
|
||||
} else if (subtags.length == 3) {
|
||||
sortedLanguageMap2.put(subtags[0],
|
||||
new String[]{subtags[1], subtags[2]});
|
||||
sortedLanguageMap2.put(subtags[1],
|
||||
new String[]{subtags[0], subtags[2]});
|
||||
sortedLanguageMap2.put(subtags[2],
|
||||
new String[]{subtags[0], subtags[1]});
|
||||
} else {
|
||||
throw new RuntimeException("New case, need implementation."
|
||||
+ " A language subtag \"" + preferred
|
||||
+ "\" is registered for more than two subtags. ");
|
||||
}
|
||||
}
|
||||
|
||||
for (String preferred : initialRegionVariantMap.keySet()) {
|
||||
subtags =
|
||||
initialRegionVariantMap.get(preferred).toString().split(",");
|
||||
|
||||
sortedRegionVariantMap.put(subtags[0], subtags[1]);
|
||||
sortedRegionVariantMap.put(subtags[1], subtags[0]);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
System.out.println("generateEquivalentMap()");
|
||||
System.out.println(" \nSorted map for language subtags which have only one equivalent. Size="
|
||||
+ sortedLanguageMap1.size());
|
||||
for (String key : sortedLanguageMap1.keySet()) {
|
||||
System.out.println(" " + key + ": \""
|
||||
+ sortedLanguageMap1.get(key) + "\"");
|
||||
}
|
||||
|
||||
System.out.println("\n Sorted map for language subtags which have multiple equivalents. Size="
|
||||
+ sortedLanguageMap2.size());
|
||||
for (String key : sortedLanguageMap2.keySet()) {
|
||||
String[] s = sortedLanguageMap2.get(key);
|
||||
System.out.println(" " + key + ": \""
|
||||
+ s[0] + "\", \"" + s[1] + "\"");
|
||||
}
|
||||
|
||||
System.out.println("\n Sorted map for region and variant subtags. Size="
|
||||
+ sortedRegionVariantMap.size());
|
||||
for (String key : sortedRegionVariantMap.keySet()) {
|
||||
System.out.println(" " + key + ": \""
|
||||
+ sortedRegionVariantMap.get(key) + "\"");
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private final static String headerText =
|
||||
"final class LocaleEquivalentMaps {\n\n"
|
||||
+ " static final Map<String, String> singleEquivMap;\n"
|
||||
+ " static final Map<String, String[]> multiEquivsMap;\n"
|
||||
+ " static final Map<String, String> regionVariantEquivMap;\n\n"
|
||||
+ " static {\n"
|
||||
+ " singleEquivMap = new HashMap<>();\n"
|
||||
+ " multiEquivsMap = new HashMap<>();\n"
|
||||
+ " regionVariantEquivMap = new HashMap<>();\n\n"
|
||||
+ " // This is an auto-generated file and should not be manually edited.\n";
|
||||
|
||||
private final static String footerText =
|
||||
" }\n\n"
|
||||
+ "}";
|
||||
|
||||
private static void generateSourceCode() {
|
||||
System.out.println(headerText
|
||||
+ " // LSR Revision: " + LSRrevisionDate);
|
||||
|
||||
for (String key : sortedLanguageMap1.keySet()) {
|
||||
String value = sortedLanguageMap1.get(key);
|
||||
System.out.println(" singleEquivMap.put(\""
|
||||
+ key + "\", \"" + value + "\");");
|
||||
}
|
||||
System.out.println();
|
||||
for (String key : sortedLanguageMap2.keySet()) {
|
||||
String[] values = sortedLanguageMap2.get(key);
|
||||
System.out.println(" multiEquivsMap.put(\""
|
||||
+ key + "\", new String[] {\"" + values[0] + "\", \""
|
||||
+ values[1] + "\"});");
|
||||
}
|
||||
System.out.println();
|
||||
for (String key : sortedRegionVariantMap.keySet()) {
|
||||
String value = sortedRegionVariantMap.get(key);
|
||||
System.out.println(" regionVariantEquivMap.put(\""
|
||||
+ key + "\", \"" + value + "\");");
|
||||
}
|
||||
|
||||
System.out.println(footerText);
|
||||
}
|
||||
|
||||
}
|
||||
45975
jdk/test/java/util/Locale/tools/language-subtag-registry.txt
Normal file
45975
jdk/test/java/util/Locale/tools/language-subtag-registry.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user