8158402: jlink: should use regex for all pattern operations (--order-resources or --exclude-resources)

Reviewed-by: sundar, alanb
This commit is contained in:
Jim Laskey 2016-06-09 11:39:42 -03:00
parent 32757e8ed8
commit 50e2a2ced0
5 changed files with 112 additions and 149 deletions

View File

@ -28,7 +28,9 @@ package jdk.tools.jimage;
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 jdk.tools.jlink.internal.TaskHelper.BadArgs;
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 @@ class JImageTask {
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 @@ class JImageTask {
void setLog(PrintWriter out) {
log = out;
taskHelper.setLog(log);
TASK_HELPER.setLog(log);
}
static class OptionsValues {
@ -160,32 +164,32 @@ class JImageTask {
}
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 @@ class JImageTask {
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 @@ class JImageTask {
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 @@ class JImageTask {
}
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 @@ class JImageTask {
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 @@ class JImageTask {
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 @@ class JImageTask {
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;

View File

@ -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 class Utils {
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);
}
}

View File

@ -26,7 +26,9 @@ package jdk.tools.jlink.internal.plugins;
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 @@ import jdk.tools.jlink.internal.Utils;
*/
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 @@ public final class OrderResourcesPlugin implements TransformerPlugin {
}
}
} 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);
}
}
}
}
}

View File

@ -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<>();
// 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 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);
}
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);
}
}
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);
public boolean test(String name) {
Path path = JRT_FILE_SYSTEM.getPath(name);
for (PathMatcher matcher : matchers) {
if (matcher.matches(path)) {
return !negate;
}
}
return negate;
}
}

View File

@ -1072,6 +1072,10 @@ public class JmodTask {
@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 @@ public class JmodTask {
@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 @@ public class JmodTask {
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"))