< prev index next >

src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java

Print this page
rev 15060 : imported patch 8159214


   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.tools.jlink.internal.plugins;
  26 

  27 import java.util.ArrayList;
  28 import java.util.Arrays;
  29 import java.util.IllformedLocaleException;
  30 import java.util.Locale;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.Optional;

  34 import java.util.Set;
  35 import java.util.function.Predicate;
  36 import java.util.regex.Pattern;
  37 import java.util.stream.Collectors;
  38 import java.util.stream.IntStream;
  39 import java.util.stream.Stream;
  40 import jdk.internal.org.objectweb.asm.ClassReader;
  41 import jdk.tools.jlink.internal.ResourcePrevisitor;
  42 import jdk.tools.jlink.internal.StringTable;
  43 import jdk.tools.jlink.plugin.LinkModule;
  44 import jdk.tools.jlink.plugin.ModuleEntry;
  45 import jdk.tools.jlink.plugin.PluginException;
  46 import jdk.tools.jlink.plugin.ModulePool;
  47 import jdk.tools.jlink.plugin.Plugin;




  48 
  49 /**
  50  * Plugin to explicitly specify the locale data included in jdk.localedata
  51  * module. This plugin provides a jlink command line option "--include-locales"
  52  * with an argument. The argument is a list of BCP 47 language tags separated
  53  * by a comma. E.g.,
  54  *
  55  *  "jlink --include-locales en,ja,*-IN"
  56  *
  57  * This option will include locale data for all available English and Japanese
  58  * languages, and ones for the country of India. All other locale data are
  59  * filtered out on the image creation.
  60  *
  61  * Here are a few assumptions:
  62  *
  63  *  0. All locale data in java.base are unconditionally included.
  64  *  1. All the selective locale data are in jdk.localedata module
  65  *  2. Their package names are constructed by appending ".ext" to
  66  *     the corresponding ones in java.base module.
  67  *  3. Available locales string in LocaleDataMetaInfo class should


  78         "sun.util.resources.cldr.ext",
  79         "sun.util.resources.cldr.provider",
  80         "sun.util.resources.ext",
  81         "sun.util.resources.provider");
  82     private static final String METAINFONAME = "LocaleDataMetaInfo";
  83     private static final List<String> META_FILES = List.of(
  84         ".+module-info.class",
  85         ".+LocaleDataProvider.class",
  86         ".+" + METAINFONAME + ".class");
  87     private static final List<String> INCLUDE_LOCALE_FILES = List.of(
  88         ".+sun/text/resources/ext/[^_]+_",
  89         ".+sun/util/resources/ext/[^_]+_",
  90         ".+sun/text/resources/cldr/ext/[^_]+_",
  91         ".+sun/util/resources/cldr/ext/[^_]+_");
  92     private Predicate<String> predicate;
  93     private String userParam;
  94     private List<Locale.LanguageRange> priorityList;
  95     private List<Locale> available;
  96     private List<String> filtered;
  97 




































  98     // Special COMPAT provider locales
  99     private static final String jaJPJPTag = "ja-JP-JP";
 100     private static final String noNONYTag = "no-NO-NY";
 101     private static final String thTHTHTag = "th-TH-TH";
 102     private static final Locale jaJPJP = new Locale("ja", "JP", "JP");
 103     private static final Locale noNONY = new Locale("no", "NO", "NY");
 104     private static final Locale thTHTH = new Locale("th", "TH", "TH");
 105 
 106     @Override
 107     public String getName() {
 108         return NAME;
 109     }
 110 
 111     @Override
 112     public void visit(ModulePool in, ModulePool out) {
 113         in.transformAndCopy((resource) -> {
 114             if (resource.getModule().equals(MODULENAME)) {
 115                 String path = resource.getPath();
 116                 resource = predicate.test(path) ? resource: null;
 117                 if (resource != null &&


 135     }
 136 
 137     @Override
 138     public String getDescription() {
 139         return PluginsResourceBundle.getDescription(NAME);
 140     }
 141 
 142     @Override
 143     public boolean hasArguments() {
 144         return true;
 145     }
 146 
 147     @Override
 148     public String getArgumentsDescription() {
 149        return PluginsResourceBundle.getArgument(NAME);
 150     }
 151 
 152     @Override
 153     public void configure(Map<String, String> config) {
 154         userParam = config.get(NAME);
 155         priorityList = Arrays.stream(userParam.split(","))
 156             .map(s -> {
 157                 try {
 158                     return new Locale.LanguageRange(s);
 159                 } catch (IllegalArgumentException iae) {
 160                     throw new IllegalArgumentException(String.format(
 161                         PluginsResourceBundle.getMessage(NAME + ".invalidtag"), s));

 162                 }
 163             })
 164             .collect(Collectors.toList());
 165     }
 166 
 167     @Override
 168     public void previsit(ModulePool resources, StringTable strings) {
 169         final Pattern p = Pattern.compile(".*((Data_)|(Names_))(?<tag>.*)\\.class");
 170         Optional<LinkModule> optMod = resources.findModule(MODULENAME);
 171 
 172         // jdk.localedata module validation
 173         if (optMod.isPresent()) {
 174             LinkModule module = optMod.get();
 175             Set<String> packages = module.getAllPackages();
 176             if (!packages.containsAll(LOCALEDATA_PACKAGES)) {
 177                 throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".missingpackages") +
 178                     LOCALEDATA_PACKAGES.stream()
 179                         .filter(pn -> !packages.contains(pn))
 180                         .collect(Collectors.joining(",\n\t")));
 181             }
 182 
 183             available = Stream.concat(module.entries()
 184                                         .map(md -> p.matcher(md.getPath()))
 185                                         .filter(m -> m.matches())
 186                                         .map(m -> m.group("tag").replaceAll("_", "-")),
 187                                     Stream.concat(Stream.of(jaJPJPTag), Stream.of(thTHTHTag)))
 188                 .distinct()
 189                 .sorted()
 190                 .map(IncludeLocalesPlugin::tagToLocale)
 191                 .collect(Collectors.toList());
 192         } else {
 193             // jdk.localedata is not added.
 194             throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".localedatanotfound"));
 195         }

 196         filtered = filterLocales(available);
 197 
 198         if (filtered.isEmpty()) {
 199             throw new PluginException(
 200                 String.format(PluginsResourceBundle.getMessage(NAME + ".nomatchinglocales"), userParam));
 201         }
 202 
 203         List<String> value = Stream.concat(
 204                 META_FILES.stream(),
 205                 filtered.stream().flatMap(s -> includeLocaleFilePatterns(s).stream()))
 206             .map(s -> "regex:" + s)
 207             .collect(Collectors.toList());

 208         predicate = ResourceFilter.includeFilter(value);
 209     }
 210 
 211     private List<String> includeLocaleFilePatterns(String tag) {
 212         List<String> files = new ArrayList<>();
 213         String pTag = tag.replaceAll("-", "_");
 214         int lastDelimiter = tag.length();
 215         String isoSpecial = pTag.matches("^(he|yi|id).*") ?
 216                             pTag.replaceFirst("he", "iw")
 217                                 .replaceFirst("yi", "ji")
 218                                 .replaceFirst("id", "in") : "";
 219 
 220         // Add tag patterns including parents
 221         while (true) {
 222             pTag = pTag.substring(0, lastDelimiter);
 223             files.addAll(includeLocaleFiles(pTag));
 224 
 225             if (!isoSpecial.isEmpty()) {
 226                 isoSpecial = isoSpecial.substring(0, lastDelimiter);
 227                 files.addAll(includeLocaleFiles(isoSpecial));
 228             }
 229 
 230             lastDelimiter = pTag.lastIndexOf('_');
 231             if (lastDelimiter == -1) {
 232                 break;
 233             }
 234         }
 235 
 236         final String lang = pTag;
 237 
 238         // Add possible special locales of the COMPAT provider
 239         Set.of(jaJPJPTag, noNONYTag, thTHTHTag).stream()
 240             .filter(stag -> lang.equals(stag.substring(0,2)))
 241             .map(t -> includeLocaleFiles(t.replaceAll("-", "_")))
 242             .forEach(files::addAll);
 243 
 244         // Add possible UN.M49 files (unconditional for now) for each language
 245         files.addAll(includeLocaleFiles(lang + "_[0-9]{3}"));
 246         if (!isoSpecial.isEmpty()) {
 247             files.addAll(includeLocaleFiles(isoSpecial + "_[0-9]{3}"));
 248         }
 249 
 250         // Add Thai BreakIterator related data files
 251         if (lang.equals("th")) {
 252             files.add(".+sun/text/resources/thai_dict");
 253             files.add(".+sun/text/resources/[^_]+BreakIteratorData_th");
 254         }
 255 
 256         // Add Taiwan resource bundles for Hong Kong
 257         if (tag.startsWith("zh-HK")) {
 258             files.addAll(includeLocaleFiles("zh_TW"));
 259         }
 260 
 261         return files;
 262     }
 263 
 264     private List<String> includeLocaleFiles(String localeStr) {
 265         return INCLUDE_LOCALE_FILES.stream()
 266             .map(s -> s + localeStr + ".class")
 267             .collect(Collectors.toList());
 268     }
 269 
 270     private boolean stripUnsupportedLocales(byte[] bytes, ClassReader cr) {
 271         char[] buf = new char[cr.getMaxStringLength()];
 272         boolean[] modified = new boolean[1];
 273 
 274         IntStream.range(1, cr.getItemCount())
 275             .map(item -> cr.getItem(item))
 276             .forEach(itemIndex -> {
 277                 if (bytes[itemIndex - 1] == 1 &&         // UTF-8


 289 
 290         return modified[0];
 291     }
 292 
 293     private boolean filterOutUnsupportedTags(byte[] b) {
 294         List<Locale> locales;
 295 
 296         try {
 297             locales = Arrays.asList(new String(b).split(" ")).stream()
 298                 .filter(tag -> !tag.isEmpty())
 299                 .map(IncludeLocalesPlugin::tagToLocale)
 300                 .collect(Collectors.toList());
 301         } catch (IllformedLocaleException ile) {
 302             // Seems not an available locales string literal.
 303             return false;
 304         }
 305 
 306         byte[] filteredBytes = filterLocales(locales).stream()
 307             .collect(Collectors.joining(" "))
 308             .getBytes();





 309         System.arraycopy(filteredBytes, 0, b, 0, filteredBytes.length);
 310         Arrays.fill(b, filteredBytes.length, b.length, (byte)' ');
 311         return true;
 312     }
 313 
 314     private List<String> filterLocales(List<Locale> locales) {
 315         List<String> ret =
 316             Locale.filter(priorityList, locales, Locale.FilteringMode.EXTENDED_FILTERING).stream()



 317                 .map(loc ->
 318                     // Locale.filter() does not preserve the case, which is
 319                     // significant for "variant" equality. Retrieve the original
 320                     // locales from the pre-filtered list.
 321                     locales.stream()
 322                         .filter(l -> l.toString().equalsIgnoreCase(loc.toString()))
 323                         .findAny()
 324                         .orElse(Locale.ROOT)
 325                         .toLanguageTag())


 326                 .collect(Collectors.toList());
 327 
 328         // no-NO-NY.toLanguageTag() returns "nn-NO", so specially handle it here
 329         if (ret.contains("no-NO")) {
 330             ret.add(noNONYTag);
 331         }
 332 
 333         return ret;
 334     }
 335 
 336     private static final Locale.Builder LOCALE_BUILDER = new Locale.Builder();
 337     private static Locale tagToLocale(String tag) {
 338         // ISO3166 compatibility
 339         tag = tag.replaceFirst("^iw", "he").replaceFirst("^ji", "yi").replaceFirst("^in", "id");
 340 

 341         switch (tag) {
 342             case jaJPJPTag:
 343                 return jaJPJP;
 344             case noNONYTag:
 345                 return noNONY;
 346             case thTHTHTag:
 347                 return thTHTH;
 348             default:
 349                 LOCALE_BUILDER.clear();
 350                 LOCALE_BUILDER.setLanguageTag(tag);
 351                 return LOCALE_BUILDER.build();
 352         }
 353     }






































 354 }


   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.tools.jlink.internal.plugins;
  26 
  27 import java.util.AbstractMap;
  28 import java.util.ArrayList;
  29 import java.util.Arrays;
  30 import java.util.IllformedLocaleException;
  31 import java.util.Locale;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Optional;
  35 import static java.util.ResourceBundle.Control;
  36 import java.util.Set;
  37 import java.util.function.Predicate;
  38 import java.util.regex.Pattern;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.IntStream;
  41 import java.util.stream.Stream;
  42 import jdk.internal.org.objectweb.asm.ClassReader;
  43 import jdk.tools.jlink.internal.ResourcePrevisitor;
  44 import jdk.tools.jlink.internal.StringTable;
  45 import jdk.tools.jlink.plugin.LinkModule;
  46 import jdk.tools.jlink.plugin.ModuleEntry;
  47 import jdk.tools.jlink.plugin.PluginException;
  48 import jdk.tools.jlink.plugin.ModulePool;
  49 import jdk.tools.jlink.plugin.Plugin;
  50 import sun.util.cldr.CLDRBaseLocaleDataMetaInfo;
  51 import sun.util.locale.provider.LocaleProviderAdapter;
  52 import sun.util.locale.provider.LocaleProviderAdapter.Type;
  53 import sun.util.locale.provider.ResourceBundleBasedAdapter;
  54 
  55 /**
  56  * Plugin to explicitly specify the locale data included in jdk.localedata
  57  * module. This plugin provides a jlink command line option "--include-locales"
  58  * with an argument. The argument is a list of BCP 47 language tags separated
  59  * by a comma. E.g.,
  60  *
  61  *  "jlink --include-locales en,ja,*-IN"
  62  *
  63  * This option will include locale data for all available English and Japanese
  64  * languages, and ones for the country of India. All other locale data are
  65  * filtered out on the image creation.
  66  *
  67  * Here are a few assumptions:
  68  *
  69  *  0. All locale data in java.base are unconditionally included.
  70  *  1. All the selective locale data are in jdk.localedata module
  71  *  2. Their package names are constructed by appending ".ext" to
  72  *     the corresponding ones in java.base module.
  73  *  3. Available locales string in LocaleDataMetaInfo class should


  84         "sun.util.resources.cldr.ext",
  85         "sun.util.resources.cldr.provider",
  86         "sun.util.resources.ext",
  87         "sun.util.resources.provider");
  88     private static final String METAINFONAME = "LocaleDataMetaInfo";
  89     private static final List<String> META_FILES = List.of(
  90         ".+module-info.class",
  91         ".+LocaleDataProvider.class",
  92         ".+" + METAINFONAME + ".class");
  93     private static final List<String> INCLUDE_LOCALE_FILES = List.of(
  94         ".+sun/text/resources/ext/[^_]+_",
  95         ".+sun/util/resources/ext/[^_]+_",
  96         ".+sun/text/resources/cldr/ext/[^_]+_",
  97         ".+sun/util/resources/cldr/ext/[^_]+_");
  98     private Predicate<String> predicate;
  99     private String userParam;
 100     private List<Locale.LanguageRange> priorityList;
 101     private List<Locale> available;
 102     private List<String> filtered;
 103 
 104     private static final ResourceBundleBasedAdapter CLDR_ADAPTER =
 105         (ResourceBundleBasedAdapter)LocaleProviderAdapter.forType(Type.CLDR);
 106     private static final Map<Locale, String[]> CLDR_PARENT_LOCALES =
 107         new CLDRBaseLocaleDataMetaInfo().parentLocales();
 108 
 109     // Equivalent map
 110     private static final Map<String, List<String>> EQUIV_MAP =
 111         Stream.concat(
 112             // COMPAT equivalence
 113             Map.of(
 114                 "zh-Hans", List.of("zh-Hans", "zh-CN", "zh-SG"),
 115                 "zh-Hant", List.of("zh-Hant", "zh-HK", "zh-MO", "zh-TW"))
 116                 .entrySet()
 117                 .stream(),
 118 
 119             // CLDR parent locales
 120             CLDR_PARENT_LOCALES.entrySet().stream()
 121                 .map(entry -> {
 122                     String parent = entry.getKey().toLanguageTag();
 123                     List<String> children = new ArrayList<>();
 124                     children.add(parent);
 125 
 126                     Arrays.stream(entry.getValue())
 127                         .filter(child -> !child.isEmpty())
 128                         .flatMap(child -> 
 129                             Stream.concat(
 130                                 Arrays.stream(CLDR_PARENT_LOCALES.getOrDefault(
 131                                     Locale.forLanguageTag(child), new String[0]))
 132                                         .filter(grandchild -> !grandchild.isEmpty()),
 133                                 List.of(child).stream()))
 134                         .distinct()
 135                         .forEach(children::add);
 136                     return new AbstractMap.SimpleEntry<String, List<String>>(parent, children);
 137                 })
 138         ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
 139 
 140     // Special COMPAT provider locales
 141     private static final String jaJPJPTag = "ja-JP-JP";
 142     private static final String noNONYTag = "no-NO-NY";
 143     private static final String thTHTHTag = "th-TH-TH";
 144     private static final Locale jaJPJP = new Locale("ja", "JP", "JP");
 145     private static final Locale noNONY = new Locale("no", "NO", "NY");
 146     private static final Locale thTHTH = new Locale("th", "TH", "TH");
 147 
 148     @Override
 149     public String getName() {
 150         return NAME;
 151     }
 152 
 153     @Override
 154     public void visit(ModulePool in, ModulePool out) {
 155         in.transformAndCopy((resource) -> {
 156             if (resource.getModule().equals(MODULENAME)) {
 157                 String path = resource.getPath();
 158                 resource = predicate.test(path) ? resource: null;
 159                 if (resource != null &&


 177     }
 178 
 179     @Override
 180     public String getDescription() {
 181         return PluginsResourceBundle.getDescription(NAME);
 182     }
 183 
 184     @Override
 185     public boolean hasArguments() {
 186         return true;
 187     }
 188 
 189     @Override
 190     public String getArgumentsDescription() {
 191        return PluginsResourceBundle.getArgument(NAME);
 192     }
 193 
 194     @Override
 195     public void configure(Map<String, String> config) {
 196         userParam = config.get(NAME);
 197 

 198         try {
 199             priorityList = Locale.LanguageRange.parse(userParam, EQUIV_MAP);
 200         } catch (IllegalArgumentException iae) {
 201             throw new IllegalArgumentException(String.format(
 202                 PluginsResourceBundle.getMessage(NAME + ".invalidtag"),
 203                     iae.getMessage().replaceFirst("^range=", "")));
 204         }


 205     }
 206 
 207     @Override
 208     public void previsit(ModulePool resources, StringTable strings) {
 209         final Pattern p = Pattern.compile(".*((Data_)|(Names_))(?<tag>.*)\\.class");
 210         Optional<LinkModule> optMod = resources.findModule(MODULENAME);
 211 
 212         // jdk.localedata module validation
 213         if (optMod.isPresent()) {
 214             LinkModule module = optMod.get();
 215             Set<String> packages = module.getAllPackages();
 216             if (!packages.containsAll(LOCALEDATA_PACKAGES)) {
 217                 throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".missingpackages") +
 218                     LOCALEDATA_PACKAGES.stream()
 219                         .filter(pn -> !packages.contains(pn))
 220                         .collect(Collectors.joining(",\n\t")));
 221             }
 222 
 223             available = Stream.concat(module.entries()
 224                                         .map(md -> p.matcher(md.getPath()))
 225                                         .filter(m -> m.matches())
 226                                         .map(m -> m.group("tag").replaceAll("_", "-")),
 227                                     Stream.concat(Stream.of(jaJPJPTag), Stream.of(thTHTHTag)))
 228                 .distinct()
 229                 .sorted()
 230                 .map(IncludeLocalesPlugin::tagToLocale)
 231                 .collect(Collectors.toList());
 232         } else {
 233             // jdk.localedata is not added.
 234             throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".localedatanotfound"));
 235         }
 236 
 237         filtered = filterLocales(available);
 238 
 239         if (filtered.isEmpty()) {
 240             throw new PluginException(
 241                 String.format(PluginsResourceBundle.getMessage(NAME + ".nomatchinglocales"), userParam));
 242         }
 243 
 244         List<String> value = Stream.concat(
 245                 META_FILES.stream(),
 246                 filtered.stream().flatMap(s -> includeLocaleFilePatterns(s).stream()))
 247             .map(s -> "regex:" + s)
 248             .collect(Collectors.toList());
 249 
 250         predicate = ResourceFilter.includeFilter(value);
 251     }
 252 
 253     private List<String> includeLocaleFilePatterns(String tag) {
 254         // Ignore extension variations
 255         if (tag.matches(".+-[a-z]-.+")) {
 256             return List.of();













 257         }
 258 
 259         List<String> files = new ArrayList<>(includeLocaleFiles(tag.replaceAll("-", "_")));


















 260 
 261         // Add Thai BreakIterator related data files
 262         if (tag.equals("th")) {
 263             files.add(".+sun/text/resources/thai_dict");
 264             files.add(".+sun/text/resources/[^_]+BreakIteratorData_th");
 265         }
 266 
 267         // Add Taiwan resource bundles for Hong Kong
 268         if (tag.equals("zh-HK")) {
 269             files.addAll(includeLocaleFiles("zh_TW"));
 270         }
 271 
 272         return files;
 273     }
 274 
 275     private List<String> includeLocaleFiles(String localeStr) {
 276         return INCLUDE_LOCALE_FILES.stream()
 277             .map(s -> s + localeStr + ".class")
 278             .collect(Collectors.toList());
 279     }
 280 
 281     private boolean stripUnsupportedLocales(byte[] bytes, ClassReader cr) {
 282         char[] buf = new char[cr.getMaxStringLength()];
 283         boolean[] modified = new boolean[1];
 284 
 285         IntStream.range(1, cr.getItemCount())
 286             .map(item -> cr.getItem(item))
 287             .forEach(itemIndex -> {
 288                 if (bytes[itemIndex - 1] == 1 &&         // UTF-8


 300 
 301         return modified[0];
 302     }
 303 
 304     private boolean filterOutUnsupportedTags(byte[] b) {
 305         List<Locale> locales;
 306 
 307         try {
 308             locales = Arrays.asList(new String(b).split(" ")).stream()
 309                 .filter(tag -> !tag.isEmpty())
 310                 .map(IncludeLocalesPlugin::tagToLocale)
 311                 .collect(Collectors.toList());
 312         } catch (IllformedLocaleException ile) {
 313             // Seems not an available locales string literal.
 314             return false;
 315         }
 316 
 317         byte[] filteredBytes = filterLocales(locales).stream()
 318             .collect(Collectors.joining(" "))
 319             .getBytes();
 320 
 321         if (filteredBytes.length > b.length) {
 322             throw new InternalError("Size of filtered locales is bigger than the original one");
 323         }
 324 
 325         System.arraycopy(filteredBytes, 0, b, 0, filteredBytes.length);
 326         Arrays.fill(b, filteredBytes.length, b.length, (byte)' ');
 327         return true;
 328     }
 329 
 330     private List<String> filterLocales(List<Locale> locales) {
 331         List<String> ret =
 332             Locale.filter(priorityList, locales, Locale.FilteringMode.EXTENDED_FILTERING).stream()
 333                 .flatMap(loc -> Stream.concat(Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
 334                                      .getCandidateLocales("", loc).stream(),
 335                                 CLDR_ADAPTER.getCandidateLocales("", loc).stream()))
 336                 .map(loc ->
 337                     // Locale.filter() does not preserve the case, which is
 338                     // significant for "variant" equality. Retrieve the original
 339                     // locales from the pre-filtered list.
 340                     locales.stream()
 341                         .filter(l -> l.toString().equalsIgnoreCase(loc.toString()))
 342                         .findAny()
 343                         .orElse(Locale.ROOT))
 344                 .filter(loc -> !loc.equals(Locale.ROOT))
 345                 .flatMap(IncludeLocalesPlugin::localeToTags)
 346                 .distinct()
 347                 .collect(Collectors.toList());
 348 





 349         return ret;
 350     }
 351 
 352     private static final Locale.Builder LOCALE_BUILDER = new Locale.Builder();
 353     private static Locale tagToLocale(String tag) {
 354         // ISO3166 compatibility
 355         tag = tag.replaceFirst("^iw", "he").replaceFirst("^ji", "yi").replaceFirst("^in", "id");
 356 
 357         // Special COMPAT provider locales
 358         switch (tag) {
 359             case jaJPJPTag:
 360                 return jaJPJP;
 361             case noNONYTag:
 362                 return noNONY;
 363             case thTHTHTag:
 364                 return thTHTH;
 365             default:
 366                 LOCALE_BUILDER.clear();
 367                 LOCALE_BUILDER.setLanguageTag(tag);
 368                 return LOCALE_BUILDER.build();
 369         }
 370     }
 371 
 372     private static Stream<String> localeToTags(Locale loc) {
 373         String tag = loc.toLanguageTag();
 374         Stream<String> ret = null;
 375         
 376         switch (loc.getLanguage()) {
 377             // ISO3166 compatibility
 378             case "iw":
 379                 ret = List.of(tag, tag.replaceFirst("^he", "iw")).stream();
 380                 break;
 381             case "in":
 382                 ret = List.of(tag, tag.replaceFirst("^id", "in")).stream();
 383                 break;
 384             case "ji":
 385                 ret = List.of(tag, tag.replaceFirst("^yi", "ji")).stream();
 386                 break;
 387                 
 388             // Special COMPAT provider locales
 389             case "ja":
 390                 if (loc.getCountry() == "JP") {
 391                     ret = List.of(tag, jaJPJPTag).stream();
 392                 }
 393                 break;
 394             case "no":
 395             case "nn":
 396                 if (loc.getCountry() == "NO") {
 397                     ret = List.of(tag, noNONYTag).stream();
 398                 }
 399                 break;
 400             case "th":
 401                 if (loc.getCountry() == "TH") {
 402                     ret = List.of(tag, thTHTHTag).stream();
 403                 }
 404                 break;
 405         }
 406 
 407         return ret == null ? List.of(tag).stream() : ret;
 408     }
 409 }
< prev index next >