//
// Copyright © 2014, David Tesler (https://github.com/protobufel)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
package com.github.protobufel.resource.exec.maven.plugin;
import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.nio.file.FileSystem;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import javax.inject.Inject;
import org.apache.maven.model.FileSet;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
// import org.apache.maven.shared.model.fileset.util.FileSetManager;
import org.eclipse.sisu.Description;
@Mojo(name = "exec", defaultPhase = LifecyclePhase.GENERATE_TEST_RESOURCES, requiresProject = true)
@Description("Executes the specified command")
public class ExecMojo extends AbstractMojo {
public interface IFileSetMatcher extends PathMatcher {
}
// TODO remove me!
private Jsr330Component component;
/**
* A path to executable
*/
@Parameter(name = "execLocation", alias = "exec", property = "execLocation", required = true)
private String _execLocation = null;
@Parameter(property = "execLocationAsIs", name = "execLocationAsIs", defaultValue = "true")
private boolean _execLocationAsIs;
@Parameter(property = "systemCommand", name = "systemCommand", defaultValue = "false")
private boolean _systemCommand;
@Parameter(property = "argQuote", name = "argQuote", defaultValue = "\"")
private String _argQuote;
@Parameter(property = "args", name = "args", defaultValue = "")
private List<String> _args = Collections.emptyList();
@Parameter(property = "workDir", name = "workDir", defaultValue = "${basedir}")
private File _workDir;
@Parameter(property = "environment", name = "environment", defaultValue = "")
private Map<String, String> _environment;
@Parameter(property = "errorProperty", name = "errorProperty", defaultValue = "")
private String _errorProperty;
@Parameter(property = "errorFile", name = "errorFile", defaultValue = "")
private File _errorFile;
@Parameter(property = "errorPipe", name = "errorPipe", defaultValue = "true")
private boolean _errorPipe;
@Parameter(property = "errorInherit", name = "errorInherit", defaultValue = "false")
private boolean _errorInherit;
@Parameter(property = "errorAppend", name = "errorAppend", defaultValue = "false")
private boolean _errorAppend;
@Parameter(property = "outProperty", name = "outProperty", defaultValue = "")
private String _outProperty;
@Parameter(property = "outFile", name = "outFile", defaultValue = "")
private File _outFile;
@Parameter(property = "outPipe", name = "outPipe", defaultValue = "true")
private boolean _outPipe;
@Parameter(property = "outInherit", name = "outInherit", defaultValue = "false")
private boolean _outInherit;
@Parameter(property = "outAppend", name = "outAppend", defaultValue = "false")
private boolean _outAppend;
@Parameter(property = "exitCode")
private int _exitCode;
@Parameter(property = "redirectErrorStream", name = "redirectErrorStream", defaultValue = "false")
private boolean _redirectErrorStream;
@Parameter(property = "fileSets", name = "fileSets", defaultValue = "")
private List<FileSet> _fileSets = Collections.emptyList();
@Parameter(property = "followLinks", name = "followLinks", defaultValue = "false")
private boolean _followLinks;
@Parameter(property = "allowDuplicates", name = "allowDuplicates", defaultValue = "true")
private boolean _allowDuplicates;
@Parameter(property = "allowFiles", name = "allowFiles", defaultValue = "true")
private boolean _allowFiles;
@Parameter(property = "allowDirs", name = "allowDirs", defaultValue = "false")
private boolean _allowDirs;
@Inject
public ExecMojo(final Jsr330Component component) {
this.component = component;
}
@Override
public void execute() throws MojoExecutionException {
//
// Say hello to the world, my little constructor injected component!
//
//component.hello();
final List<String> command = new ArrayList<>();
command.add(getCanonicalPath(_execLocation, "exec"));
getLog().debug(String.format("args=%s", _args));
if (!_args.isEmpty()) {
command.addAll(_args);
}
if (!_fileSets.isEmpty()) {
getLog().debug(String.format("fileSets=%s", _fileSets));
quoteList(getResourceFiles(_fileSets, _followLinks, _allowDuplicates, _allowFiles,
_allowDirs),
command);
}
if (_systemCommand) {
convertToSystemCommandArgs(command, _argQuote);
}
getLog().debug(String.format("command=%s", command));
Redirect errorRedirect;
// if (!getCanonicalPath(_errorFile, "_errorFile").isEmpty()) {
if (_errorFile != null) {
_errorFile = getCanonicalFile(_errorFile, "_errorFile");
errorRedirect = _errorAppend ? Redirect.appendTo(_errorFile) : Redirect.to(_errorFile);
} else if (!_errorProperty.isEmpty()) {
throw new MojoExecutionException("_errorProperty currently unsupported");
} else if (_errorInherit) {
errorRedirect = Redirect.INHERIT;
} else if (_errorPipe) {
errorRedirect = Redirect.PIPE;
} else {
throw new MojoExecutionException(
"must specify one of these: _errorProperty, _errorFile, _errorPipe, _errorInherit");
}
Redirect outRedirect;
// if (!getCanonicalPath(_outFile, "_outFile").isEmpty()) {
if (_outFile != null) {
_outFile = getCanonicalFile(_outFile, "_outFile");
outRedirect = _outAppend ? Redirect.appendTo(_outFile) : Redirect.to(_outFile);
} else if (_outInherit) {
outRedirect = Redirect.INHERIT;
} else if (_outPipe) {
outRedirect = Redirect.PIPE;
} else if (!_outProperty.isEmpty()) {
throw new MojoExecutionException("_outProperty currently unsupported");
} else {
throw new MojoExecutionException(
"must specify one of these: _outProperty, _outFile, _outPipe, _outInherit");
}
_exitCode =
createProcess(command, errorRedirect, outRedirect, _workDir, _environment,
_redirectErrorStream);
}
private void convertToSystemCommandArgs(final List<String> command, final CharSequence quote) {
final StringBuilder sb = new StringBuilder();
sb.append(command.get(0)).append(" ");
if (System.getProperty("os.name").toLowerCase().indexOf("win") != -1) {
// for Windows construct cmd /c "arg1 "arg2" ... "arg<N>""
appendArgs(sb, command.subList(1, command.size()), "\"", true);
command.clear();
command.add("cmd");
command.add("/c");
command.add(sb.toString());
} else {
// for any other OS construct /bin/bash -c "arg1 Qarg1Q Qarg2Q ... Qarg<N>Q", where Q={'|"}
appendArgs(sb, command.subList(1, command.size()), quote, false);
command.clear();
command.add("/bin/bash");
command.add("-c");
command.add(sb.toString());
}
}
private StringBuilder appendArgs(final StringBuilder sb, final Collection<? extends CharSequence> args,
final CharSequence quote, final boolean testDoubleQuoteOnly) {
if (args.isEmpty()) {
return sb;
}
for (final CharSequence arg : args) {
appendArg(sb, arg, quote, testDoubleQuoteOnly).append(" ");
}
sb.setLength(sb.length() - 1);
return sb;
}
private StringBuilder appendArg(final StringBuilder sb, final CharSequence arg,
final CharSequence quote, final boolean testDoubleQuoteOnly) {
if ((arg.charAt(0) == '"') || (!testDoubleQuoteOnly && (arg.charAt(0) == '\''))) {
sb.append(arg);
} else {
sb.append(quote).append(arg).append(quote);
}
return sb;
}
private void quoteList(final Iterable<String> source, final List<String> target) {
final String quote =
(System.getProperty("os.name").toLowerCase().indexOf("win") == -1) ? "'" : "\"";
for (final String s : source) {
target.add(quote + s + quote);
}
}
Collection<String> getResourceFiles(final Iterable<? extends FileSet> fileSets,
final boolean followLinks, final boolean allowDuplicates, final boolean allowFiles,
final boolean allowDirs) throws MojoExecutionException {
final Set<String> result = new LinkedHashSet<>();
for (final FileSet fileSet : Objects.requireNonNull(fileSets)) {
final Collection<String> files =
getResourceFiles(fileSet, followLinks, allowDuplicates, allowFiles, allowDirs);
for (final String file : files) {
if (!result.add(file) && !allowDuplicates) {
getLog()
.error(
String.format("a duplicate file %s under directory %s", file,
fileSet.getDirectory()));
throw new MojoExecutionException(String.format("a duplicate file %s under directory %s",
file, fileSet.getDirectory()));
}
}
}
return result;
}
private Collection<String> getResourceFiles(final FileSet fileSet, final boolean followLinks,
final boolean allowDuplicates, final boolean allowFiles, final boolean allowDirs)
throws MojoExecutionException {
final Path dirPath = Paths.get(Objects.requireNonNull(fileSet).getDirectory());
final FileSetPathMatcher matcher =
new FileSetPathMatcher(fileSet.getIncludes(), fileSet.getExcludes(), dirPath);
final EnumSet<FileVisitOption> options =
followLinks ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) : EnumSet
.noneOf(FileVisitOption.class);
final Set<String> result = new LinkedHashSet<>();
try {
final ResourceVisitor resourceVisitor =
new ResourceVisitor(matcher, allowDuplicates, allowFiles, allowDirs, result, getLog());
Files.walkFileTree(dirPath, options, Integer.MAX_VALUE, resourceVisitor);
if (resourceVisitor.getErrorMessage() != null) {
throw new MojoExecutionException(resourceVisitor.getErrorMessage());
}
} catch (final IOException e) {
getLog().error("fileSet caused error", e);
throw new MojoExecutionException("fileSet caused error", e);
}
return result;
}
private String getCanonicalPath(final String _execLocation, final String propName)
throws MojoExecutionException {
if (_execLocationAsIs) {
return _execLocation;
}
try {
return getWorkDir().toPath().resolve(_execLocation).toAbsolutePath().toString();
} catch (final Throwable e) {
// getLog().error(String.format("%s has wrong path", propName), e);
throw new MojoExecutionException(String.format("%s has wrong path %s", propName,
_execLocation));
}
}
private File getCanonicalFile(final File file, final String propName)
throws MojoExecutionException {
try {
return file.getCanonicalFile();
} catch (final IOException e) {
// getLog().error(String.format("%s has wrong path", propName), e);
throw new MojoExecutionException(String.format("%s has wrong path", propName));
}
}
private int createProcess(final List<String> command, final Redirect errorRedirect,
final Redirect outRedirect, final File directory, final Map<String, String> environment,
final boolean redirectErrorStream) {
final ProcessBuilder processBuilder =
new ProcessBuilder(command).redirectErrorStream(redirectErrorStream)
.redirectError(errorRedirect).redirectOutput(outRedirect).directory(directory);
if ((environment != null) && !environment.isEmpty()) {
processBuilder.environment().putAll(environment);
}
Process process;
try {
process = processBuilder.start();
return process.waitFor();
} catch (final IOException e) {
getLog().error(e);
} catch (final InterruptedException e) {
getLog().error(e);
}
return -1;
}
// public Jsr330Component getComponent() {
// return component;
// }
//
// public void setComponent(final Jsr330Component component) {
// this.component = component;
// }
public String getExecLocation() {
return _execLocation;
}
public void setExecLocation(final String execLocation) {
_execLocation = execLocation;
}
public boolean isExecLocationAsIs() {
return _execLocationAsIs;
}
public void setExecLocationAsIs(final boolean execLocationAsIs) {
_execLocationAsIs = execLocationAsIs;
}
public boolean isSystemCommand() {
return _systemCommand;
}
public void setSystemCommand(final boolean systemCommand) {
_systemCommand = systemCommand;
}
public String getArgQuote() {
return _argQuote;
}
public void setArgQuote(final String argQuote) {
_argQuote = argQuote;
}
public List<String> getArgs() {
return Collections.unmodifiableList(_args);
}
public void setArgs(final List<String> args) {
_args = new ArrayList<>(args);
}
public File getWorkDir() {
return _workDir;
}
public void setWorkDir(final File workDir) {
_workDir = workDir;
}
public Map<String, String> getEnvironment() {
return _environment;
}
public void setEnvironment(final Map<String, String> environment) {
_environment = environment;
}
public String getErrorProperty() {
return _errorProperty;
}
public void setErrorProperty(final String errorProperty) {
_errorProperty = errorProperty;
}
public File getErrorFile() {
return _errorFile;
}
public void setErrorFile(final File errorFile) {
_errorFile = errorFile;
}
public boolean isErrorPipe() {
return _errorPipe;
}
public void setErrorPipe(final boolean errorPipe) {
_errorPipe = errorPipe;
}
public boolean isErrorInherit() {
return _errorInherit;
}
public void setErrorInherit(final boolean errorInherit) {
_errorInherit = errorInherit;
}
public boolean isErrorAppend() {
return _errorAppend;
}
public void setErrorAppend(final boolean errorAppend) {
_errorAppend = errorAppend;
}
public String getOutProperty() {
return _outProperty;
}
public void setOutProperty(final String outProperty) {
_outProperty = outProperty;
}
public File getOutFile() {
return _outFile;
}
public void setOutFile(final File outFile) {
_outFile = outFile;
}
public boolean isOutPipe() {
return _outPipe;
}
public void setOutPipe(final boolean outPipe) {
_outPipe = outPipe;
}
public boolean isOutInherit() {
return _outInherit;
}
public void setOutInherit(final boolean outInherit) {
_outInherit = outInherit;
}
public boolean isOutAppend() {
return _outAppend;
}
public void setOutAppend(final boolean outAppend) {
_outAppend = outAppend;
}
public int getExitCode() {
return _exitCode;
}
public void setExitCode(final int exitCode) {
_exitCode = exitCode;
}
public boolean isRedirectErrorStream() {
return _redirectErrorStream;
}
public void setRedirectErrorStream(final boolean redirectErrorStream) {
_redirectErrorStream = redirectErrorStream;
}
public List<FileSet> getFileSets() {
return _fileSets;
}
public void setFileSets(final List<? extends FileSet> fileSets) {
_fileSets = new ArrayList<>(fileSets);
}
public boolean isFollowLinks() {
return _followLinks;
}
public void setFollowLinks(final boolean followLinks) {
_followLinks = followLinks;
}
public boolean isAllowDuplicates() {
return _allowDuplicates;
}
public void setAllowDuplicates(final boolean allowDuplicates) {
_allowDuplicates = allowDuplicates;
}
public boolean isAllowFiles() {
return _allowFiles;
}
public void setAllowFiles(final boolean allowFiles) {
_allowFiles = allowFiles;
}
public boolean isAllowDirs() {
return _allowDirs;
}
public void setAllowDirs(final boolean allowDirs) {
_allowDirs = allowDirs;
}
// TODO replace with common-files-v7
static final class ResourceVisitor extends SimpleFileVisitor<Path> {
private final IFileSetMatcher matcher;
private final boolean allowDuplicates;
private final boolean allowFiles;
private final boolean allowDirs;
private final Set<String> result;
private String errorMessage;
private final Log log;
private boolean skipRoot = true;
private ResourceVisitor(final IFileSetMatcher matcher, final boolean allowDuplicates,
final boolean allowFiles, final boolean allowDirs, final Set<String> result, final Log log) {
if (!allowDirs && !allowFiles) {
throw new IllegalArgumentException("_allowDirs and _allowFiles cannot be both false");
}
this.matcher = Objects.requireNonNull(matcher);
this.result = Objects.requireNonNull(result);
this.log = Objects.requireNonNull(log);
this.allowDuplicates = allowDuplicates;
this.allowFiles = allowFiles;
this.allowDirs = allowDirs;
errorMessage = null;
}
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs)
throws IOException {
super.preVisitDirectory(dir, attrs);
if (skipRoot) {
skipRoot = false;
return FileVisitResult.CONTINUE;
} else if (!allowDirs) {
return FileVisitResult.CONTINUE;
}
if (matcher.matches(dir)) {
if (!result.add(dir.toAbsolutePath().normalize().toString()) && !allowDuplicates) {
errorMessage = String.format("found a duplicate folder %s", dir);
log.error(errorMessage);
return FileVisitResult.TERMINATE;
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
throws IOException {
if (!allowFiles) {
return super.visitFile(file, attrs);
}
super.visitFile(file, attrs);
if (matcher.matches(file)) {
if (!result.add(file.toAbsolutePath().normalize().toString()) && !allowDuplicates) {
errorMessage = String.format("found a duplicate file %s", file);
log.error(errorMessage);
return FileVisitResult.TERMINATE;
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(final Path file, final IOException exc)
throws IOException {
Objects.requireNonNull(file);
log.error(String.format("file %s cannot be visited", file), exc);
throw exc;
}
public String getErrorMessage() {
return errorMessage;
}
}
public static final class CompositeFileMatcher implements IFileSetMatcher {
private static final CompositeFileMatcher EMPTY = new CompositeFileMatcher();
private final Map<Path, Set<FileSetPathMatcher>> matchers;
private CompositeFileMatcher() {
matchers = Collections.emptyMap();
}
public CompositeFileMatcher(final Iterable<? extends FileSetPathMatcher> matchers) {
final Map<Path, Set<FileSetPathMatcher>> matcherMap =
updateMatcherMap(new HashMap<Path, Set<FileSetPathMatcher>>(), matchers);
this.matchers = getVerifiedMatcherMap(matcherMap);
}
private CompositeFileMatcher(final Map<Path, Set<FileSetPathMatcher>> matcherMap) {
matchers = getVerifiedMatcherMap(matcherMap);
}
private Map<Path, Set<FileSetPathMatcher>> getVerifiedMatcherMap(
final Map<Path, Set<FileSetPathMatcher>> matcherMap) {
if (matcherMap.isEmpty()) {
throw new IllegalArgumentException("matchers cannot be empty");
}
for (final Entry<Path, Set<FileSetPathMatcher>> entry : matcherMap.entrySet()) {
entry.setValue(Collections.unmodifiableSet(entry.getValue()));
}
return Collections.unmodifiableMap(matcherMap);
}
private Map<Path, Set<FileSetPathMatcher>> updateMatcherMap(
final Map<Path, Set<FileSetPathMatcher>> matcherMap,
final Map<Path, Set<FileSetPathMatcher>> matchers) {
for (final Entry<Path, Set<FileSetPathMatcher>> entry : Objects.requireNonNull(matchers)
.entrySet()) {
Set<FileSetPathMatcher> valueSet = matcherMap.get(entry.getKey());
if (valueSet == null) {
valueSet = new LinkedHashSet<>();
matcherMap.put(entry.getKey(), valueSet);
}
valueSet.addAll(entry.getValue());
}
return matcherMap;
}
private Map<Path, Set<FileSetPathMatcher>> updateMatcherMap(
final Map<Path, Set<FileSetPathMatcher>> matcherMap,
final Iterable<? extends FileSetPathMatcher> matchers) {
for (final FileSetPathMatcher matcher : Objects.requireNonNull(matchers)) {
updateMatcherMap(matcherMap, matcher);
}
return matcherMap;
}
private Map<Path, Set<FileSetPathMatcher>> updateMatcherMap(
final Map<Path, Set<FileSetPathMatcher>> matcherMap, final FileSetPathMatcher matcher) {
if (Objects.requireNonNull(matcher) == FileSetPathMatcher.EMPTY) {
return Objects.requireNonNull(matcherMap);
}
Set<FileSetPathMatcher> valueSet = matcherMap.get(matcher.dir);
if (valueSet == null) {
valueSet = new LinkedHashSet<>();
matcherMap.put(matcher.dir, valueSet);
}
valueSet.add(matcher);
return matcherMap;
}
public static CompositeFileMatcher emptyInstance() {
return EMPTY;
}
public CompositeFileMatcher and(final CompositeFileMatcher other) {
final Map<Path, Set<FileSetPathMatcher>> matcherMap =
updateMatcherMap(new HashMap<Path, Set<FileSetPathMatcher>>(matchers), other.matchers);
return new CompositeFileMatcher(matcherMap);
}
public CompositeFileMatcher and(final Iterable<? extends CompositeFileMatcher> others) {
final Map<Path, Set<FileSetPathMatcher>> matcherMap =
new HashMap<Path, Set<FileSetPathMatcher>>(matchers);
for (final CompositeFileMatcher matcher : others) {
updateMatcherMap(matcherMap, matcher.matchers);
}
return new CompositeFileMatcher(matcherMap);
}
@Override
public boolean matches(final Path path) {
for (final Entry<Path, Set<FileSetPathMatcher>> entry : matchers.entrySet()) {
final Path dir = entry.getKey();
if (dir.getFileSystem().equals(path.getFileSystem())) {
final Path relativePath;
try {
relativePath = dir.relativize(path.toAbsolutePath());
} catch (final IllegalArgumentException e) {
// this is not applicable to the current dir
continue;
}
for (final FileSetPathMatcher matcher : entry.getValue()) {
if (matcher.matches(relativePath, true)) {
return true;
}
}
}
}
return false;
}
}
public static final class FileSetPathMatcher implements IFileSetMatcher {
private static final FileSetPathMatcher EMPTY = new FileSetPathMatcher();
final List<PathMatcher> includes;
final List<PathMatcher> excludes;
final Path dir;
public static FileSetPathMatcher emptyInstance() {
return EMPTY;
}
private FileSetPathMatcher() {
includes = Collections.emptyList();
excludes = Collections.emptyList();
dir = null;
}
public FileSetPathMatcher(final List<String> includes, final List<String> excludes,
final Path dir) {
this.dir = Objects.requireNonNull(dir).toAbsolutePath().normalize();
final FileSystem fs = this.dir.getFileSystem();
if (Objects.requireNonNull(includes).isEmpty()) {
this.includes = Collections.emptyList();
} else {
final List<PathMatcher> includesList = new ArrayList<>(includes.size());
for (final String include : Objects.requireNonNull(includes)) {
includesList.add(fs.getPathMatcher("glob:" + include));
}
this.includes = Collections.unmodifiableList(includesList);
}
if (Objects.requireNonNull(excludes).isEmpty()) {
this.excludes = Collections.emptyList();
} else {
final List<PathMatcher> excludesList = new ArrayList<>(excludes.size());
for (final String exclude : Objects.requireNonNull(excludes)) {
excludesList.add(fs.getPathMatcher("glob:" + exclude));
}
this.excludes = Collections.unmodifiableList(excludesList);
}
}
private FileSetPathMatcher(final Path dir, final List<PathMatcher> includesList,
final List<PathMatcher> excludesList) {
this.dir = dir;
includes = Collections.unmodifiableList(includesList);
excludes = Collections.unmodifiableList(excludesList);
}
public FileSetPathMatcher and(final FileSetPathMatcher other) {
final List<PathMatcher> includesList = new ArrayList<>(includes);
final List<PathMatcher> excludesList = new ArrayList<>(excludes);
addMatcher(dir, includesList, excludesList, other);
return new FileSetPathMatcher(dir, includesList, excludesList);
}
public FileSetPathMatcher and(final Iterable<? extends FileSetPathMatcher> other) {
final List<PathMatcher> includesList = new ArrayList<>(includes);
final List<PathMatcher> excludesList = new ArrayList<>(excludes);
for (final FileSetPathMatcher matcher : Objects.requireNonNull(other)) {
addMatcher(dir, includesList, excludesList, matcher);
}
return new FileSetPathMatcher(dir, includesList, excludesList);
}
private void addMatcher(final Path dir, final List<PathMatcher> includesList,
final List<PathMatcher> excludesList, final FileSetPathMatcher other) {
try {
if (!Files.isSameFile(dir, Objects.requireNonNull(other).dir)) {
throw new IllegalArgumentException("the other's directory cannont be different");
}
} catch (final IOException e) {
throw new RuntimeException(e);
}
includesList.addAll(other.includes);
excludesList.addAll(other.excludes);
}
@Override
public boolean matches(final Path path) {
return matches(path, false);
}
public boolean matches(final Path path, final boolean isSanitized) {
final Path sanitizedPath;
if (!isSanitized) {
// we should succeed relativizing, otherwise it's a user error
// for applying a wrong matcher!
sanitizedPath = dir.relativize(path.toAbsolutePath());
} else {
sanitizedPath = path;
}
if (this == EMPTY) {
return false;
}
boolean matched = false;
if (!includes.isEmpty()) {
for (final PathMatcher include : includes) {
if (include.matches(sanitizedPath)) {
matched = true;
break;
}
}
}
if (!matched) {
return false;
}
if (!excludes.isEmpty()) {
for (final PathMatcher exclude : excludes) {
if (exclude.matches(sanitizedPath)) {
return false;
}
}
}
return true;
}
}
}