Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@
* If above changes are not desired, put an explicit {@code "glob:"} prefix before the pattern.
* Note that putting such a prefix is recommended anyway for better performances.
*
* @author Benjamin Bentmann
* @author Martin Desruisseaux
*
* @see java.nio.file.FileSystem#getPathMatcher(String)
*/
public class PathSelector implements PathMatcher {
Expand Down Expand Up @@ -171,14 +168,16 @@ public class PathSelector implements PathMatcher {
/**
* String representations of the normalized include filters.
* Each pattern shall be prefixed by its syntax, which is {@value #DEFAULT_SYNTAX} by default.
* An empty array means to include all files.
*
* @see #toString()
*/
private final String[] includePatterns;

/**
* String representations of the normalized exclude filters.
* Each pattern shall be prefixed by its syntax, which is {@value #DEFAULT_SYNTAX} by default.
* Each pattern shall be prefixed by its syntax. If no syntax is specified,
* the default is a Maven 3 syntax similar, but not identical, to {@value #DEFAULT_SYNTAX}.
* This array may be longer or shorter than the user-supplied excludes, depending on whether
* default excludes have been added and whether some unnecessary excludes have been omitted.
*
Expand All @@ -188,6 +187,7 @@ public class PathSelector implements PathMatcher {

/**
* The matcher for includes. The length of this array is equal to {@link #includePatterns} array length.
* An empty array means to include all files.
*/
private final PathMatcher[] includes;

Expand All @@ -200,6 +200,7 @@ public class PathSelector implements PathMatcher {
* The matcher for all directories to include. This array includes the parents of all those directories,
* because they need to be accepted before we can walk to the sub-directories.
* This is an optimization for skipping whole directories when possible.
* An empty array means to include all directories.
*/
private final PathMatcher[] dirIncludes;

Expand All @@ -215,6 +216,13 @@ public class PathSelector implements PathMatcher {
*/
private final Path baseDirectory;

/**
* Whether paths must be relativized before being given to a matcher. If {@code true}, then every paths
* will be made relative to {@link #baseDirectory} for allowing patterns like {@code "foo/bar/*.java"}
* to work. As a slight optimization, we can skip this step if all patterns start with {@code "**"}.
*/
private final boolean needRelativize;

/**
* Creates a new selector from the given includes and excludes.
*
Expand All @@ -232,17 +240,18 @@ public PathSelector(
baseDirectory = Objects.requireNonNull(directory, "directory cannot be null");
includePatterns = normalizePatterns(includes, false);
excludePatterns = normalizePatterns(effectiveExcludes(excludes, includePatterns, useDefaultExcludes), true);
FileSystem system = baseDirectory.getFileSystem();
this.includes = matchers(system, includePatterns);
this.excludes = matchers(system, excludePatterns);
dirIncludes = matchers(system, directoryPatterns(includePatterns, false));
dirExcludes = matchers(system, directoryPatterns(excludePatterns, true));
FileSystem fileSystem = baseDirectory.getFileSystem();
this.includes = matchers(fileSystem, includePatterns);
this.excludes = matchers(fileSystem, excludePatterns);
dirIncludes = matchers(fileSystem, directoryPatterns(includePatterns, false));
dirExcludes = matchers(fileSystem, directoryPatterns(excludePatterns, true));
needRelativize = needRelativize(includePatterns) || needRelativize(excludePatterns);
}

/**
* Returns the given array of excludes, optionally expanded with a default set of excludes,
* then with unnecessary excludes omitted. An unnecessary exclude is an exclude which will never
* match a file because there is no include which would accept a file that could match the exclude.
* match a file because there are no includes which would accept a file that could match the exclude.
* For example, if the only include is {@code "*.java"}, then the <code>"**&sol;project.pj"</code>,
* <code>"**&sol;.DS_Store"</code> and other excludes will never match a file and can be omitted.
* Because the list of {@linkplain #DEFAULT_EXCLUDES default excludes} contains many elements,
Expand All @@ -269,10 +278,14 @@ private static Collection<String> effectiveExcludes(
}
} else {
excludes = new ArrayList<>(excludes);
excludes.removeIf(Objects::isNull);
if (useDefaultExcludes) {
excludes.addAll(DEFAULT_EXCLUDES);
}
}
if (includes.length == 0) {
return excludes;
}
/*
* Get the prefixes and suffixes of all includes, stopping at the first special character.
* Redundant prefixes and suffixes are omitted.
Expand Down Expand Up @@ -473,7 +486,7 @@ private static void addPatternsWithOneDirRemoved(final Set<String> patterns, fin
* Applies some heuristic rules for simplifying the set of patterns,
* then returns the patterns as an array.
*
* @param patterns the patterns to simplify and return asarray
* @param patterns the patterns to simplify and return as an array
* @param excludes whether the patterns are exclude patterns
* @return the set content as an array, after simplification
*/
Expand All @@ -497,7 +510,7 @@ private static String[] simplify(Set<String> patterns, boolean excludes) {
*
* @param patterns the normalized include or exclude patterns
* @param excludes whether the patterns are exclude patterns
* @return pattens of directories to include or exclude
* @return patterns of directories to include or exclude
*/
private static String[] directoryPatterns(final String[] patterns, final boolean excludes) {
// TODO: use `LinkedHashSet.newLinkedHashSet(int)` instead with JDK19.
Expand All @@ -523,6 +536,21 @@ private static String[] directoryPatterns(final String[] patterns, final boolean
return simplify(directories, excludes);
}

/**
* Returns {@code true} if at least one pattern requires path being relativized before to be matched.
*
* @param patterns include or exclude patterns
* @return whether at least one pattern require relativization
*/
private static boolean needRelativize(String[] patterns) {
for (String pattern : patterns) {
if (!pattern.startsWith(DEFAULT_SYNTAX + "**/")) {
return true;
}
}
return false;
}

/**
* Creates the path matchers for the given patterns.
* The syntax (usually {@value #DEFAULT_SYNTAX}) must be specified for each pattern.
Expand All @@ -535,12 +563,20 @@ private static PathMatcher[] matchers(final FileSystem fs, final String[] patter
return matchers;
}

/**
* {@return whether there are no include or exclude filters}.
* In such case, this {@code PathSelector} instance should be ignored.
*/
public boolean isEmpty() {
return includes.length == 0 && excludes.length == 0;
}

/**
* {@return a potentially simpler matcher equivalent to this matcher}.
*/
@SuppressWarnings("checkstyle:MissingSwitchDefault")
public PathMatcher simplify() {
if (excludes.length == 0) {
if (!needRelativize && excludes.length == 0) {
switch (includes.length) {
case 0:
return INCLUDES_ALL;
Expand All @@ -560,7 +596,9 @@ public PathMatcher simplify() {
*/
@Override
public boolean matches(Path path) {
path = baseDirectory.relativize(path);
if (needRelativize) {
path = baseDirectory.relativize(path);
}
return (includes.length == 0 || isMatched(path, includes))
&& (excludes.length == 0 || !isMatched(path, excludes));
}
Expand Down
Loading