    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.io;
    018
    019import java.io.File;
    020import java.io.FileFilter;
    021import java.io.IOException;
    022import java.nio.file.Files;
    023import java.util.Collection;
    024import java.util.Objects;
    025
    026import org.apache.commons.io.file.PathUtils;
    027import org.apache.commons.io.filefilter.FileFilterUtils;
    028import org.apache.commons.io.filefilter.IOFileFilter;
    029import org.apache.commons.io.filefilter.TrueFileFilter;
    030
    031/**
    032 * Abstract class that walks through a directory hierarchy and provides subclasses with convenient hooks to add specific
    033 * behavior.
    034 * <p>
    035 * This class operates with a {@link FileFilter} and maximum depth to limit the files and directories visited. Commons
    036 * IO supplies many common filter implementations in the <a href="filefilter/package-summary.html"> filefilter</a>
    037 * package.
    038 * </p>
    039 * <p>
    040 * The following sections describe:
    041 * </p>
    042 * <ul>
    043 * <li><a href="#example">1. Example Implementation</a> - example {@link FileCleaner} implementation.</li>
    044 * <li><a href="#filter">2. Filter Example</a> - using {@link FileFilter}(s) with {@link DirectoryWalker}.</li>
    045 * <li><a href="#cancel">3. Cancellation</a> - how to implement cancellation behavior.</li>
    046 * </ul>
    047 *
    048 * <h2 id="example">1. Example Implementation</h2>
    049 *
    050 * There are many possible extensions, for example, to delete all files and '.svn' directories, and return a list of
    051 * deleted files:
    052 *
    053 * <pre>
    054 * public class FileCleaner extends DirectoryWalker {
    055 *
    056 *     public FileCleaner() {
    057 *         super();
    058 *     }
    059 *
    060 *     public List clean(File startDirectory) {
    061 *         List results = new ArrayList();
    062 *         walk(startDirectory, results);
    063 *         return results;
    064 *     }
    065 *
    066 *     protected boolean handleDirectory(File directory, int depth, Collection results) {
    067 *         // delete svn directories and then skip
    068 *         if (".svn".equals(directory.getName())) {
    069 *             directory.delete();
    070 *             return false;
    071 *         } else {
    072 *             return true;
    073 *         }
    074 *
    075 *     }
    076 *
    077 *     protected void handleFile(File file, int depth, Collection results) {
    078 *         // delete file and add to list of deleted
    079 *         file.delete();
    080 *         results.add(file);
    081 *     }
    082 * }
    083 * </pre>
    084 *
    085 * <h2 id="filter">2. Filter Example</h2>
    086 *
    087 * <p>
    088 * Choosing which directories and files to process can be a key aspect of using this class. This information can be
    089 * setup in three ways, via three different constructors.
    090 * </p>
    091 * <p>
    092 * The first option is to visit all directories and files. This is achieved via the no-args constructor.
    093 * </p>
    094 * <p>
    095 * The second constructor option is to supply a single {@link FileFilter} that describes the files and directories to
    096 * visit. Care must be taken with this option as the same filter is used for both directories and files.
    097 * </p>
    098 * <p>
    099 * For example, if you wanted all directories which are not hidden and files which end in ".txt":
    100 * </p>
    101 *
    102 * <pre>
    103 * public class FooDirectoryWalker extends DirectoryWalker {
    104 *     public FooDirectoryWalker(FileFilter filter) {
    105 *         super(filter, -1);
    106 *     }
    107 * }
    108 *
    109 * // Build up the filters and create the walker
    110 * // Create a filter for Non-hidden directories
    111 * IOFileFilter fooDirFilter = FileFilterUtils.andFileFilter(FileFilterUtils.directoryFileFilter,
    112 *     HiddenFileFilter.VISIBLE);
    113 *
    114 * // Create a filter for Files ending in ".txt"
    115 * IOFileFilter fooFileFilter = FileFilterUtils.andFileFilter(FileFilterUtils.fileFileFilter,
    116 *     FileFilterUtils.suffixFileFilter(".txt"));
    117 *
    118 * // Combine the directory and file filters using an OR condition
    119 * java.io.FileFilter fooFilter = FileFilterUtils.orFileFilter(fooDirFilter, fooFileFilter);
    120 *
    121 * // Use the filter to construct a DirectoryWalker implementation
    122 * FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter);
    123 * </pre>
    124 * <p>
    125 * The third constructor option is to specify separate filters, one for directories and one for files. These are
    126 * combined internally to form the correct {@link FileFilter}, something which is very easy to get wrong when
    127 * attempted manually, particularly when trying to express constructs like 'any file in directories named docs'.
    128 * </p>
    129 * <p>
    130 * For example, if you wanted all directories which are not hidden and files which end in ".txt":
    131 * </p>
    132 *
    133 * <pre>
    134 *  public class FooDirectoryWalker extends DirectoryWalker {
    135 *    public FooDirectoryWalker(IOFileFilter dirFilter, IOFileFilter fileFilter) {
    136 *      super(dirFilter, fileFilter, -1);
    137 *    }
    138 *  }
    139 *
    140 *  // Use the filters to construct the walker
    141 *  FooDirectoryWalker walker = new FooDirectoryWalker(
    142 *    HiddenFileFilter.VISIBLE,
    143 *    FileFilterUtils.suffixFileFilter(".txt"),
    144 *  );
    145 * </pre>
    146 * <p>
    147 * This is much simpler than the previous example, and is why it is the preferred option for filtering.
    148 * </p>
    149 *
    150 * <h2 id="cancel">3. Cancellation</h2>
    151 *
    152 * <p>
    153 * The DirectoryWalker contains some of the logic required for cancel processing. Subclasses must complete the
    154 * implementation.
    155 * </p>
    156 * <p>
    157 * What {@link DirectoryWalker} does provide for cancellation is:
    158 * </p>
    159 * <ul>
    160 * <li>{@link CancelException} which can be thrown in any of the <em>lifecycle</em> methods to stop processing.</li>
    161 * <li>The {@code walk()} method traps thrown {@link CancelException} and calls the {@code handleCancelled()}
    162 * method, providing a place for custom cancel processing.</li>
    163 * </ul>
    164 * <p>
    165 * Implementations need to provide:
    166 * </p>
    167 * <ul>
    168 * <li>The decision logic on whether to cancel processing or not.</li>
    169 * <li>Constructing and throwing a {@link CancelException}.</li>
    170 * <li>Custom cancel processing in the {@code handleCancelled()} method.</li>
    171 * </ul>
    172 * <p>
    173 * Two possible scenarios are envisaged for cancellation:
    174 * </p>
    175 * <ul>
    176 * <li><a href="#external">3.1 External / Multi-threaded</a> - cancellation being decided/initiated by an external
    177 * process.</li>
    178 * <li><a href="#internal">3.2 Internal</a> - cancellation being decided/initiated from within a DirectoryWalker
    179 * implementation.</li>
    180 * </ul>
    181 * <p>
    182 * The following sections provide example implementations for these two different scenarios.
    183 * </p>
    184 *
    185 * <h3 id="external">3.1 External / Multi-threaded</h3>
    186 *
    187 * <p>
    188 * This example provides a public {@code cancel()} method that can be called by another thread to stop the
    189 * processing. A typical example use-case is a cancel button on a GUI. Calling this method sets a
    190 * <a href='https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#d5e12277'>(@code volatile}</a>
    191 * flag to ensure it works properly in a multi-threaded environment.
    192 * The flag is returned by the {@code handleIsCancelled()} method, which causes the walk to stop
    193 * immediately. The {@code handleCancelled()} method will be the next, and last, callback method received once cancellation has occurred.
    194 * </p>
    195 *
    196 * <pre>
    197 * public class FooDirectoryWalker extends DirectoryWalker {
    198 *
    199 *     private volatile boolean canceled = false;
    200 *
    201 *     public void cancel() {
    202 *         canceled = true;
    203 *     }
    204 *
    205 *     protected boolean handleIsCancelled(File file, int depth, Collection results) {
    206 *         return canceled;
    207 *     }
    208 *
    209 *     protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
    210 *         // implement processing required when a cancellation occurs
    211 *     }
    212 * }
    213 * </pre>
    214 *
    215 * <h3 id="internal">3.2 Internal</h3>
    216 *
    217 * <p>
    218 * This shows an example of how internal cancellation processing could be implemented. <strong>Note</strong> the decision logic
    219 * and throwing a {@link CancelException} could be implemented in any of the <em>lifecycle</em> methods.
    220 * </p>
    221 *
    222 * <pre>
    223 * public class BarDirectoryWalker extends DirectoryWalker {
    224 *
    225 *     protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
    226 *         // cancel if hidden directory
    227 *         if (directory.isHidden()) {
    228 *             throw new CancelException(file, depth);
    229 *         }
    230 *         return true;
    231 *     }
    232 *
    233 *     protected void handleFile(File file, int depth, Collection results) throws IOException {
    234 *         // cancel if read-only file
    235 *         if (!file.canWrite()) {
    236 *             throw new CancelException(file, depth);
    237 *         }
    238 *         results.add(file);
    239 *     }
    240 *
    241 *     protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
    242 *         // implement processing required when a cancellation occurs
    243 *     }
    244 * }
    245 * </pre>
    246 *
    247 * @param <T> The result type, like {@link File}.
    248 * @since 1.3
    249 * @deprecated Apache Commons IO no longer uses this class. Instead, use
    250 *             {@link PathUtils#walk(java.nio.file.Path, org.apache.commons.io.file.PathFilter, int, boolean, java.nio.file.FileVisitOption...)}
    251 *             or {@link Files#walkFileTree(java.nio.file.Path, java.util.Set, int, java.nio.file.FileVisitor)}, and
    252 *             friends.
    253 */
    254@Deprecated
    255public abstract class DirectoryWalker<T> {
    256
    257    /**
    258     * CancelException is thrown in DirectoryWalker to cancel the current
    259     * processing.
    260     */
    261    public static class CancelException extends IOException {
    262
    263        /** Serialization id. */
    264        private static final long serialVersionUID = 1347339620135041008L;
    265
    266        /** The file being processed when the exception was thrown. */
    267        private final File file;
    268
    269        /** The file depth when the exception was thrown. */
    270        private final int depth;
    271
    272        /**
    273         * Constructs a {@link CancelException} with
    274         * the file and depth when cancellation occurred.
    275         *
    276         * @param file  the file when the operation was canceled, may be null.
    277         * @param depth  the depth when the operation was canceled, may be null.
    278         */
    279        public CancelException(final File file, final int depth) {
    280            this("Operation Cancelled", file, depth);
    281        }
    282
    283        /**
    284         * Constructs a {@link CancelException} with
    285         * an appropriate message and the file and depth when
    286         * cancellation occurred.
    287         *
    288         * @param message  the detail message.
    289         * @param file  the file when the operation was canceled.
    290         * @param depth  the depth when the operation was canceled.
    291         */
    292        public CancelException(final String message, final File file, final int depth) {
    293            super(message);
    294            this.file = file;
    295            this.depth = depth;
    296        }
    297
    298        /**
    299         * Returns the depth when the operation was canceled.
    300         *
    301         * @return the depth when the operation was canceled.
    302         */
    303        public int getDepth() {
    304            return depth;
    305        }
    306
    307        /**
    308         * Returns the file when the operation was canceled.
    309         *
    310         * @return the file when the operation was canceled.
    311         */
    312        public File getFile() {
    313            return file;
    314        }
    315    }
    316
    317    /**
    318     * The file filter to use to filter files and directories.
    319     */
    320    private final FileFilter filter;
    321
    322    /**
    323     * The limit on the directory depth to walk.
    324     */
    325    private final int depthLimit;
    326
    327    /**
    328     * Constructs an instance with no filtering and unlimited <em>depth</em>.
    329     */
    330    protected DirectoryWalker() {
    331        this(null, -1);
    332    }
    333
    334    /**
    335     * Constructs an instance with a filter and limit the <em>depth</em> navigated to.
    336     * <p>
    337     * The filter controls which files and directories will be navigated to as
    338     * part of the walk. The {@link FileFilterUtils} class is useful for combining
    339     * various filters together. A {@code null} filter means that no
    340     * filtering should occur and all files and directories will be visited.
    341     * </p>
    342     *
    343     * @param filter  the filter to apply, null means visit all files.
    344     * @param depthLimit  controls how <em>deep</em> the hierarchy is
    345     *  navigated to (less than 0 means unlimited).
    346     */
    347    protected DirectoryWalker(final FileFilter filter, final int depthLimit) {
    348        this.filter = filter;
    349        this.depthLimit = depthLimit;
    350    }
    351
    352    /**
    353     * Constructs an instance with a directory and a file filter and an optional
    354     * limit on the <em>depth</em> navigated to.
    355     * <p>
    356     * The filters control which files and directories will be navigated to as part
    357     * of the walk. This constructor uses {@link FileFilterUtils#makeDirectoryOnly(IOFileFilter)}
    358     * and {@link FileFilterUtils#makeFileOnly(IOFileFilter)} internally to combine the filters.
    359     * A {@code null} filter means that no filtering should occur.
    360     * </p>
    361     *
    362     * @param directoryFilter  the filter to apply to directories, null means visit all directories.
    363     * @param fileFilter  the filter to apply to files, null means visit all files.
    364     * @param depthLimit  controls how <em>deep</em> the hierarchy is
    365     *  navigated to (less than 0 means unlimited).
    366     */
    367    protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter, final int depthLimit) {
    368        if (directoryFilter == null && fileFilter == null) {
    369            this.filter = null;
    370        } else {
    371            directoryFilter = directoryFilter != null ? directoryFilter : TrueFileFilter.TRUE;
    372            fileFilter = fileFilter != null ? fileFilter : TrueFileFilter.TRUE;
    373            directoryFilter = FileFilterUtils.makeDirectoryOnly(directoryFilter);
    374            fileFilter = FileFilterUtils.makeFileOnly(fileFilter);
    375            this.filter = directoryFilter.or(fileFilter);
    376        }
    377        this.depthLimit = depthLimit;
    378    }
    379
    380    /**
    381     * Checks whether the walk has been canceled by calling {@link #handleIsCancelled},
    382     * throwing a {@link CancelException} if it has.
    383     * <p>
    384     * Writers of subclasses should not normally call this method as it is called
    385     * automatically by the walk of the tree. However, sometimes a single method,
    386     * typically {@link #handleFile}, may take a long time to run. In that case,
    387     * you may wish to check for cancellation by calling this method.
    388     * </p>
    389     *
    390     * @param file  the current file being processed.
    391     * @param depth  the current file level (starting directory = 0).
    392     * @param results  the collection of result objects, may be updated.
    393     * @throws IOException if an I/O Error occurs.
    394     */
    395    protected final void checkIfCancelled(final File file, final int depth, final Collection<T> results) throws
    396            IOException {
    397        if (handleIsCancelled(file, depth, results)) {
    398            throw new CancelException(file, depth);
    399        }
    400    }
    401
    402    /**
    403     * Overridable callback method invoked with the contents of each directory.
    404     * <p>
    405     * This implementation returns the files unchanged
    406     * </p>
    407     *
    408     * @param directory  the current directory being processed.
    409     * @param depth  the current directory level (starting directory = 0).
    410     * @param files the files (possibly filtered) in the directory, may be {@code null}
    411     * @return the filtered list of files.
    412     * @throws IOException if an I/O Error occurs.
    413     * @since 2.0
    414     */
    415    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    416    protected File[] filterDirectoryContents(final File directory, final int depth, final File... files) throws
    417            IOException {
    418        return files;
    419    }
    420
    421    /**
    422     * Overridable callback method invoked when the operation is canceled.
    423     * The file being processed when the cancellation occurred can be
    424     * obtained from the exception.
    425     * <p>
    426     * This implementation just re-throws the {@link CancelException}.
    427     * </p>
    428     *
    429     * @param startDirectory  the directory that the walk started from.
    430     * @param results  the collection of result objects, may be updated.
    431     * @param cancel  the exception throw to cancel further processing
    432     * containing details at the point of cancellation.
    433     * @throws IOException if an I/O Error occurs.
    434     */
    435    protected void handleCancelled(final File startDirectory, final Collection<T> results,
    436                       final CancelException cancel) throws IOException {
    437        // re-throw exception - overridable by subclass
    438        throw cancel;
    439    }
    440
    441    /**
    442     * Overridable callback method invoked to determine if a directory should be processed.
    443     * <p>
    444     * This method returns a boolean to indicate if the directory should be examined or not.
    445     * If you return false, the entire directory and any subdirectories will be skipped.
    446     * Note that this functionality is in addition to the filtering by file filter.
    447     * </p>
    448     * <p>
    449     * This implementation does nothing and returns true.
    450     * </p>
    451     *
    452     * @param directory  the current directory being processed.
    453     * @param depth  the current directory level (starting directory = 0).
    454     * @param results  the collection of result objects, may be updated.
    455     * @return true to process this directory, false to skip this directory.
    456     * @throws IOException if an I/O Error occurs.
    457     */
    458    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    459    protected boolean handleDirectory(final File directory, final int depth, final Collection<T> results) throws
    460            IOException {
    461        // do nothing - overridable by subclass
    462        return true;  // process directory
    463    }
    464
    465    /**
    466     * Overridable callback method invoked at the end of processing each directory.
    467     * <p>
    468     * This implementation does nothing.
    469     * </p>
    470     *
    471     * @param directory  the directory being processed.
    472     * @param depth  the current directory level (starting directory = 0).
    473     * @param results  the collection of result objects, may be updated.
    474     * @throws IOException if an I/O Error occurs.
    475     */
    476    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    477    protected void handleDirectoryEnd(final File directory, final int depth, final Collection<T> results) throws
    478            IOException {
    479        // do nothing - overridable by subclass
    480    }
    481
    482    /**
    483     * Overridable callback method invoked at the start of processing each directory.
    484     * <p>
    485     * This implementation does nothing.
    486     * </p>
    487     *
    488     * @param directory  the current directory being processed.
    489     * @param depth  the current directory level (starting directory = 0).
    490     * @param results  the collection of result objects, may be updated.
    491     * @throws IOException if an I/O Error occurs.
    492     */
    493    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    494    protected void handleDirectoryStart(final File directory, final int depth, final Collection<T> results) throws
    495            IOException {
    496        // do nothing - overridable by subclass
    497    }
    498
    499    /**
    500     * Overridable callback method invoked at the end of processing.
    501     * <p>
    502     * This implementation does nothing.
    503     * </p>
    504     *
    505     * @param results  the collection of result objects, may be updated.
    506     * @throws IOException if an I/O Error occurs.
    507     */
    508    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    509    protected void handleEnd(final Collection<T> results) throws IOException {
    510        // do nothing - overridable by subclass
    511    }
    512
    513    /**
    514     * Overridable callback method invoked for each (non-directory) file.
    515     * <p>
    516     * This implementation does nothing.
    517     * </p>
    518     *
    519     * @param file  the current file being processed.
    520     * @param depth  the current directory level (starting directory = 0).
    521     * @param results  the collection of result objects, may be updated.
    522     * @throws IOException if an I/O Error occurs.
    523     */
    524    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    525    protected void handleFile(final File file, final int depth, final Collection<T> results) throws IOException {
    526        // do nothing - overridable by subclass
    527    }
    528
    529    /**
    530     * Overridable callback method invoked to determine if the entire walk
    531     * operation should be immediately canceled.
    532     * <p>
    533     * This method should be implemented by those subclasses that want to
    534     * provide a public {@code cancel()} method available from another
    535     * thread. The design pattern for the subclass should be as follows:
    536     * </p>
    537     * <pre>
    538     *  public class FooDirectoryWalker extends DirectoryWalker {
    539     *    private volatile boolean canceled = false;
    540     *
    541     *    public void cancel() {
    542     *        canceled = true;
    543     *    }
    544     *    private void handleIsCancelled(File file, int depth, Collection results) {
    545     *        return canceled;
    546     *    }
    547     *    protected void handleCancelled(File startDirectory,
    548     *              Collection results, CancelException cancel) {
    549     *        // implement processing required when a cancellation occurs
    550     *    }
    551     *  }
    552     * </pre>
    553     * <p>
    554     * If this method returns true, then the directory walk is immediately
    555     * canceled. The next callback method will be {@link #handleCancelled}.
    556     * </p>
    557     * <p>
    558     * This implementation returns false.
    559     * </p>
    560     *
    561     * @param file  the file or directory being processed.
    562     * @param depth  the current directory level (starting directory = 0).
    563     * @param results  the collection of result objects, may be updated.
    564     * @return true if the walk has been canceled.
    565     * @throws IOException if an I/O Error occurs.
    566     */
    567    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    568    protected boolean handleIsCancelled(
    569            final File file, final int depth, final Collection<T> results) throws IOException {
    570        // do nothing - overridable by subclass
    571        return false;  // not canceled
    572    }
    573
    574    /**
    575     * Overridable callback method invoked for each restricted directory.
    576     * <p>
    577     * This implementation does nothing.
    578     * </p>
    579     *
    580     * @param directory  the restricted directory.
    581     * @param depth  the current directory level (starting directory = 0).
    582     * @param results  the collection of result objects, may be updated.
    583     * @throws IOException if an I/O Error occurs.
    584     */
    585    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    586    protected void handleRestricted(final File directory, final int depth, final Collection<T> results) throws
    587            IOException {
    588        // do nothing - overridable by subclass
    589    }
    590
    591    /**
    592     * Overridable callback method invoked at the start of processing.
    593     * <p>
    594     * This implementation does nothing.
    595     * </p>
    596     *
    597     * @param startDirectory  the directory to start from.
    598     * @param results  the collection of result objects, may be updated.
    599     * @throws IOException if an I/O Error occurs.
    600     */
    601    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    602    protected void handleStart(final File startDirectory, final Collection<T> results) throws IOException {
    603        // do nothing - overridable by subclass
    604    }
    605
    606    /**
    607     * Internal method that walks the directory hierarchy in a depth-first manner.
    608     * <p>
    609     * Users of this class do not need to call this method. This method will
    610     * be called automatically by another (public) method on the specific subclass.
    611     * </p>
    612     * <p>
    613     * Writers of subclasses should call this method to start the directory walk.
    614     * Once called, this method will emit events as it walks the hierarchy.
    615     * The event methods have the prefix {@code handle}.
    616     * </p>
    617     *
    618     * @param startDirectory  the directory to start from, not null.
    619     * @param results  the collection of result objects, may be updated.
    620     * @throws NullPointerException if the start directory is null.
    621     * @throws IOException if an I/O Error occurs.
    622     */
    623    protected final void walk(final File startDirectory, final Collection<T> results) throws IOException {
    624        Objects.requireNonNull(startDirectory, "startDirectory");
    625        try {
    626            handleStart(startDirectory, results);
    627            walk(startDirectory, 0, results);
    628            handleEnd(results);
    629        } catch (final CancelException cancel) {
    630            handleCancelled(startDirectory, results, cancel);
    631        }
    632    }
    633
    634    /**
    635     * Main recursive method to examine the directory hierarchy.
    636     *
    637     * @param directory  the directory to examine, not null.
    638     * @param depth  the directory level (starting directory = 0).
    639     * @param results  the collection of result objects, may be updated.
    640     * @throws IOException if an I/O Error occurs.
    641     */
    642    private void walk(final File directory, final int depth, final Collection<T> results) throws IOException {
    643        checkIfCancelled(directory, depth, results);
    644        if (handleDirectory(directory, depth, results)) {
    645            handleDirectoryStart(directory, depth, results);
    646            final int childDepth = depth + 1;
    647            if (depthLimit < 0 || childDepth <= depthLimit) {
    648                checkIfCancelled(directory, depth, results);
    649                File[] childFiles = directory.listFiles(filter);
    650                childFiles = filterDirectoryContents(directory, depth, childFiles);
    651                if (childFiles == null) {
    652                    handleRestricted(directory, childDepth, results);
    653                } else {
    654                    for (final File childFile : childFiles) {
    655                        if (childFile.isDirectory()) {
    656                            walk(childFile, childDepth, results);
    657                        } else {
    658                            checkIfCancelled(childFile, childDepth, results);
    659                            handleFile(childFile, childDepth, results);
    660                            checkIfCancelled(childFile, childDepth, results);
    661                        }
    662                    }
    663                }
    664            }
    665            handleDirectoryEnd(directory, depth, results);
    666        }
    667        checkIfCancelled(directory, depth, results);
    668    }
    669}