/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.tools.ant;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.apache.tools.ant.taskdefs.condition.Os;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceFactory;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.types.selectors.FileSelector;
import org.apache.tools.ant.types.selectors.SelectorScanner;
import org.apache.tools.ant.types.selectors.SelectorUtils;
import org.apache.tools.ant.types.selectors.TokenizedPath;
import org.apache.tools.ant.types.selectors.TokenizedPattern;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.VectorSet;

Class for scanning a directory for files/directories which match certain criteria.

These criteria consist of selectors and patterns which have been specified. With the selectors you can select which files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude files based on their filename.

The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is matched against a set of selectors, including special support for matching against filenames with include and and exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will be placed in the list of files/directories found.

When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors are supplied, none are applied.

The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same is done for the pattern against which should be matched.

The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in the pattern, it matches zero or more path segments of the name.

There is a special case regarding the use of File.separators at the beginning of the pattern and the string to match:

  • When a pattern starts with a File.separator, the string to match must also start with a File.separator.
  • When a pattern does not start with a File.separator, the string to match may not start with a File.separator.
  • When one of the above rules is not obeyed, the string will not match.

When a name path segment is matched against a pattern path segment, the following special characters can be used:
'*' matches zero or more characters
'?' matches one character.

Examples:

"**\*.class" matches all .class files/dirs in a directory tree.

"test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a directory called test.

"**" matches everything in a directory tree.

"**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").

Case sensitivity may be turned off if necessary. By default, it is turned on.

Example of usage:

  String[] includes = {"**\\*.class"};
  String[] excludes = {"modules\\*\\**"};
  ds.setIncludes(includes);
  ds.setExcludes(excludes);
  ds.setBasedir(new File("test"));
  ds.setCaseSensitive(true);
  ds.scan();
  System.out.println("FILES:");
  String[] files = ds.getIncludedFiles();
  for (int i = 0; i < files.length; i++) {
    System.out.println(files[i]);
  }
This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a directory called "modules".
/** * Class for scanning a directory for files/directories which match certain * criteria. * <p> * These criteria consist of selectors and patterns which have been specified. * With the selectors you can select which files you want to have included. * Files which are not selected are excluded. With patterns you can include * or exclude files based on their filename. * </p> * <p> * The idea is simple. A given directory is recursively scanned for all files * and directories. Each file/directory is matched against a set of selectors, * including special support for matching against filenames with include and * and exclude patterns. Only files/directories which match at least one * pattern of the include pattern list or other file selector, and don't match * any pattern of the exclude pattern list or fail to match against a required * selector will be placed in the list of files/directories found. * </p> * <p> * When no list of include patterns is supplied, "**" will be used, which * means that everything will be matched. When no list of exclude patterns is * supplied, an empty list is used, such that nothing will be excluded. When * no selectors are supplied, none are applied. * </p> * <p> * The filename pattern matching is done as follows: * The name to be matched is split up in path segments. A path segment is the * name of a directory or file, which is bounded by * <code>File.separator</code> ('/' under UNIX, '\' under Windows). * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", * "def","ghi" and "xyz.java". * The same is done for the pattern against which should be matched. * </p> * <p> * The segments of the name and the pattern are then matched against each * other. When '**' is used for a path segment in the pattern, it matches * zero or more path segments of the name. * </p> * <p> * There is a special case regarding the use of <code>File.separator</code>s * at the beginning of the pattern and the string to match: * </p> * <ul> * <li>When a pattern starts with a <code>File.separator</code>, the string * to match must also start with a <code>File.separator</code>.</li> * <li>When a pattern does not start with a <code>File.separator</code>, the * string to match may not start with a <code>File.separator</code>.</li> * <li>When one of the above rules is not obeyed, the string will not * match.</li> * </ul> * <p> * When a name path segment is matched against a pattern path segment, the * following special characters can be used:<br> * '*' matches zero or more characters<br> * '?' matches one character. * </p> * <p> * Examples: * </p> * <p> * "**\*.class" matches all .class files/dirs in a directory tree. * </p> * <p> * "test\a??.java" matches all files/dirs which start with an 'a', then two * more characters and then ".java", in a directory called test. * </p> * <p> * "**" matches everything in a directory tree. * </p> * <p> * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). * </p> * <p> * Case sensitivity may be turned off if necessary. By default, it is * turned on. * </p> * <p> * Example of usage: * </p> * <pre> * String[] includes = {"**\\*.class"}; * String[] excludes = {"modules\\*\\**"}; * ds.setIncludes(includes); * ds.setExcludes(excludes); * ds.setBasedir(new File("test")); * ds.setCaseSensitive(true); * ds.scan(); * * System.out.println("FILES:"); * String[] files = ds.getIncludedFiles(); * for (int i = 0; i &lt; files.length; i++) { * System.out.println(files[i]); * } * </pre> * This will scan a directory called test for .class files, but excludes all * files in all proper subdirectories of a directory called "modules". * */
public class DirectoryScanner implements FileScanner, SelectorScanner, ResourceFactory {
Is OpenVMS the operating system we're running on?
/** Is OpenVMS the operating system we're running on? */
private static final boolean ON_VMS = Os.isFamily("openvms");
Patterns which should be excluded by default.

Note that you can now add patterns to the list of default excludes. Added patterns will not become part of this array that has only been kept around for backwards compatibility reasons.

Deprecated:since 1.6.x. Use the getDefaultExcludes method instead.
/** * Patterns which should be excluded by default. * * <p>Note that you can now add patterns to the list of default * excludes. Added patterns will not become part of this array * that has only been kept around for backwards compatibility * reasons.</p> * * @deprecated since 1.6.x. * Use the {@link #getDefaultExcludes getDefaultExcludes} * method instead. */
@Deprecated protected static final String[] DEFAULTEXCLUDES = { //NOSONAR // Miscellaneous typical temporary files SelectorUtils.DEEP_TREE_MATCH + "/*~", SelectorUtils.DEEP_TREE_MATCH + "/#*#", SelectorUtils.DEEP_TREE_MATCH + "/.#*", SelectorUtils.DEEP_TREE_MATCH + "/%*%", SelectorUtils.DEEP_TREE_MATCH + "/._*", // CVS SelectorUtils.DEEP_TREE_MATCH + "/CVS", SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH, SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore", // SCCS SelectorUtils.DEEP_TREE_MATCH + "/SCCS", SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH, // Visual SourceSafe SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc", // Subversion SelectorUtils.DEEP_TREE_MATCH + "/.svn", SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH, // Git SelectorUtils.DEEP_TREE_MATCH + "/.git", SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH, SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes", SelectorUtils.DEEP_TREE_MATCH + "/.gitignore", SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules", // Mercurial SelectorUtils.DEEP_TREE_MATCH + "/.hg", SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH, SelectorUtils.DEEP_TREE_MATCH + "/.hgignore", SelectorUtils.DEEP_TREE_MATCH + "/.hgsub", SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate", SelectorUtils.DEEP_TREE_MATCH + "/.hgtags", // Bazaar SelectorUtils.DEEP_TREE_MATCH + "/.bzr", SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH, SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore", // Mac SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store" };
default value for maxLevelsOfSymlinks
Since:Ant 1.8.0
/** * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks} * @since Ant 1.8.0 */
public static final int MAX_LEVELS_OF_SYMLINKS = 5;
The end of the exception message if something that should be there doesn't exist.
/** * The end of the exception message if something that should be * there doesn't exist. */
public static final String DOES_NOT_EXIST_POSTFIX = " does not exist.";
Helper.
/** Helper. */
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
Patterns which should be excluded by default.
See Also:
  • addDefaultExcludes()
/** * Patterns which should be excluded by default. * * @see #addDefaultExcludes() */
private static final Set<String> defaultExcludes = new HashSet<>(); static { resetDefaultExcludes(); } // CheckStyle:VisibilityModifier OFF - bc
The base directory to be scanned.
/** The base directory to be scanned. */
protected File basedir;
The patterns for the files to be included.
/** The patterns for the files to be included. */
protected String[] includes;
The patterns for the files to be excluded.
/** The patterns for the files to be excluded. */
protected String[] excludes;
Selectors that will filter which files are in our candidate list.
/** Selectors that will filter which files are in our candidate list. */
protected FileSelector[] selectors = null;
The files which matched at least one include and no excludes and were selected.
/** * The files which matched at least one include and no excludes * and were selected. */
protected Vector<String> filesIncluded;
The files which did not match any includes or selectors.
/** The files which did not match any includes or selectors. */
protected Vector<String> filesNotIncluded;
The files which matched at least one include and at least one exclude.
/** * The files which matched at least one include and at least * one exclude. */
protected Vector<String> filesExcluded;
The directories which matched at least one include and no excludes and were selected.
/** * The directories which matched at least one include and no excludes * and were selected. */
protected Vector<String> dirsIncluded;
The directories which were found and did not match any includes.
/** The directories which were found and did not match any includes. */
protected Vector<String> dirsNotIncluded;
The directories which matched at least one include and at least one exclude.
/** * The directories which matched at least one include and at least one * exclude. */
protected Vector<String> dirsExcluded;
The files which matched at least one include and no excludes and which a selector discarded.
/** * The files which matched at least one include and no excludes and * which a selector discarded. */
protected Vector<String> filesDeselected;
The directories which matched at least one include and no excludes but which a selector discarded.
/** * The directories which matched at least one include and no excludes * but which a selector discarded. */
protected Vector<String> dirsDeselected;
Whether or not our results were built by a slow scan.
/** Whether or not our results were built by a slow scan. */
protected boolean haveSlowResults = false;
Whether or not the file system should be treated as a case sensitive one.
/** * Whether or not the file system should be treated as a case sensitive * one. */
protected boolean isCaseSensitive = true;
Whether a missing base directory is an error.
Since:Ant 1.7.1
/** * Whether a missing base directory is an error. * @since Ant 1.7.1 */
protected boolean errorOnMissingDir = true;
Whether or not symbolic links should be followed.
Since:Ant 1.5
/** * Whether or not symbolic links should be followed. * * @since Ant 1.5 */
private boolean followSymlinks = true;
Whether or not everything tested so far has been included.
/** Whether or not everything tested so far has been included. */
protected boolean everythingIncluded = true; // CheckStyle:VisibilityModifier ON
List of all scanned directories.
Since:Ant 1.6
/** * List of all scanned directories. * * @since Ant 1.6 */
private final Set<String> scannedDirs = new HashSet<>();
Map of all include patterns that are full file names and don't contain any wildcards.

Maps pattern string to TokenizedPath.

If this instance is not case sensitive, the file names get turned to upper case.

Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan method (cleared in clearCaches, actually).

Since:Ant 1.8.0
/** * Map of all include patterns that are full file names and don't * contain any wildcards. * * <p>Maps pattern string to TokenizedPath.</p> * * <p>If this instance is not case sensitive, the file names get * turned to upper case.</p> * * <p>Gets lazily initialized on the first invocation of * isIncluded or isExcluded and cleared at the end of the scan * method (cleared in clearCaches, actually).</p> * * @since Ant 1.8.0 */
private final Map<String, TokenizedPath> includeNonPatterns = new HashMap<>();
Map of all exclude patterns that are full file names and don't contain any wildcards.

Maps pattern string to TokenizedPath.

If this instance is not case sensitive, the file names get turned to upper case.

Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan method (cleared in clearCaches, actually).

Since:Ant 1.8.0
/** * Map of all exclude patterns that are full file names and don't * contain any wildcards. * * <p>Maps pattern string to TokenizedPath.</p> * * <p>If this instance is not case sensitive, the file names get * turned to upper case.</p> * * <p>Gets lazily initialized on the first invocation of * isIncluded or isExcluded and cleared at the end of the scan * method (cleared in clearCaches, actually).</p> * * @since Ant 1.8.0 */
private final Map<String, TokenizedPath> excludeNonPatterns = new HashMap<>();
Array of all include patterns that contain wildcards.

Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan method (cleared in clearCaches, actually).

/** * Array of all include patterns that contain wildcards. * * <p>Gets lazily initialized on the first invocation of * isIncluded or isExcluded and cleared at the end of the scan * method (cleared in clearCaches, actually).</p> */
private TokenizedPattern[] includePatterns;
Array of all exclude patterns that contain wildcards.

Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan method (cleared in clearCaches, actually).

/** * Array of all exclude patterns that contain wildcards. * * <p>Gets lazily initialized on the first invocation of * isIncluded or isExcluded and cleared at the end of the scan * method (cleared in clearCaches, actually).</p> */
private TokenizedPattern[] excludePatterns;
Have the non-pattern sets and pattern arrays for in- and excludes been initialized?
Since:Ant 1.6.3
/** * Have the non-pattern sets and pattern arrays for in- and * excludes been initialized? * * @since Ant 1.6.3 */
private boolean areNonPatternSetsReady = false;
Scanning flag.
Since:Ant 1.6.3
/** * Scanning flag. * * @since Ant 1.6.3 */
private boolean scanning = false;
Scanning lock.
Since:Ant 1.6.3
/** * Scanning lock. * * @since Ant 1.6.3 */
private final Object scanLock = new Object();
Slow scanning flag.
Since:Ant 1.6.3
/** * Slow scanning flag. * * @since Ant 1.6.3 */
private boolean slowScanning = false;
Slow scanning lock.
Since:Ant 1.6.3
/** * Slow scanning lock. * * @since Ant 1.6.3 */
private final Object slowScanLock = new Object();
Exception thrown during scan.
Since:Ant 1.6.3
/** * Exception thrown during scan. * * @since Ant 1.6.3 */
private IllegalStateException illegal = null;
The maximum number of times a symbolic link may be followed during a scan.
Since:Ant 1.8.0
/** * The maximum number of times a symbolic link may be followed * during a scan. * * @since Ant 1.8.0 */
private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS;
Absolute paths of all symlinks that haven't been followed but would have been if followsymlinks had been true or maxLevelsOfSymlinks had been higher.
Since:Ant 1.8.0
/** * Absolute paths of all symlinks that haven't been followed but * would have been if followsymlinks had been true or * maxLevelsOfSymlinks had been higher. * * @since Ant 1.8.0 */
private final Set<String> notFollowedSymlinks = new HashSet<>();
Test whether or not a given path matches the start of a given pattern up to the first "**".

This is not a general purpose test and should only be used if you can live with false positives. For example, pattern=**\a and str=b will yield true.

Params:
  • pattern – The pattern to match against. Must not be null.
  • str – The path to match, as a String. Must not be null.
Returns:whether or not a given path matches the start of a given pattern up to the first "**".
/** * Test whether or not a given path matches the start of a given * pattern up to the first "**". * <p> * This is not a general purpose test and should only be used if you * can live with false positives. For example, <code>pattern=**\a</code> * and <code>str=b</code> will yield <code>true</code>. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * * @return whether or not a given path matches the start of a given * pattern up to the first "**". */
protected static boolean matchPatternStart(final String pattern, final String str) { return SelectorUtils.matchPatternStart(pattern, str); }
Test whether or not a given path matches the start of a given pattern up to the first "**".

This is not a general purpose test and should only be used if you can live with false positives. For example, pattern=**\a and str=b will yield true.

Params:
  • pattern – The pattern to match against. Must not be null.
  • str – The path to match, as a String. Must not be null.
  • isCaseSensitive – Whether or not matching should be performed case sensitively.
Returns:whether or not a given path matches the start of a given pattern up to the first "**".
/** * Test whether or not a given path matches the start of a given * pattern up to the first "**". * <p> * This is not a general purpose test and should only be used if you * can live with false positives. For example, <code>pattern=**\a</code> * and <code>str=b</code> will yield <code>true</code>. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * @return whether or not a given path matches the start of a given * pattern up to the first "**". */
protected static boolean matchPatternStart(final String pattern, final String str, final boolean isCaseSensitive) { return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive); }
Test whether or not a given path matches a given pattern.
Params:
  • pattern – The pattern to match against. Must not be null.
  • str – The path to match, as a String. Must not be null.
Returns:true if the pattern matches against the string, or false otherwise.
/** * Test whether or not a given path matches a given pattern. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * * @return <code>true</code> if the pattern matches against the string, * or <code>false</code> otherwise. */
protected static boolean matchPath(final String pattern, final String str) { return SelectorUtils.matchPath(pattern, str); }
Test whether or not a given path matches a given pattern.
Params:
  • pattern – The pattern to match against. Must not be null.
  • str – The path to match, as a String. Must not be null.
  • isCaseSensitive – Whether or not matching should be performed case sensitively.
Returns:true if the pattern matches against the string, or false otherwise.
/** * Test whether or not a given path matches a given pattern. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * @return <code>true</code> if the pattern matches against the string, * or <code>false</code> otherwise. */
protected static boolean matchPath(final String pattern, final String str, final boolean isCaseSensitive) { return SelectorUtils.matchPath(pattern, str, isCaseSensitive); }
Test whether or not a string matches against a pattern. The pattern may contain two special characters:
'*' means zero or more characters
'?' means one and only one character
Params:
  • pattern – The pattern to match against. Must not be null.
  • str – The string which must be matched against the pattern. Must not be null.
Returns:true if the string matches against the pattern, or false otherwise.
/** * Test whether or not a string matches against a pattern. * The pattern may contain two special characters:<br> * '*' means zero or more characters<br> * '?' means one and only one character * * @param pattern The pattern to match against. * Must not be <code>null</code>. * @param str The string which must be matched against the pattern. * Must not be <code>null</code>. * * @return <code>true</code> if the string matches against the pattern, * or <code>false</code> otherwise. */
public static boolean match(final String pattern, final String str) { return SelectorUtils.match(pattern, str); }
Test whether or not a string matches against a pattern. The pattern may contain two special characters:
'*' means zero or more characters
'?' means one and only one character
Params:
  • pattern – The pattern to match against. Must not be null.
  • str – The string which must be matched against the pattern. Must not be null.
  • isCaseSensitive – Whether or not matching should be performed case sensitively.
Returns:true if the string matches against the pattern, or false otherwise.
/** * Test whether or not a string matches against a pattern. * The pattern may contain two special characters:<br> * '*' means zero or more characters<br> * '?' means one and only one character * * @param pattern The pattern to match against. * Must not be <code>null</code>. * @param str The string which must be matched against the pattern. * Must not be <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * * @return <code>true</code> if the string matches against the pattern, * or <code>false</code> otherwise. */
protected static boolean match(final String pattern, final String str, final boolean isCaseSensitive) { return SelectorUtils.match(pattern, str, isCaseSensitive); }
Get the list of patterns that should be excluded by default.
Returns:An array of String based on the current contents of the defaultExcludes Set.
Since:Ant 1.6
/** * Get the list of patterns that should be excluded by default. * * @return An array of <code>String</code> based on the current * contents of the <code>defaultExcludes</code> * <code>Set</code>. * * @since Ant 1.6 */
public static String[] getDefaultExcludes() { synchronized (defaultExcludes) { return defaultExcludes.toArray(new String[defaultExcludes .size()]); } }
Add a pattern to the default excludes unless it is already a default exclude.
Params:
  • s – A string to add as an exclude pattern.
Returns: true if the string was added; false if it already existed.
Since:Ant 1.6
/** * Add a pattern to the default excludes unless it is already a * default exclude. * * @param s A string to add as an exclude pattern. * @return <code>true</code> if the string was added; * <code>false</code> if it already existed. * * @since Ant 1.6 */
public static boolean addDefaultExclude(final String s) { synchronized (defaultExcludes) { return defaultExcludes.add(s); } }
Remove a string if it is a default exclude.
Params:
  • s – The string to attempt to remove.
Returns: true if s was a default exclude (and thus was removed); false if s was not in the default excludes list to begin with.
Since:Ant 1.6
/** * Remove a string if it is a default exclude. * * @param s The string to attempt to remove. * @return <code>true</code> if <code>s</code> was a default * exclude (and thus was removed); * <code>false</code> if <code>s</code> was not * in the default excludes list to begin with. * * @since Ant 1.6 */
public static boolean removeDefaultExclude(final String s) { synchronized (defaultExcludes) { return defaultExcludes.remove(s); } }
Go back to the hardwired default exclude patterns.
Since:Ant 1.6
/** * Go back to the hardwired default exclude patterns. * * @since Ant 1.6 */
public static void resetDefaultExcludes() { synchronized (defaultExcludes) { defaultExcludes.clear(); Collections.addAll(defaultExcludes, DEFAULTEXCLUDES); } }
Set the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match File.separatorChar.
Params:
  • basedir – The base directory to scan.
/** * Set the base directory to be scanned. This is the directory which is * scanned recursively. All '/' and '\' characters are replaced by * <code>File.separatorChar</code>, so the separator used need not match * <code>File.separatorChar</code>. * * @param basedir The base directory to scan. */
@Override public void setBasedir(final String basedir) { setBasedir(basedir == null ? null : new File(basedir.replace('/', File.separatorChar).replace( '\\', File.separatorChar))); }
Set the base directory to be scanned. This is the directory which is scanned recursively.
Params:
  • basedir – The base directory for scanning.
/** * Set the base directory to be scanned. This is the directory which is * scanned recursively. * * @param basedir The base directory for scanning. */
@Override public synchronized void setBasedir(final File basedir) { this.basedir = basedir; }
Return the base directory to be scanned. This is the directory which is scanned recursively.
Returns:the base directory to be scanned.
/** * Return the base directory to be scanned. * This is the directory which is scanned recursively. * * @return the base directory to be scanned. */
@Override public synchronized File getBasedir() { return basedir; }
Find out whether include exclude patterns are matched in a case sensitive way.
Returns:whether or not the scanning is case sensitive.
Since:Ant 1.6
/** * Find out whether include exclude patterns are matched in a * case sensitive way. * @return whether or not the scanning is case sensitive. * @since Ant 1.6 */
public synchronized boolean isCaseSensitive() { return isCaseSensitive; }
Set whether or not include and exclude patterns are matched in a case sensitive way.
Params:
  • isCaseSensitive – whether or not the file system should be regarded as a case sensitive one.
/** * Set whether or not include and exclude patterns are matched * in a case sensitive way. * * @param isCaseSensitive whether or not the file system should be * regarded as a case sensitive one. */
@Override public synchronized void setCaseSensitive(final boolean isCaseSensitive) { this.isCaseSensitive = isCaseSensitive; }
Sets whether or not a missing base directory is an error
Params:
  • errorOnMissingDir – whether or not a missing base directory is an error
Since:Ant 1.7.1
/** * Sets whether or not a missing base directory is an error * * @param errorOnMissingDir whether or not a missing base directory * is an error * @since Ant 1.7.1 */
public void setErrorOnMissingDir(final boolean errorOnMissingDir) { this.errorOnMissingDir = errorOnMissingDir; }
Get whether or not a DirectoryScanner follows symbolic links.
Returns:flag indicating whether symbolic links should be followed.
Since:Ant 1.6
/** * Get whether or not a DirectoryScanner follows symbolic links. * * @return flag indicating whether symbolic links should be followed. * * @since Ant 1.6 */
public synchronized boolean isFollowSymlinks() { return followSymlinks; }
Set whether or not symbolic links should be followed.
Params:
  • followSymlinks – whether or not symbolic links should be followed.
/** * Set whether or not symbolic links should be followed. * * @param followSymlinks whether or not symbolic links should be followed. */
public synchronized void setFollowSymlinks(final boolean followSymlinks) { this.followSymlinks = followSymlinks; }
The maximum number of times a symbolic link may be followed during a scan.
Params:
  • max – int
Since:Ant 1.8.0
/** * The maximum number of times a symbolic link may be followed * during a scan. * * @param max int * @since Ant 1.8.0 */
public void setMaxLevelsOfSymlinks(final int max) { maxLevelsOfSymlinks = max; }
Set the list of include patterns to use. All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match File.separatorChar.

When a pattern ends with a '/' or '\', "**" is appended.

Params:
  • includes – A list of include patterns. May be null, indicating that all files should be included. If a non-null list is given, all elements must be non-null.
/** * Set the list of include patterns to use. All '/' and '\' characters * are replaced by <code>File.separatorChar</code>, so the separator used * need not match <code>File.separatorChar</code>. * <p> * When a pattern ends with a '/' or '\', "**" is appended. * * @param includes A list of include patterns. * May be <code>null</code>, indicating that all files * should be included. If a non-<code>null</code> * list is given, all elements must be * non-<code>null</code>. */
@Override public synchronized void setIncludes(final String[] includes) { if (includes == null) { this.includes = null; } else { this.includes = Stream.of(includes) .map(DirectoryScanner::normalizePattern).toArray(String[]::new); } }
Set the list of exclude patterns to use. All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match File.separatorChar.

When a pattern ends with a '/' or '\', "**" is appended.

Params:
  • excludes – A list of exclude patterns. May be null, indicating that no files should be excluded. If a non-null list is given, all elements must be non-null.
/** * Set the list of exclude patterns to use. All '/' and '\' characters * are replaced by <code>File.separatorChar</code>, so the separator used * need not match <code>File.separatorChar</code>. * <p> * When a pattern ends with a '/' or '\', "**" is appended. * * @param excludes A list of exclude patterns. * May be <code>null</code>, indicating that no files * should be excluded. If a non-<code>null</code> list is * given, all elements must be non-<code>null</code>. */
@Override public synchronized void setExcludes(final String[] excludes) { if (excludes == null) { this.excludes = null; } else { this.excludes = Stream.of(excludes).map(DirectoryScanner::normalizePattern) .toArray(String[]::new); } }
Add to the list of exclude patterns to use. All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match File.separatorChar.

When a pattern ends with a '/' or '\', "**" is appended.

Params:
  • excludes – A list of exclude patterns. May be null, in which case the exclude patterns don't get changed at all.
Since:Ant 1.6.3
/** * Add to the list of exclude patterns to use. All '/' and '\' * characters are replaced by <code>File.separatorChar</code>, so * the separator used need not match <code>File.separatorChar</code>. * <p> * When a pattern ends with a '/' or '\', "**" is appended. * * @param excludes A list of exclude patterns. * May be <code>null</code>, in which case the * exclude patterns don't get changed at all. * * @since Ant 1.6.3 */
public synchronized void addExcludes(final String[] excludes) { if (excludes != null && excludes.length > 0) { if (this.excludes == null || this.excludes.length == 0) { setExcludes(excludes); } else { this.excludes = Stream.concat(Stream.of(this.excludes), Stream.of(excludes).map(DirectoryScanner::normalizePattern)) .toArray(String[]::new); } } }
All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match File.separatorChar.

When a pattern ends with a '/' or '\', "**" is appended.

Since:Ant 1.6.3
/** * All '/' and '\' characters are replaced by * <code>File.separatorChar</code>, so the separator used need not * match <code>File.separatorChar</code>. * * <p>When a pattern ends with a '/' or '\', "**" is appended.</p> * * @since Ant 1.6.3 */
private static String normalizePattern(final String p) { String pattern = p.replace('/', File.separatorChar) .replace('\\', File.separatorChar); if (pattern.endsWith(File.separator)) { pattern += SelectorUtils.DEEP_TREE_MATCH; } return pattern; }
Set the selectors that will select the filelist.
Params:
  • selectors – specifies the selectors to be invoked on a scan.
/** * Set the selectors that will select the filelist. * * @param selectors specifies the selectors to be invoked on a scan. */
@Override public synchronized void setSelectors(final FileSelector[] selectors) { this.selectors = selectors; }
Return whether or not the scanner has included all the files or directories it has come across so far.
Returns:true if all files and directories which have been found so far have been included.
/** * Return whether or not the scanner has included all the files or * directories it has come across so far. * * @return <code>true</code> if all files and directories which have * been found so far have been included. */
public synchronized boolean isEverythingIncluded() { return everythingIncluded; }
Scan for files which match at least one include pattern and don't match any exclude patterns. If there are selectors then the files must pass muster there, as well. Scans under basedir, if set; otherwise the include patterns without leading wildcards specify the absolute paths of the files that may be included.
Throws:
  • IllegalStateException – if the base directory was set incorrectly (i.e. if it doesn't exist or isn't a directory).
/** * Scan for files which match at least one include pattern and don't match * any exclude patterns. If there are selectors then the files must pass * muster there, as well. Scans under basedir, if set; otherwise the * include patterns without leading wildcards specify the absolute paths of * the files that may be included. * * @exception IllegalStateException if the base directory was set * incorrectly (i.e. if it doesn't exist or isn't a directory). */
@Override public void scan() throws IllegalStateException { synchronized (scanLock) { if (scanning) { while (scanning) { try { scanLock.wait(); } catch (final InterruptedException ignored) { } } if (illegal != null) { throw illegal; } return; } scanning = true; } final File savedBase = basedir; try { synchronized (this) { illegal = null; clearResults(); // set in/excludes to reasonable defaults if needed: final boolean nullIncludes = includes == null; includes = nullIncludes ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes; final boolean nullExcludes = excludes == null; excludes = nullExcludes ? new String[0] : excludes; if (basedir != null && !followSymlinks && Files.isSymbolicLink(basedir.toPath())) { notFollowedSymlinks.add(basedir.getAbsolutePath()); basedir = null; } if (basedir == null) { // if no basedir and no includes, nothing to do: if (nullIncludes) { return; } } else { if (!basedir.exists()) { if (errorOnMissingDir) { illegal = new IllegalStateException("basedir " + basedir + DOES_NOT_EXIST_POSTFIX); } else { // Nothing to do - basedir does not exist return; } } else if (!basedir.isDirectory()) { illegal = new IllegalStateException("basedir " + basedir + " is not a directory."); } if (illegal != null) { throw illegal; } } if (isIncluded(TokenizedPath.EMPTY_PATH)) { if (isExcluded(TokenizedPath.EMPTY_PATH)) { dirsExcluded.addElement(""); } else if (isSelected("", basedir)) { dirsIncluded.addElement(""); } else { dirsDeselected.addElement(""); } } else { dirsNotIncluded.addElement(""); } checkIncludePatterns(); clearCaches(); includes = nullIncludes ? null : includes; excludes = nullExcludes ? null : excludes; } } finally { basedir = savedBase; synchronized (scanLock) { scanning = false; scanLock.notifyAll(); } } }
This routine is actually checking all the include patterns in order to avoid scanning everything under base dir.
Since:Ant 1.6
/** * This routine is actually checking all the include patterns in * order to avoid scanning everything under base dir. * @since Ant 1.6 */
private void checkIncludePatterns() { ensureNonPatternSetsReady(); final Map<TokenizedPath, String> newroots = new HashMap<>(); // put in the newroots map the include patterns without // wildcard tokens for (TokenizedPattern includePattern : includePatterns) { final String pattern = includePattern.toString(); if (!shouldSkipPattern(pattern)) { newroots.put(includePattern.rtrimWildcardTokens(), pattern); } } for (final Map.Entry<String, TokenizedPath> entry : includeNonPatterns .entrySet()) { final String pattern = entry.getKey(); if (!shouldSkipPattern(pattern)) { newroots.put(entry.getValue(), pattern); } } if (newroots.containsKey(TokenizedPath.EMPTY_PATH) && basedir != null) { // we are going to scan everything anyway scandir(basedir, "", true); } else { File canonBase = null; if (basedir != null) { try { canonBase = basedir.getCanonicalFile(); } catch (final IOException ex) { throw new BuildException(ex); } } // only scan directories that can include matched files or // directories for (final Map.Entry<TokenizedPath, String> entry : newroots.entrySet()) { TokenizedPath currentPath = entry.getKey(); String currentelement = currentPath.toString(); if (basedir == null && !FileUtils.isAbsolutePath(currentelement)) { continue; } File myfile = new File(basedir, currentelement); if (myfile.exists()) { // may be on a case insensitive file system. We want // the results to show what's really on the disk, so // we need to double check. try { final String path = (basedir == null) ? myfile.getCanonicalPath() : FILE_UTILS.removeLeadingPath(canonBase, myfile.getCanonicalFile()); if (!path.equals(currentelement) || ON_VMS) { myfile = currentPath.findFile(basedir, true); if (myfile != null && basedir != null) { currentelement = FILE_UTILS.removeLeadingPath( basedir, myfile); if (!currentPath.toString().equals(currentelement)) { currentPath = new TokenizedPath(currentelement); } } } } catch (final IOException ex) { throw new BuildException(ex); } } if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) { final File f = currentPath.findFile(basedir, false); if (f != null && f.exists()) { // adapt currentelement to the case we've // actually found currentelement = (basedir == null) ? f.getAbsolutePath() : FILE_UTILS.removeLeadingPath(basedir, f); myfile = f; currentPath = new TokenizedPath(currentelement); } } if (myfile != null && myfile.exists()) { if (!followSymlinks && currentPath.isSymlink(basedir)) { accountForNotFollowedSymlink(currentPath, myfile); continue; } if (myfile.isDirectory()) { if (isIncluded(currentPath) && !currentelement.isEmpty()) { accountForIncludedDir(currentPath, myfile, true); } else { scandir(myfile, currentPath, true); } } else if (myfile.isFile()) { final String originalpattern = entry.getValue(); final boolean included = isCaseSensitive() ? originalpattern.equals(currentelement) : originalpattern.equalsIgnoreCase(currentelement); if (included) { accountForIncludedFile(currentPath, myfile); } } } } } }
true if the pattern specifies a relative path without basedir or an absolute path not inside basedir.
Since:Ant 1.8.0
/** * true if the pattern specifies a relative path without basedir * or an absolute path not inside basedir. * * @since Ant 1.8.0 */
private boolean shouldSkipPattern(final String pattern) { if (FileUtils.isAbsolutePath(pattern)) { //skip abs. paths not under basedir, if set: return !(basedir == null || SelectorUtils.matchPatternStart(pattern, basedir.getAbsolutePath(), isCaseSensitive())); } return basedir == null; }
Clear the result caches for a scan.
/** * Clear the result caches for a scan. */
protected synchronized void clearResults() { filesIncluded = new VectorSet<>(); filesNotIncluded = new VectorSet<>(); filesExcluded = new VectorSet<>(); filesDeselected = new VectorSet<>(); dirsIncluded = new VectorSet<>(); dirsNotIncluded = new VectorSet<>(); dirsExcluded = new VectorSet<>(); dirsDeselected = new VectorSet<>(); everythingIncluded = (basedir != null); scannedDirs.clear(); notFollowedSymlinks.clear(); }
Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, whereas a fast scan will only have full results for included files, as it ignores directories which can't possibly hold any included files/directories.

Returns immediately if a slow scan has already been completed.

/** * Top level invocation for a slow scan. A slow scan builds up a full * list of excluded/included files/directories, whereas a fast scan * will only have full results for included files, as it ignores * directories which can't possibly hold any included files/directories. * <p> * Returns immediately if a slow scan has already been completed. */
protected void slowScan() { synchronized (slowScanLock) { if (haveSlowResults) { return; } if (slowScanning) { while (slowScanning) { try { slowScanLock.wait(); } catch (final InterruptedException e) { // Empty } } return; } slowScanning = true; } try { synchronized (this) { // set in/excludes to reasonable defaults if needed: final boolean nullIncludes = (includes == null); includes = nullIncludes ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes; final boolean nullExcludes = (excludes == null); excludes = nullExcludes ? new String[0] : excludes; final String[] excl = new String[dirsExcluded.size()]; dirsExcluded.copyInto(excl); final String[] notIncl = new String[dirsNotIncluded.size()]; dirsNotIncluded.copyInto(notIncl); ensureNonPatternSetsReady(); processSlowScan(excl); processSlowScan(notIncl); clearCaches(); includes = nullIncludes ? null : includes; excludes = nullExcludes ? null : excludes; } } finally { synchronized (slowScanLock) { haveSlowResults = true; slowScanning = false; slowScanLock.notifyAll(); } } } private void processSlowScan(final String[] arr) { for (String element : arr) { final TokenizedPath path = new TokenizedPath(element); if (!couldHoldIncluded(path) || contentsExcluded(path)) { scandir(new File(basedir, element), path, false); } } }
Scan the given directory for files and directories. Found files and directories are placed in their respective collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is scanned recursively.
Params:
  • dir – The directory to scan. Must not be null.
  • vpath – The path relative to the base directory (needed to prevent problems with an absolute path when using dir). Must not be null.
  • fast – Whether or not this call is part of a fast scan.
See Also:
/** * Scan the given directory for files and directories. Found files and * directories are placed in their respective collections, based on the * matching of includes, excludes, and the selectors. When a directory * is found, it is scanned recursively. * * @param dir The directory to scan. Must not be <code>null</code>. * @param vpath The path relative to the base directory (needed to * prevent problems with an absolute path when using * dir). Must not be <code>null</code>. * @param fast Whether or not this call is part of a fast scan. * * @see #filesIncluded * @see #filesNotIncluded * @see #filesExcluded * @see #dirsIncluded * @see #dirsNotIncluded * @see #dirsExcluded * @see #slowScan */
protected void scandir(final File dir, final String vpath, final boolean fast) { scandir(dir, new TokenizedPath(vpath), fast); }
Scan the given directory for files and directories. Found files and directories are placed in their respective collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is scanned recursively.
Params:
  • dir – The directory to scan. Must not be null.
  • path – The path relative to the base directory (needed to prevent problems with an absolute path when using dir). Must not be null.
  • fast – Whether or not this call is part of a fast scan.
See Also:
/** * Scan the given directory for files and directories. Found files and * directories are placed in their respective collections, based on the * matching of includes, excludes, and the selectors. When a directory * is found, it is scanned recursively. * * @param dir The directory to scan. Must not be <code>null</code>. * @param path The path relative to the base directory (needed to * prevent problems with an absolute path when using * dir). Must not be <code>null</code>. * @param fast Whether or not this call is part of a fast scan. * * @see #filesIncluded * @see #filesNotIncluded * @see #filesExcluded * @see #dirsIncluded * @see #dirsNotIncluded * @see #dirsExcluded * @see #slowScan */
private void scandir(final File dir, final TokenizedPath path, final boolean fast) { if (dir == null) { throw new BuildException("dir must not be null."); } final String[] newfiles = dir.list(); if (newfiles == null) { if (!dir.exists()) { throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX); } else if (!dir.isDirectory()) { throw new BuildException("%s is not a directory.", dir); } else { throw new BuildException("IO error scanning directory '%s'", dir.getAbsolutePath()); } } scandir(dir, path, fast, newfiles, new LinkedList<>()); } private void scandir(final File dir, final TokenizedPath path, final boolean fast, String[] newFiles, final Deque<String> directoryNamesFollowed) { String vpath = path.toString(); if (!vpath.isEmpty() && !vpath.endsWith(File.separator)) { vpath += File.separator; } // avoid double scanning of directories, can only happen in fast mode if (fast && hasBeenScanned(vpath)) { return; } if (!followSymlinks) { final ArrayList<String> noLinks = new ArrayList<>(); for (final String newFile : newFiles) { final Path filePath; if (dir == null) { filePath = Paths.get(newFile); } else { filePath = Paths.get(dir.toPath().toString(), newFile); } if (Files.isSymbolicLink(filePath)) { final String name = vpath + newFile; final File file = new File(dir, newFile); if (file.isDirectory()) { dirsExcluded.addElement(name); } else if (file.isFile()) { filesExcluded.addElement(name); } accountForNotFollowedSymlink(name, file); } else { noLinks.add(newFile); } } newFiles = noLinks.toArray(new String[noLinks.size()]); } else { directoryNamesFollowed.addFirst(dir.getName()); } for (String newFile : newFiles) { final String name = vpath + newFile; final TokenizedPath newPath = new TokenizedPath(path, newFile); final File file = new File(dir, newFile); final String[] children = file.list(); if (children == null || (children.length == 0 && file.isFile())) { if (isIncluded(newPath)) { accountForIncludedFile(newPath, file); } else { everythingIncluded = false; filesNotIncluded.addElement(name); } } else if (file.isDirectory()) { // dir if (followSymlinks && causesIllegalSymlinkLoop(newFile, dir, directoryNamesFollowed)) { // will be caught and redirected to Ant's logging system System.err.println("skipping symbolic link " + file.getAbsolutePath() + " -- too many levels of symbolic" + " links."); notFollowedSymlinks.add(file.getAbsolutePath()); continue; } if (isIncluded(newPath)) { accountForIncludedDir(newPath, file, fast, children, directoryNamesFollowed); } else { everythingIncluded = false; dirsNotIncluded.addElement(name); if (fast && couldHoldIncluded(newPath) && !contentsExcluded(newPath)) { scandir(file, newPath, fast, children, directoryNamesFollowed); } } if (!fast) { scandir(file, newPath, fast, children, directoryNamesFollowed); } } } if (followSymlinks) { directoryNamesFollowed.removeFirst(); } }
Process included file.
Params:
  • name – path of the file relative to the directory of the FileSet.
  • file – included File.
/** * Process included file. * @param name path of the file relative to the directory of the FileSet. * @param file included File. */
private void accountForIncludedFile(final TokenizedPath name, final File file) { processIncluded(name, file, filesIncluded, filesExcluded, filesDeselected); }
Process included directory.
Params:
  • name – path of the directory relative to the directory of the FileSet.
  • file – directory as File.
  • fast – whether to perform fast scans.
/** * Process included directory. * @param name path of the directory relative to the directory of * the FileSet. * @param file directory as File. * @param fast whether to perform fast scans. */
private void accountForIncludedDir(final TokenizedPath name, final File file, final boolean fast) { processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected); if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) { scandir(file, name, fast); } } private void accountForIncludedDir(final TokenizedPath name, final File file, final boolean fast, final String[] children, final Deque<String> directoryNamesFollowed) { processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected); if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) { scandir(file, name, fast, children, directoryNamesFollowed); } } private void accountForNotFollowedSymlink(final String name, final File file) { accountForNotFollowedSymlink(new TokenizedPath(name), file); } private void accountForNotFollowedSymlink(final TokenizedPath name, final File file) { if (!isExcluded(name) && (isIncluded(name) || (file.isDirectory() && couldHoldIncluded(name) && !contentsExcluded(name)))) { notFollowedSymlinks.add(file.getAbsolutePath()); } } private void processIncluded(final TokenizedPath path, final File file, final List<String> inc, final List<String> exc, final List<String> des) { final String name = path.toString(); if (inc.contains(name) || exc.contains(name) || des.contains(name)) { return; } boolean included = false; if (isExcluded(path)) { exc.add(name); } else if (isSelected(name, file)) { included = true; inc.add(name); } else { des.add(name); } everythingIncluded &= included; }
Test whether or not a name matches against at least one include pattern.
Params:
  • name – The path to match. Must not be null.
Returns:true when the name matches against at least one include pattern, or false otherwise.
/** * Test whether or not a name matches against at least one include * pattern. * * @param name The path to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against at least one * include pattern, or <code>false</code> otherwise. */
protected boolean isIncluded(final String name) { return isIncluded(new TokenizedPath(name)); }
Test whether or not a name matches against at least one include pattern.
Params:
  • path – The tokenized path to match. Must not be null.
Returns:true when the name matches against at least one include pattern, or false otherwise.
/** * Test whether or not a name matches against at least one include * pattern. * * @param path The tokenized path to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against at least one * include pattern, or <code>false</code> otherwise. */
private boolean isIncluded(final TokenizedPath path) { ensureNonPatternSetsReady(); String toMatch = path.toString(); if (!isCaseSensitive()) { toMatch = toMatch.toUpperCase(); } return includeNonPatterns.containsKey(toMatch) || Stream.of(includePatterns).anyMatch(p -> p.matchPath(path, isCaseSensitive())); }
Test whether or not a name matches the start of at least one include pattern.
Params:
  • name – The name to match. Must not be null.
Returns:true when the name matches against the start of at least one include pattern, or false otherwise.
/** * Test whether or not a name matches the start of at least one include * pattern. * * @param name The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against the start of at * least one include pattern, or <code>false</code> otherwise. */
protected boolean couldHoldIncluded(final String name) { return couldHoldIncluded(new TokenizedPath(name)); }
Test whether or not a name matches the start of at least one include pattern.
Params:
  • tokenizedName – The name to match. Must not be null.
Returns:true when the name matches against the start of at least one include pattern, or false otherwise.
/** * Test whether or not a name matches the start of at least one include * pattern. * * @param tokenizedName The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against the start of at * least one include pattern, or <code>false</code> otherwise. */
private boolean couldHoldIncluded(final TokenizedPath tokenizedName) { return Stream.concat(Stream.of(includePatterns), includeNonPatterns.values().stream().map(TokenizedPath::toPattern)) .anyMatch(pat -> couldHoldIncluded(tokenizedName, pat)); }
Test whether or not a name matches the start of the given include pattern.
Params:
  • tokenizedName – The name to match. Must not be null.
Returns:true when the name matches against the start of the include pattern, or false otherwise.
/** * Test whether or not a name matches the start of the given * include pattern. * * @param tokenizedName The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against the start of the * include pattern, or <code>false</code> otherwise. */
private boolean couldHoldIncluded(final TokenizedPath tokenizedName, final TokenizedPattern tokenizedInclude) { return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive()) && isMorePowerfulThanExcludes(tokenizedName.toString()) && isDeeper(tokenizedInclude, tokenizedName); }
Verify that a pattern specifies files deeper than the level of the specified file.
Params:
  • pattern – the pattern to check.
  • name – the name to check.
Returns:whether the pattern is deeper than the name.
Since:Ant 1.6.3
/** * Verify that a pattern specifies files deeper * than the level of the specified file. * @param pattern the pattern to check. * @param name the name to check. * @return whether the pattern is deeper than the name. * @since Ant 1.6.3 */
private boolean isDeeper(final TokenizedPattern pattern, final TokenizedPath name) { return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH) || pattern.depth() > name.depth(); }
Find out whether one particular include pattern is more powerful than all the excludes. Note: the power comparison is based on the length of the include pattern and of the exclude patterns without the wildcards. Ideally the comparison should be done based on the depth of the match; that is to say how many file separators have been matched before the first ** or the end of the pattern. IMPORTANT : this function should return false "with care". @param name the relative path to test. @return true if there is no exclude pattern more powerful than this include pattern. @since Ant 1.6
/** * Find out whether one particular include pattern is more powerful * than all the excludes. * Note: the power comparison is based on the length of the include pattern * and of the exclude patterns without the wildcards. * Ideally the comparison should be done based on the depth * of the match; that is to say how many file separators have been matched * before the first ** or the end of the pattern. * * IMPORTANT : this function should return false "with care". * * @param name the relative path to test. * @return true if there is no exclude pattern more powerful than * this include pattern. * @since Ant 1.6 */
private boolean isMorePowerfulThanExcludes(final String name) { final String soughtexclude = name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH; return Stream.of(excludePatterns).map(Object::toString) .noneMatch(Predicate.isEqual(soughtexclude)); }
Test whether all contents of the specified directory must be excluded.
Params:
  • path – the path to check.
Returns:whether all the specified directory's contents are excluded.
/** * Test whether all contents of the specified directory must be excluded. * @param path the path to check. * @return whether all the specified directory's contents are excluded. */
/* package */ boolean contentsExcluded(final TokenizedPath path) { return Stream.of(excludePatterns) .filter(p -> p.endsWith(SelectorUtils.DEEP_TREE_MATCH)) .map(TokenizedPattern::withoutLastToken) .anyMatch(wlt -> wlt.matchPath(path, isCaseSensitive())); }
Test whether or not a name matches against at least one exclude pattern.
Params:
  • name – The name to match. Must not be null.
Returns:true when the name matches against at least one exclude pattern, or false otherwise.
/** * Test whether or not a name matches against at least one exclude * pattern. * * @param name The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against at least one * exclude pattern, or <code>false</code> otherwise. */
protected boolean isExcluded(final String name) { return isExcluded(new TokenizedPath(name)); }
Test whether or not a name matches against at least one exclude pattern.
Params:
  • name – The name to match. Must not be null.
Returns:true when the name matches against at least one exclude pattern, or false otherwise.
/** * Test whether or not a name matches against at least one exclude * pattern. * * @param name The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against at least one * exclude pattern, or <code>false</code> otherwise. */
private boolean isExcluded(final TokenizedPath name) { ensureNonPatternSetsReady(); String toMatch = name.toString(); if (!isCaseSensitive()) { toMatch = toMatch.toUpperCase(); } return excludeNonPatterns.containsKey(toMatch) || Stream.of(excludePatterns).anyMatch(p -> p.matchPath(name, isCaseSensitive())); }
Test whether a file should be selected.
Params:
  • name – the filename to check for selecting.
  • file – the java.io.File object for this filename.
Returns:false when the selectors says that the file should not be selected, true otherwise.
/** * Test whether a file should be selected. * * @param name the filename to check for selecting. * @param file the java.io.File object for this filename. * @return <code>false</code> when the selectors says that the file * should not be selected, <code>true</code> otherwise. */
protected boolean isSelected(final String name, final File file) { return selectors == null || Stream.of(selectors).allMatch(sel -> sel.isSelected(basedir, name, file)); }
Return the names of the files which matched at least one of the include patterns and none of the exclude patterns. The names are relative to the base directory.
Returns:the names of the files which matched at least one of the include patterns and none of the exclude patterns.
/** * Return the names of the files which matched at least one of the * include patterns and none of the exclude patterns. * The names are relative to the base directory. * * @return the names of the files which matched at least one of the * include patterns and none of the exclude patterns. */
@Override public String[] getIncludedFiles() { String[] files; synchronized (this) { if (filesIncluded == null) { throw new IllegalStateException("Must call scan() first"); } files = filesIncluded.toArray(new String[filesIncluded.size()]); } Arrays.sort(files); return files; }
Return the count of included files.
Returns:int.
Since:Ant 1.6.3
/** * Return the count of included files. * @return <code>int</code>. * @since Ant 1.6.3 */
public synchronized int getIncludedFilesCount() { if (filesIncluded == null) { throw new IllegalStateException("Must call scan() first"); } return filesIncluded.size(); }
Return the names of the files which matched none of the include patterns. The names are relative to the base directory. This involves performing a slow scan if one has not already been completed.
See Also:
Returns:the names of the files which matched none of the include patterns.
/** * Return the names of the files which matched none of the include * patterns. The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed. * * @return the names of the files which matched none of the include * patterns. * * @see #slowScan */
@Override public synchronized String[] getNotIncludedFiles() { slowScan(); return filesNotIncluded.toArray(new String[filesNotIncluded.size()]); }
Return the names of the files which matched at least one of the include patterns and at least one of the exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has not already been completed.
See Also:
Returns:the names of the files which matched at least one of the include patterns and at least one of the exclude patterns.
/** * Return the names of the files which matched at least one of the * include patterns and at least one of the exclude patterns. * The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed. * * @return the names of the files which matched at least one of the * include patterns and at least one of the exclude patterns. * * @see #slowScan */
@Override public synchronized String[] getExcludedFiles() { slowScan(); return filesExcluded.toArray(new String[filesExcluded.size()]); }

Return the names of the files which were selected out and therefore not ultimately included.

The names are relative to the base directory. This involves performing a slow scan if one has not already been completed.

See Also:
Returns:the names of the files which were deselected.
/** * <p>Return the names of the files which were selected out and * therefore not ultimately included.</p> * * <p>The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed.</p> * * @return the names of the files which were deselected. * * @see #slowScan */
@Override public synchronized String[] getDeselectedFiles() { slowScan(); return filesDeselected.toArray(new String[filesDeselected.size()]); }
Return the names of the directories which matched at least one of the include patterns and none of the exclude patterns. The names are relative to the base directory.
Returns:the names of the directories which matched at least one of the include patterns and none of the exclude patterns.
/** * Return the names of the directories which matched at least one of the * include patterns and none of the exclude patterns. * The names are relative to the base directory. * * @return the names of the directories which matched at least one of the * include patterns and none of the exclude patterns. */
@Override public String[] getIncludedDirectories() { String[] directories; synchronized (this) { if (dirsIncluded == null) { throw new IllegalStateException("Must call scan() first"); } directories = dirsIncluded.toArray(new String[dirsIncluded.size()]); } Arrays.sort(directories); return directories; }
Return the count of included directories.
Returns:int.
Since:Ant 1.6.3
/** * Return the count of included directories. * @return <code>int</code>. * @since Ant 1.6.3 */
public synchronized int getIncludedDirsCount() { if (dirsIncluded == null) { throw new IllegalStateException("Must call scan() first"); } return dirsIncluded.size(); }
Return the names of the directories which matched none of the include patterns. The names are relative to the base directory. This involves performing a slow scan if one has not already been completed.
See Also:
Returns:the names of the directories which matched none of the include patterns.
/** * Return the names of the directories which matched none of the include * patterns. The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed. * * @return the names of the directories which matched none of the include * patterns. * * @see #slowScan */
@Override public synchronized String[] getNotIncludedDirectories() { slowScan(); return dirsNotIncluded.toArray(new String[dirsNotIncluded.size()]); }
Return the names of the directories which matched at least one of the include patterns and at least one of the exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has not already been completed.
See Also:
Returns:the names of the directories which matched at least one of the include patterns and at least one of the exclude patterns.
/** * Return the names of the directories which matched at least one of the * include patterns and at least one of the exclude patterns. * The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed. * * @return the names of the directories which matched at least one of the * include patterns and at least one of the exclude patterns. * * @see #slowScan */
@Override public synchronized String[] getExcludedDirectories() { slowScan(); return dirsExcluded.toArray(new String[dirsExcluded.size()]); }

Return the names of the directories which were selected out and therefore not ultimately included.

The names are relative to the base directory. This involves performing a slow scan if one has not already been completed.

See Also:
Returns:the names of the directories which were deselected.
/** * <p>Return the names of the directories which were selected out and * therefore not ultimately included.</p> * * <p>The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed.</p> * * @return the names of the directories which were deselected. * * @see #slowScan */
@Override public synchronized String[] getDeselectedDirectories() { slowScan(); return dirsDeselected.toArray(new String[dirsDeselected.size()]); }
Absolute paths of all symbolic links that haven't been followed but would have been followed had followsymlinks been true or maxLevelsOfSymlinks been bigger.
See Also:
Returns:sorted array of not followed symlinks
Since:Ant 1.8.0
/** * Absolute paths of all symbolic links that haven't been followed * but would have been followed had followsymlinks been true or * maxLevelsOfSymlinks been bigger. * * @return sorted array of not followed symlinks * @since Ant 1.8.0 * @see #notFollowedSymlinks */
public synchronized String[] getNotFollowedSymlinks() { String[] links; synchronized (this) { links = notFollowedSymlinks.toArray(new String[notFollowedSymlinks.size()]); } Arrays.sort(links); return links; }
Add default exclusions to the current exclusions set.
/** * Add default exclusions to the current exclusions set. */
@Override public synchronized void addDefaultExcludes() { Stream<String> s = Stream.of(getDefaultExcludes()).map(p -> p.replace('/', File.separatorChar).replace('\\', File.separatorChar)); if (excludes != null) { s = Stream.concat(Stream.of(excludes), s); } excludes = s.toArray(String[]::new); }
Get the named resource.
Params:
  • name – path name of the file relative to the dir attribute.
Returns:the resource with the given name.
Since:Ant 1.5.2
/** * Get the named resource. * @param name path name of the file relative to the dir attribute. * * @return the resource with the given name. * @since Ant 1.5.2 */
@Override public synchronized Resource getResource(final String name) { return new FileResource(basedir, name); }
Has the directory with the given path relative to the base directory already been scanned?

Registers the given directory as scanned as a side effect.

Since:Ant 1.6
/** * Has the directory with the given path relative to the base * directory already been scanned? * * <p>Registers the given directory as scanned as a side effect.</p> * * @since Ant 1.6 */
private boolean hasBeenScanned(final String vpath) { return !scannedDirs.add(vpath); }
This method is of interest for testing purposes. The returned Set is live and should not be modified.
Returns:the Set of relative directory names that have been scanned.
/** * This method is of interest for testing purposes. The returned * Set is live and should not be modified. * @return the Set of relative directory names that have been scanned. */
/* package-private */ Set<String> getScannedDirs() { return scannedDirs; }
Clear internal caches.
Since:Ant 1.6
/** * Clear internal caches. * * @since Ant 1.6 */
private synchronized void clearCaches() { includeNonPatterns.clear(); excludeNonPatterns.clear(); includePatterns = null; excludePatterns = null; areNonPatternSetsReady = false; }
Ensure that the in|exclude "patterns" have been properly divided up.
Since:Ant 1.6.3
/** * Ensure that the in|exclude &quot;patterns&quot; * have been properly divided up. * * @since Ant 1.6.3 */
/* package */ synchronized void ensureNonPatternSetsReady() { if (!areNonPatternSetsReady) { includePatterns = fillNonPatternSet(includeNonPatterns, includes); excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes); areNonPatternSetsReady = true; } }
Add all patterns that are not real patterns (do not contain wildcards) to the set and returns the real patterns.
Params:
  • map – Map to populate.
  • patterns – String[] of patterns.
Since:Ant 1.8.0
/** * Add all patterns that are not real patterns (do not contain * wildcards) to the set and returns the real patterns. * * @param map Map to populate. * @param patterns String[] of patterns. * @since Ant 1.8.0 */
private TokenizedPattern[] fillNonPatternSet(final Map<String, TokenizedPath> map, final String[] patterns) { final List<TokenizedPattern> al = new ArrayList<>(patterns.length); for (String pattern : patterns) { if (SelectorUtils.hasWildcards(pattern)) { al.add(new TokenizedPattern(pattern)); } else { final String s = isCaseSensitive() ? pattern : pattern.toUpperCase(); map.put(s, new TokenizedPath(s)); } } return al.toArray(new TokenizedPattern[al.size()]); }
Would following the given directory cause a loop of symbolic links deeper than allowed?

Can only happen if the given directory has been seen at least more often than allowed during the current scan and it is a symbolic link and enough other occurrences of the same name higher up are symbolic links that point to the same place.

Since:Ant 1.8.0
/** * Would following the given directory cause a loop of symbolic * links deeper than allowed? * * <p>Can only happen if the given directory has been seen at * least more often than allowed during the current scan and it is * a symbolic link and enough other occurrences of the same name * higher up are symbolic links that point to the same place.</p> * * @since Ant 1.8.0 */
private boolean causesIllegalSymlinkLoop(final String dirName, final File parent, final Deque<String> directoryNamesFollowed) { try { final Path dirPath; if (parent == null) { dirPath = Paths.get(dirName); } else { dirPath = Paths.get(parent.toPath().toString(), dirName); } if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks && Collections.frequency(directoryNamesFollowed, dirName) >= maxLevelsOfSymlinks && Files.isSymbolicLink(dirPath)) { final List<String> files = new ArrayList<>(); File f = FILE_UTILS.resolveFile(parent, dirName); final String target = f.getCanonicalPath(); files.add(target); StringBuilder relPath = new StringBuilder(); for (final String dir : directoryNamesFollowed) { relPath.append("../"); if (dirName.equals(dir)) { f = FILE_UTILS.resolveFile(parent, relPath + dir); files.add(f.getCanonicalPath()); if (files.size() > maxLevelsOfSymlinks && Collections.frequency(files, target) > maxLevelsOfSymlinks) { return true; } } } } return false; } catch (final IOException ex) { throw new BuildException( "Caught error while checking for symbolic links", ex); } } }