You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sc...@apache.org on 2006/10/07 17:15:26 UTC
svn commit: r453928 - in /jakarta/commons/proper/io/trunk/src:
java/org/apache/commons/io/DirectoryWalker.java
test/org/apache/commons/io/DirectoryWalkerTestCase.java
test/org/apache/commons/io/PackageTestSuite.java
Author: scolebourne
Date: Sat Oct 7 08:15:26 2006
New Revision: 453928
URL: http://svn.apache.org/viewvc?view=rev&rev=453928
Log:
Add cancellation support to DirectoryWalker
Modified:
jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java
jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java
jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java
Modified: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java?view=diff&rev=453928&r1=453927&r2=453928
==============================================================================
--- jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java (original)
+++ jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java Sat Oct 7 08:15:26 2006
@@ -94,6 +94,49 @@
*
* </pre>
*
+ * <h3>Cancellation</h3>
+ *
+ * The DirectoryWalker contains some of the logic required for cancel processing.
+ * Subclasses must complete the implementation.
+ * This is for performance and to ensure you think about the multihreaded implications.
+ * <p>
+ * Before any processing occurs on each file or directory the
+ * <code>isCancelled()</code> method is called. If it returns <code>true</code>
+ * then <code>handleCancelled()<code> is called. This method can decide whether
+ * to accept or ignore the cancellation. If it accepts it then all further
+ * processing is skipped and the operation returns. If it rejects it then
+ * processing continues on as before. This is useful if a group of files has
+ * meaning and cancellation cannot occur in the middle of the group.
+ * <p>
+ * The default implementation of <code>isCancelled()</code> always
+ * returns <code>false</code> and it is down to the implementation
+ * to fully implement the <code>isCancelled()</code> behaviour.
+ * <p>
+ * The following example uses the
+ * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">
+ * volatile</a> keyword to (hopefully) ensure it will work properly in
+ * a multi-threaded environment.
+ *
+ * <pre>
+ * public class FooDirectoryWalker extends DirectoryWalker {
+ *
+ * private volatile boolean cancelled = false;
+ *
+ * public void cancel() {
+ * cancelled = true;
+ * }
+ *
+ * public boolean isCancelled() {
+ * return cancelled;
+ * }
+ *
+ * protected boolean handleCancelled(File file, int depth, Collection results) {
+ * // implement any cancel processing here
+ * return true; // accept cancellation
+ * }
+ * }
+ * </pre>
+ *
* @since Commons IO 1.3
* @version $Revision: 424748 $
*/
@@ -109,6 +152,13 @@
private final int depthLimit;
/**
+ * Construct an instance with no filtering and unlimited <i>depth</i>.
+ */
+ protected DirectoryWalker() {
+ this(null, -1);
+ }
+
+ /**
* Construct an instance with a filter and limit the <i>depth</i> navigated to.
*
* @param filter the filter to limit the navigation/results, may be null
@@ -131,35 +181,49 @@
* Once called, this method will emit events as it walks the hierarchy.
* The event methods have the prefix <code>handle</code>.
*
- * @param startDirectory the directory to start from
+ * @param startDirectory the directory to start from, not null
* @param results the collection of result objects, may be updated
+ * @return true if completed, false if cancelled
+ * @throws NullPointerException if the start directory is null
*/
- protected void walk(File startDirectory, Collection results) {
+ protected boolean walk(File startDirectory, Collection results) {
handleStart(startDirectory, results);
- walk(startDirectory, 0, results);
+ if (walk(startDirectory, 0, results) == false) {
+ return false; // cancelled
+ }
handleEnd(results);
+ return true;
}
/**
* Main recursive method to examine the directory hierarchy.
*
- * @param directory the directory to examine
+ * @param directory the directory to examine, not null
* @param depth the directory level (starting directory = 0)
* @param results the collection of result objects, may be updated
+ * @return false if cancelled
*/
- private void walk(File directory, int depth, Collection results) {
+ private boolean walk(File directory, int depth, Collection results) {
+ if (isCancelled() && handleCancelled(directory, depth, results)) {
+ return false; // cancelled
+ }
if (handleDirectory(directory, depth, results)) {
handleDirectoryStart(directory, depth, results);
int childDepth = depth + 1;
if (depthLimit < 0 || childDepth <= depthLimit) {
File[] files = (filter == null ? directory.listFiles() : directory.listFiles(filter));
if (files == null) {
- handleRestricted(directory, results);
+ handleRestricted(directory, childDepth, results);
} else {
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
- walk(files[i], childDepth, results);
+ if (walk(files[i], childDepth, results) == false) {
+ return false; // cancelled
+ }
} else {
+ if (isCancelled() && handleCancelled(files[i], childDepth, results)) {
+ return false; // cancelled
+ }
handleFile(files[i], childDepth, results);
}
}
@@ -167,6 +231,7 @@
}
handleDirectoryEnd(directory, depth, results);
}
+ return true;
}
//-----------------------------------------------------------------------
@@ -198,7 +263,7 @@
*/
protected boolean handleDirectory(File directory, int depth, Collection results) {
// do nothing - overridable by subclass
- return true;
+ return true; // process directory
}
/**
@@ -233,9 +298,10 @@
* This implementation does nothing.
*
* @param directory the restricted directory
+ * @param depth the current directory level (starting directory = 0)
* @param results the collection of result objects, may be updated
*/
- protected void handleRestricted(File directory, Collection results) {
+ protected void handleRestricted(File directory, int depth, Collection results) {
// do nothing - overridable by subclass
}
@@ -261,6 +327,48 @@
*/
protected void handleEnd(Collection results) {
// do nothing - overridable by subclass
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Indicates whether the operation has been cancelled or not.
+ * <p>
+ * This implementation always returns <code>false</code>.
+ *
+ * @return true if the operation has been cancelled
+ */
+ protected boolean isCancelled() {
+ return false;
+ }
+
+ /**
+ * Overridable callback method invoked when the operation is cancelled.
+ * <p>
+ * This method returns a boolean to indicate if the cancellation is being
+ * accepted or rejected. This could be useful if you need to finish processing
+ * all the files in a directory before accepting the cancellation request.
+ * For example, this only accepts the cancel when the current directory is complete:
+ * <pre>
+ * protected boolean handleCancelled(File file, int depth, Collection results) {
+ * return file.isDirectory();
+ * }
+ * </pre>
+ * If you return true, then the whole operation is cancelled and no more event
+ * methods will be called.
+ * <p>
+ * If you return false, then normal processing will continue until the next time
+ * the <code>isCancelled()</code> method returns false.
+ * <p>
+ * This implementation returns true, accepting the cancellation.
+ *
+ * @param file the file about to be processed which may be a file or a directory
+ * @param depth the current directory level (starting directory = 0)
+ * @param results the collection of result objects, may be updated
+ * @return true to accept the cancellation, false to reject it
+ */
+ protected boolean handleCancelled(File file, int depth, Collection results) {
+ // do nothing - overridable by subclass
+ return true; // accept cancellation
}
}
Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java?view=diff&rev=453928&r1=453927&r2=453928
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java (original)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java Sat Oct 7 08:15:26 2006
@@ -21,6 +21,8 @@
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
+
+import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
@@ -124,11 +126,11 @@
*/
public void testFilterAndLimitC() {
List results = new TestFileFinder(NOT_SVN, 3).find(javaDir);
- assertEquals("[A] Result Size", 4, results.size());
- assertTrue("[A] Start Dir", results.contains(javaDir));
- assertTrue("[A] Org Dir", results.contains(orgDir));
- assertTrue("[A] Apache Dir", results.contains(apacheDir));
- assertTrue("[A] Commons Dir", results.contains(commonsDir));
+ assertEquals("[C] Result Size", 4, results.size());
+ assertTrue("[C] Start Dir", results.contains(javaDir));
+ assertTrue("[C] Org Dir", results.contains(orgDir));
+ assertTrue("[C] Apache Dir", results.contains(apacheDir));
+ assertTrue("[C] Commons Dir", results.contains(commonsDir));
}
/**
@@ -162,7 +164,6 @@
assertEquals("Result Size", 1, results.size());
assertTrue("Current Dir", results.contains(invalidDir));
- // TODO is this what we want with Null directory?
try {
new TestFileFinder(null, -1).find(null);
fail("Null start directory didn't throw Exception");
@@ -204,6 +205,20 @@
return new NameFileFilter(names);
}
+ /**
+ * Test Cancel
+ */
+ public void testCancel() {
+ List results = new TestCancelWalker(2, true).find(javaDir);
+ assertEquals(2, results.size());
+
+ results = new TestCancelWalker(3, true).find(javaDir);
+ assertEquals(3, results.size());
+
+ results = new TestCancelWalker(3, false).find(javaDir);
+ assertEquals(6, results.size());
+ }
+
// ------------ Test DirectoryWalker implementation --------------------------
/**
@@ -219,7 +234,7 @@
/** find files. */
protected List find(File startDirectory) {
List results = new ArrayList();
- walk(startDirectory, results);
+ Assert.assertEquals(true, walk(startDirectory, results));
return results;
}
@@ -249,6 +264,84 @@
/** Always returns false. */
protected boolean handleDirectory(File directory, int depth, Collection results) {
return false;
+ }
+ }
+
+ // ------------ Test DirectoryWalker implementation --------------------------
+
+ /**
+ * Test DirectoryWalker implementation that finds files in a directory hierarchy
+ * applying a file filter.
+ */
+ static class TestCancelWalker extends DirectoryWalker {
+ private boolean cancelled;
+ private int count;
+ private boolean accept;
+
+ TestCancelWalker(int count, boolean accept) {
+ super();
+ this.count = count;
+ this.accept = accept;
+ }
+
+ /** find files. */
+ public List find(File startDirectory) {
+ List results = new ArrayList();
+ Assert.assertEquals(false, walk(startDirectory, results));
+ return results;
+ }
+
+ /** Return cancelled flag. */
+ protected boolean isCancelled() {
+ return cancelled;
+ }
+
+ /** Handles a directory start. */
+ protected void handleDirectoryStart(File directory, int depth, Collection results) {
+ if (accept) {
+ Assert.assertEquals(false, cancelled);
+ }
+ }
+
+ /** Handles a directory end by adding the File to the result set. */
+ protected void handleDirectoryEnd(File directory, int depth, Collection results) {
+ if (accept) {
+ Assert.assertEquals(false, cancelled);
+ }
+ results.add(directory);
+ cancelled = (results.size() >= count);
+ }
+
+ /** Handles a file by adding the File to the result set. */
+ protected void handleFile(File file, int depth, Collection results) {
+ if (accept) {
+ Assert.assertEquals(false, cancelled);
+ }
+ results.add(file);
+ cancelled = (results.size() >= count);
+ }
+
+ /** Handles start. */
+ protected void handleStart(File directory, Collection results) {
+ if (accept) {
+ Assert.assertEquals(false, cancelled);
+ }
+ }
+
+ /** Handles end. */
+ protected void handleEnd(Collection results) {
+ if (accept) {
+ Assert.assertEquals(false, cancelled);
+ }
+ }
+
+ /** Handles end. */
+ protected boolean handleCancelled(File file, int depth, Collection results) {
+ Assert.assertEquals(true, cancelled);
+ if (accept) {
+ return true;
+ }
+ return (results.size() >= (count * 2));
}
}
Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java?view=diff&rev=453928&r1=453927&r2=453928
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java (original)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java Sat Oct 7 08:15:26 2006
@@ -37,6 +37,7 @@
TestSuite suite = new TestSuite("IO Utilities");
suite.addTest(new TestSuite(CopyUtilsTest.class));
suite.addTest(new TestSuite(DemuxTestCase.class));
+ suite.addTest(new TestSuite(DirectoryWalkerTestCase.class));
suite.addTest(new TestSuite(EndianUtilsTest.class));
suite.addTest(new TestSuite(FileCleanerTestCase.class));
suite.addTest(new TestSuite(FileDeleteStrategyTestCase.class));
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org