< prev index next >
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java
Print this page
rev 15060 : imported patch 8159214
*** 22,38 ****
--- 22,40 ----
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.tools.jlink.internal.plugins;
+ import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IllformedLocaleException;
import java.util.Locale;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+ import static java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
*** 43,52 ****
--- 45,58 ----
import jdk.tools.jlink.plugin.LinkModule;
import jdk.tools.jlink.plugin.ModuleEntry;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ModulePool;
import jdk.tools.jlink.plugin.Plugin;
+ import sun.util.cldr.CLDRBaseLocaleDataMetaInfo;
+ import sun.util.locale.provider.LocaleProviderAdapter;
+ import sun.util.locale.provider.LocaleProviderAdapter.Type;
+ import sun.util.locale.provider.ResourceBundleBasedAdapter;
/**
* Plugin to explicitly specify the locale data included in jdk.localedata
* module. This plugin provides a jlink command line option "--include-locales"
* with an argument. The argument is a list of BCP 47 language tags separated
*** 93,102 ****
--- 99,144 ----
private String userParam;
private List<Locale.LanguageRange> priorityList;
private List<Locale> available;
private List<String> filtered;
+ private static final ResourceBundleBasedAdapter CLDR_ADAPTER =
+ (ResourceBundleBasedAdapter)LocaleProviderAdapter.forType(Type.CLDR);
+ private static final Map<Locale, String[]> CLDR_PARENT_LOCALES =
+ new CLDRBaseLocaleDataMetaInfo().parentLocales();
+
+ // Equivalent map
+ private static final Map<String, List<String>> EQUIV_MAP =
+ Stream.concat(
+ // COMPAT equivalence
+ Map.of(
+ "zh-Hans", List.of("zh-Hans", "zh-CN", "zh-SG"),
+ "zh-Hant", List.of("zh-Hant", "zh-HK", "zh-MO", "zh-TW"))
+ .entrySet()
+ .stream(),
+
+ // CLDR parent locales
+ CLDR_PARENT_LOCALES.entrySet().stream()
+ .map(entry -> {
+ String parent = entry.getKey().toLanguageTag();
+ List<String> children = new ArrayList<>();
+ children.add(parent);
+
+ Arrays.stream(entry.getValue())
+ .filter(child -> !child.isEmpty())
+ .flatMap(child ->
+ Stream.concat(
+ Arrays.stream(CLDR_PARENT_LOCALES.getOrDefault(
+ Locale.forLanguageTag(child), new String[0]))
+ .filter(grandchild -> !grandchild.isEmpty()),
+ List.of(child).stream()))
+ .distinct()
+ .forEach(children::add);
+ return new AbstractMap.SimpleEntry<String, List<String>>(parent, children);
+ })
+ ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
// Special COMPAT provider locales
private static final String jaJPJPTag = "ja-JP-JP";
private static final String noNONYTag = "no-NO-NY";
private static final String thTHTHTag = "th-TH-TH";
private static final Locale jaJPJP = new Locale("ja", "JP", "JP");
*** 150,169 ****
}
@Override
public void configure(Map<String, String> config) {
userParam = config.get(NAME);
! priorityList = Arrays.stream(userParam.split(","))
! .map(s -> {
try {
! return new Locale.LanguageRange(s);
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException(String.format(
! PluginsResourceBundle.getMessage(NAME + ".invalidtag"), s));
}
- })
- .collect(Collectors.toList());
}
@Override
public void previsit(ModulePool resources, StringTable strings) {
final Pattern p = Pattern.compile(".*((Data_)|(Names_))(?<tag>.*)\\.class");
--- 192,209 ----
}
@Override
public void configure(Map<String, String> config) {
userParam = config.get(NAME);
!
try {
! priorityList = Locale.LanguageRange.parse(userParam, EQUIV_MAP);
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException(String.format(
! PluginsResourceBundle.getMessage(NAME + ".invalidtag"),
! iae.getMessage().replaceFirst("^range=", "")));
}
}
@Override
public void previsit(ModulePool resources, StringTable strings) {
final Pattern p = Pattern.compile(".*((Data_)|(Names_))(?<tag>.*)\\.class");
*** 191,200 ****
--- 231,241 ----
.collect(Collectors.toList());
} else {
// jdk.localedata is not added.
throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".localedatanotfound"));
}
+
filtered = filterLocales(available);
if (filtered.isEmpty()) {
throw new PluginException(
String.format(PluginsResourceBundle.getMessage(NAME + ".nomatchinglocales"), userParam));
*** 203,262 ****
List<String> value = Stream.concat(
META_FILES.stream(),
filtered.stream().flatMap(s -> includeLocaleFilePatterns(s).stream()))
.map(s -> "regex:" + s)
.collect(Collectors.toList());
predicate = ResourceFilter.includeFilter(value);
}
private List<String> includeLocaleFilePatterns(String tag) {
! List<String> files = new ArrayList<>();
! String pTag = tag.replaceAll("-", "_");
! int lastDelimiter = tag.length();
! String isoSpecial = pTag.matches("^(he|yi|id).*") ?
! pTag.replaceFirst("he", "iw")
! .replaceFirst("yi", "ji")
! .replaceFirst("id", "in") : "";
!
! // Add tag patterns including parents
! while (true) {
! pTag = pTag.substring(0, lastDelimiter);
! files.addAll(includeLocaleFiles(pTag));
!
! if (!isoSpecial.isEmpty()) {
! isoSpecial = isoSpecial.substring(0, lastDelimiter);
! files.addAll(includeLocaleFiles(isoSpecial));
}
! lastDelimiter = pTag.lastIndexOf('_');
! if (lastDelimiter == -1) {
! break;
! }
! }
!
! final String lang = pTag;
!
! // Add possible special locales of the COMPAT provider
! Set.of(jaJPJPTag, noNONYTag, thTHTHTag).stream()
! .filter(stag -> lang.equals(stag.substring(0,2)))
! .map(t -> includeLocaleFiles(t.replaceAll("-", "_")))
! .forEach(files::addAll);
!
! // Add possible UN.M49 files (unconditional for now) for each language
! files.addAll(includeLocaleFiles(lang + "_[0-9]{3}"));
! if (!isoSpecial.isEmpty()) {
! files.addAll(includeLocaleFiles(isoSpecial + "_[0-9]{3}"));
! }
// Add Thai BreakIterator related data files
! if (lang.equals("th")) {
files.add(".+sun/text/resources/thai_dict");
files.add(".+sun/text/resources/[^_]+BreakIteratorData_th");
}
// Add Taiwan resource bundles for Hong Kong
! if (tag.startsWith("zh-HK")) {
files.addAll(includeLocaleFiles("zh_TW"));
}
return files;
}
--- 244,273 ----
List<String> value = Stream.concat(
META_FILES.stream(),
filtered.stream().flatMap(s -> includeLocaleFilePatterns(s).stream()))
.map(s -> "regex:" + s)
.collect(Collectors.toList());
+
predicate = ResourceFilter.includeFilter(value);
}
private List<String> includeLocaleFilePatterns(String tag) {
! // Ignore extension variations
! if (tag.matches(".+-[a-z]-.+")) {
! return List.of();
}
! List<String> files = new ArrayList<>(includeLocaleFiles(tag.replaceAll("-", "_")));
// Add Thai BreakIterator related data files
! if (tag.equals("th")) {
files.add(".+sun/text/resources/thai_dict");
files.add(".+sun/text/resources/[^_]+BreakIteratorData_th");
}
// Add Taiwan resource bundles for Hong Kong
! if (tag.equals("zh-HK")) {
files.addAll(includeLocaleFiles("zh_TW"));
}
return files;
}
*** 304,345 ****
}
byte[] filteredBytes = filterLocales(locales).stream()
.collect(Collectors.joining(" "))
.getBytes();
System.arraycopy(filteredBytes, 0, b, 0, filteredBytes.length);
Arrays.fill(b, filteredBytes.length, b.length, (byte)' ');
return true;
}
private List<String> filterLocales(List<Locale> locales) {
List<String> ret =
Locale.filter(priorityList, locales, Locale.FilteringMode.EXTENDED_FILTERING).stream()
.map(loc ->
// Locale.filter() does not preserve the case, which is
// significant for "variant" equality. Retrieve the original
// locales from the pre-filtered list.
locales.stream()
.filter(l -> l.toString().equalsIgnoreCase(loc.toString()))
.findAny()
! .orElse(Locale.ROOT)
! .toLanguageTag())
.collect(Collectors.toList());
- // no-NO-NY.toLanguageTag() returns "nn-NO", so specially handle it here
- if (ret.contains("no-NO")) {
- ret.add(noNONYTag);
- }
-
return ret;
}
private static final Locale.Builder LOCALE_BUILDER = new Locale.Builder();
private static Locale tagToLocale(String tag) {
// ISO3166 compatibility
tag = tag.replaceFirst("^iw", "he").replaceFirst("^ji", "yi").replaceFirst("^in", "id");
switch (tag) {
case jaJPJPTag:
return jaJPJP;
case noNONYTag:
return noNONY;
--- 315,362 ----
}
byte[] filteredBytes = filterLocales(locales).stream()
.collect(Collectors.joining(" "))
.getBytes();
+
+ if (filteredBytes.length > b.length) {
+ throw new InternalError("Size of filtered locales is bigger than the original one");
+ }
+
System.arraycopy(filteredBytes, 0, b, 0, filteredBytes.length);
Arrays.fill(b, filteredBytes.length, b.length, (byte)' ');
return true;
}
private List<String> filterLocales(List<Locale> locales) {
List<String> ret =
Locale.filter(priorityList, locales, Locale.FilteringMode.EXTENDED_FILTERING).stream()
+ .flatMap(loc -> Stream.concat(Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
+ .getCandidateLocales("", loc).stream(),
+ CLDR_ADAPTER.getCandidateLocales("", loc).stream()))
.map(loc ->
// Locale.filter() does not preserve the case, which is
// significant for "variant" equality. Retrieve the original
// locales from the pre-filtered list.
locales.stream()
.filter(l -> l.toString().equalsIgnoreCase(loc.toString()))
.findAny()
! .orElse(Locale.ROOT))
! .filter(loc -> !loc.equals(Locale.ROOT))
! .flatMap(IncludeLocalesPlugin::localeToTags)
! .distinct()
.collect(Collectors.toList());
return ret;
}
private static final Locale.Builder LOCALE_BUILDER = new Locale.Builder();
private static Locale tagToLocale(String tag) {
// ISO3166 compatibility
tag = tag.replaceFirst("^iw", "he").replaceFirst("^ji", "yi").replaceFirst("^in", "id");
+ // Special COMPAT provider locales
switch (tag) {
case jaJPJPTag:
return jaJPJP;
case noNONYTag:
return noNONY;
*** 349,354 ****
--- 366,409 ----
LOCALE_BUILDER.clear();
LOCALE_BUILDER.setLanguageTag(tag);
return LOCALE_BUILDER.build();
}
}
+
+ private static Stream<String> localeToTags(Locale loc) {
+ String tag = loc.toLanguageTag();
+ Stream<String> ret = null;
+
+ switch (loc.getLanguage()) {
+ // ISO3166 compatibility
+ case "iw":
+ ret = List.of(tag, tag.replaceFirst("^he", "iw")).stream();
+ break;
+ case "in":
+ ret = List.of(tag, tag.replaceFirst("^id", "in")).stream();
+ break;
+ case "ji":
+ ret = List.of(tag, tag.replaceFirst("^yi", "ji")).stream();
+ break;
+
+ // Special COMPAT provider locales
+ case "ja":
+ if (loc.getCountry() == "JP") {
+ ret = List.of(tag, jaJPJPTag).stream();
+ }
+ break;
+ case "no":
+ case "nn":
+ if (loc.getCountry() == "NO") {
+ ret = List.of(tag, noNONYTag).stream();
+ }
+ break;
+ case "th":
+ if (loc.getCountry() == "TH") {
+ ret = List.of(tag, thTHTHTag).stream();
+ }
+ break;
+ }
+
+ return ret == null ? List.of(tag).stream() : ret;
+ }
}
< prev index next >