DirectoryWalker (Commons IO 2.5)
Date: 2016-12-01

org.apache.commons.io

## Class DirectoryWalker<T>

- java.lang.Object
- - org.apache.commons.io.DirectoryWalker<T>

-     public abstract class DirectoryWalker<T>
      extends java.lang.Object

  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 - example `FileCleaner` implementation.
  - 2. Filter Example - using `FileFilter`(s) with `DirectoryWalker`.
  - 3. Cancellation - how to implement cancellation behaviour.

  ### 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:

  - `DirectoryWalker.CancelException` which can be thrown in any of the lifecycle methods to stop processing.
  - The `walk()` method traps thrown `DirectoryWalker.CancelException` and calls the `handleCancelled()` method, providing a place for custom cancel processing.

  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:

  - 3.1 External / Mult-threaded - cancellation being decided/initiated by an external process.
  - 3.2 Internal - cancellation being decided/initiated from within a DirectoryWalker implementation.

  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;
          }

          protected boolean 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
          }
        }

  Since:
    1.3
  Version:
    $Id: DirectoryWalker.java 1723627 2016-01-07 21:15:47Z niallp $

- - ### Nested Class Summary

    Nested Classes
    Modifier and Type | Class and Description
    --- | ---
    `static class` | `DirectoryWalker.CancelException` CancelException is thrown in DirectoryWalker to cancel the current processing.

  - ### Field Summary

    Fields
    Modifier and Type | Field and Description
    --- | ---
    `private int` | `depthLimit` The limit on the directory depth to walk.
    `private java.io.FileFilter` | `filter` The file filter to use to filter files and directories.

  - ### Constructor Summary

    Constructors
    Modifier | Constructor and Description
    --- | ---
    `protected` | `DirectoryWalker()` Construct an instance with no filtering and unlimited depth.
    `protected` | `DirectoryWalker(java.io.FileFilter filter, int depthLimit)` Construct an instance with a filter and limit the depth navigated to.
    `protected` | `DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter, int depthLimit)` Construct an instance with a directory and a file filter and an optional limit on the depth navigated to.

  - ### Method Summary

    Methods
    Modifier and Type | Method and Description
    --- | ---
    `protected void` | `checkIfCancelled(java.io.File file, int depth, java.util.Collection<T> results)` Checks whether the walk has been cancelled by calling `handleIsCancelled(java.io.File, int, java.util.Collection<T>)`, throwing a `CancelException` if it has.
    `protected java.io.File[]` | `filterDirectoryContents(java.io.File directory, int depth, java.io.File[] files)` Overridable callback method invoked with the contents of each directory.
    `protected void` | `handleCancelled(java.io.File startDirectory, java.util.Collection<T> results, DirectoryWalker.CancelException cancel)` Overridable callback method invoked when the operation is cancelled.
    `protected boolean` | `handleDirectory(java.io.File directory, int depth, java.util.Collection<T> results)` Overridable callback method invoked to determine if a directory should be processed.
    `protected void` | `handleDirectoryEnd(java.io.File directory, int depth, java.util.Collection<T> results)` Overridable callback method invoked at the end of processing each directory.
    `protected void` | `handleDirectoryStart(java.io.File directory, int depth, java.util.Collection<T> results)` Overridable callback method invoked at the start of processing each directory.
    `protected void` | `handleEnd(java.util.Collection<T> results)` Overridable callback method invoked at the end of processing.
    `protected void` | `handleFile(java.io.File file, int depth, java.util.Collection<T> results)` Overridable callback method invoked for each (non-directory) file.
    `protected boolean` | `handleIsCancelled(java.io.File file, int depth, java.util.Collection<T> results)` Overridable callback method invoked to determine if the entire walk operation should be immediately cancelled.
    `protected void` | `handleRestricted(java.io.File directory, int depth, java.util.Collection<T> results)` Overridable callback method invoked for each restricted directory.
    `protected void` | `handleStart(java.io.File startDirectory, java.util.Collection<T> results)` Overridable callback method invoked at the start of processing.
    `protected void` | `walk(java.io.File startDirectory, java.util.Collection<T> results)` Internal method that walks the directory hierarchy in a depth-first manner.
    `private void` | `walk(java.io.File directory, int depth, java.util.Collection<T> results)` Main recursive method to examine the directory hierarchy.

    - ### Methods inherited from class java.lang.Object

      `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Field Detail

    - #### filter

          private final java.io.FileFilter filter

      The file filter to use to filter files and directories.

    - #### depthLimit

          private final int depthLimit

      The limit on the directory depth to walk.

  - ### Constructor Detail

    - #### DirectoryWalker

          protected DirectoryWalker()

      Construct an instance with no filtering and unlimited depth.

    - #### DirectoryWalker

          protected DirectoryWalker(java.io.FileFilter filter,
                         int depthLimit)

      Construct an instance with a filter and limit the depth navigated to.

      The filter controls which files and directories will be navigated to as part of the walk. The `FileFilterUtils` class is useful for combining various filters together. A `null` filter means that no filtering should occur and all files and directories will be visited.

      Parameters:
        `filter` - the filter to apply, null means visit all files
        `depthLimit` - controls how deep the hierarchy is navigated to (less than 0 means unlimited)

    - #### DirectoryWalker

          protected DirectoryWalker(IOFileFilter directoryFilter,
                         IOFileFilter fileFilter,
                         int depthLimit)

      Construct an instance with a directory and a file filter and an optional limit on the depth navigated to.

      The filters control which files and directories will be navigated to as part of the walk. This constructor uses `FileFilterUtils.makeDirectoryOnly(IOFileFilter)` and `FileFilterUtils.makeFileOnly(IOFileFilter)` internally to combine the filters. A `null` filter means that no filtering should occur.

      Parameters:
        `directoryFilter` - the filter to apply to directories, null means visit all directories
        `fileFilter` - the filter to apply to files, null means visit all files
        `depthLimit` - controls how deep the hierarchy is navigated to (less than 0 means unlimited)

  - ### Method Detail

    - #### walk

          protected final void walk(java.io.File startDirectory,
                  java.util.Collection<T> results)
                             throws java.io.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`.

      Parameters:
        `startDirectory` - the directory to start from, not null
        `results` - the collection of result objects, may be updated
      Throws:
        `java.lang.NullPointerException` - if the start directory is null
        `java.io.IOException` - if an I/O Error occurs

    - #### walk

          private void walk(java.io.File directory,
                  int depth,
                  java.util.Collection<T> results)
                     throws java.io.IOException

      Main recursive method to examine the directory hierarchy.
      Parameters:
        `directory` - the directory to examine, not null
        `depth` - the directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### checkIfCancelled

          protected final void checkIfCancelled(java.io.File file,
                              int depth,
                              java.util.Collection<T> results)
                                         throws java.io.IOException

      Checks whether the walk has been cancelled by calling `handleIsCancelled(java.io.File, int, java.util.Collection<T>)`, 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(java.io.File, int, java.util.Collection<T>)`, may take a long time to run. In that case, you may wish to check for cancellation by calling this method.

      Parameters:
        `file` - the current file being processed
        `depth` - the current file level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleIsCancelled

          protected boolean handleIsCancelled(java.io.File file,
                                  int depth,
                                  java.util.Collection<T> results)
                                       throws java.io.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(java.io.File, java.util.Collection<T>, org.apache.commons.io.DirectoryWalker.CancelException)`.

      This implementation returns false.

      Parameters:
        `file` - the file or directory being processed
        `depth` - the current directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Returns:
        true if the walk has been cancelled
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleCancelled

          protected void handleCancelled(java.io.File startDirectory,
                             java.util.Collection<T> results,
                             DirectoryWalker.CancelException cancel)
                                  throws java.io.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`.

      Parameters:
        `startDirectory` - the directory that the walk started from
        `results` - the collection of result objects, may be updated
        `cancel` - the exception throw to cancel further processing containing details at the point of cancellation.
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleStart

          protected void handleStart(java.io.File startDirectory,
                         java.util.Collection<T> results)
                              throws java.io.IOException

      Overridable callback method invoked at the start of processing.

      This implementation does nothing.

      Parameters:
        `startDirectory` - the directory to start from
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleDirectory

          protected boolean handleDirectory(java.io.File directory,
                                int depth,
                                java.util.Collection<T> results)
                                     throws java.io.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.

      Parameters:
        `directory` - the current directory being processed
        `depth` - the current directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Returns:
        true to process this directory, false to skip this directory
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleDirectoryStart

          protected void handleDirectoryStart(java.io.File directory,
                                  int depth,
                                  java.util.Collection<T> results)
                                       throws java.io.IOException

      Overridable callback method invoked at the start of processing each directory.

      This implementation does nothing.

      Parameters:
        `directory` - the current directory being processed
        `depth` - the current directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### filterDirectoryContents

          protected java.io.File[] filterDirectoryContents(java.io.File directory,
                                               int depth,
                                               java.io.File[] files)
                                                    throws java.io.IOException

      Overridable callback method invoked with the contents of each directory.

      This implementation returns the files unchanged

      Parameters:
        `directory` - the current directory being processed
        `depth` - the current directory level (starting directory = 0)
        `files` - the files (possibly filtered) in the directory, may be `null`
      Returns:
        the filtered list of files
      Throws:
        `java.io.IOException` - if an I/O Error occurs
      Since:
        2.0

    - #### handleFile

          protected void handleFile(java.io.File file,
                        int depth,
                        java.util.Collection<T> results)
                             throws java.io.IOException

      Overridable callback method invoked for each (non-directory) file.

      This implementation does nothing.

      Parameters:
        `file` - the current file being processed
        `depth` - the current directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleRestricted

          protected void handleRestricted(java.io.File directory,
                              int depth,
                              java.util.Collection<T> results)
                                   throws java.io.IOException

      Overridable callback method invoked for each restricted directory.

      This implementation does nothing.

      Parameters:
        `directory` - the restricted directory
        `depth` - the current directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleDirectoryEnd

          protected void handleDirectoryEnd(java.io.File directory,
                                int depth,
                                java.util.Collection<T> results)
                                     throws java.io.IOException

      Overridable callback method invoked at the end of processing each directory.

      This implementation does nothing.

      Parameters:
        `directory` - the directory being processed
        `depth` - the current directory level (starting directory = 0)
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

    - #### handleEnd

          protected void handleEnd(java.util.Collection<T> results)
                            throws java.io.IOException

      Overridable callback method invoked at the end of processing.

      This implementation does nothing.

      Parameters:
        `results` - the collection of result objects, may be updated
      Throws:
        `java.io.IOException` - if an I/O Error occurs

Copyright (c) 2002-2016 Apache Software Foundation