79cac0a1f8bda257ab6d0dd7149d7e894803c1b3
[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 public class FileUtils
21 {
22   /*
23    * Given string glob pattern (see
24    * https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
25    * ) return a List of Files that match the pattern.
26    * Note this is a java style glob, not necessarily a bash-style glob, though there are sufficient similarities. 
27    */
28   public static List<File> getFilesFromGlob(String pattern)
29   {
30     return getFilesFromGlob(pattern, true);
31   }
32
33   public static List<File> getFilesFromGlob(String pattern,
34           boolean allowSingleFilenameThatDoesNotExist)
35   {
36     pattern = substituteHomeDir(pattern);
37     String relativePattern = pattern.startsWith(File.separator) ? null
38             : 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       if ("".equals(pS))
64       {
65         pS = ".";
66       }
67       Path parentDir = Paths.get(pS);
68       if (parentDir.toFile().exists())
69       {
70         try
71         {
72           String glob = "glob:" + parentDir.toString() + File.separator
73                   + rest;
74           PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob);
75           int maxDepth = rest.contains("**") ? 1028
76                   : (int) (rest.chars()
77                           .filter(ch -> ch == File.separatorChar).count())
78                           + 1;
79
80           Files.walkFileTree(parentDir,
81                   EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth,
82                   new SimpleFileVisitor<Path>()
83                   {
84                     @Override
85                     public FileVisitResult visitFile(Path path,
86                             BasicFileAttributes attrs) throws IOException
87                     {
88                       if (pm.matches(path))
89                       {
90                         files.add(path.toFile());
91                       }
92                       return FileVisitResult.CONTINUE;
93                     }
94
95                     @Override
96                     public FileVisitResult visitFileFailed(Path file,
97                             IOException exc) throws IOException
98                     {
99                       return FileVisitResult.CONTINUE;
100                     }
101                   });
102         } catch (IOException e)
103         {
104           e.printStackTrace();
105         }
106       }
107     }
108     else
109     {
110       // no wildcards
111       File f = new File(pattern);
112       if (allowSingleFilenameThatDoesNotExist || f.exists())
113       {
114         files.add(f);
115       }
116     }
117     Collections.sort(files);
118
119     return files;
120   }
121
122   public static List<String> getFilenamesFromGlob(String pattern)
123   {
124     // convert list of Files to list of File.getPath() Strings
125     return getFilesFromGlob(pattern).stream().map(f -> f.getPath())
126             .collect(Collectors.toList());
127   }
128
129   public static String substituteHomeDir(String path)
130   {
131     return path.startsWith("~" + File.separator)
132             ? System.getProperty("user.home") + path.substring(1)
133             : path;
134   }
135
136   /*
137    * This method returns the basename of File file
138    */
139   public static String getBasename(File file)
140   {
141     return getBasenameOrExtension(file, false);
142   }
143
144   /*
145    * This method returns the extension of File file.
146    */
147   public static String getExtension(File file)
148   {
149     return getBasenameOrExtension(file, true);
150   }
151
152   public static String getBasenameOrExtension(File file, boolean extension)
153   {
154     if (file == null)
155       return null;
156
157     String value = null;
158     String filename = file.getName();
159     int lastDot = filename.lastIndexOf('.');
160     if (lastDot > 0) // don't truncate if starts with '.'
161     {
162       value = extension ? filename.substring(lastDot + 1)
163               : filename.substring(0, lastDot);
164     }
165     else
166     {
167       value = extension ? "" : filename;
168     }
169     return value;
170   }
171
172   /*
173    * This method returns the dirname of the first --append or --open value. 
174    * Used primarily for substitutions in output filenames.
175    */
176   public static String getDirname(File file)
177   {
178     if (file == null)
179       return null;
180
181     String dirname = null;
182     File p = file.getParentFile();
183     if (p == null)
184     {
185       p = new File(".");
186     }
187     File d = new File(substituteHomeDir(p.getPath()));
188     dirname = d.getPath();
189     return dirname;
190   }
191
192   public static String convertWildcardsToPath(String value, String wildcard,
193           String dirname, String basename)
194   {
195     if (value == null)
196     {
197       return null;
198     }
199     StringBuilder path = new StringBuilder();
200     int lastFileSeparatorIndex = value.lastIndexOf(File.separatorChar);
201     int wildcardBeforeIndex = value.indexOf(wildcard);
202     if (lastFileSeparatorIndex > wildcard.length() - 1
203             && wildcardBeforeIndex < lastFileSeparatorIndex)
204     {
205       path.append(value.substring(0, wildcardBeforeIndex));
206       path.append(dirname);
207       path.append(value.substring(wildcardBeforeIndex + wildcard.length(),
208               lastFileSeparatorIndex + 1));
209     }
210     else
211     {
212       path.append(value.substring(0, lastFileSeparatorIndex + 1));
213     }
214     int wildcardAfterIndex = value.indexOf(wildcard,
215             lastFileSeparatorIndex);
216     if (wildcardAfterIndex > lastFileSeparatorIndex)
217     {
218       path.append(value.substring(lastFileSeparatorIndex + 1,
219               wildcardAfterIndex));
220       path.append(basename);
221       path.append(value.substring(wildcardAfterIndex + wildcard.length()));
222     }
223     else
224     {
225       path.append(value.substring(lastFileSeparatorIndex + 1));
226     }
227     return path.toString();
228   }
229 }