Merge branch 'spotless_2' into JAL-1551_2_11_3_spotlett
[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.Collections;
16 import java.util.EnumSet;
17 import java.util.List;
18 import java.util.stream.Collectors;
19
20 import jalview.bin.Console;
21
22 public class FileUtils
23 {
24   /*
25    * Given string glob pattern (see
26    * https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
27    * ) return a List of Files that match the pattern.
28    * Note this is a java style glob, not necessarily a bash-style glob, though there are sufficient similarities. 
29    */
30   public static List<File> getFilesFromGlob(String pattern)
31   {
32     return getFilesFromGlob(pattern, true);
33   }
34
35   public static List<File> getFilesFromGlob(String pattern,
36           boolean allowSingleFilenameThatDoesNotExist)
37   {
38     pattern = substituteHomeDir(pattern);
39     List<File> files = new ArrayList<>();
40     /*
41      * For efficiency of the Files.walkFileTree(), let's find the longest path that doesn't need globbing.
42      * We look for the first glob character * { ? and then look for the last File.separator before that.
43      * Then we can reset the path to look at and shorten the globbing pattern.
44      * Relative paths can be used in pattern, which work from the pwd (though these are converted into
45      * full paths in the match). 
46      */
47     int firstGlobChar = -1;
48     boolean foundGlobChar = false;
49     for (char c : new char[] { '*', '{', '?' })
50     {
51       if (pattern.indexOf(c) > -1
52               && (pattern.indexOf(c) < firstGlobChar || !foundGlobChar))
53       {
54         firstGlobChar = pattern.indexOf(c);
55         foundGlobChar = true;
56       }
57     }
58     int lastFS = pattern.lastIndexOf(File.separatorChar, firstGlobChar);
59     if (foundGlobChar)
60     {
61       String pS = pattern.substring(0, lastFS + 1);
62       String rest = pattern.substring(lastFS + 1);
63       Path parentDir = Paths.get(pS).toAbsolutePath();
64       if (parentDir.toFile().exists())
65       {
66         try
67         {
68           String glob = "glob:" + parentDir.toString() + File.separator
69                   + rest;
70           PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob);
71           int maxDepth = rest.contains("**") ? 1028
72                   : (int) (rest.chars()
73                           .filter(ch -> ch == File.separatorChar).count())
74                           + 1;
75
76           Files.walkFileTree(parentDir,
77                   EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth,
78                   new SimpleFileVisitor<Path>()
79                   {
80                     @Override
81                     public FileVisitResult visitFile(Path path,
82                             BasicFileAttributes attrs) throws IOException
83                     {
84                       if (pm.matches(path))
85                       {
86                         files.add(path.toFile());
87                       }
88                       return FileVisitResult.CONTINUE;
89                     }
90
91                     @Override
92                     public FileVisitResult visitFileFailed(Path file,
93                             IOException exc) throws IOException
94                     {
95                       return FileVisitResult.CONTINUE;
96                     }
97                   });
98         } catch (IOException e)
99         {
100           e.printStackTrace();
101         }
102       }
103     }
104     else
105     {
106       // no wildcards
107       File f = new File(pattern);
108       if (allowSingleFilenameThatDoesNotExist || f.exists())
109       {
110         files.add(f);
111       }
112     }
113     Collections.sort(files);
114
115     return files;
116   }
117
118   public static List<String> getFilenamesFromGlob(String pattern)
119   {
120     // convert list of Files to list of File.getPath() Strings
121     return getFilesFromGlob(pattern).stream().map(f -> f.getPath())
122             .collect(Collectors.toList());
123   }
124
125   public static String substituteHomeDir(String path)
126   {
127     return path.startsWith("~" + File.separator)
128             ? System.getProperty("user.home") + path.substring(1)
129             : path;
130   }
131
132   /*
133    * This method returns the basename of File file
134    */
135   public static String getBasename(File file)
136   {
137     return getBasenameOrExtension(file, false);
138   }
139
140   /*
141    * This method returns the extension of File file.
142    */
143   public static String getExtension(File file)
144   {
145     return getBasenameOrExtension(file, true);
146   }
147
148   public static String getBasenameOrExtension(File file, boolean extension)
149   {
150     if (file == null)
151       return null;
152
153     String value = null;
154     String filename = file.getName();
155     int lastDot = filename.lastIndexOf('.');
156     if (lastDot > 0) // don't truncate if starts with '.'
157     {
158       value = extension ? filename.substring(lastDot + 1)
159               : filename.substring(0, lastDot);
160     }
161     else
162     {
163       value = extension ? "" : filename;
164     }
165     return value;
166   }
167
168   /*
169    * This method returns the dirname of the first --append or --open value. 
170    * Used primarily for substitutions in output filenames.
171    */
172   public static String getDirname(File file)
173   {
174     if (file == null)
175       return null;
176
177     String dirname = null;
178     try
179     {
180       File p = file.getParentFile();
181       File d = new File(substituteHomeDir(p.getPath()));
182       dirname = d.getCanonicalPath();
183     } catch (IOException e)
184     {
185       Console.debug(
186               "Exception when getting dirname of '" + file.getPath() + "'",
187               e);
188       dirname = "";
189     }
190     return dirname;
191   }
192 }