DirectoryWalker.java

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

  18. import java.io.File;
  19. import java.io.FileFilter;
  20. import java.io.IOException;
  21. import java.nio.file.Files;
  22. import java.util.Collection;
  23. import java.util.Objects;

  24. import org.apache.commons.io.file.PathUtils;
  25. import org.apache.commons.io.filefilter.FileFilterUtils;
  26. import org.apache.commons.io.filefilter.IOFileFilter;
  27. import org.apache.commons.io.filefilter.TrueFileFilter;

  28. /**
  29.  * Abstract class that walks through a directory hierarchy and provides subclasses with convenient hooks to add specific
  30.  * behavior.
  31.  * <p>
  32.  * This class operates with a {@link FileFilter} and maximum depth to limit the files and directories visited. Commons
  33.  * IO supplies many common filter implementations in the <a href="filefilter/package-summary.html"> filefilter</a>
  34.  * package.
  35.  * </p>
  36.  * <p>
  37.  * The following sections describe:
  38.  * </p>
  39.  * <ul>
  40.  * <li><a href="#example">1. Example Implementation</a> - example {@link FileCleaner} implementation.</li>
  41.  * <li><a href="#filter">2. Filter Example</a> - using {@link FileFilter}(s) with {@link DirectoryWalker}.</li>
  42.  * <li><a href="#cancel">3. Cancellation</a> - how to implement cancellation behavior.</li>
  43.  * </ul>
  44.  *
  45.  * <h2 id="example">1. Example Implementation</h2>
  46.  *
  47.  * There are many possible extensions, for example, to delete all files and '.svn' directories, and return a list of
  48.  * deleted files:
  49.  *
  50.  * <pre>
  51.  * public class FileCleaner extends DirectoryWalker {
  52.  *
  53.  *     public FileCleaner() {
  54.  *         super();
  55.  *     }
  56.  *
  57.  *     public List clean(File startDirectory) {
  58.  *         List results = new ArrayList();
  59.  *         walk(startDirectory, results);
  60.  *         return results;
  61.  *     }
  62.  *
  63.  *     protected boolean handleDirectory(File directory, int depth, Collection results) {
  64.  *         // delete svn directories and then skip
  65.  *         if (".svn".equals(directory.getName())) {
  66.  *             directory.delete();
  67.  *             return false;
  68.  *         } else {
  69.  *             return true;
  70.  *         }
  71.  *
  72.  *     }
  73.  *
  74.  *     protected void handleFile(File file, int depth, Collection results) {
  75.  *         // delete file and add to list of deleted
  76.  *         file.delete();
  77.  *         results.add(file);
  78.  *     }
  79.  * }
  80.  * </pre>
  81.  *
  82.  * <h2 id="filter">2. Filter Example</h2>
  83.  *
  84.  * <p>
  85.  * Choosing which directories and files to process can be a key aspect of using this class. This information can be
  86.  * setup in three ways, via three different constructors.
  87.  * </p>
  88.  * <p>
  89.  * The first option is to visit all directories and files. This is achieved via the no-args constructor.
  90.  * </p>
  91.  * <p>
  92.  * The second constructor option is to supply a single {@link FileFilter} that describes the files and directories to
  93.  * visit. Care must be taken with this option as the same filter is used for both directories and files.
  94.  * </p>
  95.  * <p>
  96.  * For example, if you wanted all directories which are not hidden and files which end in ".txt":
  97.  * </p>
  98.  *
  99.  * <pre>
  100.  * public class FooDirectoryWalker extends DirectoryWalker {
  101.  *     public FooDirectoryWalker(FileFilter filter) {
  102.  *         super(filter, -1);
  103.  *     }
  104.  * }
  105.  *
  106.  * // Build up the filters and create the walker
  107.  * // Create a filter for Non-hidden directories
  108.  * IOFileFilter fooDirFilter = FileFilterUtils.andFileFilter(FileFilterUtils.directoryFileFilter,
  109.  *     HiddenFileFilter.VISIBLE);
  110.  *
  111.  * // Create a filter for Files ending in ".txt"
  112.  * IOFileFilter fooFileFilter = FileFilterUtils.andFileFilter(FileFilterUtils.fileFileFilter,
  113.  *     FileFilterUtils.suffixFileFilter(".txt"));
  114.  *
  115.  * // Combine the directory and file filters using an OR condition
  116.  * java.io.FileFilter fooFilter = FileFilterUtils.orFileFilter(fooDirFilter, fooFileFilter);
  117.  *
  118.  * // Use the filter to construct a DirectoryWalker implementation
  119.  * FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter);
  120.  * </pre>
  121.  * <p>
  122.  * The third constructor option is to specify separate filters, one for directories and one for files. These are
  123.  * combined internally to form the correct {@link FileFilter}, something which is very easy to get wrong when
  124.  * attempted manually, particularly when trying to express constructs like 'any file in directories named docs'.
  125.  * </p>
  126.  * <p>
  127.  * For example, if you wanted all directories which are not hidden and files which end in ".txt":
  128.  * </p>
  129.  *
  130.  * <pre>
  131.  *  public class FooDirectoryWalker extends DirectoryWalker {
  132.  *    public FooDirectoryWalker(IOFileFilter dirFilter, IOFileFilter fileFilter) {
  133.  *      super(dirFilter, fileFilter, -1);
  134.  *    }
  135.  *  }
  136.  *
  137.  *  // Use the filters to construct the walker
  138.  *  FooDirectoryWalker walker = new FooDirectoryWalker(
  139.  *    HiddenFileFilter.VISIBLE,
  140.  *    FileFilterUtils.suffixFileFilter(".txt"),
  141.  *  );
  142.  * </pre>
  143.  * <p>
  144.  * This is much simpler than the previous example, and is why it is the preferred option for filtering.
  145.  * </p>
  146.  *
  147.  * <h2 id="cancel">3. Cancellation</h2>
  148.  *
  149.  * <p>
  150.  * The DirectoryWalker contains some of the logic required for cancel processing. Subclasses must complete the
  151.  * implementation.
  152.  * </p>
  153.  * <p>
  154.  * What {@link DirectoryWalker} does provide for cancellation is:
  155.  * </p>
  156.  * <ul>
  157.  * <li>{@link CancelException} which can be thrown in any of the <em>lifecycle</em> methods to stop processing.</li>
  158.  * <li>The {@code walk()} method traps thrown {@link CancelException} and calls the {@code handleCancelled()}
  159.  * method, providing a place for custom cancel processing.</li>
  160.  * </ul>
  161.  * <p>
  162.  * Implementations need to provide:
  163.  * </p>
  164.  * <ul>
  165.  * <li>The decision logic on whether to cancel processing or not.</li>
  166.  * <li>Constructing and throwing a {@link CancelException}.</li>
  167.  * <li>Custom cancel processing in the {@code handleCancelled()} method.
  168.  * </ul>
  169.  * <p>
  170.  * Two possible scenarios are envisaged for cancellation:
  171.  * </p>
  172.  * <ul>
  173.  * <li><a href="#external">3.1 External / Multi-threaded</a> - cancellation being decided/initiated by an external
  174.  * process.</li>
  175.  * <li><a href="#internal">3.2 Internal</a> - cancellation being decided/initiated from within a DirectoryWalker
  176.  * implementation.</li>
  177.  * </ul>
  178.  * <p>
  179.  * The following sections provide example implementations for these two different scenarios.
  180.  * </p>
  181.  *
  182.  * <h3 id="external">3.1 External / Multi-threaded</h3>
  183.  *
  184.  * <p>
  185.  * This example provides a public {@code cancel()} method that can be called by another thread to stop the
  186.  * processing. A typical example use-case is a cancel button on a GUI. Calling this method sets a
  187.  * <a href='https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#d5e12277'>(@code volatile}</a>
  188.  * flag to ensure it works properly in a multi-threaded environment.
  189.  * The flag is returned by the {@code handleIsCancelled()} method, which causes the walk to stop
  190.  * immediately. The {@code handleCancelled()} method will be the next, and last, callback method received once cancellation has occurred.
  191.  * </p>
  192.  *
  193.  * <pre>
  194.  * public class FooDirectoryWalker extends DirectoryWalker {
  195.  *
  196.  *     private volatile boolean cancelled = false;
  197.  *
  198.  *     public void cancel() {
  199.  *         cancelled = true;
  200.  *     }
  201.  *
  202.  *     protected boolean handleIsCancelled(File file, int depth, Collection results) {
  203.  *         return cancelled;
  204.  *     }
  205.  *
  206.  *     protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
  207.  *         // implement processing required when a cancellation occurs
  208.  *     }
  209.  * }
  210.  * </pre>
  211.  *
  212.  * <h3 id="internal">3.2 Internal</h3>
  213.  *
  214.  * <p>
  215.  * This shows an example of how internal cancellation processing could be implemented. <strong>Note</strong> the decision logic
  216.  * and throwing a {@link CancelException} could be implemented in any of the <em>lifecycle</em> methods.
  217.  * </p>
  218.  *
  219.  * <pre>
  220.  * public class BarDirectoryWalker extends DirectoryWalker {
  221.  *
  222.  *     protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
  223.  *         // cancel if hidden directory
  224.  *         if (directory.isHidden()) {
  225.  *             throw new CancelException(file, depth);
  226.  *         }
  227.  *         return true;
  228.  *     }
  229.  *
  230.  *     protected void handleFile(File file, int depth, Collection results) throws IOException {
  231.  *         // cancel if read-only file
  232.  *         if (!file.canWrite()) {
  233.  *             throw new CancelException(file, depth);
  234.  *         }
  235.  *         results.add(file);
  236.  *     }
  237.  *
  238.  *     protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
  239.  *         // implement processing required when a cancellation occurs
  240.  *     }
  241.  * }
  242.  * </pre>
  243.  *
  244.  * @param <T> The result type, like {@link File}.
  245.  * @since 1.3
  246.  * @deprecated Apache Commons IO no longer uses this class. Instead, use
  247.  *             {@link PathUtils#walk(java.nio.file.Path, org.apache.commons.io.file.PathFilter, int, boolean, java.nio.file.FileVisitOption...)}
  248.  *             or {@link Files#walkFileTree(java.nio.file.Path, java.util.Set, int, java.nio.file.FileVisitor)}, and
  249.  *             friends.
  250.  */
  251. @Deprecated
  252. public abstract class DirectoryWalker<T> {

  253.     /**
  254.      * CancelException is thrown in DirectoryWalker to cancel the current
  255.      * processing.
  256.      */
  257.     public static class CancelException extends IOException {

  258.         /** Serialization id. */
  259.         private static final long serialVersionUID = 1347339620135041008L;

  260.         /** The file being processed when the exception was thrown. */
  261.         private final File file;
  262.         /** The file depth when the exception was thrown. */
  263.         private final int depth;

  264.         /**
  265.          * Constructs a {@link CancelException} with
  266.          * the file and depth when cancellation occurred.
  267.          *
  268.          * @param file  the file when the operation was cancelled, may be null
  269.          * @param depth  the depth when the operation was cancelled, may be null
  270.          */
  271.         public CancelException(final File file, final int depth) {
  272.             this("Operation Cancelled", file, depth);
  273.         }

  274.         /**
  275.          * Constructs a {@link CancelException} with
  276.          * an appropriate message and the file and depth when
  277.          * cancellation occurred.
  278.          *
  279.          * @param message  the detail message
  280.          * @param file  the file when the operation was cancelled
  281.          * @param depth  the depth when the operation was cancelled
  282.          */
  283.         public CancelException(final String message, final File file, final int depth) {
  284.             super(message);
  285.             this.file = file;
  286.             this.depth = depth;
  287.         }

  288.         /**
  289.          * Returns the depth when the operation was cancelled.
  290.          *
  291.          * @return the depth when the operation was cancelled
  292.          */
  293.         public int getDepth() {
  294.             return depth;
  295.         }

  296.         /**
  297.          * Returns the file when the operation was cancelled.
  298.          *
  299.          * @return the file when the operation was cancelled
  300.          */
  301.         public File getFile() {
  302.             return file;
  303.         }
  304.     }
  305.     /**
  306.      * The file filter to use to filter files and directories.
  307.      */
  308.     private final FileFilter filter;

  309.     /**
  310.      * The limit on the directory depth to walk.
  311.      */
  312.     private final int depthLimit;

  313.     /**
  314.      * Constructs an instance with no filtering and unlimited <em>depth</em>.
  315.      */
  316.     protected DirectoryWalker() {
  317.         this(null, -1);
  318.     }

  319.     /**
  320.      * Constructs an instance with a filter and limit the <em>depth</em> navigated to.
  321.      * <p>
  322.      * The filter controls which files and directories will be navigated to as
  323.      * part of the walk. The {@link FileFilterUtils} class is useful for combining
  324.      * various filters together. A {@code null} filter means that no
  325.      * filtering should occur and all files and directories will be visited.
  326.      * </p>
  327.      *
  328.      * @param filter  the filter to apply, null means visit all files
  329.      * @param depthLimit  controls how <em>deep</em> the hierarchy is
  330.      *  navigated to (less than 0 means unlimited)
  331.      */
  332.     protected DirectoryWalker(final FileFilter filter, final int depthLimit) {
  333.         this.filter = filter;
  334.         this.depthLimit = depthLimit;
  335.     }

  336.     /**
  337.      * Constructs an instance with a directory and a file filter and an optional
  338.      * limit on the <em>depth</em> navigated to.
  339.      * <p>
  340.      * The filters control which files and directories will be navigated to as part
  341.      * of the walk. This constructor uses {@link FileFilterUtils#makeDirectoryOnly(IOFileFilter)}
  342.      * and {@link FileFilterUtils#makeFileOnly(IOFileFilter)} internally to combine the filters.
  343.      * A {@code null} filter means that no filtering should occur.
  344.      * </p>
  345.      *
  346.      * @param directoryFilter  the filter to apply to directories, null means visit all directories
  347.      * @param fileFilter  the filter to apply to files, null means visit all files
  348.      * @param depthLimit  controls how <em>deep</em> the hierarchy is
  349.      *  navigated to (less than 0 means unlimited)
  350.      */
  351.     protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter, final int depthLimit) {
  352.         if (directoryFilter == null && fileFilter == null) {
  353.             this.filter = null;
  354.         } else {
  355.             directoryFilter = directoryFilter != null ? directoryFilter : TrueFileFilter.TRUE;
  356.             fileFilter = fileFilter != null ? fileFilter : TrueFileFilter.TRUE;
  357.             directoryFilter = FileFilterUtils.makeDirectoryOnly(directoryFilter);
  358.             fileFilter = FileFilterUtils.makeFileOnly(fileFilter);
  359.             this.filter = directoryFilter.or(fileFilter);
  360.         }
  361.         this.depthLimit = depthLimit;
  362.     }

  363.     /**
  364.      * Checks whether the walk has been cancelled by calling {@link #handleIsCancelled},
  365.      * throwing a {@link CancelException} if it has.
  366.      * <p>
  367.      * Writers of subclasses should not normally call this method as it is called
  368.      * automatically by the walk of the tree. However, sometimes a single method,
  369.      * typically {@link #handleFile}, may take a long time to run. In that case,
  370.      * you may wish to check for cancellation by calling this method.
  371.      * </p>
  372.      *
  373.      * @param file  the current file being processed
  374.      * @param depth  the current file level (starting directory = 0)
  375.      * @param results  the collection of result objects, may be updated
  376.      * @throws IOException if an I/O Error occurs
  377.      */
  378.     protected final void checkIfCancelled(final File file, final int depth, final Collection<T> results) throws
  379.             IOException {
  380.         if (handleIsCancelled(file, depth, results)) {
  381.             throw new CancelException(file, depth);
  382.         }
  383.     }

  384.     /**
  385.      * Overridable callback method invoked with the contents of each directory.
  386.      * <p>
  387.      * This implementation returns the files unchanged
  388.      * </p>
  389.      *
  390.      * @param directory  the current directory being processed
  391.      * @param depth  the current directory level (starting directory = 0)
  392.      * @param files the files (possibly filtered) in the directory, may be {@code null}
  393.      * @return the filtered list of files
  394.      * @throws IOException if an I/O Error occurs
  395.      * @since 2.0
  396.      */
  397.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  398.     protected File[] filterDirectoryContents(final File directory, final int depth, final File... files) throws
  399.             IOException {
  400.         return files;
  401.     }

  402.     /**
  403.      * Overridable callback method invoked when the operation is cancelled.
  404.      * The file being processed when the cancellation occurred can be
  405.      * obtained from the exception.
  406.      * <p>
  407.      * This implementation just re-throws the {@link CancelException}.
  408.      * </p>
  409.      *
  410.      * @param startDirectory  the directory that the walk started from
  411.      * @param results  the collection of result objects, may be updated
  412.      * @param cancel  the exception throw to cancel further processing
  413.      * containing details at the point of cancellation.
  414.      * @throws IOException if an I/O Error occurs
  415.      */
  416.     protected void handleCancelled(final File startDirectory, final Collection<T> results,
  417.                        final CancelException cancel) throws IOException {
  418.         // re-throw exception - overridable by subclass
  419.         throw cancel;
  420.     }

  421.     /**
  422.      * Overridable callback method invoked to determine if a directory should be processed.
  423.      * <p>
  424.      * This method returns a boolean to indicate if the directory should be examined or not.
  425.      * If you return false, the entire directory and any subdirectories will be skipped.
  426.      * Note that this functionality is in addition to the filtering by file filter.
  427.      * </p>
  428.      * <p>
  429.      * This implementation does nothing and returns true.
  430.      * </p>
  431.      *
  432.      * @param directory  the current directory being processed
  433.      * @param depth  the current directory level (starting directory = 0)
  434.      * @param results  the collection of result objects, may be updated
  435.      * @return true to process this directory, false to skip this directory
  436.      * @throws IOException if an I/O Error occurs
  437.      */
  438.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  439.     protected boolean handleDirectory(final File directory, final int depth, final Collection<T> results) throws
  440.             IOException {
  441.         // do nothing - overridable by subclass
  442.         return true;  // process directory
  443.     }

  444.     /**
  445.      * Overridable callback method invoked at the end of processing each directory.
  446.      * <p>
  447.      * This implementation does nothing.
  448.      * </p>
  449.      *
  450.      * @param directory  the directory being processed
  451.      * @param depth  the current directory level (starting directory = 0)
  452.      * @param results  the collection of result objects, may be updated
  453.      * @throws IOException if an I/O Error occurs
  454.      */
  455.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  456.     protected void handleDirectoryEnd(final File directory, final int depth, final Collection<T> results) throws
  457.             IOException {
  458.         // do nothing - overridable by subclass
  459.     }

  460.     /**
  461.      * Overridable callback method invoked at the start of processing each directory.
  462.      * <p>
  463.      * This implementation does nothing.
  464.      * </p>
  465.      *
  466.      * @param directory  the current directory being processed
  467.      * @param depth  the current directory level (starting directory = 0)
  468.      * @param results  the collection of result objects, may be updated
  469.      * @throws IOException if an I/O Error occurs
  470.      */
  471.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  472.     protected void handleDirectoryStart(final File directory, final int depth, final Collection<T> results) throws
  473.             IOException {
  474.         // do nothing - overridable by subclass
  475.     }

  476.     /**
  477.      * Overridable callback method invoked at the end of processing.
  478.      * <p>
  479.      * This implementation does nothing.
  480.      * </p>
  481.      *
  482.      * @param results  the collection of result objects, may be updated
  483.      * @throws IOException if an I/O Error occurs
  484.      */
  485.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  486.     protected void handleEnd(final Collection<T> results) throws IOException {
  487.         // do nothing - overridable by subclass
  488.     }

  489.     /**
  490.      * Overridable callback method invoked for each (non-directory) file.
  491.      * <p>
  492.      * This implementation does nothing.
  493.      * </p>
  494.      *
  495.      * @param file  the current file being processed
  496.      * @param depth  the current directory level (starting directory = 0)
  497.      * @param results  the collection of result objects, may be updated
  498.      * @throws IOException if an I/O Error occurs
  499.      */
  500.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  501.     protected void handleFile(final File file, final int depth, final Collection<T> results) throws IOException {
  502.         // do nothing - overridable by subclass
  503.     }

  504.     /**
  505.      * Overridable callback method invoked to determine if the entire walk
  506.      * operation should be immediately cancelled.
  507.      * <p>
  508.      * This method should be implemented by those subclasses that want to
  509.      * provide a public {@code cancel()} method available from another
  510.      * thread. The design pattern for the subclass should be as follows:
  511.      * </p>
  512.      * <pre>
  513.      *  public class FooDirectoryWalker extends DirectoryWalker {
  514.      *    private volatile boolean cancelled = false;
  515.      *
  516.      *    public void cancel() {
  517.      *        cancelled = true;
  518.      *    }
  519.      *    private void handleIsCancelled(File file, int depth, Collection results) {
  520.      *        return cancelled;
  521.      *    }
  522.      *    protected void handleCancelled(File startDirectory,
  523.      *              Collection results, CancelException cancel) {
  524.      *        // implement processing required when a cancellation occurs
  525.      *    }
  526.      *  }
  527.      * </pre>
  528.      * <p>
  529.      * If this method returns true, then the directory walk is immediately
  530.      * cancelled. The next callback method will be {@link #handleCancelled}.
  531.      * </p>
  532.      * <p>
  533.      * This implementation returns false.
  534.      * </p>
  535.      *
  536.      * @param file  the file or directory being processed
  537.      * @param depth  the current directory level (starting directory = 0)
  538.      * @param results  the collection of result objects, may be updated
  539.      * @return true if the walk has been cancelled
  540.      * @throws IOException if an I/O Error occurs
  541.      */
  542.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  543.     protected boolean handleIsCancelled(
  544.             final File file, final int depth, final Collection<T> results) throws IOException {
  545.         // do nothing - overridable by subclass
  546.         return false;  // not cancelled
  547.     }

  548.     /**
  549.      * Overridable callback method invoked for each restricted directory.
  550.      * <p>
  551.      * This implementation does nothing.
  552.      * </p>
  553.      *
  554.      * @param directory  the restricted directory
  555.      * @param depth  the current directory level (starting directory = 0)
  556.      * @param results  the collection of result objects, may be updated
  557.      * @throws IOException if an I/O Error occurs
  558.      */
  559.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  560.     protected void handleRestricted(final File directory, final int depth, final Collection<T> results) throws
  561.             IOException {
  562.         // do nothing - overridable by subclass
  563.     }

  564.     /**
  565.      * Overridable callback method invoked at the start of processing.
  566.      * <p>
  567.      * This implementation does nothing.
  568.      * </p>
  569.      *
  570.      * @param startDirectory  the directory to start from
  571.      * @param results  the collection of result objects, may be updated
  572.      * @throws IOException if an I/O Error occurs
  573.      */
  574.     @SuppressWarnings("unused") // Possibly thrown from subclasses.
  575.     protected void handleStart(final File startDirectory, final Collection<T> results) throws IOException {
  576.         // do nothing - overridable by subclass
  577.     }

  578.     /**
  579.      * Internal method that walks the directory hierarchy in a depth-first manner.
  580.      * <p>
  581.      * Users of this class do not need to call this method. This method will
  582.      * be called automatically by another (public) method on the specific subclass.
  583.      * </p>
  584.      * <p>
  585.      * Writers of subclasses should call this method to start the directory walk.
  586.      * Once called, this method will emit events as it walks the hierarchy.
  587.      * The event methods have the prefix {@code handle}.
  588.      * </p>
  589.      *
  590.      * @param startDirectory  the directory to start from, not null
  591.      * @param results  the collection of result objects, may be updated
  592.      * @throws NullPointerException if the start directory is null
  593.      * @throws IOException if an I/O Error occurs
  594.      */
  595.     protected final void walk(final File startDirectory, final Collection<T> results) throws IOException {
  596.         Objects.requireNonNull(startDirectory, "startDirectory");
  597.         try {
  598.             handleStart(startDirectory, results);
  599.             walk(startDirectory, 0, results);
  600.             handleEnd(results);
  601.         } catch (final CancelException cancel) {
  602.             handleCancelled(startDirectory, results, cancel);
  603.         }
  604.     }

  605.     /**
  606.      * Main recursive method to examine the directory hierarchy.
  607.      *
  608.      * @param directory  the directory to examine, not null
  609.      * @param depth  the directory level (starting directory = 0)
  610.      * @param results  the collection of result objects, may be updated
  611.      * @throws IOException if an I/O Error occurs
  612.      */
  613.     private void walk(final File directory, final int depth, final Collection<T> results) throws IOException {
  614.         checkIfCancelled(directory, depth, results);
  615.         if (handleDirectory(directory, depth, results)) {
  616.             handleDirectoryStart(directory, depth, results);
  617.             final int childDepth = depth + 1;
  618.             if (depthLimit < 0 || childDepth <= depthLimit) {
  619.                 checkIfCancelled(directory, depth, results);
  620.                 File[] childFiles = directory.listFiles(filter);
  621.                 childFiles = filterDirectoryContents(directory, depth, childFiles);
  622.                 if (childFiles == null) {
  623.                     handleRestricted(directory, childDepth, results);
  624.                 } else {
  625.                     for (final File childFile : childFiles) {
  626.                         if (childFile.isDirectory()) {
  627.                             walk(childFile, childDepth, results);
  628.                         } else {
  629.                             checkIfCancelled(childFile, childDepth, results);
  630.                             handleFile(childFile, childDepth, results);
  631.                             checkIfCancelled(childFile, childDepth, results);
  632.                         }
  633.                     }
  634.                 }
  635.             }
  636.             handleDirectoryEnd(directory, depth, results);
  637.         }
  638.         checkIfCancelled(directory, depth, results);
  639.     }
  640. }