package jalview.util; import java.io.File; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.stream.Collectors; public class FileUtils { /* * Given string glob pattern (see * https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String) * ) return a List of Files that match the pattern. * Note this is a java style glob, not necessarily a bash-style glob, though there are sufficient similarities. */ public static List getFilesFromGlob(String pattern) { List files = new ArrayList<>(); /* * For efficiency of the Files.walkFileTree, let's find the longest path that doesn't need globbing. * We look for the first glob character * { ? and then look for the last File.separator before that. * Then we can reset the path to look at and shorten the globbing pattern. * Relative paths can be used in pattern, which work from the pwd (though these are converted into * full paths in the match). */ int firstGlobChar = -1; boolean foundGlobChar = false; for (char c : new char[] { '*', '{', '?' }) { if (pattern.indexOf(c) > -1 && (pattern.indexOf(c) < firstGlobChar || !foundGlobChar)) { firstGlobChar = pattern.indexOf(c); foundGlobChar = true; } } int lastFS = pattern.lastIndexOf(File.separatorChar, firstGlobChar); if (foundGlobChar) { String pS = pattern.substring(0, lastFS + 1); String rest = pattern.substring(lastFS + 1); Path parentDir = Paths.get(pS).toAbsolutePath(); if (parentDir.toFile().exists()) { try { String glob = "glob:" + parentDir.toString() + File.separator + rest; PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob); int maxDepth = rest.contains("**") ? Integer.MAX_VALUE : (int) (rest.chars() .filter(ch -> ch == File.separatorChar).count()) + 1; Files.walkFileTree(parentDir, EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { if (pm.matches(path)) { files.add(path.toFile()); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } }); } catch (IOException e) { e.printStackTrace(); } } } else { // no wildcards File f = new File(pattern); if (f.exists()) { files.add(f); } } return files; } public static List getFilenamesFromGlob(String pattern) { // convert list of Files to list of File.getPath() Strings return getFilesFromGlob(pattern).stream().map(f -> f.getPath()) .collect(Collectors.toList()); } }