/* MonkeyTalk - a cross-platform functional testing tool Copyright (C) 2012 Gorilla Logic, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.gorillalogic.monkeytalk.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Scanner; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public class FileUtils { private static final String TEMP_DIR_PREFIX = "report"; private static final int BUFFER_SIZE = 4096; /** * Read the given input stream with UTF-8 encoding into a string and return it. * * @param in * the input stream to be read * @return the contents as text * @throws IOException */ public static String readStream(InputStream in) throws IOException { StringBuilder sb = new StringBuilder(); String line = null; BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); while ((line = reader.readLine()) != null) { sb.append(line).append('\n'); } return (sb.length() > 0 ? sb.substring(0, sb.length() - 1) : ""); } /** * Read the given file with UTF-8 encoding into a string and return it. * * @param f * the file to be read * @return the contents as text * @throws IOException */ public static String readFile(File f) throws FileNotFoundException, IOException { StringBuilder sb = new StringBuilder(); Scanner scanner = new Scanner(new FileInputStream(f), "UTF-8"); try { while (scanner.hasNextLine()) { sb.append(scanner.nextLine()).append('\n'); } } finally { scanner.close(); } return (sb.length() > 0 ? sb.substring(0, sb.length() - 1) : ""); } /** * Write the given contents with UTF-8 encoding to the given file. * * @param f * the file to be written * @param contents * the contents to be written * @throws IOException */ public static void writeFile(File f, String contents) throws IOException { Writer out = new OutputStreamWriter(new FileOutputStream(f), "UTF-8"); try { out.write(contents); } finally { out.close(); } } /** * Write the given contents with UTF-8 encoding to the given filename. * * @param filename * the filename * @param contents * the contents to be written * @throws IOException */ public static void writeFile(String filename, String contents) throws IOException { writeFile(new File(filename), contents); } /** * Write the given raw bytes to the given file. * * @param f * the file to be written * @param bytes * the raw bytes to be written * @throws IOException */ public static void writeFile(File f, byte[] bytes) throws IOException { OutputStream out = new FileOutputStream(f); try { out.write(bytes); } finally { out.close(); } } /** * Remote the extension (if it exists) from the given filename and return the trimmed filename. * * @param filename * the filename * @param ext * the extension to be removed * @return the filename trimmed of the extension */ public static String removeExt(String filename, String ext) { if (filename != null && filename.toLowerCase().endsWith(ext)) { return filename.substring(0, filename.length() - ext.length()); } return filename; } /** * Delete the given dir and recursively delete all of its children. * * @param dir * the folder to be deleted * @throws IOException */ public static void deleteDir(File dir) throws IOException { for (File f : dir.listFiles()) { if (f.isDirectory()) { deleteDir(f); } else { f.delete(); } } dir.delete(); } /** * Find a file by filename in the given directory (does not search recursively). NOTE: search is * case-insensitive on filename. * * @param filename * the filename to search for * @param dir * the search directory * @return the file if found, otherwise null */ public static File findFile(String filename, File dir) { if (dir != null && dir.isDirectory()) { for (File f : dir.listFiles()) { if (f.getName().equalsIgnoreCase(filename)) { return f; } } } return null; } /** * Read the resource into a string. * * @param resource * the resource name * * @return contents of the file as a string * @throws IOException */ public static String resourceToString(String resource) throws IOException { InputStream is = Thread.currentThread().getContextClassLoader() .getResourceAsStream(resource); if (is != null) { Writer writer = new StringWriter(); char[] buf = new char[1024]; try { Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); int n; while ((n = reader.read(buf)) != -1) { writer.write(buf, 0, n); } } finally { is.close(); } return writer.toString(); } else { return ""; } } /** * Make the given folder (and any parent folders), throws an {@link IOException} on failure. * * @param dir * the folder * @throws IOException */ public static void makeDir(File dir) throws IOException { makeDir(dir, null); } /** * Make the given folder (and any parent folders), throws an {@link IOException} with the error * message on failure. * * @param dir * the folder * @param msg * the error message * @throws IOException */ public static void makeDir(File dir, String msg) throws IOException { if (dir == null) { throw new IOException((msg != null ? msg : "dir") + " is null"); } else if (!dir.exists()) { boolean success = dir.mkdirs(); if (!success) { throw new IOException("Failed to make " + (msg != null ? msg : "dir") + ": " + dir.getAbsolutePath()); } } else if (!dir.isDirectory()) { throw new IOException((msg != null ? msg : "dir") + " not a folder: " + dir.getAbsolutePath()); } } /** * use the supplied object to find the resource, and copy its contents to the supplied file * * @param o * an <code>Object</code> which can be used to locate the desired resource. If null, * <code>FileUtils.class</code> * @param resource * a <code>String</code>, the path to the resource * @param target * the <code>File</code> which will be written with the contents of the resource * @throws IOException */ public static void copyResourceToFile(Object o, String resource, File target) throws IOException { if (target == null) { throw new IOException("could not copy resource \"" + resource + "\" to file: null target file specified"); } Class<?> klass = null; if (o == null) { klass = FileUtils.class; } else if (o instanceof Class) { klass = (Class<?>) o; } else { klass = o.getClass(); } InputStream resourceStream = klass.getResourceAsStream(resource); if (resourceStream == null) { String errorHeader = "could not copy resource \"" + resource + "\" to file \"" + target.getPath() + "\": "; throw new IOException(errorHeader + "could not locate resource"); } writeFile(target, resourceStream); } public static void copyFile(File srcFile, File desDir) throws IOException { InputStream is = null; OutputStream os = null; if (!desDir.exists()) { try { is = new FileInputStream(srcFile); os = new FileOutputStream(desDir); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } } finally { is.close(); os.close(); } } } /** * create a zip file of the contents of the folder, optionally including the folder itself. * * if "includeDirInZip" is true, the specified directory - one level only - will be included in * the zip file, i.e. all entry paths will begin with the directory name. For example, suppose * the "dir" is /home/user/banana and contains two files, "apple.txt" and "orange.txt". If * "includeDirInZip" is false, the zip file will contain two entries, "apple.txt" and * "orange.txt". If "includeDirInZip" is true, the zip file will contain three entries, "banana" * (a directory), and "banana/apple.txt" and "banana/orange.txt". * * @param dir * the folder * @param includeDirInZip * if true, include the target directory in the zip file * @return the zip file * @throws IOException */ public static File zipDirectory(File dir, boolean includeDirInZip, boolean includeHidden) throws IOException { return zipDirectory(dir, includeDirInZip, includeHidden, null); } /** * Create a child temp folder inside the main temp dir. * * @return the folder * @throws IOException */ public static File tempDir() throws IOException { final File dir = File.createTempFile(TEMP_DIR_PREFIX, Long.toString(System.nanoTime())); if (!dir.delete()) { throw new IOException("failed to delete file: " + dir.getAbsolutePath()); } if (!dir.mkdir()) { throw new IOException("failed to create dir: " + dir.getAbsolutePath()); } return dir; } /** * create a zip file of the contents of the folder, optionally including the folder itself, with * optional filter by Extensions to include * * if "includeDirInZip" is true, the specified directory - one level only - will be included in * the zip file, i.e. all entry paths will begin with the directory name. For example, suppose * the "dir" is /home/user/banana and contains two files, "apple.txt" and "orange.txt". If * "includeDirInZip" is false, the zip file will contain two entries, "apple.txt" and * "orange.txt". If "includeDirInZip" is true, the zip file will contain three entries, "banana" * (a directory), and "banana/apple.txt" and "banana/orange.txt". * * @param dir * the folder * @param includeDirInZip * if true, include the target directory in the zip file * @param excludeExtensions * a list of file extensions to include; if null, all files will be included * @return the zip file * @throws IOException */ public static File zipDirectory(File dir, boolean includeDirInZip, boolean includeHidden, List<String> extFilter) throws IOException { if (dir == null) { throw new IOException("zipDirectory was passed a null directory to zip"); } File outputDir = tempDir(); String dirName = dir.getName(); File zipFile = new File(outputDir, dirName + ".zip"); FileOutputStream dest = new FileOutputStream(zipFile); ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); BufferedInputStream origin = null; try { // out.setMethod(ZipOutputStream.DEFLATED); byte data[] = new byte[BUFFER_SIZE]; // get a list of files from current directory List<String> files = getAllFilesInDir(true, dir, includeHidden, null); for (String file : files) { if (extFilter != null && !extFilter.contains(getExtFromFileName(file))) { System.out.println("zipDirectory: " + file + " excluded by extension"); continue; } File ff = new File(dir, file); if (!ff.exists()) { System.out.println("zipDirectory: " + file + " no longer exists...."); continue; } if (includeDirInZip) { file = dir.getName() + "/" + file; } System.out.println("zipDirectory: adding " + file); FileInputStream fi = new FileInputStream(ff); ZipEntry entry = new ZipEntry(file); out.putNextEntry(entry); if (!ff.isDirectory()) { try { origin = new BufferedInputStream(fi, BUFFER_SIZE); int count; while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) { out.write(data, 0, count); } } finally { origin.close(); } } } } finally { if (out != null) { out.close(); } } return zipFile; } // relative filenames private static List<String> getAllFilesInDir(boolean traverseSubDirs, File dir, boolean includeHidden, String prefix) { List<String> files = new ArrayList<String>(); String fileNames[] = dir.list(); for (String fileName : fileNames) { File currentFile = new File(dir, fileName); if (currentFile.isHidden() && !includeHidden) { continue; } if (currentFile.isDirectory()) { if (traverseSubDirs) { String pfx; if (prefix == null || prefix.length() == 0) { pfx = fileName; } else { pfx = prefix + "/" + fileName; } files.addAll(getAllFilesInDir(true, currentFile, includeHidden, pfx)); } } else { String fullname = fileName; if (prefix != null && prefix.length() > 0) { fullname = prefix + "/" + fileName; } files.add(fullname); } } return files; } public static String getExtFromFileName(String name) { name = new File(name).getName(); return name.substring(name.lastIndexOf(".") + 1); } /** * Write the binary input stream to the given file. * * @param f * the file to be written * @param in * the contents * @throws IOException */ public static void writeFile(File f, InputStream in) throws IOException { byte[] buf = new byte[4096]; try { OutputStream out = new FileOutputStream(f); try { int len; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } } finally { out.close(); } } finally { in.close(); } } /** * useful for Runtime.exec() calls */ public static class StreamEater { StringBuilder sb = new StringBuilder(); final InputStream _in; boolean running = false; Thread eaterThread; @Override public String toString() { while (running) { try { Thread.sleep(10); } catch (InterruptedException e) { // nothing } } return (sb.length() > 0 ? sb.substring(0, sb.length() - 1) : ""); } public StreamEater(InputStream in) { this._in = in; this.sb = new StringBuilder(); running = true; eaterThread = new Thread(new Runnable() { @Override public void run() { InputStreamReader rdr = new InputStreamReader(_in); try { int i; while ((i = rdr.read()) != -1) { sb.append((char) i); } rdr.close(); } catch (IOException e) { e.printStackTrace(); } running = false; } }); eaterThread.start(); } public Thread getEaterThread() { return eaterThread; } } /** * Unzips a file in the supplied target directory, or in the current working director * * @param zipfile * the file to unzip, cannot be null * @param targetDir * the directory to unzip the file in. Will be created if it does not exist. if null * will use the current working directory * @return * @throws IOException * @throws ZipException */ @SuppressWarnings("unchecked") public static void unzipFile(File zipfile, File targetDir) throws IOException, ZipException { if (targetDir == null) { targetDir = new File(System.getProperty("user.dir")); } if (!targetDir.exists()) { targetDir.mkdirs(); } // unzip in the temp dir ZipFile zfile = new ZipFile(zipfile); for (Enumeration<ZipEntry> e = (Enumeration<ZipEntry>) zfile.entries(); e.hasMoreElements();) { ZipEntry entry = e.nextElement(); File target = new File(targetDir.getAbsolutePath() + File.separator + entry.getName()); if (entry.isDirectory()) { target.mkdirs(); } else { FileUtils.writeFile(target, zfile.getInputStream(entry)); target.setExecutable(true); } } } }