package org.xmlsh.util; import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; import static java.nio.file.FileVisitResult.SKIP_SUBTREE; import static java.nio.file.FileVisitResult.TERMINATE; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /* * Similar to FileTreeWalker but implements breadth-first walking then sorting then depth-first visiting * */ public class PathTreeWalker { private Path mRoot ; private Comparator<UnifiedFileAttributes> mSort ; private LinkOption[] linkOptions = FileUtils._pathNoFollowLinks; private PathMatchOptions mMatchOptions; private boolean mRecursive; static Logger mLogger = LogManager.getLogger(); public PathTreeWalker( Path root , boolean recursive , PathMatchOptions options , Comparator<UnifiedFileAttributes> sort ){ mRoot = root ; mMatchOptions = options ; mRecursive = recursive ; mSort = sort ; } public PathTreeWalker( Path root, boolean recursive , PathMatchOptions options ){ this( root , recursive , options , new Comparator<UnifiedFileAttributes>() { final Comparator<Path> pathComparator = FileUtils.alphaPathComparator(); @Override public int compare(UnifiedFileAttributes o1, UnifiedFileAttributes o2) { return pathComparator.compare(o1.getPath() , o2.getPath() ); } }); } protected <V extends IPathTreeVisitor> FileVisitResult walk(Path start, V visitor, int depth ) throws IOException { try { BasicFileAttributes attrs = FileUtils.getBasicFileAttributes(start,LinkOption.NOFOLLOW_LINKS); if( attrs == null ) return FileVisitResult.CONTINUE; UnifiedFileAttributes uattrs = FileUtils.getUnifiedFileAttributes(start, attrs , LinkOption.NOFOLLOW_LINKS ); // Start is a file not a directory if( depth == 0 && ! uattrs.isDirectory()){ // Show exact matches on root reguardless of the options return visitor.visitFile(mRoot, start , uattrs ); } List<UnifiedFileAttributes> paths = new ArrayList<UnifiedFileAttributes>(); try ( DirectoryStream<Path> ds = Files.newDirectoryStream(start) ){ for (Path path : ds) { UnifiedFileAttributes ua = FileUtils.getUnifiedFileAttributes(path, LinkOption.NOFOLLOW_LINKS ); if( mMatchOptions.doVisit(path, ua )) paths.add( ua ); } } Collections.sort(paths, mSort); FileVisitResult result = CONTINUE ; // NOW visit the sorted lists for( UnifiedFileAttributes info : paths ){ if( info.isDirectory()){ result = visitor.enterDirectory(mRoot, info.getPath() , info); if( result == CONTINUE ){ result = visitor.visitDirectory(mRoot, info.getPath(), info); if( result == CONTINUE && mRecursive ){ result = walk(info.getPath() , visitor , depth + 1 ); } result = visitor.exitDirectory(mRoot, info.getPath(), info); } else if( result == SKIP_SUBTREE ) break ; } else { if( mMatchOptions.doVisit(info.getPath(), info)) result = visitor.visitFile(mRoot, info.getPath(), info); } if( result == TERMINATE) return result ; if( result == SKIP_SIBLINGS ) break ; } return result ; } catch (IOException x ) { mLogger.catching(x); return FileVisitResult.CONTINUE; } catch (SecurityException x) { return FileVisitResult.CONTINUE; } } public <V extends IPathTreeVisitor> FileVisitResult walk( V visitor ) throws IOException { return walk( mRoot , visitor , 0 ); } }