X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Futil%2FFileUtils.java;fp=src%2Fjalview%2Futil%2FFileUtils.java;h=7e607ab2936fc99498906b19b0a9b074085f9cc3;hb=7692386ccfe778075dd83a753d30a7a27fe507be;hp=0000000000000000000000000000000000000000;hpb=97264690f394f98cf13cd1d834a53e8956fb1b0e;p=jalview.git diff --git a/src/jalview/util/FileUtils.java b/src/jalview/util/FileUtils.java new file mode 100644 index 0000000..7e607ab --- /dev/null +++ b/src/jalview/util/FileUtils.java @@ -0,0 +1,208 @@ +package jalview.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Miscellaneous file-related functions + */ +public final class FileUtils +{ + + /** + * Answers the executable file for the given command, or null if not found or + * not executable. The path to the executable is the command name prefixed by + * the given folder path, optionally with .exe appended. + * + * @param cmd + * command short name, for example hmmbuild + * @param binaryPath + * parent folder for the executable + * @return + */ + public static File getExecutable(String cmd, String binaryPath) + { + File file = new File(binaryPath, cmd); + if (!file.canExecute()) + { + file = new File(binaryPath, cmd + ".exe"); + { + if (!file.canExecute()) + { + file = null; + } + } + } + return file; + } + + /** + * Answers the path to the folder containing the given executable file, by + * searching the PATH environment variable. Answers null if no such executable + * can be found. + * + * @param cmd + * @return + */ + public static String getPathTo(String cmd) + { + String paths = System.getenv("PATH"); + // backslash is to escape regular expression argument + for (String path : paths.split("\\" + File.pathSeparator)) + { + if (getExecutable(cmd, path) != null) + { + return path; + } + } + return null; + } + + /** + * A convenience method to create a temporary file that is deleted on exit of + * the JVM + * + * @param prefix + * @param suffix + * @return + * @throws IOException + */ + public static File createTempFile(String prefix, String suffix) + throws IOException + { + File f = File.createTempFile(prefix, suffix); + f.deleteOnExit(); + return f; + } + + /** + * Answers a (possibly empty) list of file paths found by searching below + * from that match the supplied regular expression + * pattern. Results may include from itself if it + * matches. If an exception occurs it is written to syserr and any results up to + * that point are returned. Note that the regular expression match applies to + * the whole path of any matched file. + *

+ * WARNING: because the whole directory tree below from is + * searched, this method may be slow if used for a high level directory, or may + * exit prematurely if security or other exceptions occur. + * + *

+   * Example: 
+   *   findMatchingPaths(Paths.get("C:/Program Files"), ".*/chimera.exe$")
+   * 
+ * + * @param from + * @param pattern + * + * @return + * @see https://stackoverflow.com/questions/794381/how-to-find-files-that-match-a-wildcard-string-in-java/31685610#comment62441832_31685610 + */ + public static List findMatchingPaths(Path from, String pattern) + { + List matches = new ArrayList<>(); + PathMatcher pathMatcher = FileSystems.getDefault() + .getPathMatcher("regex:" + pattern); + try + { + Files.walk(from).filter(pathMatcher::matches) + .forEach(m -> matches.add(m.toString())); + } catch (IOException e) + { + System.err.println( + "Error searching for " + pattern + " : " + e.toString()); + } + + return matches; + } + + /** + * Answers a (possibly empty) list of paths to files below the given root path, + * that match the given pattern. The pattern should be a '/' delimited set of + * glob patterns, each of which is used to match child file names (not full + * paths). Note that 'directory spanning' glob patterns (**) are not + * supported by this method. + *

+ * For example + * + *

+   *   findMatches("C:\\", "Program Files*/Chimera*/bin/{chimera,chimera.exe}"
+   * 
+ * + * would match "C:\Program Files\Chimera 1.11\bin\chimera.exe" and "C:\Program + * Files (x86)\Chimera 1.10.1\bin\chimera" + * + * @param root + * @param pattern + * @return + * @see https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob + */ + public static List findMatches(String root, String pattern) + { + List results = new ArrayList<>(); + try + { + Path from = Paths.get(root); + findMatches(results, from, Arrays.asList(pattern.split("/"))); + } catch (Throwable e) + { + // Paths.get can throw IllegalArgumentException + System.err.println(String.format("Error searching %s for %s: %s", + root, pattern, e.toString())); + } + + return results; + } + + /** + * A helper method that performs recursive search of file patterns and adds any + * 'leaf node' matches to the results list + * + * @param results + * @param from + * @param patterns + */ + protected static void findMatches(List results, Path from, + List patterns) + { + if (patterns.isEmpty()) + { + /* + * reached end of recursion with all components matched + */ + results.add(from.toString()); + return; + } + + String pattern = patterns.get(0); + try (DirectoryStream dirStream = Files.newDirectoryStream(from, + pattern)) + { + dirStream.forEach(p -> { + + /* + * matched a next level file - search below it + * (ignore non-directory non-leaf matches) + */ + List subList = patterns.subList(1, patterns.size()); + if (subList.isEmpty() || p.toFile().isDirectory()) + { + findMatches(results, p, subList); + } + }); + } catch (IOException e) + { + System.err.println(String.format("Error searching %s: %s", pattern, + e.toString())); + } + } +}