/** * Lists recursive files of this directory with pattern matching. * <p>The default implementation calls {@link #list()} recursively inside {@link #run} and applies filtering to the result. * Implementations may wish to override this more efficiently. * @param includes comma-separated Ant-style globs as per {@link Util#createFileSet(File, String, String)} using {@code /} as a path separator; * the empty string means <em>no matches</em> (use {@link SelectorUtils#DEEP_TREE_MATCH} if you want to match everything except some excludes) * @param excludes optional excludes in similar format to {@code includes} * @param useDefaultExcludes as per {@link AbstractFileSet#setDefaultexcludes} * @return a list of {@code /}-separated relative names of children (files directly inside or in subdirectories) * @throws IOException if this is not a directory, or listing was not possible for some other reason * @since 2.118 */ @Restricted(Beta.class) public @Nonnull Collection<String> list(@Nonnull String includes, @CheckForNull String excludes, boolean useDefaultExcludes) throws IOException { Collection<String> r = run(new CollectFiles(this)); List<TokenizedPattern> includePatterns = patterns(includes); List<TokenizedPattern> excludePatterns = patterns(excludes); if (useDefaultExcludes) { for (String patt : DirectoryScanner.getDefaultExcludes()) { excludePatterns.add(new TokenizedPattern(patt.replace('/', File.separatorChar))); } } return r.stream().filter(p -> { TokenizedPath path = new TokenizedPath(p.replace('/', File.separatorChar)); return includePatterns.stream().anyMatch(patt -> patt.matchPath(path, true)) && !excludePatterns.stream().anyMatch(patt -> patt.matchPath(path, true)); }).collect(Collectors.toSet()); } private static final class CollectFiles extends MasterToSlaveCallable<Collection<String>, IOException> {