/*
* Copyright ( c ) 2014 , 2021 , 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 8042360 8032842 8175539 8210443 8242010 8276302
* @ summary Verify implementation for Locale matching .
* @ run testng / othervm Bug7069824
*/
import java.util.*;
import java.util.Locale.*;
import static java.util.Locale.FilteringMode.*;
import static java.util.Locale.LanguageRange.*;
import static org.testng.Assert .*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class Bug7069824 {
@DataProvider(name = "LRConstructorData" )
Object[][] LRConstructorData() {
return new Object[][] {
// Range, Weight
{"elvish" , MAX_WEIGHT},
{"de-DE" , MAX_WEIGHT},
{"de-Latn-DE-1996" , MAX_WEIGHT},
{"zh-Hant-CN-x-private1-private2" , MAX_WEIGHT},
{"ar" , 0 .8 },
{"en-US" , 0 .5 },
{"sr-Latn-BA" , 0 },
{"ja" , 1 },
};
}
@DataProvider(name = "LRConstructorNPEData" )
Object[][] LRConstructorNPEData() {
return new Object[][] {
// Range, Weight
{null , MAX_WEIGHT},
{null , 0 .8 },
};
}
@DataProvider(name = "LRConstructorIAEData" )
Object[][] LRConstructorIAEData() {
return new Object[][] {
// Range, Weight
{"ja" , -0 .8 },
{"Elvish" , 3 .0 },
{"-ja" , MAX_WEIGHT},
{"ja--JP" , MAX_WEIGHT},
{"en-US-" , MAX_WEIGHT},
{"a4r" , MAX_WEIGHT},
{"ar*" , MAX_WEIGHT},
{"ar-*EG" , MAX_WEIGHT},
{"abcdefghijklmn" , MAX_WEIGHT},
{"ja-J=" , MAX_WEIGHT},
{"ja-opqrstuvwxyz" , MAX_WEIGHT},
{"zh_CN" , MAX_WEIGHT},
{"1996-de-Latn" , MAX_WEIGHT},
// Testcase for 8042360
{"en-Latn-1234567890" , MAX_WEIGHT},
};
}
@DataProvider(name = "LRParseData" )
Object[][] LRParseData() {
return new Object[][] {
// Ranges, Expected result
{"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=0.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" ,
List.of(new LanguageRange("fr-fx" , 1 .0 ),
new LanguageRange("fr-fr" , 1 .0 ),
new LanguageRange("ja" , 1 .0 ),
new LanguageRange("in-tpp" , 1 .0 ),
new LanguageRange("id-tpp" , 1 .0 ),
new LanguageRange("en-fr" , 0 .7 ),
new LanguageRange("en-fx" , 0 .7 ),
new LanguageRange("en-x-tp" , 0 .6 ),
new LanguageRange("de-de" , 0 .5 ),
new LanguageRange("de-dd" , 0 .5 ),
new LanguageRange("iw" , 0 .4 ),
new LanguageRange("he" , 0 .4 ),
new LanguageRange("in-tp" , 0 .2 ),
new LanguageRange("id-tl" , 0 .2 ),
new LanguageRange("id-tp" , 0 .2 ),
new LanguageRange("in-tl" , 0 .2 ),
new LanguageRange("fr-tp-x-foo" , 0 .1 ),
new LanguageRange("fr-tl-x-foo" , 0 .1 ))},
{"Accept-Language: hak-CN;q=0.8, no-bok-NO;q=0.9, no-nyn, cmn-CN;q=0.1" ,
List.of(new LanguageRange("no-nyn" , 1 .0 ),
new LanguageRange("nn" , 1 .0 ),
new LanguageRange("no-bok-no" , 0 .9 ),
new LanguageRange("nb-no" , 0 .9 ),
new LanguageRange("hak-CN" , 0 .8 ),
new LanguageRange("zh-hakka-CN" , 0 .8 ),
new LanguageRange("i-hak-CN" , 0 .8 ),
new LanguageRange("zh-hak-CN" , 0 .8 ),
new LanguageRange("cmn-CN" , 0 .1 ),
new LanguageRange("zh-guoyu-CN" , 0 .1 ),
new LanguageRange("zh-cmn-CN" , 0 .1 ))},
{"Accept-Language: rki;q=0.4, no-bok-NO;q=0.9, ccq;q=0.5" ,
List.of(new LanguageRange("no-bok-no" , 0 .9 ),
new LanguageRange("nb-no" , 0 .9 ),
new LanguageRange("rki" , 0 .4 ),
new LanguageRange("ybd" , 0 .4 ),
new LanguageRange("ccq" , 0 .4 ))},
};
}
@DataProvider(name = "LRParseIAEData" )
Object[][] LRParseIAEData() {
return new Object[][] {
// Ranges
{"" },
{"ja;q=3" },
};
}
@DataProvider(name = "LRMapEquivalentsData" )
Object[][] LRMapEquivalentsData() {
return new Object[][] {
// Ranges, Map, Expected result
{LanguageRange.parse("zh, zh-TW;q=0.8, ar;q=0.9, EN, zh-HK, ja-JP;q=0.2, es;q=0.4" ),
new HashMap<>(),
LanguageRange.parse("zh, zh-TW;q=0.8, ar;q=0.9, EN, zh-HK, ja-JP;q=0.2, es;q=0.4" )},
{LanguageRange.parse("zh, zh-TW;q=0.8, ar;q=0.9, EN, zh-HK, ja-JP;q=0.2, es;q=0.4" ),
null ,
LanguageRange.parse("zh, zh-TW;q=0.8, ar;q=0.9, EN, zh-HK, ja-JP;q=0.2, es;q=0.4" )},
{LanguageRange.parse("zh, zh-TW;q=0.8, ar;q=0.9, EN, zh-HK, ja-JP;q=0.2, es;q=0.4" ),
new LinkedHashMap<String, List<String>>() {
{
put("ja" , List.of("ja" , "ja-Hira" ));
put("zh" , List.of("zh-Hans" , "zh-Hans-CN" , "zh-CN" ));
put("zh-TW" , List.of("zh-TW" , "zh-Hant" ));
put("es" , null );
put("en" , List.of());
put("zh-HK" , List.of("de" ));
}
},
List.of(new LanguageRange("zh-hans" , 1 .0 ),
new LanguageRange("zh-hans-cn" , 1 .0 ),
new LanguageRange("zh-cn" , 1 .0 ),
new LanguageRange("de" , 1 .0 ),
new LanguageRange("ar" , 0 .9 ),
new LanguageRange("zh-tw" , 0 .8 ),
new LanguageRange("zh-hant" , 0 .8 ),
new LanguageRange("ja-jp" , 0 .2 ),
new LanguageRange("ja-hira-jp" , 0 .2 ))},
};
}
@DataProvider(name = "LFilterData" )
Object[][] LFilterData() {
return new Object[][] {
// Range, LanguageTags, FilteringMode, Expected locales
{"ja-JP, fr-FR" , "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" ,
EXTENDED_FILTERING, "ja-JP-hepburn, ja-Latn-JP" },
{"ja-*-JP, fr-FR" , "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" ,
EXTENDED_FILTERING, "ja-JP-hepburn, ja-Latn-JP" },
{"ja-*-JP, fr-FR, de-de;q=0.2" , "de-DE, en, ja-JP-hepburn, de-de, fr, he, ja-Latn-JP" ,
AUTOSELECT_FILTERING, "ja-JP-hepburn, ja-Latn-JP, de-DE" },
{"ja-JP, fr-FR, de-de;q=0.2" , "de-DE, en, ja-JP-hepburn, de-de, fr, he, ja-Latn-JP" ,
AUTOSELECT_FILTERING, "ja-JP-hepburn, de-DE" },
{"en;q=0.2, ja-*-JP, fr-JP" , "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" ,
IGNORE_EXTENDED_RANGES, "en" },
{"en;q=0.2, ja-*-JP, fr-JP" , "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" ,
MAP_EXTENDED_RANGES, "ja-JP-hepburn, en" },
{"en;q=0.2, ja-JP, fr-JP" , "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" ,
REJECT_EXTENDED_RANGES, "ja-JP-hepburn, en" },
{"en;q=0.2, ja-*-JP, fr-JP" , "" , REJECT_EXTENDED_RANGES, "" },
};
}
@DataProvider(name = "LFilterNPEData" )
Object[][] LFilterNPEData() {
return new Object[][] {
// Range, LanguageTags, FilteringMode
{"en;q=0.2, ja-*-JP, fr-JP" , null , REJECT_EXTENDED_RANGES},
{null , "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" , REJECT_EXTENDED_RANGES},
};
}
@DataProvider(name = "LFilterTagsData" )
Object[][] LFilterTagsData() {
return new Object[][] {
// Range, LanguageTags, FilteringMode, Expected language tags
{"fr-FR, fr-BG;q=0.8, *;q=0.5, en;q=0" , "en-US, fr-FR, fr-CA, fr-BG" ,
null , "fr-FR, fr-BG, fr-CA" },
{"fr-FR, fr-*-BG;q=0.8, *;q=0.5, en;q=0" , "en-US, fr-FR, fr-CA, fr-BG" ,
null , "fr-FR, fr-BG, fr-CA" },
{"en;q=0.2, *;q=0.6, ja" , "de-DE, en, ja-JP-hepburn, fr-JP, he" ,
null , "ja-JP-hepburn, de-DE, en, fr-JP, he" },
{"en;q=0.2, ja-JP, fr-JP" , "de-DE, en, ja-JP-hepburn, fr, he" ,
null , "ja-JP-hepburn, en" },
{"en;q=0.2, ja-JP, fr-JP, iw" , "de-DE, he, en, ja-JP-hepburn, fr, he-IL" ,
null , "ja-JP-hepburn, he, he-IL, en" },
{"en;q=0.2, ja-JP, fr-JP, he" , "de-DE, en, ja-JP-hepburn, fr, iw-IL" ,
null , "ja-JP-hepburn, iw-IL, en" },
{"de-DE" , "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" ,
null , "de-DE, de-DE-x-goethe" },
{"de-*-DE" , "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" ,
null ,
"de-DE, de-Latn-DE, de-Latf-DE, de-DE-x-goethe, "
+ "de-Latn-DE-1996, de-Deva-DE" },
{"de-DE" , "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" ,
EXTENDED_FILTERING,
"de-DE, de-Latn-DE, de-Latf-DE, de-DE-x-goethe, "
+ "de-Latn-DE-1996, de-Deva-DE" },
{"de-*-DE" , "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" ,
EXTENDED_FILTERING,
"de-DE, de-Latn-DE, de-Latf-DE, de-DE-x-goethe, "
+ "de-Latn-DE-1996, de-Deva-DE" },
{"de-DE" , "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" ,
IGNORE_EXTENDED_RANGES,
"de-DE, de-DE-x-goethe" },
{"de-*-DE" , "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" ,
IGNORE_EXTENDED_RANGES,
"" },
{"de-DE" , "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" ,
MAP_EXTENDED_RANGES, "de-DE, de-DE-x-goethe" },
{"de-*-DE" , "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" ,
MAP_EXTENDED_RANGES, "de-DE, de-DE-x-goethe" },
{"de-DE" , "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" ,
REJECT_EXTENDED_RANGES, "de-DE, de-DE-x-goethe" },
// The next test in this chain is in testLFilterTagsIAE.
};
}
@DataProvider(name = "LLookupData" )
Object[][] LLookupData() {
return new Object[][] {
// Range, LanguageTags, Expected locale
{"en;q=0.2, *-JP;q=0.6, iw" , "de-DE, en, ja-JP-hepburn, fr-JP, he" , "he" },
{"en;q=0.2, *-JP;q=0.6, iw" , "de-DE, he-IL, en, iw" , "he" },
{"en;q=0.2, ja-*-JP-x-foo;q=0.6, iw" , "de-DE, fr, en, ja-Latn-JP" , "ja-Latn-JP" },
};
}
@DataProvider(name = "LLookupTagData" )
Object[][] LLookupTagData() {
return new Object[][] {
// Range, LanguageTags, Expected language tag
{"en, *" , "es, de, ja-JP" , null },
{"en;q=0.2, *-JP" , "de-DE, en, ja-JP-hepburn, fr-JP, en-JP" , "fr-JP" },
{"en;q=0.2, ar-MO, iw" , "de-DE, he, fr-JP" , "he" },
{"en;q=0.2, ar-MO, he" , "de-DE, iw, fr-JP" , "iw" },
{"de-DE-1996;q=0.8, en;q=0.2, iw;q=0.9, zh-Hans-CN;q=0.7" , "de-DE, zh-CN, he, iw, fr-JP" , "iw" },
{"de-DE-1996;q=0.8, en;q=0.2, he;q=0.9, zh-Hans-CN;q=0.7" , "de-DE, zh-CN, he, iw, fr-JP" , "he" },
};
}
@Test
public void testLRConstants() {
assertEquals(MIN_WEIGHT, 0 .0 , " MIN_WEIGHT should be 0.0 but got "
+ MIN_WEIGHT);
assertEquals(MAX_WEIGHT, 1 .0 , " MAX_WEIGHT should be 1.0 but got "
+ MAX_WEIGHT);
}
@Test(dataProvider = "LRConstructorData" )
public void testLRConstructors(String range, double weight) {
LanguageRange lr;
if (weight == MAX_WEIGHT) {
lr = new LanguageRange(range);
} else {
lr = new LanguageRange(range, weight);
}
assertEquals(lr.getRange(), range.toLowerCase(Locale.ROOT),
" LR.getRange() returned unexpected value. Expected: "
+ range.toLowerCase(Locale.ROOT) + ", got: " + lr.getRange());
assertEquals(lr.getWeight(), weight,
" LR.getWeight() returned unexpected value. Expected: "
+ weight + ", got: " + lr.getWeight());
}
@Test(dataProvider = "LRConstructorNPEData" , expectedExceptions = NullPointerException.class )
public void testLRConstructorNPE(String range, double weight) {
if (weight == MAX_WEIGHT) {
new LanguageRange(range);
} else {
new LanguageRange(range, weight);
}
}
@Test(dataProvider = "LRConstructorIAEData" , expectedExceptions = IllegalArgumentException.class )
public void testLRConstructorIAE(String range, double weight) {
if (weight == MAX_WEIGHT) {
new LanguageRange(range);
} else {
new LanguageRange(range, weight);
}
}
@Test
public void testLREquals() {
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 );
assertEquals(lr1, lr2, " LR(ja, 1.0).equals(LR(ja)) should return true." );
assertNotEquals(lr1, lr3, " LR(ja, 1.0).equals(LR(ja, 0.1)) should return false." );
assertNotEquals(lr1, lr4, " LR(ja, 1.0).equals(LR(en, 1.0)) should return false." );
assertNotNull(lr1, " LR(ja, 1.0).equals(null) should return false." );
assertNotEquals(lr1, "" , " LR(ja, 1.0).equals(\" \") should return false." );
}
@Test(dataProvider = "LRParseData" )
public void testLRParse(String ranges, List<LanguageRange> expected) {
assertEquals(LanguageRange.parse(ranges), expected,
" LR.parse(" + ranges + ") test failed." );
}
@Test(expectedExceptions = NullPointerException.class )
public void testLRParseNPE() {
LanguageRange.parse(null );
}
@Test(dataProvider = "LRParseIAEData" , expectedExceptions = IllegalArgumentException.class )
public void testLRParseIAE(String ranges) {
LanguageRange.parse(ranges);
}
@Test(dataProvider = "LRMapEquivalentsData" )
public void testLRMapEquivalents(List<Locale.LanguageRange> priorityList,
Map<String,List<String>> map, List<LanguageRange> expected) {
assertEquals(LanguageRange.mapEquivalents(priorityList, map), expected,
" LR.mapEquivalents() test failed." );
}
@Test(expectedExceptions = NullPointerException.class )
public void testLRMapEquivalentsNPE() {
LanguageRange.mapEquivalents(null , Map.of("ja" , List.of("ja" , "ja-Hira" )));
}
@Test(dataProvider = "LFilterData" )
public void testLFilter(String ranges, String tags, FilteringMode mode, String expectedLocales) {
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
List<Locale> tagList = generateLocales(tags);
String actualLocales =
showLocales(Locale.filter(priorityList, tagList, mode));
assertEquals(actualLocales, expectedLocales, showErrorMessage(" L.Filter(" + mode + ")" ,
ranges, tags, expectedLocales, actualLocales));
}
@Test(dataProvider = "LFilterNPEData" , expectedExceptions = NullPointerException.class )
public void testLFilterNPE(String ranges, String tags, FilteringMode mode) {
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
List<Locale> tagList = generateLocales(tags);
showLocales(Locale.filter(priorityList, tagList, mode));
}
@Test(expectedExceptions = IllegalArgumentException.class )
public void testLFilterIAE() {
String ranges = "en;q=0.2, ja-*-JP, fr-JP" ;
String tags = "de-DE, en, ja-JP-hepburn, fr, he, ja-Latn-JP" ;
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
List<Locale> tagList = generateLocales(tags);
showLocales(Locale.filter(priorityList, tagList, REJECT_EXTENDED_RANGES));
}
@Test(dataProvider = "LFilterTagsData" )
public void testLFilterTags(String ranges, String tags, FilteringMode mode, String expectedTags) {
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
List<String> tagList = generateLanguageTags(tags);
String actualTags;
if (mode == null ) {
actualTags = showLanguageTags(Locale.filterTags(priorityList, tagList));
} else {
actualTags = showLanguageTags(Locale.filterTags(priorityList, tagList, mode));
}
assertEquals(actualTags, expectedTags,
showErrorMessage(" L.FilterTags(" + (mode != null ? mode : "" ) + ")" ,
ranges, tags, expectedTags, actualTags));
}
@Test(expectedExceptions = IllegalArgumentException.class )
public void testLFilterTagsIAE() {
String ranges = "de-*-DE" ;
String 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" ;
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
showLanguageTags(Locale.filterTags(priorityList, generateLanguageTags(tags), REJECT_EXTENDED_RANGES));
}
@Test(dataProvider = "LLookupData" )
public void testLLookup(String ranges, String tags, String expectedLocale) {
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
List<Locale> localeList = generateLocales(tags);
String actualLocale =
Locale.lookup(priorityList, localeList).toLanguageTag();
assertEquals(actualLocale, expectedLocale, showErrorMessage(" L.Lookup()" ,
ranges, tags, expectedLocale, actualLocale));
}
@Test(dataProvider = "LLookupTagData" )
public void testLLookupTag(String ranges, String tags, String expectedTag) {
List<LanguageRange> priorityList = LanguageRange.parse(ranges);
List<String> tagList = generateLanguageTags(tags);
String actualTag = Locale.lookupTag(priorityList, tagList);
assertEquals(actualTag, expectedTag, showErrorMessage(" L.LookupTag()" ,
ranges, tags, expectedTag, actualTag));
}
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 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 String showErrorMessage(String methodName,
String priorityList,
String tags,
String expectedTags,
String actualTags) {
return "Incorrect " + methodName + " result."
+ " Priority list : " + priorityList
+ " Language tags : " + tags
+ " Expected value : " + expectedTags
+ " Actual value : " + actualTags;
}
}
Messung V0.5 in Prozent C=95 H=91 G=92
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland