Skip to content

[MNG-8686] Add SourceRoot.matcher(boolean) method #2236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,39 @@ default Path directory() {
}

/**
* {@return the list of pattern matchers for the files to include}.
* {@return the list of patterns for the files to include}.
* The path separator is {@code /} on all platforms, including Windows.
* The prefix before the {@code :} character, if present and longer than 1 character, is the syntax.
* If no syntax is specified, or if its length is 1 character (interpreted as a Windows drive),
* the default is a Maven-specific variation of the {@code "glob"} pattern.
*
* <p>
* The default implementation returns an empty list, which means to apply a language-dependent pattern.
* For example, for the Java language, the pattern includes all files with the {@code .java} suffix.
*/
default List<PathMatcher> includes() {
default List<String> includes() {
return List.of();
}

/**
* {@return the list of pattern matchers for the files to exclude}.
* {@return the list of patterns for the files to exclude}.
* The exclusions are applied after the inclusions.
* The default implementation returns an empty list.
*/
default List<PathMatcher> excludes() {
default List<String> excludes() {
return List.of();
}

/**
* {@return a matcher combining the include and exclude patterns}.
* The matcher is absent if the includes/excludes lists are empty
* and {@code useDefaultExcludes} is {@code false}.
*
* @param useDefaultExcludes whether to add the default set of patterns to exclude,
* mostly Source Code Management (<abbr>SCM</abbr>) files
*/
Optional<PathMatcher> matcher(boolean useDefaultExcludes);

/**
* {@return in which context the source files will be used}.
* Not to be confused with dependency scope.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -822,8 +822,8 @@ public boolean add(Resource resource) {
private static Resource toResource(SourceRoot sourceRoot) {
return new Resource(org.apache.maven.api.model.Resource.newBuilder()
.directory(sourceRoot.directory().toString())
.includes(sourceRoot.includes().stream().map(Object::toString).toList())
.excludes(sourceRoot.excludes().stream().map(Object::toString).toList())
.includes(sourceRoot.includes())
.excludes(sourceRoot.excludes())
.filtering(Boolean.toString(sourceRoot.stringFiltering()))
.build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package org.apache.maven.impl;

import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.List;
Expand All @@ -39,9 +38,21 @@
public final class DefaultSourceRoot implements SourceRoot {
private final Path directory;

private final List<PathMatcher> includes;
private final List<String> includes;

private final List<PathMatcher> excludes;
private final List<String> excludes;

/**
* Matcher combining the includes and excludes.
* Computed when first requested, then cached.
*/
private transient PathMatcher matcher;

/**
* Matcher combining the includes, excludes, and default excludes.
* Computed when first requested, then cached.
*/
private transient PathMatcher matcherWithDefaults;

private final ProjectScope scope;

Expand All @@ -65,9 +76,8 @@ public final class DefaultSourceRoot implements SourceRoot {
* @param source a source element from the model
*/
public DefaultSourceRoot(final Session session, final Path baseDir, final Source source) {
FileSystem fs = baseDir.getFileSystem();
includes = matchers(fs, source.getIncludes());
excludes = matchers(fs, source.getExcludes());
includes = source.getIncludes();
excludes = source.getExcludes();
stringFiltering = source.isStringFiltering();
enabled = source.isEnabled();
moduleName = nonBlank(source.getModule());
Expand Down Expand Up @@ -106,9 +116,8 @@ public DefaultSourceRoot(final Path baseDir, ProjectScope scope, Resource resour
throw new IllegalArgumentException("Source declaration without directory value.");
}
directory = baseDir.resolve(value).normalize();
FileSystem fs = directory.getFileSystem();
includes = matchers(fs, resource.getIncludes());
excludes = matchers(fs, resource.getExcludes());
includes = resource.getIncludes();
excludes = resource.getExcludes();
stringFiltering = Boolean.parseBoolean(resource.getFiltering());
enabled = true;
moduleName = null;
Expand Down Expand Up @@ -144,13 +153,15 @@ public DefaultSourceRoot(final ProjectScope scope, final Language language, fina
* @param scope scope of source code (main or test)
* @param language language of the source code
* @param directory directory of the source code
* @param includes list of patterns for the files to include, or {@code null} if unspecified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unspecified or nothing to include?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unspecified. The default is plugin-dependent. For example, the compiler plugin will default to the *.java includes. Other plugins may have different default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effective Java suggests returning empty lists instead of nulls as a general practice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this "null or empty" (will update the documentation). This is a parameter rather than a return value, accepting both for convenience.

* @param excludes list of patterns for the files to exclude, or {@code null} if nothing to exclude
*/
public DefaultSourceRoot(
final ProjectScope scope,
final Language language,
final Path directory,
List<PathMatcher> includes,
List<PathMatcher> excludes) {
List<String> includes,
List<String> excludes) {
this.scope = Objects.requireNonNull(scope);
this.language = language;
this.directory = Objects.requireNonNull(directory);
Expand All @@ -176,38 +187,6 @@ private static String nonBlank(String value) {
return value;
}

/**
* Creates a path matcher for each pattern.
* The path separator is {@code /} on all platforms, including Windows.
* The prefix before the {@code :} character is the syntax.
* If no syntax is specified, {@code "glob"} is assumed.
*
* @param fs the file system of the root directory
* @param patterns the patterns for which to create path matcher
* @return a path matcher for each pattern
*/
private static List<PathMatcher> matchers(FileSystem fs, List<String> patterns) {
final var matchers = new PathMatcher[patterns.size()];
for (int i = 0; i < matchers.length; i++) {
String rawPattern = patterns.get(i);
String pattern = rawPattern.contains(":") ? rawPattern : "glob:" + rawPattern;
matchers[i] = new PathMatcher() {
final PathMatcher delegate = fs.getPathMatcher(pattern);

@Override
public boolean matches(Path path) {
return delegate.matches(path);
}

@Override
public String toString() {
return rawPattern;
}
};
}
return List.of(matchers);
}

/**
* {@return the root directory where the sources are stored}.
*/
Expand All @@ -217,23 +196,46 @@ public Path directory() {
}

/**
* {@return the list of pattern matchers for the files to include}.
* {@return the list of patterns for the files to include}.
*/
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField") // Safe because unmodifiable
public List<PathMatcher> includes() {
public List<String> includes() {
return includes;
}

/**
* {@return the list of pattern matchers for the files to exclude}.
* {@return the list of patterns for the files to exclude}.
*/
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField") // Safe because unmodifiable
public List<PathMatcher> excludes() {
public List<String> excludes() {
return excludes;
}

/**
* {@return a matcher combining the include and exclude patterns}.
*
* @param useDefaultExcludes whether to add the default set of patterns to exclude,
* mostly Source Code Management (<abbr>SCM</abbr>) files
*/
@Override
public Optional<PathMatcher> matcher(boolean useDefaultExcludes) {
PathMatcher cached = useDefaultExcludes ? matcherWithDefaults : matcher;
if (cached != null) {
return Optional.of(cached);
}
Optional<PathMatcher> selector =
new PathSelector(directory(), includes(), excludes(), useDefaultExcludes).simplify();
cached = selector.orElse(null);
if (useDefaultExcludes) {
matcherWithDefaults = cached;
} else {
matcher = cached;
}
return selector;
}

/**
* {@return in which context the source files will be used}.
*/
Expand Down
Loading
Loading