package nl.helixsoft.util; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; public abstract class HFileUtils { private HFileUtils() { /* no instantiation of util class */ } //TODO: copied from pathvisio /** * Get all files in a directory * @param directory The directory to get the files from * @param recursive Whether to include subdirectories or not * @return A list of files in the given directory */ public static List<File> getFiles(File directory, boolean recursive) { List<File> fileList = new ArrayList<File>(); if(!directory.isDirectory()) { //File is not a directory, return file itself (if has correct extension) fileList.add(directory); return fileList; } //Get all files in this directory File[] files = directory.listFiles(); //Recursively add the files for(File f : files) { if(f.isDirectory()) { if (recursive) fileList.addAll(getFiles(f, true)); } else fileList.add(f); } return fileList; } //TODO: copied from pathvisio /** * Get all files in a directory * @param directory The directory to get the files from * @param extension The extension of the files to get, without the dot so e.g. "gpml" * @param recursive Whether to include subdirectories or not * @return A list of files with given extension present in the given directory */ public static List<File> getFiles(File directory, final String extension, boolean recursive) { List<File> fileList = new ArrayList<File>(); if(!directory.isDirectory()) { //File is not a directory, return file itself (if has correct extension) if(directory.getName().endsWith("." + extension)) fileList.add(directory); return fileList; } //Get all files in this directory File[] files = directory.listFiles(new FileFilter() { public boolean accept(File f) { return (f.isDirectory() || f.getName().endsWith("." + extension)) ? true : false; } }); //Recursively add the files for(File f : files) { if(f.isDirectory()) { if (recursive) fileList.addAll(getFiles(f, extension, true)); } else fileList.add(f); } return fileList; } /** * Open a file as a regular file input stream or a gzip input stream, depending on the extension. * @throws IOException */ public static InputStream openZipStream(File g) throws IOException { InputStream is; if (g.getName().endsWith(".gz")) { is = new GZIPInputStream(new FileInputStream(g)); } else if (g.getName().endsWith(".xz")) { // optional dependency on xz try { Constructor<?> cl = Class.forName("org.tukaani.xz.XZInputStream").getConstructor(InputStream.class); is = (InputStream)cl.newInstance(new FileInputStream(g)); } catch (NoSuchMethodException | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IOException ("Could not create an XZInputStream", e); } } else { is = new FileInputStream(g); } return is; } public static String addBeforeExtension(String fname, String string) { int last = fname.lastIndexOf('.'); if (last >= 0) { return fname.substring (0, last) + string + fname.substring (last); } else { return fname + string; } } public static boolean isBelowDirectory(File ancestorDir, File child) { File current = child.getAbsoluteFile(); while (current.getParentFile() != null) { if (current.equals (ancestorDir.getAbsoluteFile())) return true; current = current.getParentFile(); } return false; } /** * Expand a unix GLOB, such as '~' or '*.java' into a list of files. * only files or directories that actually exist are returned. If there are no matches, * returns an empty list. * * <p> * TODO only tested on unix, not likely to work well on Windows. * * @param glob the glob pattern * @param baseDir if the glob is relative, it will be taken relative to this Directory. If it is absolute, this parameter has no effect. * If baseDir is null, the current directory will be used. * @return list of files or directories, or an empty list if there are no matches. */ public static List<File> expandGlob (String glob, File baseDir) { List<File> result = new ArrayList<File>(); File base; if (glob.startsWith ("/")) { base = new File ("/"); glob = glob.substring(1); } else if (glob.equals ("~") || glob.startsWith ("~/")) { base = new File (System.getProperty("user.home")); glob = glob.substring(1); } else { // relative - use supplied base Directory (if it isn't null) base = baseDir == null ? new File(".") : baseDir; } result.addAll (expandGlobHelper (glob, base)); return result; } /** expand glob relative to current directory */ public static List<File> expandGlob (String glob) { return expandGlob (glob, new File(".")); } /** * Helper for expandGlob. * <br> * Tests if a given file name matches a given glob pattern with * or ?. * Does not handle directories. */ private static boolean matches(String text, String glob) { String rest = null; int pos = glob.indexOf('*'); if (pos != -1) { rest = glob.substring(pos + 1); glob = glob.substring(0, pos); } if (glob.length() > text.length()) return false; // handle the part up to the first * for (int i = 0; i < glob.length(); i++) if (glob.charAt(i) != '?' && !glob.substring(i, i + 1).equalsIgnoreCase(text.substring(i, i + 1))) return false; // recurse for the part after the first *, if any if (rest == null) { return glob.length() == text.length(); } else { for (int i = glob.length(); i <= text.length(); i++) { if (matches(text.substring(i), rest)) return true; } return false; } } /** * Helper for expandGlob. * <br> * get a list of files matching a glob in a directory. Does not traverse directories. */ private static List<File> getLocalMatches (String remain, File base) { List<File> result = new ArrayList<File>(); if (remain.equals (".")) { result.add (base); } else if (remain.equals ("..")) { result.add (new File(base, "..")); } else if (remain.contains("*") || remain.contains ("?")) { // look for directories in base matching for (File child : base.listFiles()) { if (matches (child.getName(), remain)) { result.add (child); } } } else { File f = new File (base, remain); if (f.exists()) result.add (f); } return result; } private static List<File> expandGlobHelper (String glob, File base) { // deal with sequences of slashes... while (glob.startsWith ("/")) { glob = glob.substring (1); } // if nothing remains, then we only select the current directory. if (glob.equals ("")) { List<File> result = new ArrayList<File>(); result.add (base); } int pos = glob.indexOf ('/'); if (pos >= 0) { List<File> result = new ArrayList<File>(); String dir = glob.substring (0, pos); glob = glob.substring (pos); for (File f: getLocalMatches (dir, base)) { if (f.isDirectory()) { result.addAll (expandGlobHelper(glob, f)); } } return result; } else { return getLocalMatches(glob, base); } } /** * Get suitable directory to store application data in a cross-platform way. * On *NIX: $HOME * On Windows: %APPDATA% * Then create a subdirectiory in that based on name. * Create it if it doesn't yet exist. * Use a staring period on *NIX, not on windows. */ public static File makeApplicationDir(String name) { File dirApplication; if (!StringUtils.isFileNameSafe(name)) { throw new IllegalArgumentException (name + " contains illegal characters for a filename"); } //Windows specific directory configuration String os = System.getProperty("os.name"); if (os.startsWith("Win")) { dirApplication = new File(System.getenv("APPDATA"), name); } else { //All other OS dirApplication = new File(System.getProperty("user.home"), "." + name); } if (!dirApplication.exists()) dirApplication.mkdir(); return dirApplication; } /** * Get suitable directory to store application data in a cross-platform way. * On *NIX: $HOME * On Windows: %APPDATA% */ public static File getApplicationDir() { File dirApplication = null; String os = System.getProperty("os.name"); if (os.startsWith("Win")) { dirApplication = new File(System.getenv("APPDATA")); } else { //All other OS dirApplication = new File(System.getProperty("user.home")); } return dirApplication; } /** * * The standard java way of getting the machine name, using InetAddress.getLocalHost().getHostName(), * could lead to a reverse dns lookup with all kinds of failure modes. * <p> * This method simply wraps that call in a try/catch handler, and returns a reasonable default otherwise */ public static String safeMachineName() { return safeMachineName("could not determine machine name"); } /** * * Same, but specify the default if no machine name was specified. */ public static String safeMachineName(String defaultMachineName) { String machineName; try { machineName = InetAddress.getLocalHost().getHostName(); } catch (java.net.UnknownHostException e) { machineName = defaultMachineName; } return machineName; } /** Helper function to create a zip file * @throws IOException */ public static void createArchive(File targetFile, Map<File, String> files) throws IOException { File parentDir = targetFile.getParentFile(); if (!parentDir.exists()) { boolean result = parentDir.mkdirs(); assert result : "Couldn't create directory " + targetFile; } ZipOutputStream out = new ZipOutputStream(new FileOutputStream(targetFile)); try { // Compress the files for (Map.Entry<File, String> e : files.entrySet()) { InputStream ins = HFileUtils.openZipStream(e.getKey()); // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(e.getValue())); IOUtils.copy (ins, out); // Complete the entry out.closeEntry(); ins.close(); } } catch (Exception e) { // delete partial file upon exception targetFile.delete(); throw e; } // Complete the ZIP file out.close(); } }