< prev index next >

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

Print this page
rev 14873 : imported patch 8159781


   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.io.ByteArrayInputStream;

  28 import java.util.Arrays;
  29 import java.util.Collections;
  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 java.util.Set;
  36 import java.util.function.Predicate;
  37 import java.util.regex.Pattern;
  38 import java.util.stream.Collectors;
  39 import java.util.stream.IntStream;
  40 import java.util.stream.Stream;
  41 import jdk.internal.org.objectweb.asm.ClassReader;
  42 import jdk.tools.jlink.internal.ResourcePrevisitor;
  43 import jdk.tools.jlink.internal.StringTable;
  44 import jdk.tools.jlink.plugin.LinkModule;
  45 import jdk.tools.jlink.plugin.ModuleEntry;
  46 import jdk.tools.jlink.plugin.PluginException;
  47 import jdk.tools.jlink.plugin.ModulePool;


  64  *  0. All locale data in java.base are unconditionally included.
  65  *  1. All the selective locale data are in jdk.localedata module
  66  *  2. Their package names are constructed by appending ".ext" to
  67  *     the corresponding ones in java.base module.
  68  *  3. Available locales string in LocaleDataMetaInfo class should
  69  *     start with at least one white space character, e.g., " ar ar-EG ..."
  70  *                                                           ^
  71  */
  72 public final class IncludeLocalesPlugin implements TransformerPlugin, ResourcePrevisitor {
  73 
  74     public static final String NAME = "include-locales";
  75     private static final String MODULENAME = "jdk.localedata";
  76     private static final Set<String> LOCALEDATA_PACKAGES = Set.of(
  77         "sun.text.resources.cldr.ext",
  78         "sun.text.resources.ext",
  79         "sun.util.resources.cldr.ext",
  80         "sun.util.resources.cldr.provider",
  81         "sun.util.resources.ext",
  82         "sun.util.resources.provider");
  83     private static final String METAINFONAME = "LocaleDataMetaInfo";
  84     private static final String META_FILES =
  85         "*module-info.class," +
  86         "*LocaleDataProvider.class," +
  87         "*" + METAINFONAME + ".class,";
  88     private static final String INCLUDE_LOCALE_FILES =
  89         "*sun/text/resources/ext/[^\\/]+_%%.class," +
  90         "*sun/util/resources/ext/[^\\/]+_%%.class," +
  91         "*sun/text/resources/cldr/ext/[^\\/]+_%%.class," +
  92         "*sun/util/resources/cldr/ext/[^\\/]+_%%.class,";
  93     private Predicate<String> predicate;
  94     private String userParam;
  95     private List<Locale.LanguageRange> priorityList;
  96     private List<Locale> available;
  97     private List<String> filtered;
  98 
  99     // Special COMPAT provider locales
 100     private static final String jaJPJPTag = "ja-JP-JP";
 101     private static final String noNONYTag = "no-NO-NY";
 102     private static final String thTHTHTag = "th-TH-TH";
 103     private static final Locale jaJPJP = new Locale("ja", "JP", "JP");
 104     private static final Locale noNONY = new Locale("no", "NO", "NY");
 105     private static final Locale thTHTH = new Locale("th", "TH", "TH");
 106 
 107     @Override
 108     public String getName() {
 109         return NAME;
 110     }
 111 
 112     @Override


 186             available = Stream.concat(module.entries()
 187                                         .map(md -> p.matcher(md.getPath()))
 188                                         .filter(m -> m.matches())
 189                                         .map(m -> m.group("tag").replaceAll("_", "-")),
 190                                     Stream.concat(Stream.of(jaJPJPTag), Stream.of(thTHTHTag)))
 191                 .distinct()
 192                 .sorted()
 193                 .map(IncludeLocalesPlugin::tagToLocale)
 194                 .collect(Collectors.toList());
 195         } else {
 196             // jdk.localedata is not added.
 197             throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".localedatanotfound"));
 198         }
 199         filtered = filterLocales(available);
 200 
 201         if (filtered.isEmpty()) {
 202             throw new PluginException(
 203                 String.format(PluginsResourceBundle.getMessage(NAME + ".nomatchinglocales"), userParam));
 204         }
 205 
 206         String value = META_FILES + filtered.stream()
 207             .map(s -> includeLocaleFilePatterns(s))
 208             .collect(Collectors.joining(","));


 209         predicate = ResourceFilter.includeFilter(value);
 210     }
 211 
 212     private String includeLocaleFilePatterns(String tag) {

 213         String pTag = tag.replaceAll("-", "_");
 214         String files = "";
 215         int lastDelimiter = tag.length();
 216         String isoSpecial = pTag.matches("^(he|yi|id).*") ?
 217                             pTag.replaceFirst("he", "iw")
 218                                 .replaceFirst("yi", "ji")
 219                                 .replaceFirst("id", "in") : "";
 220 
 221         // Add tag patterns including parents
 222         while (true) {
 223             pTag = pTag.substring(0, lastDelimiter);
 224             files += INCLUDE_LOCALE_FILES.replaceAll("%%", pTag);
 225 
 226             if (!isoSpecial.isEmpty()) {
 227                 isoSpecial = isoSpecial.substring(0, lastDelimiter);
 228                 files += INCLUDE_LOCALE_FILES.replaceAll("%%", isoSpecial);
 229             }
 230 
 231             lastDelimiter = pTag.lastIndexOf('_');
 232             if (lastDelimiter == -1) {
 233                 break;
 234             }
 235         }
 236 
 237         final String lang = pTag;
 238 
 239         // Add possible special locales of the COMPAT provider
 240         files += Set.of(jaJPJPTag, noNONYTag, thTHTHTag).stream()
 241             .filter(stag -> lang.equals(stag.substring(0,2)))
 242             .map(t -> INCLUDE_LOCALE_FILES.replaceAll("%%", t.replaceAll("-", "_")))
 243             .collect(Collectors.joining(","));
 244 
 245         // Add possible UN.M49 files (unconditional for now) for each language
 246         files += INCLUDE_LOCALE_FILES.replaceAll("%%", lang + "_[0-9]{3}");
 247         if (!isoSpecial.isEmpty()) {
 248             files += INCLUDE_LOCALE_FILES.replaceAll("%%", isoSpecial + "_[0-9]{3}");
 249         }
 250 
 251         // Add Thai BreakIterator related data files
 252         if (lang.equals("th")) {
 253             files += "*sun/text/resources/thai_dict," +
 254                      "*sun/text/resources/[^\\/]+BreakIteratorData_th,";
 255         }
 256 
 257         // Add Taiwan resource bundles for Hong Kong
 258         if (tag.startsWith("zh-HK")) {
 259             files += INCLUDE_LOCALE_FILES.replaceAll("%%", "zh_TW");
 260         }
 261 
 262         return files;
 263     }
 264 






 265     private boolean stripUnsupportedLocales(byte[] bytes, ClassReader cr) {
 266         char[] buf = new char[cr.getMaxStringLength()];
 267         boolean[] modified = new boolean[1];
 268 
 269         IntStream.range(1, cr.getItemCount())
 270             .map(item -> cr.getItem(item))
 271             .forEach(itemIndex -> {
 272                 if (bytes[itemIndex - 1] == 1 &&         // UTF-8
 273                     bytes[itemIndex + 2] == (byte)' ') { // fast check for leading space
 274                     int length = cr.readUnsignedShort(itemIndex);
 275                     byte[] b = new byte[length];
 276                     System.arraycopy(bytes, itemIndex + 2, b, 0, length);
 277                     if (filterOutUnsupportedTags(b)) {
 278                         // copy back
 279                         System.arraycopy(b, 0, bytes, itemIndex + 2, length);
 280                         modified[0] = true;
 281                     }
 282                 }
 283             });
 284 




   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.io.ByteArrayInputStream;
  28 import java.util.ArrayList;
  29 import java.util.Arrays;
  30 import java.util.Collections;
  31 import java.util.IllformedLocaleException;
  32 import java.util.Locale;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Optional;
  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;


  65  *  0. All locale data in java.base are unconditionally included.
  66  *  1. All the selective locale data are in jdk.localedata module
  67  *  2. Their package names are constructed by appending ".ext" to
  68  *     the corresponding ones in java.base module.
  69  *  3. Available locales string in LocaleDataMetaInfo class should
  70  *     start with at least one white space character, e.g., " ar ar-EG ..."
  71  *                                                           ^
  72  */
  73 public final class IncludeLocalesPlugin implements TransformerPlugin, ResourcePrevisitor {
  74 
  75     public static final String NAME = "include-locales";
  76     private static final String MODULENAME = "jdk.localedata";
  77     private static final Set<String> LOCALEDATA_PACKAGES = Set.of(
  78         "sun.text.resources.cldr.ext",
  79         "sun.text.resources.ext",
  80         "sun.util.resources.cldr.ext",
  81         "sun.util.resources.cldr.provider",
  82         "sun.util.resources.ext",
  83         "sun.util.resources.provider");
  84     private static final String METAINFONAME = "LocaleDataMetaInfo";
  85     private static final List<String> META_FILES = List.of(
  86         ".+module-info.class",
  87         ".+LocaleDataProvider.class",
  88         ".+" + METAINFONAME + ".class");
  89     private static final List<String> INCLUDE_LOCALE_FILES = List.of(
  90         ".+sun/text/resources/ext/[^_]+_",
  91         ".+sun/util/resources/ext/[^_]+_",
  92         ".+sun/text/resources/cldr/ext/[^_]+_",
  93         ".+sun/util/resources/cldr/ext/[^_]+_");
  94     private Predicate<String> predicate;
  95     private String userParam;
  96     private List<Locale.LanguageRange> priorityList;
  97     private List<Locale> available;
  98     private List<String> filtered;
  99 
 100     // Special COMPAT provider locales
 101     private static final String jaJPJPTag = "ja-JP-JP";
 102     private static final String noNONYTag = "no-NO-NY";
 103     private static final String thTHTHTag = "th-TH-TH";
 104     private static final Locale jaJPJP = new Locale("ja", "JP", "JP");
 105     private static final Locale noNONY = new Locale("no", "NO", "NY");
 106     private static final Locale thTHTH = new Locale("th", "TH", "TH");
 107 
 108     @Override
 109     public String getName() {
 110         return NAME;
 111     }
 112 
 113     @Override


 187             available = Stream.concat(module.entries()
 188                                         .map(md -> p.matcher(md.getPath()))
 189                                         .filter(m -> m.matches())
 190                                         .map(m -> m.group("tag").replaceAll("_", "-")),
 191                                     Stream.concat(Stream.of(jaJPJPTag), Stream.of(thTHTHTag)))
 192                 .distinct()
 193                 .sorted()
 194                 .map(IncludeLocalesPlugin::tagToLocale)
 195                 .collect(Collectors.toList());
 196         } else {
 197             // jdk.localedata is not added.
 198             throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".localedatanotfound"));
 199         }
 200         filtered = filterLocales(available);
 201 
 202         if (filtered.isEmpty()) {
 203             throw new PluginException(
 204                 String.format(PluginsResourceBundle.getMessage(NAME + ".nomatchinglocales"), userParam));
 205         }
 206 
 207         List<String> value = Stream.concat(
 208                 META_FILES.stream(),
 209                 filtered.stream().flatMap(s -> includeLocaleFilePatterns(s).stream()))
 210             .map(s -> "regex:" + s)
 211             .collect(Collectors.toList());
 212         predicate = ResourceFilter.includeFilter(value);
 213     }
 214 
 215     private List<String> includeLocaleFilePatterns(String tag) {
 216         List<String> files = new ArrayList<>();
 217         String pTag = tag.replaceAll("-", "_");

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


< prev index next >