// Path: src/main/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStream.java
// public class AsynchronousRecursiveDirectoryStream implements DirectoryStream<Path> {
// 
//     private LinkedBlockingQueue<Path> pathsBlockingQueue = new LinkedBlockingQueue<>();
//     private boolean closed = false;
//     private FutureTask<Void> 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<Path> iterator() {
//         confirmNotClosed();
//         findFiles(startPath, filter);
//         return new Iterator<Path>() {
//             Path path;
//             @Override
//             public boolean hasNext() {
//                 try {
//                     path = pathsBlockingQueue.poll();
//                     while (!pathTask.isDone() && path == null) {
//                         path = pathsBlockingQueue.poll(5, TimeUnit.MILLISECONDS);
//                     }
//                     return (path != null);
//                 } 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<Void>() {
//             @Override
//             public Void call() throws Exception {
//                 Files.walkFileTree(startPath, new FunctionVisitor(getFunction(filter)));
//                 return null;
//             }
//         });
//         start(pathTask);
//     }
// 
//     private Function<Path, FileVisitResult> getFunction(final Filter<Path> filter) {
//         return new Function<Path, FileVisitResult>() {
//             @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<Void> futureTask) {
//         new Thread(futureTask).start();
//     }
// 
//     private void confirmNotClosed() {
//         if (closed) {
//             throw new IllegalStateException("DirectoryStream has already been closed");
//         }
//     }
// 
// }


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.*;
import java.util.EnumSet;
import java.util.Objects;
     * @param function
     * @throws IOException
     */
    public static void apply(Path target, Function<Path,FileVisitResult> function) throws IOException{
        validate(target);
        Files.walkFileTree(target,new FunctionVisitor(function));
    }

    /**
     * Traverses the directory structure and will only copy sub-tree structures where the provided predicate is true
     * @param from
     * @param to
     * @param predicate
     * @throws IOException
     */
    
    public static void copyWithPredicate(Path from, Path to, Predicate<Path> predicate) throws IOException{
        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<Path> glob(Path startPath, String pattern) throws IOException{
        validate(startPath);
// Path: src/main/java/bbejeck/nio/files/directory/AsynchronousRecursiveDirectoryStream.java
// public class AsynchronousRecursiveDirectoryStream implements DirectoryStream<Path> {
// 
//     private LinkedBlockingQueue<Path> pathsBlockingQueue = new LinkedBlockingQueue<>();
//     private boolean closed = false;
//     private FutureTask<Void> 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<Path> iterator() {
//         confirmNotClosed();
//         findFiles(startPath, filter);
//         return new Iterator<Path>() {
//             Path path;
//             @Override
//             public boolean hasNext() {
//                 try {
//                     path = pathsBlockingQueue.poll();
//                     while (!pathTask.isDone() && path == null) {
//                         path = pathsBlockingQueue.poll(5, TimeUnit.MILLISECONDS);
//                     }
//                     return (path != null);
//                 } 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<Void>() {
//             @Override
//             public Void call() throws Exception {
//                 Files.walkFileTree(startPath, new FunctionVisitor(getFunction(filter)));
//                 return null;
//             }
//         });
//         start(pathTask);
//     }
// 
//     private Function<Path, FileVisitResult> getFunction(final Filter<Path> filter) {
//         return new Function<Path, FileVisitResult>() {
//             @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<Void> futureTask) {
//         new Thread(futureTask).start();
//     }
// 
//     private void confirmNotClosed() {
//         if (closed) {
//             throw new IllegalStateException("DirectoryStream has already been closed");
//         }
//     }
// 
// }


// Path: src/main/java/bbejeck/nio/util/DirUtils.java
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.*;
import java.util.EnumSet;
import java.util.Objects;

     * @param function
     * @throws IOException
     */
    public static void apply(Path target, Function<Path,FileVisitResult> function) throws IOException{
        validate(target);
        Files.walkFileTree(target,new FunctionVisitor(function));
    }

    /**
     * Traverses the directory structure and will only copy sub-tree structures where the provided predicate is true
     * @param from
     * @param to
     * @param predicate
     * @throws IOException
     */
    
    public static void copyWithPredicate(Path from, Path to, Predicate<Path> predicate) throws IOException{
        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<Path> glob(Path startPath, String pattern) throws IOException{
        validate(startPath);
            return new AsynchronousRecursiveDirectoryStream(startPath,pattern);