Skip to content

Commit 25a0802

Browse files
author
Stephen Colebourne
committed
Add constructor to take directory and file filters separately
git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/io/trunk@463942 13f79535-47bb-0310-9956-ffa450edef68
1 parent 6332f98 commit 25a0802

2 files changed

Lines changed: 162 additions & 12 deletions

File tree

src/java/org/apache/commons/io/DirectoryWalker.java

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
import java.io.IOException;
2222
import java.util.Collection;
2323

24+
import org.apache.commons.io.filefilter.FileFilterUtils;
25+
import org.apache.commons.io.filefilter.IOFileFilter;
26+
import org.apache.commons.io.filefilter.TrueFileFilter;
27+
2428
/**
2529
* Abstract class that walks through a directory hierarchy and provides
2630
* subclasses with convenient hooks to add specific behaviour.
@@ -80,14 +84,28 @@
8084
* <a name="filter"></a>
8185
* <h3>2. Filter Example</h3>
8286
*
83-
* If you wanted all directories which are not hidden
84-
* and files which end in ".txt" - you could build a composite filter
85-
* using the filter implementations in the Commons IO
86-
* <a href="filefilter/package-summary.html">filefilter</a> package
87-
* in the following way:
88-
*
87+
* Choosing which directories and files to process can be a key aspect
88+
* of using this class. This information can be setup in three ways,
89+
* via three different constructors.
90+
* <p>
91+
* The first option is to visit all directories and files.
92+
* This is achieved via the no-args constructor.
93+
* <p>
94+
* The second constructor option is to supply a single {@link FileFilter}
95+
* that describes the files and directories to visit. Care must be taken
96+
* with this option as the same filter is used for both directories
97+
* and files.
98+
* <p>
99+
* For example, if you wanted all directories which are not hidden
100+
* and files which end in ".txt":
89101
* <pre>
90-
*
102+
* public class FooDirectoryWalker extends DirectoryWalker {
103+
* public FooDirectoryWalker(FileFilter filter) {
104+
* super(filter, -1);
105+
* }
106+
* }
107+
*
108+
* // Build up the filters and create the walker
91109
* // Create a filter for Non-hidden directories
92110
* IOFileFilter fooDirFilter =
93111
* FileFilterUtils.andFileFilter(FileFilterUtils.directoryFileFilter,
@@ -103,9 +121,32 @@
103121
* FileFilterUtils.orFileFilter(fooDirFilter, fooFileFilter);
104122
*
105123
* // Use the filter to construct a DirectoryWalker implementation
106-
* FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter, -1);
107-
*
124+
* FooDirectoryWalker walker = new FooDirectoryWalker(fooFilter);
108125
* </pre>
126+
* <p>
127+
* The third constructor option is to specify separate filters, one for
128+
* directories and one for files. These are combined internally to form
129+
* the correct <code>FileFilter</code>, something which is very easy to
130+
* get wrong when attempted manually, particularly when trying to
131+
* express constructs like 'any file in directories named docs'.
132+
* <p>
133+
* For example, if you wanted all directories which are not hidden
134+
* and files which end in ".txt":
135+
* <pre>
136+
* public class FooDirectoryWalker extends DirectoryWalker {
137+
* public FooDirectoryWalker(IOFileFilter dirFilter, IOFileFilter fileFilter) {
138+
* super(dirFilter, fileFilter, -1);
139+
* }
140+
* }
141+
*
142+
* // Use the filters to construct the walker
143+
* FooDirectoryWalker walker = new FooDirectoryWalker(
144+
* HiddenFileFilter.VISIBLE,
145+
* FileFilterUtils.suffixFileFilter(".txt"),
146+
* );
147+
* <pre>
148+
* This is much simpler than the previous example, and is why it is the preferred
149+
* option for filtering.
109150
*
110151
* <a name="cancel"></a>
111152
* <h3>3. Cancellation</h3>
@@ -145,8 +186,8 @@
145186
*
146187
* This example provides a <code>cancel()</code> method for external processes to
147188
* indcate that processing must stop. Calling this method sets a
148-
* <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">
149-
* volatile</a> flag to (hopefully) ensure it will work properly in
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
150191
* a multi-threaded environment. In this implementation the flag is checked in two
151192
* of the lifecycle methods using a convenience <code>checkIfCancelled()</code> method
152193
* which throws a {@link CancelException} if cancellation has been requested.
@@ -237,8 +278,13 @@ protected DirectoryWalker() {
237278

238279
/**
239280
* Construct an instance with a filter and limit the <i>depth</i> navigated to.
281+
* <p>
282+
* The filter controls which files and directories will be navigated to as
283+
* part of the walk. The {@link FileFilterUtils} class is useful for combining
284+
* various filters together. A <code>null</code> filter means that no
285+
* filtering should occur and all files and directories will be visited.
240286
*
241-
* @param filter the filter to limit the navigation/results, may be null
287+
* @param filter the filter to apply, null means visit all files
242288
* @param depthLimit controls how <i>deep</i> the hierarchy is
243289
* navigated to (less than 0 means unlimited)
244290
*/
@@ -247,6 +293,33 @@ protected DirectoryWalker(FileFilter filter, int depthLimit) {
247293
this.depthLimit = depthLimit;
248294
}
249295

296+
/**
297+
* Construct an instance with a directory and a file filter and an optional
298+
* limit on the <i>depth</i> navigated to.
299+
* <p>
300+
* The filters control which files and directories will be navigated to as part
301+
* of the walk. This constructor uses {@link FileFilterUtils#makeDirectoryOnly()}
302+
* and {@link FileFilterUtils#makeFileOnly()} internally to combine the filters.
303+
* A <code>null</code> filter means that no filtering should occur.
304+
*
305+
* @param directoryFilter the filter to apply to directories, null means visit all directories
306+
* @param fileFilter the filter to apply to files, null means visit all files
307+
* @param depthLimit controls how <i>deep</i> the hierarchy is
308+
* navigated to (less than 0 means unlimited)
309+
*/
310+
protected DirectoryWalker(IOFileFilter directoryFilter, IOFileFilter fileFilter, int depthLimit) {
311+
if (directoryFilter == null && fileFilter == null) {
312+
this.filter = null;
313+
} else {
314+
directoryFilter = (directoryFilter != null ? directoryFilter : TrueFileFilter.TRUE);
315+
fileFilter = (fileFilter != null ? fileFilter : TrueFileFilter.TRUE);
316+
directoryFilter = FileFilterUtils.makeDirectoryOnly(directoryFilter);
317+
fileFilter = FileFilterUtils.makeFileOnly(fileFilter);
318+
this.filter = FileFilterUtils.orFileFilter(directoryFilter, fileFilter);
319+
}
320+
this.depthLimit = depthLimit;
321+
}
322+
250323
//-----------------------------------------------------------------------
251324
/**
252325
* Internal method that walks the directory hierarchy in a depth-first manner.

src/test/org/apache/commons/io/DirectoryWalkerTestCase.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.File;
2020
import java.io.FileFilter;
2121
import java.io.IOException;
22+
import java.util.Iterator;
2223
import java.util.List;
2324
import java.util.ArrayList;
2425
import java.util.Collection;
@@ -145,6 +146,50 @@ public void testFilterAndLimitD() {
145146
checkContainsFiles("[D] File", ioFiles, results);
146147
}
147148

149+
/**
150+
* Test separate dir and file filters
151+
*/
152+
public void testFilterDirAndFile1() {
153+
List results = new TestFileFinder(dirsFilter, iofilesFilter, -1).find(javaDir);
154+
assertEquals("[DirAndFile1] Result Size", (1 + dirs.length + ioFiles.length), results.size());
155+
assertTrue("[DirAndFile1] Start Dir", results.contains(javaDir));
156+
checkContainsFiles("[DirAndFile1] Dir", dirs, results);
157+
checkContainsFiles("[DirAndFile1] File", ioFiles, results);
158+
}
159+
160+
/**
161+
* Test separate dir and file filters
162+
*/
163+
public void testFilterDirAndFile2() {
164+
List results = new TestFileFinder((IOFileFilter) null, (IOFileFilter) null, -1).find(javaDir);
165+
assertTrue("[DirAndFile2] Result Size", results.size() > (1 + dirs.length + ioFiles.length));
166+
assertTrue("[DirAndFile2] Start Dir", results.contains(javaDir));
167+
checkContainsFiles("[DirAndFile2] Dir", dirs, results);
168+
checkContainsFiles("[DirAndFile2] File", ioFiles, results);
169+
}
170+
171+
/**
172+
* Test separate dir and file filters
173+
*/
174+
public void testFilterDirAndFile3() {
175+
List results = new TestFileFinder(dirsFilter, (IOFileFilter) null, -1).find(javaDir);
176+
List resultDirs = directoriesOnly(results);
177+
assertEquals("[DirAndFile3] Result Size", (1 + dirs.length), resultDirs.size());
178+
assertTrue("[DirAndFile3] Start Dir", results.contains(javaDir));
179+
checkContainsFiles("[DirAndFile3] Dir", dirs, resultDirs);
180+
}
181+
182+
/**
183+
* Test separate dir and file filters
184+
*/
185+
public void testFilterDirAndFile4() {
186+
List results = new TestFileFinder((IOFileFilter) null, iofilesFilter, -1).find(javaDir);
187+
List resultFiles = filesOnly(results);
188+
assertEquals("[DirAndFile4] Result Size", ioFiles.length, resultFiles.size());
189+
assertTrue("[DirAndFile4] Start Dir", results.contains(javaDir));
190+
checkContainsFiles("[DirAndFile4] File", ioFiles, resultFiles);
191+
}
192+
148193
/**
149194
* Test Limiting to current directory
150195
*/
@@ -194,6 +239,34 @@ private void checkContainsFiles(String prefix, File[] files, Collection results)
194239
}
195240
}
196241

242+
/**
243+
* Extract the directories.
244+
*/
245+
private List directoriesOnly(Collection results) {
246+
List list = new ArrayList(results.size());
247+
for (Iterator it = results.iterator(); it.hasNext(); ) {
248+
File file = (File) it.next();
249+
if (file.isDirectory()) {
250+
list.add(file);
251+
}
252+
}
253+
return list;
254+
}
255+
256+
/**
257+
* Extract the files.
258+
*/
259+
private List filesOnly(Collection results) {
260+
List list = new ArrayList(results.size());
261+
for (Iterator it = results.iterator(); it.hasNext(); ) {
262+
File file = (File) it.next();
263+
if (file.isFile()) {
264+
list.add(file);
265+
}
266+
}
267+
return list;
268+
}
269+
197270
/**
198271
* Create an name filter containg the names of the files
199272
* in the array.
@@ -259,6 +332,10 @@ protected TestFileFinder(FileFilter filter, int depthLimit) {
259332
super(filter, depthLimit);
260333
}
261334

335+
protected TestFileFinder(IOFileFilter dirFilter, IOFileFilter fileFilter, int depthLimit) {
336+
super(dirFilter, fileFilter, depthLimit);
337+
}
338+
262339
/** find files. */
263340
protected List find(File startDirectory) {
264341
List results = new ArrayList();

0 commit comments

Comments
 (0)