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