package org.peerbox.watchservice; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import org.peerbox.watchservice.filetree.composite.FileLeaf; import org.peerbox.watchservice.filetree.composite.FolderComposite; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This utility class can be used to traverse folders. This is done for two purposes: * The first purpose is to compute a hash which represents the structure of the subtree * to detect folder moves without the need of hashing over all of its (possibly huge) * content. The second purpose is to artificially generate local create events. This is * important since new folders have to be registered to the WatchService. Until this is done, * create events can be lost. This has to be circumvented. * @author Claudio * */ public class FileWalker { private static final Logger logger = LoggerFactory.getLogger(FileWalker.class); private Path rootFolder; private FileEventManager eventManager; /** * This object is used to build the subtree structuredefined by the Path * {@link rootFolder}. */ private FolderComposite fileTree; public FileWalker(Path rootDirectory, FileEventManager eventManager){ this.rootFolder = rootDirectory; this.fileTree = new FolderComposite(rootDirectory, true); this.eventManager = eventManager; } /** * This method discovers the structure of the subtree defined by * {@link rootFolder}. The structure is defined as a recursive hash * over the {@link Path}s of all contained files and folders. * * @return a hash representing the subtree's recursive structure. */ public String computeStructureHashOfFolder(){ try { Files.walkFileTree(rootFolder, new FileIndexer(false)); } catch (IOException e) { e.printStackTrace(); } return fileTree.getStructureHash(); } /** * This method discovers the subtree defined by * {@link rootFolder} and throws local create events, which * are forwarded to the core using the {@link org.peerbox. * watchservice.FileEventManager FileEventManager}. * * @return a hash representing the subtree's recursive structure. */ public void generateLocalCreateEvents() { try { Files.walkFileTree(rootFolder, new FileIndexer(true)); } catch (IOException e) { e.printStackTrace(); } } private class FileIndexer extends SimpleFileVisitor<Path> { private boolean throwCreates = false; public FileIndexer(boolean throwCreates){ this.throwCreates = throwCreates; } @Override public FileVisitResult postVisitDirectory(Path path, IOException ex) throws IOException { return super.postVisitDirectory(path, ex); } @Override public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attr) throws IOException { if(throwCreates){ if(path.toString().equals(rootFolder.toString())){ logger.trace("Skipping root directory."); } else { eventManager.onLocalFileCreated(path); } } return super.preVisitDirectory(path, attr); } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attr) throws IOException { if(PathUtils.isFileHidden(path)){ return FileVisitResult.CONTINUE; } if (throwCreates) { eventManager.onLocalFileCreated(path); } else { if (Files.isDirectory(path)) { FolderComposite newFolder = new FolderComposite(path, false); newFolder.setIsSynchronized(true); fileTree.putComponent(path, newFolder); } else { FileLeaf newFile = new FileLeaf(path, false); newFile.setIsSynchronized(true); fileTree.putComponent(path, newFile); } } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path path, IOException ex) throws IOException { return super.visitFileFailed(path, ex); } } }