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())); } } }