Clover icon

Coverage Report

  1. Project Clover database Mon Nov 11 2024 17:27:16 GMT
  2. Package jalview.util

File FileUtils.java

 

Coverage histogram

../../img/srcFileCovDistChart8.png
20% of files have more coverage

Code metrics

84
176
23
1
541
414
75
0.43
7.65
23
3.26

Classes

Class Line # Actions
FileUtils 46 176 75
0.780918778.1%
 

Contributing tests

This file is covered by 146 tests. .

Source view

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.HashSet;
40    import java.util.List;
41    import java.util.Set;
42    import java.util.stream.Collectors;
43   
44    import jalview.bin.Console;
45   
 
46    public class FileUtils
47    {
48    /*
49    * Given string glob pattern (see
50    * https://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
51    * ) return a List of Files that match the pattern.
52    * Note this is a java style glob, not necessarily a bash-style glob, though there are sufficient similarities.
53    */
 
54  164 toggle public static List<File> getFilesFromGlob(String pattern)
55    {
56  164 return getFilesFromGlob(pattern, true);
57    }
58   
 
59  182 toggle public static List<File> getFilesFromGlob(String pattern,
60    boolean allowSingleFilenameThatDoesNotExist)
61    {
62  182 pattern = substituteHomeDir(pattern);
63  182 String relativePattern = pattern.startsWith(File.separator) ? null
64    : pattern;
65  182 List<File> files = new ArrayList<>();
66    /*
67    * For efficiency of the Files.walkFileTree(), let's find the longest path that doesn't need globbing.
68    * We look for the first glob character * { ? and then look for the last File.separator before that.
69    * Then we can reset the path to look at and shorten the globbing pattern.
70    * Relative paths can be used in pattern, which work from the pwd (though these are converted into
71    * full paths in the match).
72    */
73  182 int firstGlobChar = -1;
74  182 boolean foundGlobChar = false;
75  182 for (char c : new char[] { '*', '{', '?' })
76    {
77  546 if (pattern.indexOf(c) > -1
78    && (pattern.indexOf(c) < firstGlobChar || !foundGlobChar))
79    {
80  65 firstGlobChar = pattern.indexOf(c);
81  65 foundGlobChar = true;
82    }
83    }
84  182 int lastFS = pattern.lastIndexOf(File.separatorChar, firstGlobChar);
85  182 if (foundGlobChar)
86    {
87  65 String pS = pattern.substring(0, lastFS + 1);
88  65 String rest = pattern.substring(lastFS + 1);
89  65 if ("".equals(pS))
90    {
91  0 pS = ".";
92    }
93  65 Path parentDir = Paths.get(pS);
94  65 if (parentDir.toFile().exists())
95    {
96  65 try
97    {
98  65 String glob = "glob:" + parentDir.toString() + File.separator
99    + rest;
100  65 if (Platform.isWin())
101    {
102    // escape "\\" on Windows
103    // This ultimately replaces "\\" == '\' with "\\\\" = '\\' to escape
104    // backslashes
105  0 glob = glob.replaceAll("\\\\", "\\\\\\\\");
106    }
107  65 PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob);
108  65 int maxDepth = rest.contains("**") ? 1028
109    : (int) (rest.chars()
110    .filter(ch -> ch == File.separatorChar).count())
111    + 1;
112   
113  65 Files.walkFileTree(parentDir,
114    EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth,
115    new SimpleFileVisitor<Path>()
116    {
 
117  6437 toggle @Override
118    public FileVisitResult visitFile(Path path,
119    BasicFileAttributes attrs) throws IOException
120    {
121  6437 if (pm.matches(path))
122    {
123  3172 files.add(path.toFile());
124    }
125  6437 return FileVisitResult.CONTINUE;
126    }
127   
 
128  0 toggle @Override
129    public FileVisitResult visitFileFailed(Path file,
130    IOException exc) throws IOException
131    {
132  0 return FileVisitResult.CONTINUE;
133    }
134    });
135    } catch (IOException e)
136    {
137  0 e.printStackTrace();
138    }
139    }
140    }
141    else
142    {
143    // no wildcards
144  117 File f = new File(pattern);
145  117 if (allowSingleFilenameThatDoesNotExist || f.exists())
146    {
147  109 files.add(f);
148    }
149    }
150  182 Collections.sort(files);
151   
152  182 return files;
153    }
154   
 
155  139 toggle public static List<String> getFilenamesFromGlob(String pattern)
156    {
157    // convert list of Files to list of File.getPath() Strings
158  139 return getFilesFromGlob(pattern).stream().map(f -> f.getPath())
159    .collect(Collectors.toList());
160    }
161   
 
162  373 toggle public static String substituteHomeDir(String path)
163    {
164  373 return path.startsWith("~" + File.separator)
165    ? System.getProperty("user.home") + path.substring(1)
166    : path;
167    }
168   
169    /*
170    * This method returns the basename of File file
171    */
 
172  78 toggle public static String getBasename(File file)
173    {
174  78 return getBasenameOrExtension(file, false);
175    }
176   
177    /*
178    * This method returns the extension of File file.
179    */
 
180  10 toggle public static String getExtension(File file)
181    {
182  10 return getBasenameOrExtension(file, true);
183    }
184   
 
185  88 toggle public static String getBasenameOrExtension(File file, boolean extension)
186    {
187  88 if (file == null)
188  0 return null;
189   
190  88 String value = null;
191  88 String filename = file.getName();
192  88 int lastDot = filename.lastIndexOf('.');
193  88 if (lastDot > 0) // don't truncate if starts with '.'
194    {
195  87 value = extension ? filename.substring(lastDot + 1)
196    : filename.substring(0, lastDot);
197    }
198    else
199    {
200  1 value = extension ? "" : filename;
201    }
202  88 return value;
203    }
204   
205    /*
206    * This method returns the dirname of the first --append or --open value.
207    * Used primarily for substitutions in output filenames.
208    */
 
209  104 toggle public static String getDirname(File file)
210    {
211  104 if (file == null)
212  0 return null;
213   
214  104 String dirname = null;
215  104 File p = file.getParentFile();
216  104 if (p == null)
217    {
218  0 p = new File(".");
219    }
220  104 File d = new File(substituteHomeDir(p.getPath()));
221  104 dirname = d.getPath();
222  104 return dirname;
223    }
224   
 
225  19 toggle public static String convertWildcardsToPath(String value, String wildcard,
226    String dirname, String basename)
227    {
228  19 if (value == null)
229    {
230  0 return null;
231    }
232  19 StringBuilder path = new StringBuilder();
233  19 int lastFileSeparatorIndex = value.lastIndexOf(File.separatorChar);
234  19 int wildcardBeforeIndex = value.indexOf(wildcard);
235  19 if (lastFileSeparatorIndex > wildcard.length() - 1
236    && wildcardBeforeIndex < lastFileSeparatorIndex)
237    {
238  12 path.append(value.substring(0, wildcardBeforeIndex));
239  12 path.append(dirname);
240  12 path.append(value.substring(wildcardBeforeIndex + wildcard.length(),
241    lastFileSeparatorIndex + 1));
242    }
243    else
244    {
245  7 path.append(value.substring(0, lastFileSeparatorIndex + 1));
246    }
247  19 int wildcardAfterIndex = value.indexOf(wildcard,
248    lastFileSeparatorIndex);
249  19 if (wildcardAfterIndex > lastFileSeparatorIndex)
250    {
251  14 path.append(value.substring(lastFileSeparatorIndex + 1,
252    wildcardAfterIndex));
253  14 path.append(basename);
254  14 path.append(value.substring(wildcardAfterIndex + wildcard.length()));
255    }
256    else
257    {
258  5 path.append(value.substring(lastFileSeparatorIndex + 1));
259    }
260  19 return path.toString();
261    }
262   
 
263  114 toggle public static File getParentDir(File file)
264    {
265  114 if (file == null)
266    {
267  0 return null;
268    }
269  114 File parentDir = file.getAbsoluteFile().getParentFile();
270  114 return parentDir;
271    }
272   
 
273  114 toggle public static boolean checkParentDir(File file, boolean mkdirs)
274    {
275  114 if (file == null)
276    {
277  0 return false;
278    }
279  114 File parentDir = getParentDir(file);
280  114 if (parentDir.exists())
281    {
282    // already exists, nothing to do so nothing to worry about!
283  114 return true;
284    }
285   
286  0 if (!mkdirs)
287    {
288  0 return false;
289    }
290   
291  0 Path path = file.toPath();
292  0 for (int i = 0; i < path.getNameCount(); i++)
293    {
294  0 Path p = path.getName(i);
295  0 if ("..".equals(p.toString()))
296    {
297  0 LaunchUtils.syserr(true, false,
298    "Cautiously not running mkdirs on " + file.toString()
299    + " because the path to be made contains '..'");
300  0 return false;
301    }
302    }
303   
304  0 return mkdirs(parentDir);
305    }
306   
307    /**
308    * get a guessed file extension from a String only
309    *
310    * @param String
311    * filename
312    * @return String extension
313    */
 
314  15 toggle public static String getExtension(String filename)
315    {
316  15 return getBaseOrExtension(filename, true);
317    }
318   
319    /**
320    * getBase returns everything in a path/URI up to (and including) an extension
321    * dot. Note this is not the same as getBasename() since getBasename() only
322    * gives the filename base, not the path too. If no extension dot is found
323    * (i.e. a dot in character position 2 or more of the filename (after the last
324    * slash) then the whole path is considered the base.
325    *
326    * @param filename
327    * @return String base
328    */
 
329  15 toggle public static String getBase(String filename)
330    {
331  15 return getBaseOrExtension(filename, false);
332    }
333   
 
334  30 toggle public static String getBaseOrExtension(String filename0,
335    boolean extension)
336    {
337  30 if (filename0 == null)
338    {
339  0 return null;
340    }
341  30 String filename = filename0;
342  30 boolean isUrl = false;
343  30 if (HttpUtils.startsWithHttpOrHttps(filename))
344    {
345  10 try
346    {
347  10 URL url = new URL(filename);
348  10 filename = url.getPath();
349  10 isUrl = true;
350    } catch (MalformedURLException e)
351    {
352    // continue to treat as a filename
353    }
354    }
355  30 int dot = filename.lastIndexOf('.');
356  30 int slash = filename.lastIndexOf('/');
357  30 if (!File.separator.equals("/") && !isUrl)
358    {
359  0 slash = filename.lastIndexOf(File.separator);
360    }
361    // only the dot of the filename (not dots in path) and not if it's a .hidden
362    // file
363  30 boolean hasExtension = dot > slash + 1;
364  30 if (extension)
365    {
366  15 return hasExtension ? filename.substring(dot + 1) : null;
367    }
368    else
369    {
370  15 dot = filename0.lastIndexOf('.');
371  15 return hasExtension ? filename0.substring(0, dot + 1) : filename0;
372    }
373    }
374   
 
375  0 toggle public static Path getCanonicalPath(Path path)
376    {
377  0 return path.normalize();
378    }
379   
 
380  0 toggle public static Path getCanonicalPath(File file)
381    {
382  0 return getCanonicalPath(file.toPath());
383    }
384   
 
385  0 toggle public static Path getCanonicalPath(String pathString)
386    {
387  0 return getCanonicalPath(Paths.get(pathString));
388    }
389   
 
390  0 toggle public static File getCanonicalFile(File file)
391    {
392  0 return getCanonicalPath(file).toFile();
393    }
394   
 
395  0 toggle public static File getCanonicalFile(String pathString)
396    {
397  0 return getCanonicalPath(Paths.get(pathString)).toFile();
398    }
399   
 
400  0 toggle public static boolean mkdirs(File file)
401    {
402  0 try
403    {
404  0 Files.createDirectories(getCanonicalPath(file));
405  0 return file.exists();
406    } catch (IOException e)
407    {
408  0 LaunchUtils.syserr(true, false, "Failed to make directory " + file
409    + "\n" + e.getStackTrace());
410    }
411  0 return false;
412    }
413    /**
414    * Look for files that use a template, with two "%s" formatting entries, to
415    * look for files with the template substituted with combinations of root and
416    * version. If versionWhitelist is not null then these paths will be added. If
417    * versionBlacklist is not null then globbed versions will be looked for and
418    * these versions excluded. If both are given then both will be included. If
419    * separator is not null then it will be added before the version number, and
420    * additionally a path without the separator and version will be looked for or
421    * added if the whitelist or blacklist are supplied respectively.
422    *
423    * @param templates
424    * @param roots
425    * @param versionWhitelist
426    * @param versionBlacklist
427    * @param separator
428    * @return
429    */
 
430  7 toggle public static List<File> getMatchingVersionedFiles(String[] templates,
431    String[] roots, String[] versionWhitelist,
432    String[] versionBlacklist, String versionSeparator,
433    boolean exists)
434    {
435  7 Set<File> matchingFiles = new HashSet<>();
436  7 if (templates == null)
437    {
438  0 Console.debug(
439    "getMatchingVersionedFiles called with a null template array");
440  0 List<File> files = new ArrayList<File>();
441  0 files.addAll(matchingFiles);
442  0 return files;
443    }
444   
445  7 for (String template : templates)
446    {
447  7 Console.debug("Using template '" + template + "'");
448  7 for (String root : roots)
449    {
450  14 Console.debug("Using root '" + root + "'");
451   
452    // Blacklist. Use a file glob for version and see what's there
453  14 if (versionBlacklist != null)
454    {
455  10 String globMatch = String.format(template, root, "*");
456  10 Console.debug("Using glob '" + globMatch + "'");
457  10 List<File> foundFiles = FileUtils.getFilesFromGlob(globMatch,
458    false);
459  10 for (File found : foundFiles)
460    {
461  15 Console.debug("Checking " + found.getPath() + " is okay");
462  15 boolean add = true;
463  15 for (String notVersion : versionBlacklist)
464    {
465  20 StringBuilder vSB = new StringBuilder();
466  20 if (versionSeparator != null)
467    {
468  15 vSB.append(versionSeparator);
469    }
470  20 vSB.append(notVersion);
471  20 String versionString = vSB.toString();
472  20 if (String.format(template, root, versionString)
473    .equals(found.getPath()))
474    {
475  8 add = false;
476  8 Console.debug(
477    "Not adding " + found.getPath() + ": version '"
478    + notVersion + "' is in the blacklist");
479  8 break;
480    }
481    }
482  15 if (add)
483    {
484  7 Console.debug("Adding " + found.getPath() + " to list");
485  7 matchingFiles.add(found);
486    }
487    }
488   
489  10 if (versionSeparator != null)
490    {
491    // try without a version number too
492  8 String nonVersioned = String.format(template, root, "");
493  8 matchingFiles.addAll(
494    FileUtils.getFilesFromGlob(nonVersioned, false));
495    }
496    }
497   
498    // Whitelist. Just add a path for every whitelisted version (or check it
499    // exists).
500  14 if (versionWhitelist != null)
501    {
502  10 Console.debug("Adding " + versionWhitelist.length
503    + " whitelist versions");
504  10 for (String addVersion : versionWhitelist)
505    {
506  10 StringBuilder vSB = new StringBuilder();
507  10 if (versionSeparator != null)
508    {
509  8 vSB.append(versionSeparator);
510    }
511  10 vSB.append(addVersion);
512  10 String versionString = vSB.toString();
513  10 String versionPath = String.format(template, root,
514    versionString);
515  10 Console.debug("Adding whitelist path '" + versionPath + "'");
516  10 File file = new File(versionPath);
517  10 if (file.exists() || !exists)
518    {
519  6 matchingFiles.add(file);
520    }
521    }
522   
523  10 if (versionSeparator != null)
524    {
525    // try without a version number too
526  8 String nonVersioned = String.format(template, root, "");
527  8 File file = new File(nonVersioned);
528  8 if (file.exists() || !exists)
529    {
530  2 matchingFiles.add(file);
531    }
532    }
533    }
534    }
535    }
536   
537  7 List<File> files = new ArrayList<File>();
538  7 files.addAll(matchingFiles);
539  7 return files;
540    }
541    }