|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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; |
|
|
|
|
|
|
|
|
|
*/ |
|
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<>(); |
|
} |
|
|
|
|
|
List<String> tags = new ArrayList<>(); |
|
for (Locale locale : locales) { |
|
tags.add(locale.toLanguageTag()); |
|
} |
|
|
|
|
|
List<String> filteredTags = filterTags(priorityList, tags, mode); |
|
|
|
|
|
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<>(); |
|
} |
|
|
|
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) { |
|
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 { |
|
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; |
|
} |
|
|
|
|
|
List<String> tags = new ArrayList<>(); |
|
for (Locale locale : locales) { |
|
tags.add(locale.toLanguageTag()); |
|
} |
|
|
|
|
|
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(); |
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
int index = rangeForRegex.lastIndexOf('-'); |
|
if (index >= 0) { |
|
rangeForRegex = rangeForRegex.substring(0, index); |
|
|
|
|
|
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); |
|
} |
|
|
|
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. |
|
|
|
|
|
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) { |
|
|
|
if (!tempList.contains(equiv)) { |
|
list.add(index+1, new LanguageRange(equiv, w)); |
|
numOfRanges++; |
|
tempList.add(equiv); |
|
} |
|
|
|
|
|
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 |
|
|
|
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; |
|
} |
|
|
|
|
|
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) { |
|
|
|
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<>(); |
|
} |
|
if (map == null || map.isEmpty()) { |
|
return new ArrayList<LanguageRange>(priorityList); |
|
} |
|
|
|
|
|
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())); |
|
} |
|
} |
|
|
|
break; |
|
} |
|
|
|
|
|
int index = r.lastIndexOf('-'); |
|
if (index == -1) { |
|
break; |
|
} |
|
r = r.substring(0, index); |
|
} |
|
|
|
if (!hasEquivalent) { |
|
list.add(lr); |
|
} |
|
} |
|
|
|
return list; |
|
} |
|
|
|
private LocaleMatcher() {} |
|
|
|
} |