From 36d881c47f87bbd61eda0a8442ae0d9503a73d78 Mon Sep 17 00:00:00 2001 From: Bill Bejeck Date: Fri, 10 Feb 2012 02:07:43 -0500 Subject: [PATCH] updated java.7 home property for new java 7 SDK, pushed common test setup into new BaseFileTest class, added tests to DirectoryStreamTest, added FilterBuilder class added method to DirUtils created AsynchronousRecursiveDirectoryStream and test --- README.txt | 5 +- pom.xml | 2 +- .../AsynchronousRecursiveDirectoryStream.java | 117 +++++++++++++++++ src/main/java/bbejeck/nio/util/DirUtils.java | 18 ++- .../java/bbejeck/nio/util/FilterBuilder.java | 45 +++++++ .../java/bbejeck/nio/files/BaseFileTest.java | 79 ++++++++++++ .../nio/files/DirectoryStreamTest.java | 121 ++++++++++++++++++ .../bbejeck/nio/files/FilesCopyMoveTest.java | 51 +------- ...nchronousRecursiveDirectoryStreamTest.java | 85 ++++++++++++ 9 files changed, 467 insertions(+), 56 deletions(-) create mode 100644 src/main/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStream.java create mode 100644 src/main/java/bbejeck/nio/util/FilterBuilder.java create mode 100644 src/test/java/bbejeck/nio/files/BaseFileTest.java create mode 100644 src/test/java/bbejeck/nio/files/DirectoryStreamTest.java create mode 100644 src/test/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStreamTest.java diff --git a/README.txt b/README.txt index 8f662f1..7b0bcb0 100644 --- a/README.txt +++ b/README.txt @@ -1 +1,4 @@ -Source code for the Java 7 java.nio.file blog series. Please note that the pom file needs to have the path for Java 7 set as I'm still using Java 6 as my default and needed to specify in the pom where Java 7 lives for compilation and tests. +Source code for the Java 7 java.nio.file blog series. +Please note that the pom file needs to have the path for Java 7 +set as I'm still using Java 6 as my default JDK and needed to specify +in the pom where Java 7 lives for compilation and tests. diff --git a/pom.xml b/pom.xml index 4348464..6ff67f7 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,6 @@ - /usr/share/java/java7 + /usr/share/java/java7/Contents/Home diff --git a/src/main/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStream.java b/src/main/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStream.java new file mode 100644 index 0000000..a54aee5 --- /dev/null +++ b/src/main/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStream.java @@ -0,0 +1,117 @@ +package bbejeck.nio.files.directory; + +import bbejeck.nio.files.visitor.FunctionVisitor; +import bbejeck.nio.util.FilterBuilder; +import com.google.common.base.Function; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; +import java.util.Objects; +import java.util.concurrent.*; + +/** + * Created by IntelliJ IDEA. + * User: bbejeck + * Date: 2/5/12 + * Time: 1:05 PM + */ +public class AsynchronousRecursiveDirectoryStream implements DirectoryStream { + + private LinkedBlockingQueue pathsBlockingQueue = new LinkedBlockingQueue<>(); + private boolean closed = false; + private FutureTask pathTask; + private Path startPath; + private Filter filter; + + public AsynchronousRecursiveDirectoryStream(Path startPath, String pattern) throws IOException { + this.filter = FilterBuilder.buildGlobFilter(Objects.requireNonNull(pattern)); + this.startPath = Objects.requireNonNull(startPath); + } + + @Override + public Iterator iterator() { + confirmNotClosed(); + findFiles(startPath, filter); + return new Iterator() { + Path path; + @Override + public boolean hasNext() { + try { + path = pathsBlockingQueue.poll(); + while (!pathTask.isDone() && path == null) { + path = pathsBlockingQueue.poll(5, TimeUnit.MILLISECONDS); + } + return (path != null) ? true : false; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return false; + } + + @Override + public Path next() { + return path; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Removal not supported"); + } + }; + } + + private void findFiles(final Path startPath, final Filter filter) { + pathTask = new FutureTask(new Callable() { + @Override + public Void call() throws Exception { + Files.walkFileTree(startPath, new FunctionVisitor(getFunction(filter))); + return null; + } + }); + start(pathTask); + } + + private Function getFunction(final Filter filter) { + return new Function() { + @Override + public FileVisitResult apply(Path input) { + try { + if (filter.accept(input.getFileName())) { + pathsBlockingQueue.offer(input); + } + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return (pathTask.isCancelled()) ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE; + } + }; + } + + + @Override + public void close() throws IOException { + if(pathTask !=null){ + pathTask.cancel(true); + } + pathsBlockingQueue.clear(); + pathsBlockingQueue = null; + pathTask = null; + filter = null; + closed = true; + } + + private void start(FutureTask futureTask) { + new Thread(futureTask).start(); + } + + private void confirmNotClosed() { + if (closed) { + throw new IllegalStateException("DirectoryStream has already been closed"); + } + } + +} diff --git a/src/main/java/bbejeck/nio/util/DirUtils.java b/src/main/java/bbejeck/nio/util/DirUtils.java index 2b2e638..251eff3 100644 --- a/src/main/java/bbejeck/nio/util/DirUtils.java +++ b/src/main/java/bbejeck/nio/util/DirUtils.java @@ -1,14 +1,12 @@ package bbejeck.nio.util; +import bbejeck.nio.files.directory.AsynchronousRecursiveDirectoryStream; import bbejeck.nio.files.visitor.*; import com.google.common.base.Function; import com.google.common.base.Predicate; import java.io.IOException; -import java.nio.file.FileVisitOption; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.file.*; import java.util.EnumSet; import java.util.Objects; @@ -126,6 +124,18 @@ public static void copyWithPredicate(Path from, Path to, Predicate predica validate(from); Files.walkFileTree(from, new CopyPredicateVisitor(from,to,predicate)); } + + /** + * Returns a DirectoryStream that can iterate over files found recursively based on the pattern provided + * @param startPath the Directory to start from + * @param pattern the glob to match against files + * @return DirectoryStream + * @throws IOException + */ + public static DirectoryStream glob(Path startPath, String pattern) throws IOException{ + validate(startPath); + return new AsynchronousRecursiveDirectoryStream(startPath,pattern); + } diff --git a/src/main/java/bbejeck/nio/util/FilterBuilder.java b/src/main/java/bbejeck/nio/util/FilterBuilder.java new file mode 100644 index 0000000..25b4d66 --- /dev/null +++ b/src/main/java/bbejeck/nio/util/FilterBuilder.java @@ -0,0 +1,45 @@ +package bbejeck.nio.util; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; + +/** + * Created by IntelliJ IDEA. + * User: bbejeck + * Date: 2/2/12 + * Time: 11:46 PM + */ + +public class FilterBuilder { + + private FilterBuilder() {} + + public static DirectoryStream.Filter buildGlobFilter(String pattern) { + final PathMatcher pathMatcher = getPathMatcher("glob:"+pattern); + return new DirectoryStream.Filter() { + @Override + public boolean accept(Path entry) throws IOException { + return pathMatcher.matches(entry); + } + }; + } + + public static DirectoryStream.Filter buildRegexFilter(String pattern) { + final PathMatcher pathMatcher = getPathMatcher("regex:"+pattern); + return new DirectoryStream.Filter() { + @Override + public boolean accept(Path entry) throws IOException { + return pathMatcher.matches(entry); + } + }; + } + + + private static PathMatcher getPathMatcher(String pattern) { + return FileSystems.getDefault().getPathMatcher(pattern); + } + +} diff --git a/src/test/java/bbejeck/nio/files/BaseFileTest.java b/src/test/java/bbejeck/nio/files/BaseFileTest.java new file mode 100644 index 0000000..0bf571a --- /dev/null +++ b/src/test/java/bbejeck/nio/files/BaseFileTest.java @@ -0,0 +1,79 @@ +package bbejeck.nio.files; + +import bbejeck.nio.util.DirUtils; +import bbejeck.nio.util.FileGenerator; +import org.junit.Before; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Created by IntelliJ IDEA. + * User: bbejeck + * Date: 1/31/12 + * Time: 9:31 PM + */ + +public class BaseFileTest { + protected String baseDir = "test-files"; + protected String dir1 = "dir1"; + protected String dir2 = "dir2"; + protected String fileName; + protected String fooDir = "foo"; + protected String copyDir = "copy"; + protected String tempDir = "temp"; + protected Path basePath; + protected Path dir1Path; + protected Path dir2Path; + protected Path fooPath; + protected Path sourcePath; + protected Path copyPath; + protected String[] fakeJavaFiles = new String[]{"Code.java", "Foo.java", "Bar.java", "Baz.java"}; + protected String[] textFileNames = new String[]{"persons.csv", "counts.csv", "CountyTaxes.csv"}; + + + @Before + public void setUp() throws Exception { + basePath = Paths.get(baseDir); + dir1Path = basePath.resolve(dir1); + dir2Path = basePath.resolve(dir2); + fileName = "test.txt"; + sourcePath = basePath.resolve(fileName); + copyPath = basePath.resolve(copyDir); + fooPath = basePath.resolve(fooDir); + cleanUp(); + createDirectories(); + generateFile(Paths.get(baseDir, fileName), 500); + generateFiles(basePath, fakeJavaFiles, textFileNames); + generateFiles(dir1Path, fakeJavaFiles); + generateFiles(dir2Path, fakeJavaFiles); + } + + protected void cleanUp() throws Exception { + DirUtils.deleteIfExists(basePath); + Files.deleteIfExists(basePath.resolve(Paths.get(copyDir, fooDir))); + Files.deleteIfExists(basePath.resolve(Paths.get(copyDir, tempDir))); + Files.deleteIfExists(basePath.resolve(tempDir)); + Files.deleteIfExists(basePath.resolve("tempII")); + } + + protected void createDirectories() throws Exception { + Files.createDirectories(dir1Path); + Files.createDirectories(dir2Path); + Files.createDirectories(copyPath); + Files.createDirectories(fooPath); + } + + protected void generateFile(Path path, int numberLines) throws Exception { + FileGenerator.generate(path, numberLines); + } + + protected void generateFiles(Path path, String[]... fileNames) throws Exception { + for (String[] fileNamesArray : fileNames) { + for (String fileName : fileNamesArray) { + generateFile(path.resolve(fileName), 10); + } + } + } +} diff --git a/src/test/java/bbejeck/nio/files/DirectoryStreamTest.java b/src/test/java/bbejeck/nio/files/DirectoryStreamTest.java new file mode 100644 index 0000000..4588d1f --- /dev/null +++ b/src/test/java/bbejeck/nio/files/DirectoryStreamTest.java @@ -0,0 +1,121 @@ +package bbejeck.nio.files; + +import bbejeck.nio.files.directory.AsynchronousRecursiveDirectoryStream; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.*; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by IntelliJ IDEA. + * User: bbejeck + * Date: 1/31/12 + * Time: 9:18 PM + */ +public class DirectoryStreamTest extends BaseFileTest { + private int expectedDirectoryCount; + private int expectedFileCount; + @Before + public void setUp() throws Exception { + super.setUp(); + expectedDirectoryCount = 4; + expectedFileCount = fakeJavaFiles.length + textFileNames.length + 1; + } + + @Test + public void testDirectoryStream() throws Exception { + int directoryCount = 0; + int fileCount = 0; + try (DirectoryStream directoryStream = Files.newDirectoryStream(basePath)) { + for (Path path : directoryStream) { + if (Files.isDirectory(path)) { + directoryCount++; + } else if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(expectedDirectoryCount, is(directoryCount)); + assertThat(expectedFileCount, is(fileCount)); + } + + @Test + public void testDirectoryStreamGlobAll() throws Exception { + int directoryCount = 0; + int fileCount = 0; + try (DirectoryStream directoryStream = Files.newDirectoryStream(basePath, "*")) { + for (Path path : directoryStream) { + if (Files.isDirectory(path)) { + directoryCount++; + } else if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(expectedDirectoryCount, is(directoryCount)); + assertThat(expectedFileCount, is(fileCount)); + } + + @Test + public void testDirectoryStreamGlobPattern() throws Exception { + int directoryCount = 0; + int fileCount = 0; + try (DirectoryStream directoryStream = Files.newDirectoryStream(basePath, "Co*")) { + for (Path path : directoryStream) { + if (Files.isDirectory(path)) { + directoryCount++; + } else if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(directoryCount, is(0)); + assertThat(fileCount, is(2)); + } + + @Test + public void testDirectoryStreamGlobRegexPattern() throws Exception { + int directoryCount = 0; + int fileCount = 0; + final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("regex:.*/[^ C d f t].*\\.\\w{3,4}"); + DirectoryStream.Filter filter = new DirectoryStream.Filter() { + @Override + public boolean accept(Path entry) throws IOException { + return pathMatcher.matches(entry); + } + }; + try (DirectoryStream directoryStream = Files.newDirectoryStream(basePath, filter)) { + for (Path path : directoryStream) { + if (Files.isDirectory(path)) { + directoryCount++; + } else if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(directoryCount, is(0)); + assertThat(fileCount, is(5)); + } + + @Test + public void testDirectoryStreamGlobMultipleFileExtensions() throws Exception { + int directoryCount = 0; + int fileCount = 0; + try (DirectoryStream directoryStream = Files.newDirectoryStream(basePath, "*.{java,txt}")) { + for (Path path : directoryStream) { + if (Files.isDirectory(path)) { + directoryCount++; + } else if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(directoryCount, is(0)); + assertThat(fileCount, is(5)); + } + +} diff --git a/src/test/java/bbejeck/nio/files/FilesCopyMoveTest.java b/src/test/java/bbejeck/nio/files/FilesCopyMoveTest.java index bc7ede1..20a432a 100644 --- a/src/test/java/bbejeck/nio/files/FilesCopyMoveTest.java +++ b/src/test/java/bbejeck/nio/files/FilesCopyMoveTest.java @@ -17,37 +17,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class FilesCopyMoveTest { - - protected String baseDir = "test-files"; - protected String dir1 = "dir1"; - protected String dir2 = "dir2"; - protected String fileName; - protected String fooDir = "foo"; - private String copyDir = "copy"; - private String tempDir = "temp"; - - protected Path basePath; - protected Path dir1Path; - protected Path dir2Path; - private Path fooPath; - private Path sourcePath; - private Path copyPath; - - - @Before - public void setUp() throws Exception { - basePath = Paths.get(baseDir); - dir1Path = basePath.resolve(dir1); - dir2Path = basePath.resolve(dir2); - fileName = "test.txt"; - sourcePath = basePath.resolve(fileName); - copyPath = basePath.resolve(copyDir); - fooPath = basePath.resolve(fooDir); - cleanUp(); - createDirectories(); - generateFiles(Paths.get(baseDir, fileName), 500); - } +public class FilesCopyMoveTest extends BaseFileTest { @Test @@ -101,23 +71,4 @@ public void testMoveDirectory() throws Exception { assertThat(Files.exists(basePath.resolve(Paths.get(copyDir, tempDir))), is(true)); } - private void cleanUp() throws Exception { - DirUtils.deleteIfExists(basePath); - Files.deleteIfExists(basePath.resolve(Paths.get(copyDir, fooDir))); - Files.deleteIfExists(basePath.resolve(Paths.get(copyDir, tempDir))); - Files.deleteIfExists(basePath.resolve(tempDir)); - Files.deleteIfExists(basePath.resolve("tempII")); - } - - private void createDirectories() throws Exception { - Files.createDirectories(dir1Path); - Files.createDirectories(dir2Path); - Files.createDirectories(copyPath); - Files.createDirectories(fooPath); - } - - private void generateFiles(Path path, int numberLines) throws Exception { - FileGenerator.generate(path, numberLines); - } - } diff --git a/src/test/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStreamTest.java b/src/test/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStreamTest.java new file mode 100644 index 0000000..3e69e6c --- /dev/null +++ b/src/test/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStreamTest.java @@ -0,0 +1,85 @@ +package bbejeck.nio.files.directory; + +import bbejeck.nio.files.BaseFileTest; +import org.junit.Before; +import org.junit.Test; + +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Iterator; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by IntelliJ IDEA. + * User: bbejeck + * Date: 2/10/12 + * Time: 12:54 AM + */ + +public class AsynchronousRecursiveDirectoryStreamTest extends BaseFileTest { + private int expectedJavaFileCount; + private int expectedTotalFileCount; + + @Before + public void setUp() throws Exception { + super.setUp(); + expectedJavaFileCount = fakeJavaFiles.length * 3; + expectedTotalFileCount = expectedJavaFileCount + textFileNames.length + 1; + } + + @Test + public void testRecursiveDirectoryStream() throws Exception { + int fileCount = 0; + try (DirectoryStream directoryStream = new AsynchronousRecursiveDirectoryStream(basePath, "*")) { + for (Path path : directoryStream) { + if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(fileCount, is(expectedTotalFileCount)); + } + + @Test + public void testRecursiveDirectoryStreamJavaFiles() throws Exception { + int fileCount = 0; + try (DirectoryStream directoryStream = new AsynchronousRecursiveDirectoryStream(basePath, "*.java")) { + for (Path path : directoryStream) { + if (Files.isRegularFile(path)) { + fileCount++; + } + } + } + assertThat(fileCount, is(expectedJavaFileCount)); + } + + @Test (expected = IllegalStateException.class) + public void testErrorWhenIteratorCalledAfterClose() throws Exception { + DirectoryStream directoryStream = new AsynchronousRecursiveDirectoryStream(basePath,"*"); + directoryStream.close(); + directoryStream.iterator(); + } + + @Test (expected = IllegalStateException.class) + public void testErrorWhenIteratorCalledTwice() throws Exception { + DirectoryStream directoryStream = new AsynchronousRecursiveDirectoryStream(basePath,"*"); + directoryStream.iterator(); + directoryStream.close(); + directoryStream.iterator(); + } + + @Test(expected = UnsupportedOperationException.class) + public void testErrorOnRemove() throws Exception{ + try(DirectoryStream directoryStream = new AsynchronousRecursiveDirectoryStream(basePath,"*")){ + Iterator it = directoryStream.iterator(); + while(it.hasNext()){ + it.remove(); + } + } + } + + +}