JAL-629 Added --opennew --nonews --nosplash. Added java globbing for = e.g. --open...
[jalview.git] / src / jalview / util / FileUtils.java
1 package jalview.util;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.nio.file.FileSystems;
6 import java.nio.file.FileVisitOption;
7 import java.nio.file.FileVisitResult;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10 import java.nio.file.PathMatcher;
11 import java.nio.file.Paths;
12 import java.nio.file.SimpleFileVisitor;
13 import java.nio.file.attribute.BasicFileAttributes;
14 import java.util.ArrayList;
15 import java.util.EnumSet;
16 import java.util.List;
17 import java.util.stream.Collectors;
18
19 public class FileUtils
20 {
21   /*
22    * Given string glob pattern (see
23    * https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
24    * ) return a List of Files that match the pattern.
25    * Note this is a java style glob, not necessarily a bash-style glob, though there are sufficient similarities. 
26    */
27   public static List<File> getFilesFromGlob(String pattern)
28   {
29     return getFilesFromGlob(pattern, true);
30   }
31
32   public static List<File> getFilesFromGlob(String pattern,
33           boolean allowSingleFilenameThatDoesNotExist)
34   {
35     pattern = substituteHomeDir(pattern);
36     List<File> files = new ArrayList<>();
37     /*
38      * For efficiency of the Files.walkFileTree(), let's find the longest path that doesn't need globbing.
39      * We look for the first glob character * { ? and then look for the last File.separator before that.
40      * Then we can reset the path to look at and shorten the globbing pattern.
41      * Relative paths can be used in pattern, which work from the pwd (though these are converted into
42      * full paths in the match). 
43      */
44     int firstGlobChar = -1;
45     boolean foundGlobChar = false;
46     for (char c : new char[] { '*', '{', '?' })
47     {
48       if (pattern.indexOf(c) > -1
49               && (pattern.indexOf(c) < firstGlobChar || !foundGlobChar))
50       {
51         firstGlobChar = pattern.indexOf(c);
52         foundGlobChar = true;
53       }
54     }
55     int lastFS = pattern.lastIndexOf(File.separatorChar, firstGlobChar);
56     if (foundGlobChar)
57     {
58       String pS = pattern.substring(0, lastFS + 1);
59       String rest = pattern.substring(lastFS + 1);
60       Path parentDir = Paths.get(pS).toAbsolutePath();
61       if (parentDir.toFile().exists())
62       {
63         try
64         {
65           String glob = "glob:" + parentDir.toString() + File.separator
66                   + rest;
67           PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob);
68           int maxDepth = rest.contains("**") ? Integer.MAX_VALUE
69                   : (int) (rest.chars()
70                           .filter(ch -> ch == File.separatorChar).count())
71                           + 1;
72           Files.walkFileTree(parentDir,
73                   EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth,
74                   new SimpleFileVisitor<Path>()
75                   {
76                     @Override
77                     public FileVisitResult visitFile(Path path,
78                             BasicFileAttributes attrs) throws IOException
79                     {
80                       if (pm.matches(path))
81                       {
82                         files.add(path.toFile());
83                       }
84                       return FileVisitResult.CONTINUE;
85                     }
86
87                     @Override
88                     public FileVisitResult visitFileFailed(Path file,
89                             IOException exc) throws IOException
90                     {
91                       return FileVisitResult.CONTINUE;
92                     }
93                   });
94         } catch (IOException e)
95         {
96           e.printStackTrace();
97         }
98       }
99     }
100     else
101     {
102       // no wildcards
103       File f = new File(pattern);
104       if (allowSingleFilenameThatDoesNotExist || f.exists())
105       {
106         files.add(f);
107       }
108     }
109
110     return files;
111   }
112
113   public static List<String> getFilenamesFromGlob(String pattern)
114   {
115     // convert list of Files to list of File.getPath() Strings
116     return getFilesFromGlob(pattern).stream().map(f -> f.getPath())
117             .collect(Collectors.toList());
118   }
119
120   public static String substituteHomeDir(String path)
121   {
122     return path.startsWith("~" + File.separator)
123             ? System.getProperty("user.home") + path.substring(1)
124             : path;
125   }
126 }