Merge branch 'develop' into spike/JAL-4047/JAL-4048_columns_in_sequenceID
[jalview.git] / src / jalview / util / FileUtils.java
diff --git a/src/jalview/util/FileUtils.java b/src/jalview/util/FileUtils.java
new file mode 100644 (file)
index 0000000..e62a7d6
--- /dev/null
@@ -0,0 +1,192 @@
+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.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import jalview.bin.Console;
+
+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<File> getFilesFromGlob(String pattern)
+  {
+    return getFilesFromGlob(pattern, true);
+  }
+
+  public static List<File> getFilesFromGlob(String pattern,
+          boolean allowSingleFilenameThatDoesNotExist)
+  {
+    pattern = substituteHomeDir(pattern);
+    List<File> 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("**") ? 1028
+                  : (int) (rest.chars()
+                          .filter(ch -> ch == File.separatorChar).count())
+                          + 1;
+
+          Files.walkFileTree(parentDir,
+                  EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth,
+                  new SimpleFileVisitor<Path>()
+                  {
+                    @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 (allowSingleFilenameThatDoesNotExist || f.exists())
+      {
+        files.add(f);
+      }
+    }
+    Collections.sort(files);
+
+    return files;
+  }
+
+  public static List<String> getFilenamesFromGlob(String pattern)
+  {
+    // convert list of Files to list of File.getPath() Strings
+    return getFilesFromGlob(pattern).stream().map(f -> f.getPath())
+            .collect(Collectors.toList());
+  }
+
+  public static String substituteHomeDir(String path)
+  {
+    return path.startsWith("~" + File.separator)
+            ? System.getProperty("user.home") + path.substring(1)
+            : path;
+  }
+
+  /*
+   * This method returns the basename of File file
+   */
+  public static String getBasename(File file)
+  {
+    return getBasenameOrExtension(file, false);
+  }
+
+  /*
+   * This method returns the extension of File file.
+   */
+  public static String getExtension(File file)
+  {
+    return getBasenameOrExtension(file, true);
+  }
+
+  public static String getBasenameOrExtension(File file, boolean extension)
+  {
+    if (file == null)
+      return null;
+
+    String value = null;
+    String filename = file.getName();
+    int lastDot = filename.lastIndexOf('.');
+    if (lastDot > 0) // don't truncate if starts with '.'
+    {
+      value = extension ? filename.substring(lastDot + 1)
+              : filename.substring(0, lastDot);
+    }
+    else
+    {
+      value = extension ? "" : filename;
+    }
+    return value;
+  }
+
+  /*
+   * This method returns the dirname of the first --append or --open value. 
+   * Used primarily for substitutions in output filenames.
+   */
+  public static String getDirname(File file)
+  {
+    if (file == null)
+      return null;
+
+    String dirname = null;
+    try
+    {
+      File p = file.getParentFile();
+      File d = new File(substituteHomeDir(p.getPath()));
+      dirname = d.getCanonicalPath();
+    } catch (IOException e)
+    {
+      Console.debug(
+              "Exception when getting dirname of '" + file.getPath() + "'",
+              e);
+      dirname = "";
+    }
+    return dirname;
+  }
+}