Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
ab9e1ef1
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
ab9e1ef1
编写于
9月 28, 2012
作者:
P
peytoia
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
7069824: Support for BCP47 locale matching
Reviewed-by: naoto, okutsu
上级
05cd8405
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
48575 addition
and
4 deletion
+48575
-4
src/share/classes/java/util/Locale.java
src/share/classes/java/util/Locale.java
+684
-4
src/share/classes/sun/util/locale/LocaleEquivalentMaps.java
src/share/classes/sun/util/locale/LocaleEquivalentMaps.java
+204
-0
src/share/classes/sun/util/locale/LocaleMatcher.java
src/share/classes/sun/util/locale/LocaleMatcher.java
+455
-0
test/java/util/Locale/Bug7069824.java
test/java/util/Locale/Bug7069824.java
+1006
-0
test/java/util/Locale/tools/EquivMapsGenerator.java
test/java/util/Locale/tools/EquivMapsGenerator.java
+251
-0
test/java/util/Locale/tools/language-subtag-registry.txt
test/java/util/Locale/tools/language-subtag-registry.txt
+45975
-0
未找到文件。
src/share/classes/java/util/Locale.java
浏览文件 @
ab9e1ef1
...
...
@@ -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
);
}
}
src/share/classes/sun/util/locale/LocaleEquivalentMaps.java
0 → 100644
浏览文件 @
ab9e1ef1
/*
* 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"
);
}
}
src/share/classes/sun/util/locale/LocaleMatcher.java
0 → 100644
浏览文件 @
ab9e1ef1
/*
* 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
()
{}
}
test/java/util/Locale/Bug7069824.java
0 → 100644
浏览文件 @
ab9e1ef1
/*
* 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.
*
* 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.
*/
/*
* @test
* @bug 7069824
* @summary Verify implementation for Locale matching.
* @run main Bug7069824
*/
import
java.util.*
;
import
java.util.Locale.*
;
import
static
java
.
util
.
Locale
.
FilteringMode
.*;
import
static
java
.
util
.
Locale
.
LanguageRange
.*;
public
class
Bug7069824
{
static
boolean
err
=
false
;
public
static
void
main
(
String
[]
args
)
{
testLanguageRange
();
testLocale
();
if
(
err
)
{
throw
new
RuntimeException
(
"Failed."
);
}
}
private
static
void
testLanguageRange
()
{
System
.
out
.
println
(
"Test LanguageRange class..."
);
testConstants
();
testConstructors
();
testMethods
();
}
private
static
void
testLocale
()
{
System
.
out
.
println
(
"Test Locale class..."
);
test_filter
();
test_filterTags
();
test_lookup
();
test_lookupTag
();
}
private
static
void
testConstants
()
{
boolean
error
=
false
;
if
(
MIN_WEIGHT
!=
0.0
)
{
error
=
true
;
System
.
err
.
println
(
" MIN_WEIGHT should be 0.0 but got "
+
MIN_WEIGHT
);
}
if
(
MAX_WEIGHT
!=
1.0
)
{
error
=
true
;
System
.
err
.
println
(
" MAX_WEIGHT should be 1.0 but got "
+
MAX_WEIGHT
);
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" testConstants() failed."
);
}
else
{
System
.
out
.
println
(
" testConstants() passed."
);
}
}
private
static
void
testConstructors
()
{
boolean
error
=
false
;
LanguageRange
lr
;
String
range
;
double
weight
;
range
=
null
;
try
{
lr
=
new
LanguageRange
(
range
);
error
=
true
;
System
.
err
.
println
(
" NPE should be thrown for LanguageRange("
+
range
+
")."
);
}
catch
(
NullPointerException
ex
)
{
}
range
=
null
;
weight
=
0.8
;
try
{
lr
=
new
LanguageRange
(
range
,
weight
);
error
=
true
;
System
.
err
.
println
(
" NPE should be thrown for LanguageRange("
+
range
+
", "
+
weight
+
")."
);
}
catch
(
NullPointerException
ex
)
{
}
range
=
"elvish"
;
try
{
lr
=
new
LanguageRange
(
range
);
}
catch
(
Exception
ex
)
{
error
=
true
;
System
.
err
.
println
(
" "
+
ex
+
" should not be thrown for LanguageRange("
+
range
+
")."
);
}
range
=
"de-DE"
;
try
{
lr
=
new
LanguageRange
(
range
);
}
catch
(
Exception
ex
)
{
error
=
true
;
System
.
err
.
println
(
" "
+
ex
+
" should not be thrown for LanguageRange("
+
range
+
")."
);
}
range
=
"ar"
;
weight
=
0.8
;
try
{
lr
=
new
LanguageRange
(
range
,
weight
);
}
catch
(
Exception
ex
)
{
error
=
true
;
System
.
err
.
println
(
" "
+
ex
+
" should not be thrown for LanguageRange("
+
range
+
", "
+
weight
+
")."
);
}
range
=
"ja"
;
weight
=
-
0.8
;
try
{
lr
=
new
LanguageRange
(
range
,
weight
);
error
=
true
;
System
.
err
.
println
(
" IAE should be thrown for LanguageRange("
+
range
+
", "
+
weight
+
")."
);
}
catch
(
IllegalArgumentException
ex
)
{
}
range
=
"Elvish"
;
weight
=
3.0
;
try
{
lr
=
new
LanguageRange
(
range
,
weight
);
error
=
true
;
System
.
err
.
println
(
" IAE should be thrown for LanguageRange("
+
range
+
", "
+
weight
+
")."
);
}
catch
(
IllegalArgumentException
ex
)
{
}
String
[]
illformedRanges
=
{
"-ja"
,
"ja--JP"
,
"en-US-"
,
"a4r"
,
"ar*"
,
"ar-*EG"
,
""
,
"abcdefghijklmn"
,
"ja-J="
,
"ja-opqrstuvwxyz"
};
for
(
String
r
:
illformedRanges
)
{
try
{
lr
=
new
LanguageRange
(
r
);
error
=
true
;
System
.
err
.
println
(
" IAE should be thrown for LanguageRange("
+
r
+
")."
);
}
catch
(
IllegalArgumentException
ex
)
{
}
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" testConstructors() failed."
);
}
else
{
System
.
out
.
println
(
" testConstructors() passed."
);
}
}
private
static
void
testMethods
()
{
test_getRange
();
test_getWeight
();
test_equals
();
test_parse
();
test_mapEquivalents
();
}
private
static
void
test_getRange
()
{
boolean
error
=
false
;
String
range
=
"ja"
;
double
weight
=
0.5
;
LanguageRange
lr
=
new
LanguageRange
(
range
,
weight
);
if
(!
lr
.
getRange
().
equals
(
range
))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange.getRange() returned unexpected value. Expected: "
+
range
+
", got: "
+
lr
.
getRange
());
}
range
=
"en-US"
;
weight
=
0.5
;
lr
=
new
LanguageRange
(
range
,
weight
);
if
(!
lr
.
getRange
().
equals
(
range
.
toLowerCase
()))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange.getRange() returned unexpected value. Expected: "
+
range
+
", got: "
+
lr
.
getRange
());
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" test_getRange() failed."
);
}
else
{
System
.
out
.
println
(
" test_getRange() passed."
);
}
}
private
static
void
test_getWeight
()
{
boolean
error
=
false
;
String
range
=
"ja"
;
double
weight
=
0.5
;
LanguageRange
lr
=
new
LanguageRange
(
range
,
weight
);
if
(
lr
.
getWeight
()
!=
weight
)
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange.getWeight() returned unexpected value. Expected: "
+
weight
+
", got: "
+
lr
.
getWeight
());
}
range
=
"ja"
;
weight
=
MAX_WEIGHT
;
// default
lr
=
new
LanguageRange
(
range
);
if
(!
lr
.
getRange
().
equals
(
range
)
||
lr
.
getWeight
()
!=
MAX_WEIGHT
)
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange.getWeight() returned unexpected value. Expected: "
+
weight
+
", got: "
+
lr
.
getWeight
());
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" test_getWeight() failed."
);
}
else
{
System
.
out
.
println
(
" test_getWeight() passed."
);
}
}
private
static
void
test_equals
()
{
boolean
error
=
false
;
LanguageRange
lr1
=
new
LanguageRange
(
"ja"
,
1.0
);
LanguageRange
lr2
=
new
LanguageRange
(
"ja"
);
LanguageRange
lr3
=
new
LanguageRange
(
"ja"
,
0.1
);
LanguageRange
lr4
=
new
LanguageRange
(
"en"
,
1.0
);
if
(!
lr1
.
equals
(
lr2
))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange(LR(ja, 1.0)).equals(LR(ja)) should return true."
);
}
if
(
lr1
.
equals
(
lr3
))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange(LR(ja, 1.0)).equals(LR(ja, 0.1)) should return false."
);
}
if
(
lr1
.
equals
(
lr4
))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange(LR(ja, 1.0)).equals(LR(en, 1.0)) should return false."
);
}
if
(
lr1
.
equals
(
null
))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange(LR(ja, 1.0)).equals(null) should return false."
);
}
if
(
lr1
.
equals
(
""
))
{
error
=
true
;
System
.
err
.
println
(
" LanguageRange(LR(ja, 1.0)).equals(\"\") should return false."
);
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" test_equals() failed."
);
}
else
{
System
.
out
.
println
(
" test_equals() passed."
);
}
}
private
static
void
test_parse
()
{
boolean
error
=
false
;
List
<
LanguageRange
>
list
;
String
str
=
null
;
try
{
list
=
LanguageRange
.
parse
(
str
);
error
=
true
;
System
.
err
.
println
(
" NPE should be thrown for parse("
+
str
+
")."
);
}
catch
(
NullPointerException
ex
)
{
}
str
=
""
;
try
{
list
=
LanguageRange
.
parse
(
""
);
error
=
true
;
System
.
err
.
println
(
" IAE should be thrown for parse("
+
str
+
")."
);
}
catch
(
IllegalArgumentException
ex
)
{
}
str
=
"ja;q=3"
;
try
{
list
=
LanguageRange
.
parse
(
str
);
error
=
true
;
System
.
err
.
println
(
"IAE should be thrown for parse("
+
str
+
")."
);
}
catch
(
IllegalArgumentException
ex
)
{
}
str
=
"Accept-Language: fr-FX,de-DE;q=0.5, fr-tp-x-FOO;q=0.1,"
+
"en-X-tp;q=0.6,en-FR;q=.7,de-de;q=0.8, iw;q=0.4, "
+
"he;q=0.4, de-de;q=0.5,ja, in-tpp, in-tp;q=0.2"
;
ArrayList
<
LanguageRange
>
expected
=
new
ArrayList
<>();
expected
.
add
(
new
LanguageRange
(
"fr-fx"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"fr-fr"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"ja"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"in-tpp"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"id-tpp"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"en-fr"
,
0.7
));
expected
.
add
(
new
LanguageRange
(
"en-fx"
,
0.7
));
expected
.
add
(
new
LanguageRange
(
"en-x-tp"
,
0.6
));
expected
.
add
(
new
LanguageRange
(
"de-de"
,
0.5
));
expected
.
add
(
new
LanguageRange
(
"de-dd"
,
0.5
));
expected
.
add
(
new
LanguageRange
(
"iw"
,
0.4
));
expected
.
add
(
new
LanguageRange
(
"he"
,
0.4
));
expected
.
add
(
new
LanguageRange
(
"in-tp"
,
0.2
));
expected
.
add
(
new
LanguageRange
(
"id-tl"
,
0.2
));
expected
.
add
(
new
LanguageRange
(
"id-tp"
,
0.2
));
expected
.
add
(
new
LanguageRange
(
"in-tl"
,
0.2
));
expected
.
add
(
new
LanguageRange
(
"fr-tp-x-foo"
,
0.1
));
expected
.
add
(
new
LanguageRange
(
"fr-tl-x-foo"
,
0.1
));
List
<
LanguageRange
>
got
=
LanguageRange
.
parse
(
str
);
if
(!
areEqual
(
expected
,
got
))
{
error
=
true
;
System
.
err
.
println
(
" #1 parse() test failed."
);
}
str
=
"Accept-Language: hak-CN;q=0.8, no-bok-NO;q=0.9, no-nyn, cmn-CN;q=0.1"
;
expected
=
new
ArrayList
<>();
expected
.
add
(
new
LanguageRange
(
"no-nyn"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"nn"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"no-bok-no"
,
0.9
));
expected
.
add
(
new
LanguageRange
(
"nb-no"
,
0.9
));
expected
.
add
(
new
LanguageRange
(
"hak-CN"
,
0.8
));
expected
.
add
(
new
LanguageRange
(
"zh-hakka-CN"
,
0.8
));
expected
.
add
(
new
LanguageRange
(
"i-hak-CN"
,
0.8
));
expected
.
add
(
new
LanguageRange
(
"cmn-CN"
,
0.1
));
expected
.
add
(
new
LanguageRange
(
"zh-cmn-CN"
,
0.1
));
expected
.
add
(
new
LanguageRange
(
"zh-guoyu-CN"
,
0.1
));
got
=
LanguageRange
.
parse
(
str
);
if
(!
areEqual
(
expected
,
got
))
{
error
=
true
;
System
.
err
.
println
(
" #2 parse() test failed."
);
}
str
=
"Accept-Language: rki;q=0.4, no-bok-NO;q=0.9, ccq;q=0.5"
;
expected
=
new
ArrayList
<>();
expected
.
add
(
new
LanguageRange
(
"no-bok-no"
,
0.9
));
expected
.
add
(
new
LanguageRange
(
"nb-no"
,
0.9
));
expected
.
add
(
new
LanguageRange
(
"rki"
,
0.4
));
expected
.
add
(
new
LanguageRange
(
"ybd"
,
0.4
));
expected
.
add
(
new
LanguageRange
(
"ccq"
,
0.4
));
got
=
LanguageRange
.
parse
(
str
);
if
(!
areEqual
(
expected
,
got
))
{
error
=
true
;
System
.
err
.
println
(
" #3 parse() test failed."
);
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" test_parse() failed."
);
}
else
{
System
.
out
.
println
(
" test_parse() passed."
);
}
}
private
static
boolean
areEqual
(
List
<
LanguageRange
>
expected
,
List
<
LanguageRange
>
got
)
{
boolean
error
=
false
;
int
expectedSize
=
expected
.
size
();
int
actualSize
=
got
.
size
();
if
(
expectedSize
!=
actualSize
)
{
error
=
true
;
System
.
err
.
println
(
" Expected size="
+
expectedSize
);
for
(
LanguageRange
lr
:
expected
)
{
System
.
err
.
println
(
" range="
+
lr
.
getRange
()
+
", weight="
+
lr
.
getWeight
());
}
System
.
out
.
println
(
" Actual size="
+
actualSize
);
for
(
LanguageRange
lr
:
got
)
{
System
.
err
.
println
(
" range="
+
lr
.
getRange
()
+
", weight="
+
lr
.
getWeight
());
}
}
else
{
for
(
int
i
=
0
;
i
<
expectedSize
;
i
++)
{
LanguageRange
lr1
=
expected
.
get
(
i
);
LanguageRange
lr2
=
got
.
get
(
i
);
if
(!
lr1
.
getRange
().
equals
(
lr2
.
getRange
())
||
lr1
.
getWeight
()
!=
lr2
.
getWeight
())
{
error
=
true
;
System
.
err
.
println
(
" "
+
i
+
": Expected: range="
+
lr1
.
getRange
()
+
", weight="
+
lr1
.
getWeight
());
System
.
err
.
println
(
" "
+
i
+
": Actual: range="
+
lr2
.
getRange
()
+
", weight="
+
lr2
.
getWeight
());
}
}
}
return
!
error
;
}
private
static
void
test_mapEquivalents
()
{
boolean
error
=
false
;
String
ranges
=
"zh, zh-TW;q=0.8, ar;q=0.9, EN, zh-HK, ja-JP;q=0.2, es;q=0.4"
;
List
<
LanguageRange
>
priorityList
=
LanguageRange
.
parse
(
ranges
);
HashMap
<
String
,
List
<
String
>>
map
=
null
;
try
{
List
<
LanguageRange
>
list
=
LanguageRange
.
mapEquivalents
(
priorityList
,
null
);
}
catch
(
Exception
ex
)
{
error
=
true
;
System
.
err
.
println
(
ex
+
" should not be thrown for mapEquivalents(priorityList, null)."
);
}
map
=
new
HashMap
<>();
try
{
List
<
LanguageRange
>
list
=
LanguageRange
.
mapEquivalents
(
priorityList
,
map
);
}
catch
(
Exception
ex
)
{
error
=
true
;
System
.
err
.
println
(
ex
+
" should not be thrown for mapEquivalents(priorityList, empty map)."
);
}
ArrayList
<
String
>
equivalentList
=
new
ArrayList
<>();
equivalentList
.
add
(
"ja"
);
equivalentList
.
add
(
"ja-Hira"
);
map
.
put
(
"ja"
,
equivalentList
);
try
{
List
<
LanguageRange
>
list
=
LanguageRange
.
mapEquivalents
(
null
,
map
);
error
=
true
;
System
.
err
.
println
(
"NPE should be thrown for mapEquivalents(null, map)."
);
}
catch
(
NullPointerException
ex
)
{
}
map
=
new
LinkedHashMap
<>();
ArrayList
<
String
>
equivalentList1
=
new
ArrayList
<>();
equivalentList1
.
add
(
"ja"
);
equivalentList1
.
add
(
"ja-Hira"
);
map
.
put
(
"ja"
,
equivalentList1
);
ArrayList
<
String
>
equivalentList2
=
new
ArrayList
<>();
equivalentList2
.
add
(
"zh-Hans"
);
equivalentList2
.
add
(
"zh-Hans-CN"
);
equivalentList2
.
add
(
"zh-CN"
);
map
.
put
(
"zh"
,
equivalentList2
);
ArrayList
<
String
>
equivalentList3
=
new
ArrayList
<>();
equivalentList3
.
add
(
"zh-TW"
);
equivalentList3
.
add
(
"zh-Hant"
);
map
.
put
(
"zh-TW"
,
equivalentList3
);
map
.
put
(
"es"
,
null
);
ArrayList
<
String
>
equivalentList4
=
new
ArrayList
<>();
map
.
put
(
"en"
,
equivalentList4
);
ArrayList
<
String
>
equivalentList5
=
new
ArrayList
<>();
equivalentList5
.
add
(
"de"
);
map
.
put
(
"zh-HK"
,
equivalentList5
);
ArrayList
<
LanguageRange
>
expected
=
new
ArrayList
<>();
expected
.
add
(
new
LanguageRange
(
"zh-hans"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"zh-hans-cn"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"zh-cn"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"de"
,
1.0
));
expected
.
add
(
new
LanguageRange
(
"ar"
,
0.9
));
expected
.
add
(
new
LanguageRange
(
"zh-tw"
,
0.8
));
expected
.
add
(
new
LanguageRange
(
"zh-hant"
,
0.8
));
expected
.
add
(
new
LanguageRange
(
"ja-jp"
,
0.2
));
expected
.
add
(
new
LanguageRange
(
"ja-hira-jp"
,
0.2
));
List
<
LanguageRange
>
got
=
LanguageRange
.
mapEquivalents
(
priorityList
,
map
);
if
(!
areEqual
(
expected
,
got
))
{
error
=
true
;
}
if
(
error
)
{
err
=
true
;
System
.
err
.
println
(
" test_mapEquivalents() failed."
);
}
else
{
System
.
out
.
println
(
" test_mapEquivalents() passed."
);
}
}
private
static
void
test_filter
()
{
boolean
error
=
false
;
String
ranges
=
"ja-JP, fr-FR"
;
String
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
FilteringMode
mode
=
EXTENDED_FILTERING
;
List
<
LanguageRange
>
priorityList
=
LanguageRange
.
parse
(
ranges
);
List
<
Locale
>
tagList
=
generateLocales
(
tags
);
String
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
String
expectedLocales
=
"ja-JP-hepburn, ja-Latn-JP"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#1 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"ja-*-JP, fr-FR"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
mode
=
EXTENDED_FILTERING
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
expectedLocales
=
"ja-JP-hepburn, ja-Latn-JP"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#2 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"ja-*-JP, fr-FR, de-de;q=0.2"
;
tags
=
"de-DE, en, ja-JP-hepburn, de-de, fr, he, ja-Latn-JP"
;
mode
=
AUTOSELECT_FILTERING
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
expectedLocales
=
"ja-JP-hepburn, ja-Latn-JP, de-DE"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#3 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"ja-JP, fr-FR, de-de;q=0.2"
;
tags
=
"de-DE, en, ja-JP-hepburn, de-de, fr, he, ja-Latn-JP"
;
mode
=
AUTOSELECT_FILTERING
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
expectedLocales
=
"ja-JP-hepburn, de-DE"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#4 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"en;q=0.2, ja-*-JP, fr-JP"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
mode
=
IGNORE_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
expectedLocales
=
"en"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#5 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"en;q=0.2, ja-*-JP, fr-JP"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
mode
=
MAP_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
expectedLocales
=
"ja-JP-hepburn, en"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#6 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"en;q=0.2, ja-JP, fr-JP"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
mode
=
REJECT_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
expectedLocales
=
"ja-JP-hepburn, en"
;
if
(!
expectedLocales
.
equals
(
actualLocales
))
{
error
=
true
;
showErrorMessage
(
"#7 filter("
+
mode
+
")"
,
ranges
,
tags
,
expectedLocales
,
actualLocales
);
}
ranges
=
"en;q=0.2, ja-*-JP, fr-JP"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
mode
=
REJECT_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
try
{
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
error
=
true
;
System
.
out
.
println
(
"IAE should be thrown for filter("
+
mode
+
")."
);
}
catch
(
IllegalArgumentException
ex
)
{
}
ranges
=
"en;q=0.2, ja-*-JP, fr-JP"
;
tags
=
null
;
mode
=
REJECT_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
try
{
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
error
=
true
;
System
.
out
.
println
(
"NPE should be thrown for filter(tags=null)."
);
}
catch
(
NullPointerException
ex
)
{
}
ranges
=
null
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP"
;
mode
=
REJECT_EXTENDED_RANGES
;
try
{
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
error
=
true
;
System
.
out
.
println
(
"NPE should be thrown for filter(ranges=null)."
);
}
catch
(
NullPointerException
ex
)
{
}
ranges
=
"en;q=0.2, ja-*-JP, fr-JP"
;
tags
=
""
;
mode
=
REJECT_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLocales
(
tags
);
try
{
actualLocales
=
showLocales
(
Locale
.
filter
(
priorityList
,
tagList
,
mode
));
}
catch
(
Exception
ex
)
{
error
=
true
;
System
.
out
.
println
(
ex
+
" should not be thrown for filter("
+
ranges
+
", \"\")."
);
}
if
(
error
)
{
err
=
true
;
System
.
out
.
println
(
" test_filter() failed."
);
}
else
{
System
.
out
.
println
(
" test_filter() passed."
);
}
}
private
static
void
test_filterTags
()
{
boolean
error
=
false
;
String
ranges
=
"en;q=0.2, *;q=0.6, ja"
;
String
tags
=
"de-DE, en, ja-JP-hepburn, fr-JP, he"
;
List
<
LanguageRange
>
priorityList
=
LanguageRange
.
parse
(
ranges
);
List
<
String
>
tagList
=
generateLanguageTags
(
tags
);
String
actualTags
=
showLanguageTags
(
Locale
.
filterTags
(
priorityList
,
tagList
));
String
expectedTags
=
tags
;
if
(!
expectedTags
.
equals
(
actualTags
))
{
error
=
true
;
showErrorMessage
(
"#1 filterTags()"
,
ranges
,
tags
,
expectedTags
,
actualTags
);
}
ranges
=
"en;q=0.2, ja-JP, fr-JP"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr, he"
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTags
=
showLanguageTags
(
Locale
.
filterTags
(
priorityList
,
tagList
));
expectedTags
=
"ja-jp-hepburn, en"
;
if
(!
expectedTags
.
equals
(
actualTags
))
{
error
=
true
;
showErrorMessage
(
"#2 filterTags()"
,
ranges
,
tags
,
expectedTags
,
actualTags
);
}
ranges
=
"de-DE"
;
tags
=
"de-DE, de-de, de-Latn-DE, de-Latf-DE, de-DE-x-goethe, "
+
"de-Latn-DE-1996, de-Deva-DE, de, de-x-DE, de-Deva"
;
FilteringMode
mode
=
MAP_EXTENDED_RANGES
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTags
=
showLanguageTags
(
Locale
.
filterTags
(
priorityList
,
tagList
,
mode
));
expectedTags
=
"de-de, de-de-x-goethe"
;
if
(!
expectedTags
.
equals
(
actualTags
))
{
error
=
true
;
showErrorMessage
(
"#3 filterTags("
+
mode
+
")"
,
ranges
,
tags
,
expectedTags
,
actualTags
);
}
ranges
=
"de-DE"
;
tags
=
"de-DE, de-de, de-Latn-DE, de-Latf-DE, de-DE-x-goethe, "
+
"de-Latn-DE-1996, de-Deva-DE, de, de-x-DE, de-Deva"
;
mode
=
EXTENDED_FILTERING
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTags
=
showLanguageTags
(
Locale
.
filterTags
(
priorityList
,
tagList
,
mode
));
expectedTags
=
"de-de, de-latn-de, de-latf-de, de-de-x-goethe, "
+
"de-latn-de-1996, de-deva-de"
;
if
(!
expectedTags
.
equals
(
actualTags
))
{
error
=
true
;
showErrorMessage
(
"#4 filterTags("
+
mode
+
")"
,
ranges
,
tags
,
expectedTags
,
actualTags
);
}
ranges
=
"de-*-DE"
;
tags
=
"de-DE, de-de, de-Latn-DE, de-Latf-DE, de-DE-x-goethe, "
+
"de-Latn-DE-1996, de-Deva-DE, de, de-x-DE, de-Deva"
;
mode
=
EXTENDED_FILTERING
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTags
=
showLanguageTags
(
Locale
.
filterTags
(
priorityList
,
tagList
,
mode
));
expectedTags
=
"de-de, de-latn-de, de-latf-de, de-de-x-goethe, "
+
"de-latn-de-1996, de-deva-de"
;
if
(!
expectedTags
.
equals
(
actualTags
))
{
error
=
true
;
showErrorMessage
(
"#5 filterTags("
+
mode
+
")"
,
ranges
,
tags
,
expectedTags
,
actualTags
);
}
if
(
error
)
{
err
=
true
;
System
.
out
.
println
(
" test_filterTags() failed."
);
}
else
{
System
.
out
.
println
(
" test_filterTags() passed."
);
}
}
private
static
void
test_lookup
()
{
boolean
error
=
false
;
String
ranges
=
"en;q=0.2, *-JP;q=0.6, iw"
;
String
tags
=
"de-DE, en, ja-JP-hepburn, fr-JP, he"
;
List
<
LanguageRange
>
priorityList
=
LanguageRange
.
parse
(
ranges
);
List
<
Locale
>
localeList
=
generateLocales
(
tags
);
String
actualLocale
=
Locale
.
lookup
(
priorityList
,
localeList
).
toLanguageTag
();
String
expectedLocale
=
"he"
;
if
(!
expectedLocale
.
equals
(
actualLocale
))
{
error
=
true
;
showErrorMessage
(
"#1 lookup()"
,
ranges
,
tags
,
expectedLocale
,
actualLocale
);
}
ranges
=
"en;q=0.2, *-JP;q=0.6, iw"
;
tags
=
"de-DE, he-IL, en, iw"
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
localeList
=
generateLocales
(
tags
);
actualLocale
=
Locale
.
lookup
(
priorityList
,
localeList
).
toLanguageTag
();
expectedLocale
=
"he"
;
if
(!
expectedLocale
.
equals
(
actualLocale
))
{
error
=
true
;
showErrorMessage
(
"#2 lookup()"
,
ranges
,
tags
,
expectedLocale
,
actualLocale
);
}
ranges
=
"en;q=0.2, ja-*-JP-x-foo;q=0.6, iw"
;
tags
=
"de-DE, fr, en, ja-Latn-JP"
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
localeList
=
generateLocales
(
tags
);
actualLocale
=
Locale
.
lookup
(
priorityList
,
localeList
).
toLanguageTag
();
expectedLocale
=
"ja-Latn-JP"
;
if
(!
expectedLocale
.
equals
(
actualLocale
))
{
error
=
true
;
showErrorMessage
(
"#3 lookup()"
,
ranges
,
tags
,
expectedLocale
,
actualLocale
);
}
if
(
error
)
{
err
=
true
;
System
.
out
.
println
(
" test_lookup() failed."
);
}
else
{
System
.
out
.
println
(
" test_lookup() passed."
);
}
}
private
static
void
test_lookupTag
()
{
boolean
error
=
false
;
String
ranges
=
"en, *"
;
String
tags
=
"es, de, ja-JP"
;
List
<
LanguageRange
>
priorityList
=
LanguageRange
.
parse
(
ranges
);
List
<
String
>
tagList
=
generateLanguageTags
(
tags
);
String
actualTag
=
Locale
.
lookupTag
(
priorityList
,
tagList
);
String
expectedTag
=
null
;
if
(
actualTag
!=
null
)
{
error
=
true
;
showErrorMessage
(
"#1 lookupTag()"
,
ranges
,
tags
,
expectedTag
,
actualTag
);
}
ranges
=
"en;q=0.2, *-JP"
;
tags
=
"de-DE, en, ja-JP-hepburn, fr-JP, en-JP"
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTag
=
Locale
.
lookupTag
(
priorityList
,
tagList
);
expectedTag
=
"fr-jp"
;
if
(!
expectedTag
.
equals
(
actualTag
))
{
error
=
true
;
showErrorMessage
(
"#2 lookupTag()"
,
ranges
,
tags
,
expectedTag
,
actualTag
);
}
ranges
=
"en;q=0.2, ar-MO, iw"
;
tags
=
"de-DE, he, fr-JP"
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTag
=
Locale
.
lookupTag
(
priorityList
,
tagList
);
expectedTag
=
"he"
;
if
(!
expectedTag
.
equals
(
actualTag
))
{
error
=
true
;
showErrorMessage
(
"#3 lookupTag()"
,
ranges
,
tags
,
expectedTag
,
actualTag
);
}
ranges
=
"en;q=0.2, ar-MO, he"
;
tags
=
"de-DE, iw, fr-JP"
;
priorityList
=
LanguageRange
.
parse
(
ranges
);
tagList
=
generateLanguageTags
(
tags
);
actualTag
=
Locale
.
lookupTag
(
priorityList
,
tagList
);
expectedTag
=
"iw"
;
if
(!
expectedTag
.
equals
(
actualTag
))
{
error
=
true
;
showErrorMessage
(
"#4 lookupTag()"
,
ranges
,
tags
,
expectedTag
,
actualTag
);
}
if
(
error
)
{
err
=
true
;
System
.
out
.
println
(
" test_lookupTag() failed."
);
}
else
{
System
.
out
.
println
(
" test_lookupTag() passed."
);
}
}
private
static
List
<
Locale
>
generateLocales
(
String
tags
)
{
if
(
tags
==
null
)
{
return
null
;
}
List
<
Locale
>
localeList
=
new
ArrayList
<>();
if
(
tags
.
equals
(
""
))
{
return
localeList
;
}
String
[]
t
=
tags
.
split
(
", "
);
for
(
String
tag
:
t
)
{
localeList
.
add
(
Locale
.
forLanguageTag
(
tag
));
}
return
localeList
;
}
private
static
List
<
String
>
generateLanguageTags
(
String
tags
)
{
List
<
String
>
tagList
=
new
ArrayList
<>();
String
[]
t
=
tags
.
split
(
", "
);
for
(
String
tag
:
t
)
{
tagList
.
add
(
tag
);
}
return
tagList
;
}
private
static
String
showPriorityList
(
List
<
LanguageRange
>
priorityList
)
{
StringBuilder
sb
=
new
StringBuilder
();
Iterator
<
LanguageRange
>
itr
=
priorityList
.
iterator
();
LanguageRange
lr
;
if
(
itr
.
hasNext
())
{
lr
=
itr
.
next
();
sb
.
append
(
lr
.
getRange
());
sb
.
append
(
";q="
);
sb
.
append
(
lr
.
getWeight
());
}
while
(
itr
.
hasNext
())
{
sb
.
append
(
", "
);
lr
=
itr
.
next
();
sb
.
append
(
lr
.
getRange
());
sb
.
append
(
";q="
);
sb
.
append
(
lr
.
getWeight
());
}
return
sb
.
toString
();
}
private
static
String
showLanguageTags
(
List
<
String
>
tags
)
{
StringBuilder
sb
=
new
StringBuilder
();
Iterator
<
String
>
itr
=
tags
.
iterator
();
if
(
itr
.
hasNext
())
{
sb
.
append
(
itr
.
next
());
}
while
(
itr
.
hasNext
())
{
sb
.
append
(
", "
);
sb
.
append
(
itr
.
next
());
}
return
sb
.
toString
().
trim
();
}
private
static
String
showLocales
(
List
<
Locale
>
locales
)
{
StringBuilder
sb
=
new
StringBuilder
();
Iterator
<
Locale
>
itr
=
locales
.
iterator
();
if
(
itr
.
hasNext
())
{
sb
.
append
(
itr
.
next
().
toLanguageTag
());
}
while
(
itr
.
hasNext
())
{
sb
.
append
(
", "
);
sb
.
append
(
itr
.
next
().
toLanguageTag
());
}
return
sb
.
toString
().
trim
();
}
private
static
void
showErrorMessage
(
String
methodName
,
String
priorityList
,
String
tags
,
String
expectedTags
,
String
actualTags
)
{
System
.
out
.
println
(
"\nIncorrect "
+
methodName
+
" result."
);
System
.
out
.
println
(
" Priority list : "
+
priorityList
);
System
.
out
.
println
(
" Language tags : "
+
tags
);
System
.
out
.
println
(
" Expected value : "
+
expectedTags
);
System
.
out
.
println
(
" Actual value : "
+
actualTags
);
}
}
test/java/util/Locale/tools/EquivMapsGenerator.java
0 → 100644
浏览文件 @
ab9e1ef1
/*
* 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
);
}
}
test/java/util/Locale/tools/language-subtag-registry.txt
0 → 100644
浏览文件 @
ab9e1ef1
因为 它太大了无法显示 source diff 。你可以改为
查看blob
。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录