2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
24 import java.io.IOException;
25 import java.net.MalformedURLException;
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;
42 import jalview.bin.Console;
44 public class FileUtils
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.
52 public static List<File> getFilesFromGlob(String pattern)
54 return getFilesFromGlob(pattern, true);
57 public static List<File> getFilesFromGlob(String pattern,
58 boolean allowSingleFilenameThatDoesNotExist)
60 pattern = substituteHomeDir(pattern);
61 String relativePattern = pattern.startsWith(File.separator) ? null
63 List<File> files = new ArrayList<>();
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).
71 int firstGlobChar = -1;
72 boolean foundGlobChar = false;
73 for (char c : new char[] { '*', '{', '?' })
75 if (pattern.indexOf(c) > -1
76 && (pattern.indexOf(c) < firstGlobChar || !foundGlobChar))
78 firstGlobChar = pattern.indexOf(c);
82 int lastFS = pattern.lastIndexOf(File.separatorChar, firstGlobChar);
85 String pS = pattern.substring(0, lastFS + 1);
86 String rest = pattern.substring(lastFS + 1);
91 Path parentDir = Paths.get(pS);
92 if (parentDir.toFile().exists())
96 String glob = "glob:" + parentDir.toString() + File.separator
98 PathMatcher pm = FileSystems.getDefault().getPathMatcher(glob);
99 int maxDepth = rest.contains("**") ? 1028
100 : (int) (rest.chars()
101 .filter(ch -> ch == File.separatorChar).count())
104 Files.walkFileTree(parentDir,
105 EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth,
106 new SimpleFileVisitor<Path>()
109 public FileVisitResult visitFile(Path path,
110 BasicFileAttributes attrs) throws IOException
112 if (pm.matches(path))
114 files.add(path.toFile());
116 return FileVisitResult.CONTINUE;
120 public FileVisitResult visitFileFailed(Path file,
121 IOException exc) throws IOException
123 return FileVisitResult.CONTINUE;
126 } catch (IOException e)
135 File f = new File(pattern);
136 if (allowSingleFilenameThatDoesNotExist || f.exists())
141 Collections.sort(files);
146 public static List<String> getFilenamesFromGlob(String pattern)
148 // convert list of Files to list of File.getPath() Strings
149 return getFilesFromGlob(pattern).stream().map(f -> f.getPath())
150 .collect(Collectors.toList());
153 public static String substituteHomeDir(String path)
155 return path.startsWith("~" + File.separator)
156 ? System.getProperty("user.home") + path.substring(1)
161 * This method returns the basename of File file
163 public static String getBasename(File file)
165 return getBasenameOrExtension(file, false);
169 * This method returns the extension of File file.
171 public static String getExtension(File file)
173 return getBasenameOrExtension(file, true);
176 public static String getBasenameOrExtension(File file, boolean extension)
182 String filename = file.getName();
183 int lastDot = filename.lastIndexOf('.');
184 if (lastDot > 0) // don't truncate if starts with '.'
186 value = extension ? filename.substring(lastDot + 1)
187 : filename.substring(0, lastDot);
191 value = extension ? "" : filename;
197 * This method returns the dirname of the first --append or --open value.
198 * Used primarily for substitutions in output filenames.
200 public static String getDirname(File file)
205 String dirname = null;
206 File p = file.getParentFile();
211 File d = new File(substituteHomeDir(p.getPath()));
212 dirname = d.getPath();
216 public static String convertWildcardsToPath(String value, String wildcard,
217 String dirname, String basename)
223 StringBuilder path = new StringBuilder();
224 int lastFileSeparatorIndex = value.lastIndexOf(File.separatorChar);
225 int wildcardBeforeIndex = value.indexOf(wildcard);
226 if (lastFileSeparatorIndex > wildcard.length() - 1
227 && wildcardBeforeIndex < lastFileSeparatorIndex)
229 path.append(value.substring(0, wildcardBeforeIndex));
230 path.append(dirname);
231 path.append(value.substring(wildcardBeforeIndex + wildcard.length(),
232 lastFileSeparatorIndex + 1));
236 path.append(value.substring(0, lastFileSeparatorIndex + 1));
238 int wildcardAfterIndex = value.indexOf(wildcard,
239 lastFileSeparatorIndex);
240 if (wildcardAfterIndex > lastFileSeparatorIndex)
242 path.append(value.substring(lastFileSeparatorIndex + 1,
243 wildcardAfterIndex));
244 path.append(basename);
245 path.append(value.substring(wildcardAfterIndex + wildcard.length()));
249 path.append(value.substring(lastFileSeparatorIndex + 1));
251 return path.toString();
254 public static File getParentDir(File file)
260 File parentDir = file.getAbsoluteFile().getParentFile();
264 public static boolean checkParentDir(File file, boolean mkdirs)
270 File parentDir = getParentDir(file);
271 if (parentDir.exists())
273 // already exists, nothing to do so nothing to worry about!
282 Path path = file.toPath();
283 for (int i = 0; i < path.getNameCount(); i++)
285 Path p = path.getName(i);
286 if ("..".equals(p.toString()))
288 Console.warn("Cautiously not running mkdirs on " + file.toString()
289 + " because the path to be made contains '..'");
294 return parentDir.mkdirs();
298 * get a guessed file extension from a String only
302 * @return String extension
304 public static String getExtension(String filename)
306 return getBaseOrExtension(filename, true);
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.
317 * @return String base
319 public static String getBase(String filename)
321 return getBaseOrExtension(filename, false);
324 public static String getBaseOrExtension(String filename0,
327 if (filename0 == null)
331 String filename = filename0;
332 boolean isUrl = false;
333 if (HttpUtils.startsWithHttpOrHttps(filename))
337 URL url = new URL(filename);
338 filename = url.getPath();
340 } catch (MalformedURLException e)
342 // continue to treat as a filename
345 int dot = filename.lastIndexOf('.');
346 int slash = filename.lastIndexOf('/');
347 if (!File.separator.equals("/") && !isUrl)
349 slash = filename.lastIndexOf(File.separator);
351 // only the dot of the filename (not dots in path) and not if it's a .hidden
353 boolean hasExtension = dot > slash + 1;
356 return hasExtension ? filename.substring(dot + 1) : null;
360 dot = filename0.lastIndexOf('.');
361 return hasExtension ? filename0.substring(0, dot + 1) : filename0;