8158402: jlink: should use regex for all pattern operations (--order-resources or --exclude-resources)
authorjlaskey
Thu, 09 Jun 2016 11:39:42 -0300
changeset 38871 ec08bf1979d4
parent 38870 9fcc420c73f9
child 38872 b260c7cac3bc
8158402: jlink: should use regex for all pattern operations (--order-resources or --exclude-resources) Reviewed-by: sundar, alanb
jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OrderResourcesPlugin.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ResourceFilter.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Thu Jun 09 11:39:42 2016 -0300
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java	Thu Jun 09 11:39:42 2016 -0300
@@ -28,7 +28,9 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.FileSystem;
 import java.nio.file.Files;
+import java.nio.file.PathMatcher;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -43,9 +45,10 @@
 import static jdk.tools.jlink.internal.TaskHelper.JIMAGE_BUNDLE;
 import jdk.tools.jlink.internal.TaskHelper.Option;
 import jdk.tools.jlink.internal.TaskHelper.OptionsHelper;
+import jdk.tools.jlink.internal.Utils;
 
 class JImageTask {
-    static final Option<?>[] recognizedOptions = {
+    private static final Option<?>[] RECOGNIZED_OPTIONS = {
         new Option<JImageTask>(true, (task, option, arg) -> {
             task.options.directory = arg;
         }, "--dir"),
@@ -70,15 +73,16 @@
             task.options.version = true;
         }, "--version")
     };
-    private static final TaskHelper taskHelper
+    private static final TaskHelper TASK_HELPER
             = new TaskHelper(JIMAGE_BUNDLE);
-    private static final OptionsHelper<JImageTask> optionsHelper
-            = taskHelper.newOptionsHelper(JImageTask.class, recognizedOptions);
+    private static final OptionsHelper<JImageTask> OPTION_HELPER
+            = TASK_HELPER.newOptionsHelper(JImageTask.class, RECOGNIZED_OPTIONS);
     private static final String PROGNAME = "jimage";
+    private static final FileSystem JRT_FILE_SYSTEM = Utils.jrtFileSystem();
 
     private final OptionsValues options;
     private final List<Predicate<String>> filterPredicates;
-    private PrintWriter log = null;
+    private PrintWriter log;
 
     JImageTask() {
         this.options = new OptionsValues();
@@ -88,7 +92,7 @@
 
     void setLog(PrintWriter out) {
         log = out;
-        taskHelper.setLog(log);
+        TASK_HELPER.setLog(log);
     }
 
     static class OptionsValues {
@@ -160,32 +164,32 @@
         }
 
         if (args.length == 0) {
-            log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
+            log.println(TASK_HELPER.getMessage("main.usage.summary", PROGNAME));
             return EXIT_ABNORMAL;
         }
 
         try {
-            List<String> unhandled = optionsHelper.handleOptions(this, args);
+            List<String> unhandled = OPTION_HELPER.handleOptions(this, args);
 
             if(!unhandled.isEmpty()) {
                 try {
                     options.task = Enum.valueOf(Task.class, unhandled.get(0).toUpperCase());
                 } catch (IllegalArgumentException ex) {
-                    throw taskHelper.newBadArgs("err.not.a.task", unhandled.get(0));
+                    throw TASK_HELPER.newBadArgs("err.not.a.task", unhandled.get(0));
                 }
 
                 for(int i = 1; i < unhandled.size(); i++) {
                     options.jimages.add(new File(unhandled.get(i)));
                 }
             } else if (!options.help && !options.version && !options.fullVersion) {
-                throw taskHelper.newBadArgs("err.invalid.task", "<unspecified>");
+                throw TASK_HELPER.newBadArgs("err.invalid.task", "<unspecified>");
             }
 
             if (options.help) {
                 if (unhandled.isEmpty()) {
-                    log.println(taskHelper.getMessage("main.usage", PROGNAME));
+                    log.println(TASK_HELPER.getMessage("main.usage", PROGNAME));
 
-                    for (Option<?> o : recognizedOptions) {
+                    for (Option<?> o : RECOGNIZED_OPTIONS) {
                         String name = o.aliases()[0];
 
                         if (name.startsWith("--")) {
@@ -194,21 +198,21 @@
                             name = name.substring(1);
                         }
 
-                        log.println(taskHelper.getMessage("main.opt." + name));
+                        log.println(TASK_HELPER.getMessage("main.opt." + name));
                     }
                 } else {
                     try {
-                        log.println(taskHelper.getMessage("main.usage." +
+                        log.println(TASK_HELPER.getMessage("main.usage." +
                                 options.task.toString().toLowerCase()));
                     } catch (MissingResourceException ex) {
-                        throw taskHelper.newBadArgs("err.not.a.task", unhandled.get(0));
+                        throw TASK_HELPER.newBadArgs("err.not.a.task", unhandled.get(0));
                     }
                 }
                 return EXIT_OK;
             }
 
             if (options.version || options.fullVersion) {
-                taskHelper.showVersion(options.fullVersion);
+                TASK_HELPER.showVersion(options.fullVersion);
 
                 if (unhandled.isEmpty()) {
                     return EXIT_OK;
@@ -219,10 +223,10 @@
 
             return run() ? EXIT_OK : EXIT_ERROR;
         } catch (BadArgs e) {
-            taskHelper.reportError(e.key, e.args);
+            TASK_HELPER.reportError(e.key, e.args);
 
             if (e.showUsage) {
-                log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
+                log.println(TASK_HELPER.getMessage("main.usage.summary", PROGNAME));
             }
 
             return EXIT_CMDERR;
@@ -241,25 +245,9 @@
         }
 
         for (String filter : filters.split(",")) {
-            boolean endsWith = filter.startsWith("*");
-            boolean startsWith = filter.endsWith("*");
-            Predicate<String> function;
-
-            if (startsWith && endsWith) {
-                final String string = filter.substring(1, filter.length() - 1);
-                function = (path) -> path.contains(string);
-            } else if (startsWith) {
-                final String string = filter.substring(0, filter.length() - 1);
-                function = (path) -> path.startsWith(string);
-            } else if (endsWith) {
-                final String string = filter.substring(1);
-                function = (path) -> path.endsWith(string);
-            } else {
-                final String string = filter;
-                function = (path) -> path.equals(string);
-            }
-
-            filterPredicates.add(function);
+            final PathMatcher matcher = Utils.getPathMatcher(JRT_FILE_SYSTEM, filter);
+            Predicate<String> predicate = (path) -> matcher.matches(JRT_FILE_SYSTEM.getPath(path));
+            filterPredicates.add(predicate);
         }
     }
 
@@ -290,11 +278,11 @@
 
         if (parent.exists()) {
             if (!parent.isDirectory()) {
-                throw taskHelper.newBadArgs("err.cannot.create.dir",
+                throw TASK_HELPER.newBadArgs("err.cannot.create.dir",
                                             parent.getAbsolutePath());
             }
         } else if (!parent.mkdirs()) {
-            throw taskHelper.newBadArgs("err.cannot.create.dir",
+            throw TASK_HELPER.newBadArgs("err.cannot.create.dir",
                                         parent.getAbsolutePath());
         }
 
@@ -382,12 +370,12 @@
             ModuleAction moduleAction,
             ResourceAction resourceAction) throws IOException, BadArgs {
         if (options.jimages.isEmpty()) {
-            throw taskHelper.newBadArgs("err.no.jimage");
+            throw TASK_HELPER.newBadArgs("err.no.jimage");
         }
 
         for (File file : options.jimages) {
             if (!file.exists() || !file.isFile()) {
-                throw taskHelper.newBadArgs("err.not.a.jimage", file.getName());
+                throw TASK_HELPER.newBadArgs("err.not.a.jimage", file.getName());
             }
 
             try (BasicImageReader reader = BasicImageReader.open(file.toPath())) {
@@ -451,7 +439,7 @@
                 iterate(this::listTitle, null, this::verify);
                 break;
             default:
-                throw taskHelper.newBadArgs("err.invalid.task",
+                throw TASK_HELPER.newBadArgs("err.invalid.task",
                         options.task.name()).showUsage(true);
         }
         return true;
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java	Thu Jun 09 11:39:42 2016 -0300
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Utils.java	Thu Jun 09 11:39:42 2016 -0300
@@ -25,6 +25,10 @@
 package jdk.tools.jlink.internal;
 
 import java.lang.reflect.Module;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.PathMatcher;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -155,4 +159,20 @@
     public static boolean isBuiltin(Plugin prov) {
         return THIS_MODULE.equals(prov.getClass().getModule());
     }
+
+    public static FileSystem jrtFileSystem() {
+        return FileSystems.getFileSystem(URI.create("jrt:/"));
+    }
+
+    public static PathMatcher getPathMatcher(FileSystem fs, String pattern) {
+        if (!pattern.startsWith("glob:") && !pattern.startsWith("regex:")) {
+            pattern = "glob:" + pattern;
+        }
+
+        return fs.getPathMatcher(pattern);
+    }
+
+    public static PathMatcher getPathMatcher(String pattern) {
+        return getPathMatcher(jrtFileSystem(), pattern);
+    }
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OrderResourcesPlugin.java	Thu Jun 09 11:39:42 2016 -0300
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OrderResourcesPlugin.java	Thu Jun 09 11:39:42 2016 -0300
@@ -26,7 +26,9 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.FileSystem;
 import java.nio.file.Files;
+import java.nio.file.PathMatcher;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -47,6 +49,8 @@
  */
 public final class OrderResourcesPlugin implements TransformerPlugin {
     public static final String NAME = "order-resources";
+    private static final FileSystem JRT_FILE_SYSTEM = Utils.jrtFileSystem();
+
     private final List<ToIntFunction<String>> filters;
     private final Map<String, Integer> orderedPaths;
 
@@ -187,27 +191,11 @@
                     }
                 }
             } else {
-                boolean endsWith = pattern.startsWith("*");
-                boolean startsWith = pattern.endsWith("*");
-                ToIntFunction<String> function;
                 final int result = ordinal++;
-
-                if (startsWith && endsWith) {
-                    final String string = pattern.substring(1, pattern.length() - 1);
-                    function = (path)-> path.contains(string) ? result : Integer.MAX_VALUE;
-                } else if (startsWith) {
-                    final String string = pattern.substring(0, pattern.length() - 1);
-                    function = (path)-> path.startsWith(string) ? result : Integer.MAX_VALUE;
-                } else if (endsWith) {
-                    final String string = pattern.substring(1);
-                    function = (path)-> path.endsWith(string) ? result : Integer.MAX_VALUE;
-                } else {
-                    final String string = pattern;
-                    function = (path)-> path.equals(string) ? result : Integer.MAX_VALUE;
-                }
-
+                final PathMatcher matcher = Utils.getPathMatcher(JRT_FILE_SYSTEM, pattern);
+                ToIntFunction<String> function = (path)-> matcher.matches(JRT_FILE_SYSTEM.getPath(path)) ? result : Integer.MAX_VALUE;
                 filters.add(function);
-            }
+             }
         }
     }
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ResourceFilter.java	Thu Jun 09 11:39:42 2016 -0300
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ResourceFilter.java	Thu Jun 09 11:39:42 2016 -0300
@@ -24,113 +24,71 @@
  */
 package jdk.tools.jlink.internal.plugins;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
+import jdk.tools.jlink.internal.Utils;
+import jdk.tools.jlink.plugin.PluginException;
 
 /**
  *
- * Filter in or out a resource
+ * Filter resource resources using path matcher.
  */
 public class ResourceFilter implements Predicate<String> {
+    private static final FileSystem JRT_FILE_SYSTEM = Utils.jrtFileSystem();
 
-    private final Pattern inPatterns;
-    private final Pattern outPatterns;
-
-    static final String NEG = "^";
+    final boolean negate;
+    final List<PathMatcher> matchers;
 
     public ResourceFilter(String[] patterns) throws IOException {
         this(patterns, false);
     }
 
-    public ResourceFilter(String[] patterns, boolean negateAll) throws IOException {
+    public ResourceFilter(String[] patterns, boolean negate) throws IOException {
+        this.negate = negate;
+        this.matchers = new ArrayList<>();
+
+        for (String pattern : patterns) {
+            if (pattern.startsWith("@")) {
+                File file = new File(pattern.substring(1));
+
+                if (file.exists()) {
+                    List<String> lines;
+
+                    try {
+                        lines = Files.readAllLines(file.toPath());
+                    } catch (IOException ex) {
+                        throw new PluginException(ex);
+                    }
 
-        // Get the patterns from a file
-        if (patterns != null && patterns.length == 1) {
-            String filePath = patterns[0];
-            File f = new File(filePath);
-            if (f.exists()) {
-                List<String> pats;
-                try (FileInputStream fis = new FileInputStream(f);
-                        InputStreamReader ins = new InputStreamReader(fis,
-                                StandardCharsets.UTF_8);
-                        BufferedReader reader = new BufferedReader(ins)) {
-                    pats = reader.lines().collect(Collectors.toList());
+                    for (String line : lines) {
+                        PathMatcher matcher = Utils.getPathMatcher(JRT_FILE_SYSTEM, line);
+                        matchers.add(matcher);
+                    }
                 }
-                patterns = new String[pats.size()];
-                pats.toArray(patterns);
+            } else {
+                PathMatcher matcher = Utils.getPathMatcher(JRT_FILE_SYSTEM, pattern);
+                matchers.add(matcher);
+            }
+        }
+    }
+
+    @Override
+    public boolean test(String name) {
+        Path path = JRT_FILE_SYSTEM.getPath(name);
+
+        for (PathMatcher matcher : matchers) {
+            if (matcher.matches(path)) {
+                return !negate;
             }
         }
 
-        if (patterns != null && negateAll) {
-            String[] excluded = new String[patterns.length];
-            for (int i = 0; i < patterns.length; i++) {
-                excluded[i] = ResourceFilter.NEG + patterns[i];
-            }
-            patterns = excluded;
-        }
-
-        StringBuilder inPatternsBuilder = new StringBuilder();
-        StringBuilder outPatternsBuilder = new StringBuilder();
-        if (patterns != null) {
-            for (int i = 0; i < patterns.length; i++) {
-                String p = patterns[i];
-                p = p.replaceAll(" ", "");
-                StringBuilder builder = p.startsWith(NEG)
-                        ? outPatternsBuilder : inPatternsBuilder;
-                String pat = p.startsWith(NEG) ? p.substring(NEG.length()) : p;
-                builder.append(escape(pat));
-                if (i < patterns.length - 1) {
-                    builder.append("|");
-                }
-            }
-        }
-        this.inPatterns = inPatternsBuilder.length() == 0 ? null
-                : Pattern.compile(inPatternsBuilder.toString());
-        this.outPatterns = outPatternsBuilder.length() == 0 ? null
-                : Pattern.compile(outPatternsBuilder.toString());
-    }
-
-    public static String escape(String s) {
-        s = s.replaceAll(" ", "");
-        s = s.replaceAll("\\$", Matcher.quoteReplacement("\\$"));
-        s = s.replaceAll("\\.", Matcher.quoteReplacement("\\."));
-        s = s.replaceAll("\\*", ".+");
-        return s;
-    }
-
-    private boolean accept(String path) {
-        if (outPatterns != null) {
-            Matcher mout = outPatterns.matcher(path);
-            if (mout.matches()) {
-                //System.out.println("Excluding file " + resource.getPath());
-                return false;
-            }
-        }
-        boolean accepted = false;
-        // If the inPatterns is null, means that all resources are accepted.
-        if (inPatterns == null) {
-            accepted = true;
-        } else {
-            Matcher m = inPatterns.matcher(path);
-            if (m.matches()) {
-                //System.out.println("Including file " + resource.getPath());
-                accepted = true;
-            }
-        }
-        return accepted;
-    }
-
-    @Override
-    public boolean test(String path) {
-        return accept(path);
+        return negate;
     }
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Thu Jun 09 11:39:42 2016 -0300
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Thu Jun 09 11:39:42 2016 -0300
@@ -1072,6 +1072,10 @@
         @Override
         public Pattern convert(String value) {
             try {
+                if (value.startsWith("regex:")) {
+                    value = value.substring("regex:".length()).trim();
+                }
+
                 return Pattern.compile(value);
             } catch (PatternSyntaxException e) {
                 throw new CommandException("err.bad.pattern", value);
@@ -1083,10 +1087,15 @@
         @Override public String valuePattern() { return "pattern"; }
     }
 
-    static class GlobConverter implements ValueConverter<PathMatcher> {
+    static class PathMatcherConverter implements ValueConverter<PathMatcher> {
         @Override
         public PathMatcher convert(String pattern) {
             try {
+                if (!pattern.startsWith("glob:") &&
+                    !pattern.startsWith("regex:")) {
+                    pattern = "glob:" + pattern;
+                }
+
                 return FileSystems.getDefault()
                                   .getPathMatcher("glob:" + pattern);
             } catch (PatternSyntaxException e) {
@@ -1194,7 +1203,7 @@
         OptionSpec<PathMatcher> excludes
                 = parser.accepts("exclude", getMessage("main.opt.exclude"))
                         .withRequiredArg()
-                        .withValuesConvertedBy(new GlobConverter());
+                        .withValuesConvertedBy(new PathMatcherConverter());
 
         OptionSpec<Pattern> hashModules
                 = parser.accepts("hash-modules", getMessage("main.opt.hash-modules"))