184184 * <a name="external"></a>
185185 * <h4>3.1 External / Multi-threaded</h4>
186186 *
187- * This example provides a <code>cancel()</code> method for external processes to
188- * indcate that processing must stop. Calling this method sets a
189- * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">volatile</a>
190- * flag to (hopefully) ensure it will work properly in
191- * a multi-threaded environment. In this implementation the flag is checked in two
192- * of the lifecycle methods using a convenience <code>checkIfCancelled()</code> method
193- * which throws a {@link CancelException} if cancellation has been requested.
187+ * This example provides a public <code>cancel()</code> method that can be
188+ * called by another thread to stop the processing. A typical example use-case
189+ * would be a cancel button on a GUI. Calling this method sets a
190+ * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">
191+ * volatile</a> flag to ensure it will work properly in a multi-threaded environment.
192+ * The flag is returned by the <code>handleIsCancelled()</code> method, which
193+ * will cause the walk to stop immediately. The <code>handleCancelled()</code>
194+ * method will be the next, and last, callback method received once cancellation
195+ * has occurred.
194196 *
195197 * <pre>
196198 * public class FooDirectoryWalker extends DirectoryWalker {
201203 * cancelled = true;
202204 * }
203205 *
204- * protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
205- * checkIfCancelled(directory, depth); // Cancel Check
206- * return true;
207- * }
208- *
209- * protected void handleFile(File file, int depth, Collection results) throws IOException {
210- * checkIfCancelled(file, depth); // Cancel Check
211- * results.add(file);
212- * }
213- *
214- * private void checkIfCancelled(File file, int depth) throws CancelException {
215- * if (cancelled) {
216- * throw new CancelException(file, depth);
217- * }
206+ * private void handleIsCancelled(File file, int depth, Collection results) {
207+ * return cancelled;
218208 * }
219209 *
220210 * protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
221- * // implement cancel processing here
211+ * // implement processing required when a cancellation occurs
222212 * }
223213 * }
224214 * </pre>
250240 * }
251241 *
252242 * protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
253- * // implement cancel processing here
243+ * // implement processing required when a cancellation occurs
254244 * }
255245 * }
256246 * </pre>
@@ -324,7 +314,7 @@ protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter,
324314 /**
325315 * Internal method that walks the directory hierarchy in a depth-first manner.
326316 * <p>
327- * Most users of this class do not need to call this method. This method will
317+ * Users of this class do not need to call this method. This method will
328318 * be called automatically by another (public) method on the specific subclass.
329319 * <p>
330320 * Writers of subclasses should call this method to start the directory walk.
@@ -336,7 +326,7 @@ protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter,
336326 * @throws NullPointerException if the start directory is null
337327 * @throws IOException if an I/O Error occurs
338328 */
339- protected void walk (File startDirectory , Collection results ) throws IOException {
329+ protected final void walk (File startDirectory , Collection results ) throws IOException {
340330 if (startDirectory == null ) {
341331 throw new NullPointerException ("Start Directory is null" );
342332 }
@@ -358,10 +348,12 @@ protected void walk(File startDirectory, Collection results) throws IOException
358348 * @throws IOException if an I/O Error occurs
359349 */
360350 private void walk (File directory , int depth , Collection results ) throws IOException {
351+ checkIfCancelled (directory , depth , results );
361352 if (handleDirectory (directory , depth , results )) {
362353 handleDirectoryStart (directory , depth , results );
363354 int childDepth = depth + 1 ;
364355 if (depthLimit < 0 || childDepth <= depthLimit ) {
356+ checkIfCancelled (directory , depth , results );
365357 File [] childFiles = (filter == null ? directory .listFiles () : directory .listFiles (filter ));
366358 if (childFiles == null ) {
367359 handleRestricted (directory , childDepth , results );
@@ -371,13 +363,97 @@ private void walk(File directory, int depth, Collection results) throws IOExcept
371363 if (childFile .isDirectory ()) {
372364 walk (childFile , childDepth , results );
373365 } else {
366+ checkIfCancelled (childFile , childDepth , results );
374367 handleFile (childFile , childDepth , results );
368+ checkIfCancelled (childFile , childDepth , results );
375369 }
376370 }
377371 }
378372 }
379373 handleDirectoryEnd (directory , depth , results );
380374 }
375+ checkIfCancelled (directory , depth , results );
376+ }
377+
378+ //-----------------------------------------------------------------------
379+ /**
380+ * Checks whether the walk has been cancelled by calling {@link #handleIsCancelled},
381+ * throwing a <code>CancelException</code> if it has.
382+ * <p>
383+ * Writers of subclasses should not normally call this method as it is called
384+ * automatically by the walk of the tree. However, sometimes a single method,
385+ * typically {@link #handleFile}, may take a long time to run. In that case,
386+ * you may wish to check for cancellation by calling this method.
387+ *
388+ * @param file the current file being processed
389+ * @param depth the current file level (starting directory = 0)
390+ * @param results the collection of result objects, may be updated
391+ * @return true to process this directory, false to skip this directory
392+ * @throws IOException if an I/O Error occurs
393+ */
394+ protected final void checkIfCancelled (File file , int depth , Collection results ) throws IOException {
395+ if (handleIsCancelled (file , depth , results )) {
396+ throw new CancelException (file , depth );
397+ }
398+ }
399+
400+ /**
401+ * Overridable callback method invoked to determine if the entire walk
402+ * operation should be immediately cancelled.
403+ * <p>
404+ * This method should be implemented by those subclasses that want to
405+ * provide a public <code>cancel()</code> method available from another
406+ * thread. The design pattern for the subclass should be as follows:
407+ * <pre>
408+ * public class FooDirectoryWalker extends DirectoryWalker {
409+ * private volatile boolean cancelled = false;
410+ *
411+ * public void cancel() {
412+ * cancelled = true;
413+ * }
414+ * private void handleIsCancelled(File file, int depth, Collection results) {
415+ * return cancelled;
416+ * }
417+ * protected void handleCancelled(File startDirectory,
418+ * Collection results, CancelException cancel) {
419+ * // implement processing required when a cancellation occurs
420+ * }
421+ * }
422+ * </pre>
423+ * <p>
424+ * If this method returns true, then the directory walk is immediately
425+ * cancelled. The next callback method will be {@link #handleCancelled}.
426+ * <p>
427+ * This implementation returns false.
428+ *
429+ * @param file the file or directory being processed
430+ * @param depth the current directory level (starting directory = 0)
431+ * @return true if the walk has been cancelled
432+ * @throws IOException if an I/O Error occurs
433+ */
434+ protected boolean handleIsCancelled (
435+ File file , int depth , Collection results ) throws IOException {
436+ // do nothing - overridable by subclass
437+ return false ; // not cancelled
438+ }
439+
440+ /**
441+ * Overridable callback method invoked when the operation is cancelled.
442+ * The file being processed when the cancellation occurred can be
443+ * obtained from the exception.
444+ * <p>
445+ * This implementation just re-throws the {@link CancelException}.
446+ *
447+ * @param startDirectory the directory that the walk started from
448+ * @param results the collection of result objects, may be updated
449+ * @param cancel the exception throw to cancel further processing
450+ * containing details at the point of cancellation.
451+ * @throws IOException if an I/O Error occurs
452+ */
453+ protected void handleCancelled (File startDirectory , Collection results ,
454+ CancelException cancel ) throws IOException {
455+ // re-throw exception - overridable by subclass
456+ throw cancel ;
381457 }
382458
383459 //-----------------------------------------------------------------------
@@ -482,25 +558,6 @@ protected void handleEnd(Collection results) throws IOException {
482558 // do nothing - overridable by subclass
483559 }
484560
485- /**
486- * Overridable callback method invoked when the operation is cancelled.
487- * The file being processed when the cancellation occurred can be
488- * obtained from the exception.
489- * <p>
490- * This implementation just re-throws the {@link CancelException}.
491- *
492- * @param startDirectory the directory to start from
493- * @param results the collection of result objects, may be updated
494- * @param cancel the exception throw to cancel further processing
495- * containing details at the point of cancellation.
496- * @throws IOException if an I/O Error occurs
497- */
498- protected void handleCancelled (File startDirectory , Collection results ,
499- CancelException cancel ) throws IOException {
500- // re-throw exception - overridable by subclass
501- throw cancel ;
502- }
503-
504561 //-----------------------------------------------------------------------
505562 /**
506563 * CancelException is thrown in DirectoryWalker to cancel the current
0 commit comments