package com.marklogic.client.file; import com.marklogic.client.helper.LoggingObject; import org.springframework.util.ClassUtils; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; /** * Non-threadsafe implementation that implements FileVisitor as a way of descending one or more file paths. */ public class DefaultDocumentFileReader extends LoggingObject implements FileVisitor<Path>, DocumentFileReader { private Path currentRootPath; private List<FileFilter> fileFilters; private List<DocumentFile> documentFiles; private List<DocumentFileProcessor> documentFileProcessors; private String uriPrefix = "/"; public DefaultDocumentFileReader() { initialize(); } /** * Walk the file tree at each of the given paths, applying any configured DocumentFileProcessor instances on each * DocumentFile that is constructed by a File. * * @param paths * @return */ public List<DocumentFile> readDocumentFiles(String... paths) { documentFiles = new ArrayList<>(); for (String path : paths) { if (logger.isInfoEnabled()) { logger.info(format("Finding documents at path: %s", path)); } this.currentRootPath = constructPath(path); this.currentRootPath.toFile().mkdirs(); try { Files.walkFileTree(this.currentRootPath, this); } catch (IOException ie) { throw new RuntimeException(format("IO error while walking file tree at path: %s", path), ie); } } return documentFiles; } /** * The path is first wrapped in a File; this prevents a bug in Gradle when Gradle is run in daemon mode, where it * will try to resolve the path from its daemon directory. * * @param path * @return */ protected Path constructPath(String path) { return Paths.get(new File(path).getAbsolutePath()); } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { boolean accept = acceptPath(dir, attrs); if (accept) { if (logger.isDebugEnabled()) { logger.debug("Visiting directory: " + dir); } return FileVisitResult.CONTINUE; } else { if (logger.isDebugEnabled()) { logger.debug("Skipping directory: " + dir); } return FileVisitResult.SKIP_SUBTREE; } } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { if (exc != null) { logger.warn("Failed visiting file: " + exc.getMessage(), exc); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { if (exc != null) { logger.warn("Error in postVisitDirectory: " + exc.getMessage(), exc); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { if (acceptPath(path, attrs)) { DocumentFile documentFile = buildDocumentFile(path, currentRootPath); documentFile = processDocumentFile(documentFile); if (documentFile != null) { this.documentFiles.add(documentFile); } } return FileVisitResult.CONTINUE; } /** * If any of the configured FileFilter objects do not accept the Path, then it is not accepted. * * @param path * @param attrs * @return */ protected boolean acceptPath(Path path, BasicFileAttributes attrs) { if (fileFilters != null) { File file = path.toFile(); for (FileFilter filter : fileFilters) { if (!filter.accept(file)) { return false; } } } return true; } protected DocumentFile buildDocumentFile(Path path, Path currentRootPath) { Path relPath = currentRootPath.relativize(path); String uri = relPath.toString().replace("\\", "/"); if (uriPrefix != null) { uri = uriPrefix + uri; } File f = path.toFile(); return new DocumentFile(uri, f); } protected DocumentFile processDocumentFile(DocumentFile documentFile) { for (DocumentFileProcessor processor : documentFileProcessors) { documentFile = processor.processDocumentFile(documentFile); if (documentFile == null) { break; } } return documentFile; } protected void initialize() { CollectionsDocumentFileProcessor cdfp = new CollectionsDocumentFileProcessor(); PermissionsDocumentFileProcessor pdfp = new PermissionsDocumentFileProcessor(); addFileFilter(cdfp); addFileFilter(pdfp); addDocumentFileProcessor(cdfp); addDocumentFileProcessor(pdfp); addDocumentFileProcessor(new FormatDocumentFileProcessor()); } public DocumentFileProcessor getDocumentFileProcessor(String classShortName) { for (DocumentFileProcessor processor : documentFileProcessors) { if (ClassUtils.getShortName(processor.getClass()).equals(classShortName)) { return processor; } } return null; } public void addDocumentFileProcessor(DocumentFileProcessor processor) { if (documentFileProcessors == null) { documentFileProcessors = new ArrayList<>(); } documentFileProcessors.add(processor); } public void addFileFilter(FileFilter fileFilter) { if (fileFilters == null) { fileFilters = new ArrayList<>(); } fileFilters.add(fileFilter); } public List<DocumentFileProcessor> getDocumentFileProcessors() { return documentFileProcessors; } public void setDocumentFileProcessors(List<DocumentFileProcessor> documentFileProcessors) { this.documentFileProcessors = documentFileProcessors; } public List<FileFilter> getFileFilters() { return fileFilters; } public void setFileFilters(List<FileFilter> fileFilters) { this.fileFilters = fileFilters; } public void setUriPrefix(String uriPrefix) { this.uriPrefix = uriPrefix; } }