< 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 >