Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
FileUtils | 44 | 110 | 53 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.util; | |
22 | ||
23 | import java.io.File; | |
24 | import java.io.IOException; | |
25 | import java.net.MalformedURLException; | |
26 | import java.net.URL; | |
27 | import java.nio.file.FileSystems; | |
28 | import java.nio.file.FileVisitOption; | |
29 | import java.nio.file.FileVisitResult; | |
30 | import java.nio.file.Files; | |
31 | import java.nio.file.Path; | |
32 | import java.nio.file.PathMatcher; | |
33 | import java.nio.file.Paths; | |
34 | import java.nio.file.SimpleFileVisitor; | |
35 | import java.nio.file.attribute.BasicFileAttributes; | |
36 | import java.util.ArrayList; | |
37 | import java.util.Collections; | |
38 | import java.util.EnumSet; | |
39 | import java.util.List; | |
40 | import java.util.stream.Collectors; | |
41 | ||
42 | import jalview.bin.Console; | |
43 | ||
44 | public class FileUtils | |
45 | { | |
46 | /* | |
47 | * Given string glob pattern (see | |
48 | * https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String) | |
49 | * ) return a List of Files that match the pattern. | |
50 | * Note this is a java style glob, not necessarily a bash-style glob, though there are sufficient similarities. | |
51 | */ | |
52 | 164 | public static List<File> getFilesFromGlob(String pattern) |
53 | { | |
54 | 164 | return getFilesFromGlob(pattern, true); |
55 | } | |
56 | ||
57 | 164 | public static List<File> getFilesFromGlob(String pattern, |
58 | boolean allowSingleFilenameThatDoesNotExist) | |
59 | { | |
60 | 164 | pattern = substituteHomeDir(pattern); |
61 | 164 | String relativePattern = pattern.startsWith(File.separator) ? null |
62 | : pattern; | |
63 | 164 | List<File> files = new ArrayList<>(); |
64 | /* | |
65 | * For efficiency of the Files.walkFileTree(), let's find the longest path that doesn't need globbing. | |
66 | * We look for the first glob character * { ? and then look for the last File.separator before that. | |
67 | * Then we can reset the path to look at and shorten the globbing pattern. | |
68 | * Relative paths can be used in pattern, which work from the pwd (though these are converted into | |
69 | * full paths in the match). | |
70 | */ | |
71 | 164 | int firstGlobChar = -1; |
72 | 164 | boolean foundGlobChar = false; |
73 | 164 | for (char c : new char[] { '*', '{', '?' }) |
74 | { | |
75 | 492 | if (pattern.indexOf(c) > -1 |
76 | && (pattern.indexOf(c) < firstGlobChar || !foundGlobChar)) | |
77 | { | |
78 | 55 | firstGlobChar = pattern.indexOf(c); |
79 | 55 | foundGlobChar = true; |
80 | } | |
81 | } | |
82 | 164 | int lastFS = pattern.lastIndexOf(File.separatorChar, firstGlobChar); |
83 | 164 | if (foundGlobChar) |
84 | { | |
85 | 55 | String pS = pattern.substring(0, lastFS + 1); |
86 | 55 | String rest = pattern.substring(lastFS + 1); |
87 | 55 | if ("".equals(pS)) |
88 | { | |
89 | 0 | pS = "."; |
90 | } | |
91 | 55 | Path parentDir = Paths.get(pS); |
92 | 55 | if (parentDir.toFile().exists()) |
93 | { | |
94 | 55 | try |
95 | { | |
96 | 55 | String glob = "glob:" + parentDir.toString() + File.separator |
97 | + rest; | |
98 | 55 | PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob); |
99 | 55 | int maxDepth = rest.contains("**") ? 1028 |
100 | : (int) (rest.chars() | |
101 | .filter(ch -> ch == File.separatorChar).count()) | |
102 | + 1; | |
103 | ||
104 | 55 | Files.walkFileTree(parentDir, |
105 | EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth, | |
106 | new SimpleFileVisitor<Path>() | |
107 | { | |
108 | 6411 | @Override |
109 | public FileVisitResult visitFile(Path path, | |
110 | BasicFileAttributes attrs) throws IOException | |
111 | { | |
112 | 6411 | if (pm.matches(path)) |
113 | { | |
114 | 3153 | files.add(path.toFile()); |
115 | } | |
116 | 6411 | return FileVisitResult.CONTINUE; |
117 | } | |
118 | ||
119 | 0 | @Override |
120 | public FileVisitResult visitFileFailed(Path file, | |
121 | IOException exc) throws IOException | |
122 | { | |
123 | 0 | return FileVisitResult.CONTINUE; |
124 | } | |
125 | }); | |
126 | } catch (IOException e) | |
127 | { | |
128 | 0 | e.printStackTrace(); |
129 | } | |
130 | } | |
131 | } | |
132 | else | |
133 | { | |
134 | // no wildcards | |
135 | 109 | File f = new File(pattern); |
136 | 109 | if (allowSingleFilenameThatDoesNotExist || f.exists()) |
137 | { | |
138 | 109 | files.add(f); |
139 | } | |
140 | } | |
141 | 164 | Collections.sort(files); |
142 | ||
143 | 164 | return files; |
144 | } | |
145 | ||
146 | 139 | public static List<String> getFilenamesFromGlob(String pattern) |
147 | { | |
148 | // convert list of Files to list of File.getPath() Strings | |
149 | 139 | return getFilesFromGlob(pattern).stream().map(f -> f.getPath()) |
150 | .collect(Collectors.toList()); | |
151 | } | |
152 | ||
153 | 355 | public static String substituteHomeDir(String path) |
154 | { | |
155 | 355 | return path.startsWith("~" + File.separator) |
156 | ? System.getProperty("user.home") + path.substring(1) | |
157 | : path; | |
158 | } | |
159 | ||
160 | /* | |
161 | * This method returns the basename of File file | |
162 | */ | |
163 | 78 | public static String getBasename(File file) |
164 | { | |
165 | 78 | return getBasenameOrExtension(file, false); |
166 | } | |
167 | ||
168 | /* | |
169 | * This method returns the extension of File file. | |
170 | */ | |
171 | 10 | public static String getExtension(File file) |
172 | { | |
173 | 10 | return getBasenameOrExtension(file, true); |
174 | } | |
175 | ||
176 | 88 | public static String getBasenameOrExtension(File file, boolean extension) |
177 | { | |
178 | 88 | if (file == null) |
179 | 0 | return null; |
180 | ||
181 | 88 | String value = null; |
182 | 88 | String filename = file.getName(); |
183 | 88 | int lastDot = filename.lastIndexOf('.'); |
184 | 88 | if (lastDot > 0) // don't truncate if starts with '.' |
185 | { | |
186 | 87 | value = extension ? filename.substring(lastDot + 1) |
187 | : filename.substring(0, lastDot); | |
188 | } | |
189 | else | |
190 | { | |
191 | 1 | value = extension ? "" : filename; |
192 | } | |
193 | 88 | return value; |
194 | } | |
195 | ||
196 | /* | |
197 | * This method returns the dirname of the first --append or --open value. | |
198 | * Used primarily for substitutions in output filenames. | |
199 | */ | |
200 | 104 | public static String getDirname(File file) |
201 | { | |
202 | 104 | if (file == null) |
203 | 0 | return null; |
204 | ||
205 | 104 | String dirname = null; |
206 | 104 | File p = file.getParentFile(); |
207 | 104 | if (p == null) |
208 | { | |
209 | 0 | p = new File("."); |
210 | } | |
211 | 104 | File d = new File(substituteHomeDir(p.getPath())); |
212 | 104 | dirname = d.getPath(); |
213 | 104 | return dirname; |
214 | } | |
215 | ||
216 | 19 | public static String convertWildcardsToPath(String value, String wildcard, |
217 | String dirname, String basename) | |
218 | { | |
219 | 19 | if (value == null) |
220 | { | |
221 | 0 | return null; |
222 | } | |
223 | 19 | StringBuilder path = new StringBuilder(); |
224 | 19 | int lastFileSeparatorIndex = value.lastIndexOf(File.separatorChar); |
225 | 19 | int wildcardBeforeIndex = value.indexOf(wildcard); |
226 | 19 | if (lastFileSeparatorIndex > wildcard.length() - 1 |
227 | && wildcardBeforeIndex < lastFileSeparatorIndex) | |
228 | { | |
229 | 12 | path.append(value.substring(0, wildcardBeforeIndex)); |
230 | 12 | path.append(dirname); |
231 | 12 | path.append(value.substring(wildcardBeforeIndex + wildcard.length(), |
232 | lastFileSeparatorIndex + 1)); | |
233 | } | |
234 | else | |
235 | { | |
236 | 7 | path.append(value.substring(0, lastFileSeparatorIndex + 1)); |
237 | } | |
238 | 19 | int wildcardAfterIndex = value.indexOf(wildcard, |
239 | lastFileSeparatorIndex); | |
240 | 19 | if (wildcardAfterIndex > lastFileSeparatorIndex) |
241 | { | |
242 | 14 | path.append(value.substring(lastFileSeparatorIndex + 1, |
243 | wildcardAfterIndex)); | |
244 | 14 | path.append(basename); |
245 | 14 | path.append(value.substring(wildcardAfterIndex + wildcard.length())); |
246 | } | |
247 | else | |
248 | { | |
249 | 5 | path.append(value.substring(lastFileSeparatorIndex + 1)); |
250 | } | |
251 | 19 | return path.toString(); |
252 | } | |
253 | ||
254 | 114 | public static File getParentDir(File file) |
255 | { | |
256 | 114 | if (file == null) |
257 | { | |
258 | 0 | return null; |
259 | } | |
260 | 114 | File parentDir = file.getAbsoluteFile().getParentFile(); |
261 | 114 | return parentDir; |
262 | } | |
263 | ||
264 | 114 | public static boolean checkParentDir(File file, boolean mkdirs) |
265 | { | |
266 | 114 | if (file == null) |
267 | { | |
268 | 0 | return false; |
269 | } | |
270 | 114 | File parentDir = getParentDir(file); |
271 | 114 | if (parentDir.exists()) |
272 | { | |
273 | // already exists, nothing to do so nothing to worry about! | |
274 | 114 | return true; |
275 | } | |
276 | ||
277 | 0 | if (!mkdirs) |
278 | { | |
279 | 0 | return false; |
280 | } | |
281 | ||
282 | 0 | Path path = file.toPath(); |
283 | 0 | for (int i = 0; i < path.getNameCount(); i++) |
284 | { | |
285 | 0 | Path p = path.getName(i); |
286 | 0 | if ("..".equals(p.toString())) |
287 | { | |
288 | 0 | Console.warn("Cautiously not running mkdirs on " + file.toString() |
289 | + " because the path to be made contains '..'"); | |
290 | 0 | return false; |
291 | } | |
292 | } | |
293 | ||
294 | 0 | return parentDir.mkdirs(); |
295 | } | |
296 | ||
297 | /** | |
298 | * get a guessed file extension from a String only | |
299 | * | |
300 | * @param String | |
301 | * filename | |
302 | * @return String extension | |
303 | */ | |
304 | 15 | public static String getExtension(String filename) |
305 | { | |
306 | 15 | return getBaseOrExtension(filename, true); |
307 | } | |
308 | ||
309 | /** | |
310 | * getBase returns everything in a path/URI up to (and including) an extension | |
311 | * dot. Note this is not the same as getBasename() since getBasename() only | |
312 | * gives the filename base, not the path too. If no extension dot is found | |
313 | * (i.e. a dot in character position 2 or more of the filename (after the last | |
314 | * slash) then the whole path is considered the base. | |
315 | * | |
316 | * @param filename | |
317 | * @return String base | |
318 | */ | |
319 | 15 | public static String getBase(String filename) |
320 | { | |
321 | 15 | return getBaseOrExtension(filename, false); |
322 | } | |
323 | ||
324 | 30 | public static String getBaseOrExtension(String filename0, |
325 | boolean extension) | |
326 | { | |
327 | 30 | if (filename0 == null) |
328 | { | |
329 | 0 | return null; |
330 | } | |
331 | 30 | String filename = filename0; |
332 | 30 | boolean isUrl = false; |
333 | 30 | if (HttpUtils.startsWithHttpOrHttps(filename)) |
334 | { | |
335 | 10 | try |
336 | { | |
337 | 10 | URL url = new URL(filename); |
338 | 10 | filename = url.getPath(); |
339 | 10 | isUrl = true; |
340 | } catch (MalformedURLException e) | |
341 | { | |
342 | // continue to treat as a filename | |
343 | } | |
344 | } | |
345 | 30 | int dot = filename.lastIndexOf('.'); |
346 | 30 | int slash = filename.lastIndexOf('/'); |
347 | 30 | if (!File.separator.equals("/") && !isUrl) |
348 | { | |
349 | 0 | slash = filename.lastIndexOf(File.separator); |
350 | } | |
351 | // only the dot of the filename (not dots in path) and not if it's a .hidden | |
352 | // file | |
353 | 30 | boolean hasExtension = dot > slash + 1; |
354 | 30 | if (extension) |
355 | { | |
356 | 15 | return hasExtension ? filename.substring(dot + 1) : null; |
357 | } | |
358 | else | |
359 | { | |
360 | 15 | dot = filename0.lastIndexOf('.'); |
361 | 15 | return hasExtension ? filename0.substring(0, dot + 1) : filename0; |
362 | } | |
363 | } | |
364 | } |