Abstract class that walks through a directory hierarchy and provides
subclasses with convenient hooks to add specific behaviour.
This class operates with a
FileFilter
and maximum depth to
limit the files and direcories visited.
Commons IO supplies many common filter implementations in the
filefilter package.
The following sections describe:
1. Example Implementation
There are many possible extensions, for example, to delete all
files and '.svn' directories, and return a list of deleted files:
public class FileCleaner extends DirectoryWalker {
public FileCleaner() {
super();
}
public List clean(File startDirectory) {
List results = new ArrayList();
walk(startDirectory, results);
return results;
}
protected boolean handleDirectory(File directory, int depth, Collection results) {
// delete svn directories and then skip
if (".svn".equals(directory.getName())) {
directory.delete();
return false;
} else {
return true;
}
}
protected void handleFile(File file, int depth, Collection results) {
// delete file and add to list of deleted
file.delete();
results.add(file);
}
}
2. Filter Example
Choosing which directories and files to process can be a key aspect
of using this class. This information can be setup in three ways,
via three different constructors.
The first option is to visit all directories and files.
This is achieved via the no-args constructor.
The second constructor option is to supply a single
FileFilter
that describes the files and directories to visit. Care must be taken
with this option as the same filter is used for both directories
and files.
For example, if you wanted all directories which are not hidden
and files which end in ".txt":
public class FooDirectoryWalker extends DirectoryWalker {
public FooDirectoryWalker(FileFilter filter) {
super(filter, -1);
}
}
// Build up the filters and create the walker
// Create a filter for Non-hidden directories
IOFileFilter fooDirFilter =
FileFilterUtils.andFileFilter(FileFilterUtils.directoryFileFilter,
HiddenFileFilter.VISIBLE);
// Create a filter for Files ending in ".txt"
IOFileFilter fooFileFilter =
FileFilterUtils.andFileFilter(FileFilterUtils.fileFileFilter,
FileFilterUtils.suffixFileFilter(".txt"));
// Combine the directory and file filters using an OR condition
java.io.FileFilter fooFilter =
FileFilterUtils.orFileFilter(fooDirFilter, fooFileFilter);
// Use the filter to construct a DirectoryWalker implementation
FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter);
The third constructor option is to specify separate filters, one for
directories and one for files. These are combined internally to form
the correct
FileFilter
, something which is very easy to
get wrong when attempted manually, particularly when trying to
express constructs like 'any file in directories named docs'.
For example, if you wanted all directories which are not hidden
and files which end in ".txt":
public class FooDirectoryWalker extends DirectoryWalker {
public FooDirectoryWalker(IOFileFilter dirFilter, IOFileFilter fileFilter) {
super(dirFilter, fileFilter, -1);
}
}
// Use the filters to construct the walker
FooDirectoryWalker walker = new FooDirectoryWalker(
HiddenFileFilter.VISIBLE,
FileFilterUtils.suffixFileFilter(".txt"),
);
This is much simpler than the previous example, and is why it is the preferred
option for filtering.
3. Cancellation
The DirectoryWalker contains some of the logic required for cancel processing.
Subclasses must complete the implementation.
What
DirectoryWalker
does provide for cancellation is:
Implementations need to provide:
- The decision logic on whether to cancel processing or not.
- Constructing and throwing a
DirectoryWalker.CancelException
. - Custom cancel processing in the
handleCancelled()
method.
Two possible scenarios are envisaged for cancellation:
The following sections provide example implementations for these two different
scenarios.
3.1 External / Multi-threaded
This example provides a public
cancel()
method that can be
called by another thread to stop the processing. A typical example use-case
would be a cancel button on a GUI. Calling this method sets a
volatile flag to ensure it will work properly in a multi-threaded environment.
The flag is returned by the
handleIsCancelled()
method, which
will cause the walk to stop immediately. The
handleCancelled()
method will be the next, and last, callback method received once cancellation
has occurred.
public class FooDirectoryWalker extends DirectoryWalker {
private volatile boolean cancelled = false;
public void cancel() {
cancelled = true;
}
private void handleIsCancelled(File file, int depth, Collection results) {
return cancelled;
}
protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
// implement processing required when a cancellation occurs
}
}
3.2 Internal
This shows an example of how internal cancellation processing could be implemented.
Note the decision logic and throwing a
DirectoryWalker.CancelException
could be implemented
in any of the
lifecycle methods.
public class BarDirectoryWalker extends DirectoryWalker {
protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
// cancel if hidden directory
if (directory.isHidden()) {
throw new CancelException(file, depth);
}
return true;
}
protected void handleFile(File file, int depth, Collection results) throws IOException {
// cancel if read-only file
if (!file.canWrite()) {
throw new CancelException(file, depth);
}
results.add(file);
}
protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
// implement processing required when a cancellation occurs
}
}
checkIfCancelled
protected final void checkIfCancelled(File file,
int depth,
Collection results)
throws IOException
Checks whether the walk has been cancelled by calling
handleIsCancelled(File,int,Collection)
,
throwing a
CancelException
if it has.
Writers of subclasses should not normally call this method as it is called
automatically by the walk of the tree. However, sometimes a single method,
typically
handleFile(File,int,Collection)
, may take a long time to run. In that case,
you may wish to check for cancellation by calling this method.
file
- the current file being processeddepth
- the current file level (starting directory = 0)results
- the collection of result objects, may be updated
handleCancelled
protected void handleCancelled(File startDirectory,
Collection results,
DirectoryWalker.CancelException cancel)
throws IOException
Overridable callback method invoked when the operation is cancelled.
The file being processed when the cancellation occurred can be
obtained from the exception.
This implementation just re-throws the
DirectoryWalker.CancelException
.
startDirectory
- the directory that the walk started fromresults
- the collection of result objects, may be updatedcancel
- the exception throw to cancel further processing
containing details at the point of cancellation.
handleDirectory
protected boolean handleDirectory(File directory,
int depth,
Collection results)
throws IOException
Overridable callback method invoked to determine if a directory should be processed.
This method returns a boolean to indicate if the directory should be examined or not.
If you return false, the entire directory and any subdirectories will be skipped.
Note that this functionality is in addition to the filtering by file filter.
This implementation does nothing and returns true.
directory
- the current directory being processeddepth
- the current directory level (starting directory = 0)results
- the collection of result objects, may be updated
- true to process this directory, false to skip this directory
handleDirectoryEnd
protected void handleDirectoryEnd(File directory,
int depth,
Collection results)
throws IOException
Overridable callback method invoked at the end of processing each directory.
This implementation does nothing.
directory
- the directory being processeddepth
- the current directory level (starting directory = 0)results
- the collection of result objects, may be updated
handleDirectoryStart
protected void handleDirectoryStart(File directory,
int depth,
Collection results)
throws IOException
Overridable callback method invoked at the start of processing each directory.
This implementation does nothing.
directory
- the current directory being processeddepth
- the current directory level (starting directory = 0)results
- the collection of result objects, may be updated
handleEnd
protected void handleEnd(Collection results)
throws IOException
Overridable callback method invoked at the end of processing.
This implementation does nothing.
results
- the collection of result objects, may be updated
handleFile
protected void handleFile(File file,
int depth,
Collection results)
throws IOException
Overridable callback method invoked for each (non-directory) file.
This implementation does nothing.
file
- the current file being processeddepth
- the current directory level (starting directory = 0)results
- the collection of result objects, may be updated
handleIsCancelled
protected boolean handleIsCancelled(File file,
int depth,
Collection results)
throws IOException
Overridable callback method invoked to determine if the entire walk
operation should be immediately cancelled.
This method should be implemented by those subclasses that want to
provide a public
cancel()
method available from another
thread. The design pattern for the subclass should be as follows:
public class FooDirectoryWalker extends DirectoryWalker {
private volatile boolean cancelled = false;
public void cancel() {
cancelled = true;
}
private void handleIsCancelled(File file, int depth, Collection results) {
return cancelled;
}
protected void handleCancelled(File startDirectory,
Collection results, CancelException cancel) {
// implement processing required when a cancellation occurs
}
}
If this method returns true, then the directory walk is immediately
cancelled. The next callback method will be
handleCancelled(File,Collection,DirectoryWalker.CancelException)
.
This implementation returns false.
file
- the file or directory being processeddepth
- the current directory level (starting directory = 0)results
- the collection of result objects, may be updated
- true if the walk has been cancelled
handleRestricted
protected void handleRestricted(File directory,
int depth,
Collection results)
throws IOException
Overridable callback method invoked for each restricted directory.
This implementation does nothing.
directory
- the restricted directorydepth
- the current directory level (starting directory = 0)results
- the collection of result objects, may be updated
handleStart
protected void handleStart(File startDirectory,
Collection results)
throws IOException
Overridable callback method invoked at the start of processing.
This implementation does nothing.
startDirectory
- the directory to start fromresults
- the collection of result objects, may be updated
walk
protected final void walk(File startDirectory,
Collection results)
throws IOException
Internal method that walks the directory hierarchy in a depth-first manner.
Users of this class do not need to call this method. This method will
be called automatically by another (public) method on the specific subclass.
Writers of subclasses should call this method to start the directory walk.
Once called, this method will emit events as it walks the hierarchy.
The event methods have the prefix
handle
.
startDirectory
- the directory to start from, not nullresults
- the collection of result objects, may be updated
walk
private void walk(File directory,
int depth,
Collection results)
throws IOException
Main recursive method to examine the directory hierarchy.
directory
- the directory to examine, not nulldepth
- the directory level (starting directory = 0)results
- the collection of result objects, may be updated